Commit Graph

2 Commits

Author SHA1 Message Date
Joseph Doherty 429ead837f feat(sql): construct Postgres and Oracle providers; MySql/Odbc stay reserved
Widens the two provider gates so an authored `{"provider":"Postgres"}` or
`{"provider":"Oracle"}` binds that provider's own dialect instead of failing
"not available in this build":

- `SqlDriverFactoryExtensions.CreateDialect` gains a Postgres and an Oracle
  arm. There is deliberately no fallback arm — a default resolving to
  SqlServerDialect would construct happily and then emit T-SQL at a Postgres
  or Oracle server. The rejection message now names the three supported and
  the two still-reserved providers.
- `SqlDriverBrowser.DefaultDialectSelector` widens identically, so a provider
  the runtime constructs is also browsable; MySql/Odbc keep resolving to null
  and the caller keeps reporting them as unavailable.

Tests: the existing rejected-provider controls flip from Postgres to MySql
(factory) and Odbc (browser) so the reserved half stays falsifiable, and the
new legs assert the bound dialect TYPE rather than mere construction —
"no throw" alone would pass against a SqlServer fallback.

Doc-comment sweep (contract addition, one line each): the "v1 constructs
SqlServer only" / "reserved for P2" claims in SqlProvider, SqlDriverConfigDto,
ISqlDialect.Provider and SqlServerDialect are now false and were corrected in
the same change.
2026-08-05 06:51:48 -04:00
Joseph Doherty 3cc8069150 feat(sql): factory + connectionStringRef env resolution + string-enum guard
SqlDriverFactoryExtensions turns a deployed DriverConfig blob into a live
SqlDriver, and SqlConnectionStringResolver resolves the authored
connectionStringRef NAME from Sql__ConnectionStrings__<ref> so credentials
never ride in a config blob (design §8.2).

Three behaviours worth naming:

- String-enum guard. JsonOptions carries JsonStringEnumConverter +
  camelCase, so `provider` parses whether authored as a name or an ordinal
  and always round-trips as the NAME — the systemic AdminUI defect where a
  page serialised an enum numerically against a string-typed DTO.
- Config validation lands here because nothing below does it.
  operationTimeout must be STRICTLY greater than commandTimeout (design
  §8.3); the reader and the driver stay deliberately usable with the pair
  inverted so the frozen-database tests can prove the client-side bound
  fires. Non-positive timeouts / poll interval and maxConcurrentGroups < 1
  are rejected too.
- Credential hygiene. The resolved connection string reaches the provider
  and nothing else: messages name the ref, the environment variable, or
  SqlDriver.Endpoint's credential-free server/database rendering. Both a
  log-leak and an exception-leak test cover it.

DTO changes:
- Adds RawTags (List<RawTagEntry>), following the Modbus precedent — the
  deploy artifact delivers a driver's tags this way and the DTO had no way
  to receive them, so an authored Sql tag could never reach the driver.
- Deletes SqlProbeDto + the `probe` key. It had no consumer: SqlDriver was
  specified without a background probe loop, and deliberately so — its
  IHostConnectivityProbe state is a by-product of the Initialize liveness
  check and of every poll, i.e. a statement about traffic that actually
  happened rather than a synthetic ping. On-demand connectivity is Task
  10's SqlDriverProbe. Shipping a config key that silently does nothing is
  worse than not shipping it; UnmappedMemberHandling.Skip means a blob that
  still carries `probe` keeps parsing.

allowWrites stays inert (SqlDriver implements no write capability) but an
authored `true` now WARNS rather than passing silently — the flag cannot do
harm, an operator who believes writes are enabled can.

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
2026-07-24 15:06:58 -04:00