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:
@@ -284,7 +284,8 @@ public static class SqlDriverFactoryExtensions
|
||||
/// <para>The shape rules are <see cref="SqlNamedQueryRules.Describe"/> — the same call the AdminUI driver
|
||||
/// form makes — so the form refuses what this refuses. The one rule added here needs the dialect: every
|
||||
/// declared parameter's marker (<see cref="ISqlDialect.ParameterMarker"/>) must appear in the statement.
|
||||
/// A provider that binds positionally (<c>?</c>) is exempt, since there is no name to look for.</para>
|
||||
/// A provider that binds positionally (<c>?</c>) is checked by count instead, since there is no name to
|
||||
/// look for.</para>
|
||||
/// </summary>
|
||||
private static IReadOnlyDictionary<string, SqlNamedQuery> BuildQueries(
|
||||
SqlDriverConfigDto dto, string driverInstanceId, ISqlDialect? dialect)
|
||||
@@ -306,7 +307,7 @@ public static class SqlDriverFactoryExtensions
|
||||
if (dialect is not null)
|
||||
{
|
||||
var marker = dialect.ParameterMarker(bare);
|
||||
if (marker != "?" && !SqlReadOnlyStatement.UsesMarker(sql, marker))
|
||||
if (marker != SqlReadOnlyStatement.PositionalMarker && !SqlReadOnlyStatement.UsesMarker(sql, marker))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Sql driver config for '{driverInstanceId}': named query '{name}' declares parameter " +
|
||||
@@ -318,6 +319,18 @@ public static class SqlDriverFactoryExtensions
|
||||
values.Add(ToParameterValue(value));
|
||||
}
|
||||
|
||||
// A positional provider binds in authored order, so the only check available is the count — and it
|
||||
// matters: one '?' too few binds every later value to the wrong marker.
|
||||
if (dialect is not null
|
||||
&& dialect.ParameterMarker("p") == SqlReadOnlyStatement.PositionalMarker
|
||||
&& SqlReadOnlyStatement.CountPositionalMarkers(sql) != names.Count)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Sql driver config for '{driverInstanceId}': named query '{name}' declares {names.Count} " +
|
||||
$"parameter(s), but its statement has {SqlReadOnlyStatement.CountPositionalMarkers(sql)} " +
|
||||
$"positional '?' marker(s); {dialect.Provider} binds them in authored order, so the two must match.");
|
||||
}
|
||||
|
||||
queries[name] = new SqlNamedQuery(
|
||||
name, sql, names, values, query.MaxRows ?? SqlNamedQueryRules.DefaultMaxRows);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user