Two disposable docker fixtures (postgres:17-alpine on 15432, gvenzl/oracle-free:
23-slim on 11521) shaped like the existing mssql one — project label, distinct
container name, healthcheck, one-shot seed service — plus a live suite per
provider, gated on SQL_PG_* / SQL_ORACLE_* and skipping clean offline.
Each suite covers what a DIALECT can get wrong, rather than re-running the
provider-agnostic read semantics already pinned against SQL Server and SQLite:
liveness + catalog load, a KeyValue read, the provider's own row-limit syntax via
topByTimestamp (LIMIT 1 / FETCH FIRST 1 ROWS ONLY, where T-SQL emits TOP 1),
identifier case-folding through the design §8.1 catalog gate, and the AdminUI
browse walk driven through SqlDriverBrowser.
Two seed decisions are load-bearing rather than scaffolding:
- The Postgres schema is owned by a role the suite is NOT; the connecting login
holds USAGE + SELECT only. That is the exact provisioning PostgresDialect's
ListSchemasSql exists for (information_schema.schemata lists only schemas the
role OWNS, so a granted-not-owning poller would see none and every tag would be
rejected). A fixture that owned its own schema would pass either way.
- Every identifier in both seeds is created UNQUOTED, so the catalogs hold
lower-case (Postgres) and UPPER-case (Oracle) spellings. The case-folding legs
author the opposite case and pin SqlCatalog.TryMatch's REAL contract — exact
ordinal first, then a UNIQUE case-insensitive match whose catalog spelling is
substituted — which on both providers is what makes mixed-case authoring work
at all, since both dialects emit case-sensitive double-quoted identifiers.
Connection strings are composed through dialect.Factory.CreateConnectionString-
Builder() and the abstract DbConnectionStringBuilder indexer, so the csproj keeps
its no-direct-provider-reference discipline; every key used was verified accepted
through that indexer. The csproj gains only Driver.Sql.Browser, for the browse leg.
Live-run evidence (local OrbStack, both images arm64): Postgres 5/5 green;
Oracle 6/6 green after the ORA-01745 dialect fix this suite found. Offline the
whole project is 33 skipped / 0 failed.
Adds the frozen-peer / BadTimeout gate for the Sql driver — the single
highest-value integration test. Mirrors the S7 R2-01 blackhole gate:
docker pause a DEDICATED mssql mid-poll, assert the next read surfaces
BadTimeout within the client-side operationTimeout (≈3s) and well below
the server-side CommandTimeout backstop (30s), the driver degrades
(Degraded + host Stopped), and docker unpause recovers to Healthy/Running.
- Docker/docker-compose.yml: disposable `otopcua-sql-blackhole` mssql on
:14333 (never the shared :14330 ConfigDb server) + one-shot seed.
- Docker/seed.sql: the two sample tables.
- SqlBlackholeTimeoutTests.cs: env-gated (SQL_BLACKHOLE_ENDPOINT); pauses
ONLY the hard-coded container name; skips loudly if the endpoint is the
shared port 14330; bounds its own wait so a wedged impl fails, not hangs.
Offline: clean skip (no socket, no docker shell-out).
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW