9675823cd6
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.
77 lines
3.6 KiB
SQL
77 lines
3.6 KiB
SQL
-- Seed for the Sql driver's Oracle live gate (OracleReadTests), applied once by the compose
|
|
-- `oracle-seed` one-shot AS THE APPLICATION USER (otopcua) against the OWNED `otopcua-sql-oracle`
|
|
-- container. Oracle equates a schema with a user, so everything below lands in the OTOPCUA schema.
|
|
--
|
|
-- The table shapes mirror the SQL Server fixture (SqlPollServerFixture) row for row — a present
|
|
-- value, a present row with a NULL cell, and a wide-row table whose NEWEST row is deliberately not
|
|
-- its first — so the same assertions can be made against both backends and a divergence between
|
|
-- the two provider paths shows up as a failing assertion rather than as two suites that quietly
|
|
-- test different things.
|
|
--
|
|
-- ⚠️ EVERY IDENTIFIER HERE IS UNQUOTED, AND THAT IS THE POINT. Oracle upper-cases an unquoted
|
|
-- identifier, so the catalog stores OTOPCUA / TAG_VALUES / TAG_NAME while an operator authoring a
|
|
-- tag in the AdminUI naturally types lower case. The suite's case-folding leg authors the lower-case
|
|
-- spelling and expects `SqlCatalogGate` to resolve it through `SqlCatalog.TryMatch` (exact-ordinal
|
|
-- first, then a UNIQUE case-insensitive match) and substitute the catalog's own UPPER-case spelling
|
|
-- before `OracleDialect.QuoteIdentifier` double-quotes it — which is load-bearing, because a quoted
|
|
-- "tag_values" and a quoted "TAG_VALUES" are DIFFERENT objects to Oracle. Quoting a name here would
|
|
-- make that leg meaningless.
|
|
|
|
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
|
SET FEEDBACK OFF
|
|
SET DEFINE OFF
|
|
|
|
-- Drop-and-recreate rather than merge-or-insert: the seed is an assertion contract, and a leftover
|
|
-- row from an older revision of this file would make a passing test meaningless. ORA-00942 (table
|
|
-- does not exist) is the expected outcome on a first run and is the only error swallowed.
|
|
BEGIN
|
|
EXECUTE IMMEDIATE 'DROP TABLE tag_values PURGE';
|
|
EXCEPTION
|
|
WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;
|
|
END;
|
|
/
|
|
|
|
BEGIN
|
|
EXECUTE IMMEDIATE 'DROP TABLE latest_status PURGE';
|
|
EXCEPTION
|
|
WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;
|
|
END;
|
|
/
|
|
|
|
-- Key-value (EAV) source: tag_name → num_value, with a source timestamp.
|
|
-- num_value is NUMBER, Oracle's one numeric family — which OracleDialect.MapColumnType folds to
|
|
-- Float64 (the documented under-specification; see that method's remarks).
|
|
CREATE TABLE tag_values (
|
|
tag_name VARCHAR2(128) NOT NULL,
|
|
num_value NUMBER,
|
|
sample_ts TIMESTAMP(3) NOT NULL
|
|
);
|
|
|
|
INSERT INTO tag_values (tag_name, num_value, sample_ts)
|
|
VALUES ('Line1.Speed', 42, TIMESTAMP '2026-07-24 10:00:00');
|
|
INSERT INTO tag_values (tag_name, num_value, sample_ts)
|
|
VALUES ('Line1.Temp', NULL, TIMESTAMP '2026-07-24 10:00:01');
|
|
INSERT INTO tag_values (tag_name, num_value, sample_ts)
|
|
VALUES ('Line1.Pressure', 3.5, TIMESTAMP '2026-07-24 10:00:02');
|
|
|
|
-- Wide-row source: one row per station, several value columns. Station 8 is inserted LAST and is
|
|
-- also the newest sample, while station 7 is first — so a topByTimestamp plan that lost its
|
|
-- ORDER BY … DESC (or its FETCH FIRST 1 ROWS ONLY) returns a different, wrong value rather than a
|
|
-- coincidentally right one.
|
|
CREATE TABLE latest_status (
|
|
station_id NUMBER(10) NOT NULL,
|
|
oven_temp NUMBER,
|
|
pressure NUMBER,
|
|
sample_ts TIMESTAMP(3) NOT NULL
|
|
);
|
|
|
|
INSERT INTO latest_status (station_id, oven_temp, pressure, sample_ts)
|
|
VALUES (7, 180.5, 1.2, TIMESTAMP '2026-07-24 10:00:00');
|
|
INSERT INTO latest_status (station_id, oven_temp, pressure, sample_ts)
|
|
VALUES (9, NULL, 1.6, TIMESTAMP '2026-07-24 10:00:01');
|
|
INSERT INTO latest_status (station_id, oven_temp, pressure, sample_ts)
|
|
VALUES (8, 210.25, 1.4, TIMESTAMP '2026-07-24 10:00:02');
|
|
|
|
COMMIT;
|
|
EXIT;
|