feat(sql): Odbc provider — ANSI SQL:2008 profile over System.Data.Odbc (lmxopcua#753)
SqlProvider.Odbc is now constructed; every enum member is a working backend. - OdbcDialect: System.Data.Odbc 10.0.14 (no vulnerable packages), ANSI double- quote quoting, positional `?` markers, INFORMATION_SCHEMA catalog (system schemas of the common backends excluded by name — the live gate caught psqlODBC listing pg_catalog), FETCH FIRST n ROWS ONLY limits, a conservative 1000-parameter chunk. Positional binding is safe because every caller adds parameters in marker order (planner IN list, catalog schema-then-table). - Named queries on a positional provider are checked by `?` count against the declared params (literals/comments ignored), since there is no name to match. - Supported backends are those whose SQL accepts that profile (PostgreSQL via psqlODBC proven, Snowflake-like); SQL Server/MySQL/DB2 should use their native provider. The host needs the ODBC driver manager + vendor driver. - Reserved-provider controls in factory/probe/browser tests now use an undeclared ordinal (99); the AdminUI form offers Odbc and a test pins the dropdown theory to SqlDialects.Supported. - Tests: OdbcDialectTests (unit, any host). OdbcReadTests + Docker/odbc/ run-odbc-gate.sh run the built suite in a throwaway Linux SDK container with unixODBC + psqlODBC against a throwaway seeded PostgreSQL: KV IN-list positional binding, where-pair, topByTimestamp via FETCH FIRST, a two- parameter named query, and the browse walk. macOS has no vendor ODBC driver, so there the suite skips. (lmxopcua#753) Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -145,8 +145,9 @@ public sealed class SqlDriverFactoryTests
|
||||
// ---- provider gate ----
|
||||
|
||||
/// <summary>
|
||||
/// The falsifiability control for the widened gate: <see cref="SqlProvider.Odbc"/> is still a
|
||||
/// reserved name and must fail loudly at construction, naming itself. Without this leg, a gate that
|
||||
/// The falsifiability control for the widened gate: every declared member now constructs, so an
|
||||
/// out-of-range ordinal (a config authored against a future enum) stands in for "a provider this build
|
||||
/// does not carry" — it must fail loudly at construction, naming itself. Without this leg, a gate that
|
||||
/// had quietly become "construct anything" would pass every test below.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
@@ -157,9 +158,9 @@ public sealed class SqlDriverFactoryTests
|
||||
SqlConnectionStringResolver.EnvironmentVariableFor(name), "Host=h;Database=db");
|
||||
|
||||
var ex = Should.Throw<InvalidOperationException>(() => SqlDriverFactoryExtensions.CreateInstance(
|
||||
"d1", $$"""{"provider":"Odbc","connectionStringRef":"{{name}}"}""", null));
|
||||
"d1", $$"""{"provider":99,"connectionStringRef":"{{name}}"}""", null));
|
||||
|
||||
ex.Message.ShouldContain("Odbc");
|
||||
ex.Message.ShouldContain("99");
|
||||
ex.Message.ShouldContain("not available in this build");
|
||||
}
|
||||
|
||||
@@ -175,9 +176,10 @@ public sealed class SqlDriverFactoryTests
|
||||
SqlConnectionStringResolver.EnvironmentVariableFor(name), "Host=h;Database=db");
|
||||
|
||||
var ex = Should.Throw<InvalidOperationException>(() => SqlDriverFactoryExtensions.CreateInstance(
|
||||
"d1", $$"""{"provider":"Odbc","connectionStringRef":"{{name}}"}""", null));
|
||||
"d1", $$"""{"provider":99,"connectionStringRef":"{{name}}"}""", null));
|
||||
|
||||
ex.Message.ShouldContain(nameof(SqlProvider.SqlServer));
|
||||
ex.Message.ShouldContain(nameof(SqlProvider.Odbc));
|
||||
ex.Message.ShouldContain(nameof(SqlProvider.Postgres));
|
||||
ex.Message.ShouldContain(nameof(SqlProvider.Oracle));
|
||||
ex.Message.ShouldContain(nameof(SqlProvider.MySql));
|
||||
@@ -194,6 +196,7 @@ public sealed class SqlDriverFactoryTests
|
||||
[InlineData("Postgres", typeof(PostgresDialect))]
|
||||
[InlineData("Oracle", typeof(OracleDialect))]
|
||||
[InlineData("MySql", typeof(MySqlDialect))]
|
||||
[InlineData("Odbc", typeof(OdbcDialect))]
|
||||
public void ParseBinding_aSupportedProvider_bindsThatProvidersDialect(string provider, Type expected)
|
||||
{
|
||||
var name = NewRef();
|
||||
@@ -215,6 +218,7 @@ public sealed class SqlDriverFactoryTests
|
||||
[InlineData("Postgres")]
|
||||
[InlineData("Oracle")]
|
||||
[InlineData("MySql")]
|
||||
[InlineData("Odbc")]
|
||||
public async Task CreateInstance_aNewlySupportedProvider_constructsPastTheProviderGate(string provider)
|
||||
{
|
||||
var name = NewRef();
|
||||
|
||||
Reference in New Issue
Block a user