Skip to content

fix(sqlite,mysql): implement the non-generic Weasel.Core.ICommandBuilder - #424

Merged
jeremydmiller merged 1 commit into
masterfrom
fix/423-sqlite-mysql-icommandbuilder
Aug 3, 2026
Merged

fix(sqlite,mysql): implement the non-generic Weasel.Core.ICommandBuilder#424
jeremydmiller merged 1 commit into
masterfrom
fix/423-sqlite-mysql-icommandbuilder

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Fixes #423.

Weasel.Sqlite.CommandBuilder and Weasel.MySql.CommandBuilder derive from CommandBuilderBase<,,> but never declared the non-generic Weasel.Core.ICommandBuilder, so nothing targeting the neutral contract could be handed one.

That contract is what every Weasel.Storage closed-shape operation configures itself against:

void ConfigureCommand(ICommandBuilder builder, IStorageSession session);

so the practical effect was that no Weasel.Storage document or event operation could execute against SQLite at all — there was no way to construct the builder argument. Found while building Fisher on Weasel.Storage.

Postgresql, SqlServer and Oracle already carry the interface; Sqlite and MySql were the two left behind by #327. Only SQLite was blocking a consumer today, but MySql had the identical gap and would hit it the moment a Weasel.Storage consumer targeted it, so both are fixed here.

Missing members

Four on each provider — everything else the interface needs was already inherited from CommandBuilderBase:

Member Before
string TenantId { get; set; } absent
void AppendParameters(params object[]) absent
DbParameter AppendParameter(object) absent — the inherited overloads all return void, so none satisfied the interface
IGroupedParameterBuilder CreateGroupedParameterBuilder(char?) absent

Two deliberate choices

AppendParameter / AppendParameters are implemented explicitly, following Weasel.Oracle rather than Weasel.SqlServer. The base class already exposes void-returning AppendParameter overloads; a public member would hide them and silently change which overload existing call sites bind to. Explicit implementation adds the interface without touching the public surface, so this is additive for existing users.

StartNewCommand is not overridden. Weasel.SqlServer overrides it to a no-op, but the base is already a no-op ("multi-statement providers just keep appending"), which is correct for both of these — Microsoft.Data.Sqlite and MySqlConnector both execute several semicolon-separated statements from one command. An override would be noise implying a difference that does not exist.

Tests

A contract test per provider covering the interface itself plus each of the four members, so the set cannot drift again:

  • src/Weasel.Sqlite.Tests/CommandBuilderTests.csCommandBuilderNeutralContractTests
  • src/Weasel.MySql.Tests/CommandBuilderNeutralContractTests.cs

Both are pure type/behaviour tests needing no database server.

Verification

  • Full Weasel.slnx builds clean.
  • Weasel.Sqlite.Tests: 393 passed, 0 failed, on net9.0 and net10.0.
  • Weasel.MySql.Tests CommandBuilderNeutralContractTests: 6 passed (the rest of that suite needs a MySQL server and was not run).

Fisher currently carries a local FisherCommandBuilder shim and will delete it once this ships.

…der (weasel#423)

Weasel.Sqlite.CommandBuilder and Weasel.MySql.CommandBuilder derived from
CommandBuilderBase<,,> but never declared the non-generic
Weasel.Core.ICommandBuilder, so nothing targeting the neutral contract could be
handed one.

That contract is what every Weasel.Storage closed-shape operation configures
itself against:

    void ConfigureCommand(ICommandBuilder builder, IStorageSession session);

so the practical effect was that no Weasel.Storage document or event operation
could execute against SQLite at all -- there was no way to construct the builder
argument. Found while building Fisher, the SQLite event store, on
Weasel.Storage. Postgresql, SqlServer and Oracle already carry the interface;
Sqlite and MySql were the two outliers left behind by #327.

Four members were missing on each: TenantId, AppendParameters(params object[]),
a DbParameter-returning AppendParameter(object) (the inherited overloads all
return void, so none satisfied the interface), and
CreateGroupedParameterBuilder. Everything else the interface needs was already
inherited.

AppendParameter and AppendParameters are implemented explicitly, following
Weasel.Oracle rather than Weasel.SqlServer: the base class already exposes
void-returning AppendParameter overloads, and a public member here would hide
them and silently change which overload existing call sites bind to.

StartNewCommand is deliberately not overridden. The base is already a no-op,
which is correct for both providers, so an override would be pure noise that
implies a difference that does not exist.

Adds a contract test to each provider's suite so the set cannot drift again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DLKYzKRqfvk2vS88kfcvqc
@jeremydmiller
jeremydmiller merged commit fe850cb into master Aug 3, 2026
16 checks passed
@jeremydmiller
jeremydmiller deleted the fix/423-sqlite-mysql-icommandbuilder branch August 3, 2026 10:36
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.

Weasel.Sqlite.CommandBuilder does not implement the non-generic Weasel.Core.ICommandBuilder, blocking all Weasel.Storage operations on SQLite

1 participant