Skip to content

Fix master-table multi-tenancy on SqlServer (DB-agnostic tenant SQL); bump 6.4.3 (#3023)#3024

Merged
jeremydmiller merged 1 commit into
mainfrom
fix-mastertable-sqlserver-false-literal
Jun 4, 2026
Merged

Fix master-table multi-tenancy on SqlServer (DB-agnostic tenant SQL); bump 6.4.3 (#3023)#3024
jeremydmiller merged 1 commit into
mainfrom
fix-mastertable-sqlserver-false-literal

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #3023. Found during the persistence/EF-Core compliance survey.

The bug

UseMasterTableTenancy() was completely broken on SqlServer — EfCoreTests.MultiTenancy.multi_tenancy_with_master_table_approach_sqlserver failed 16/16 (incl. can_build_a_db_context_at_all) with SqlException: Invalid column name 'false' during multi-tenanted resource setup.

The shared tenant-registry queries in MessageDatabase<T> (Wolverine.RDBMS/MessageDatabase.Tenants.cs, used by both the Postgres and SqlServer stores) were written with PostgreSQL-only SQL:

  • where {disabled} = false / = true — SqlServer has no boolean literal, so it parses false/true as column names.
  • AddTenantRecordAsync: insert ... values (..., false) on conflict (...) do update ...ON CONFLICT is PostgreSQL-only (plus the same false).

(The shared-database and static-connection-strings SqlServer variants pass — this is specific to the master-table path. Postgres passes because it accepts all of the above.)

The fix

  • Boolean literals → a bool parameter (@disabled), which the driver maps to boolean/bit — matching the existing SetTenantDisabledAsync pattern.
  • ON CONFLICT upsert → the portable delete-then-insert already used by SeedDatabasesAsync.
  • Registered MasterTenantSource as IDynamicTenantSource<string> in the SqlServer provider — parity with the Postgres gap closed in Register MasterTenantSource as IDynamicTenantSource<string> in DI when UseMasterTableTenancy is configured #3016 (it had the same omission), so store-agnostic admin consumers can drive the lifecycle on SqlServer too.

Tests

  • SqlServerTests master_table_tenancy_dynamic_lifecycle: DI registration + a full lifecycle round-trip (add → refresh/read → disable → list-disabled → enable → re-add upsert → remove) — exercises all three fixed statements.
  • PostgresqlTests: mirrored Postgres lifecycle test (regression guard for the shared SQL change).

Verification (Postgres + SqlServer both up)

  • multi_tenancy_with_master_table_approach_sqlserver: 16/16 pass (was 16/16 fail).
  • multi_tenancy_with_master_table_approach_postgresql: 16/16 pass (no regression).
  • SqlServer lifecycle 2/2; Postgres master-table DI/lifecycle 4/4.
  • Full wolverine.slnx Release build clean (0/0).

Bumps to 6.4.3.

🤖 Generated with Claude Code

… bump 6.4.3 (GH-3023)

The shared tenant-registry queries in MessageDatabase<T> (used by both the Postgres and SqlServer
message stores) were written with PostgreSQL-only SQL, so UseMasterTableTenancy() failed entirely
on SqlServer with "Invalid column name 'false'" during multi-tenanted resource setup:

- `where {disabled} = false` / `= true` — SqlServer has no boolean literal, so it parses
  false/true as column names. Now a bool parameter (@disabled), which the driver maps to
  boolean/bit (matching the existing SetTenantDisabledAsync pattern).
- AddTenantRecordAsync used `... values (..., false) on conflict (...) do update ...` — ON CONFLICT
  is PostgreSQL-only. Now the portable delete-then-insert already used by SeedDatabasesAsync, with
  a bool parameter.

Also registers MasterTenantSource as IDynamicTenantSource<string> in the SqlServer provider —
parity with the PostgreSQL gap closed in GH-3016 — so store-agnostic admin consumers can drive the
dynamic-tenancy lifecycle on SqlServer too.

Tests: SqlServer dynamic-tenant lifecycle round-trip (add/read/disable/enable/re-add/remove) + DI
registration; mirrored Postgres lifecycle test (regression). Verified: master-table EfCore
multi-tenancy now 16/16 on SqlServer (was 16/16 fail) and still 16/16 on Postgres; full
wolverine.slnx Release build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit 70ccebd into main Jun 4, 2026
24 checks passed
This was referenced Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Master-table multi-tenancy is broken on SqlServer: tenant-table SQL uses PostgreSQL-only literals/upsert ("Invalid column name 'false'")

1 participant