Sql driver #753 follow-ups: ODBC scope, write scope, dual writable flag, deploy-time query validation #781
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-ups from lmxopcua#753 (Sql driver: named queries, MySql, Odbc, opt-in writes — master
2331f7ed). Each needs a decision before more code:?params,INFORMATION_SCHEMA,FETCH FIRST) — fits Postgres/Snowflake-like sources only; SQL Server/MySQL/DB2 should use native providers. Options: (a) keep narrow + document, (b) per-backend dialect variants, (c) read the catalog via ODBCSQLTables/SQLColumns. Every Odbc host (and AdminUI hosts, for browse) needs a driver manager + vendor driver installed — needs an ops note.UPDATE <table> SET <col>=@v WHERE <key>=@kon exactly one existing row. The design's authoredwriteblock (Insert / Upsert / stored procedure) is not built. Also undecided: stamp the timestamp column on write?WriteIdempotentis false (no retries). Status mapping:BadNoDataon 0 rows,BadCommunicationErroron permission denied."writable": trueboth exist; no deploy-time warning when they disagree./hosts), not at deploy validation.Items 3 + 5 done on master
241318e7:DraftValidatornow rejects a SqlQuerytag naming a query that isn't in the driver config'squeriesmap (SqlQueryNotDeclared, always blocking; uses the driver's ownSqlEquipmentTagParserover the same merged Driver/Device config the driver binds; ordinal key match). Configuration now referencesDriver.Sql.Contracts(no cycle).TagConfigValidationMode.Error) when Tag.AccessLevel=ReadWrite and the blob'swritabledisagree, either direction; logged and appended to the accepted deploy message.Evidence (rebased, Release 0 warnings): Configuration 214, ControlPlane 321, AdminUI 1962, OpcUaServer.IntegrationTests 132. 11 + 2 new tests; disabling the new error / warning append turns 3 and 1 tests red, positive controls stay quiet.
No AdminUI authoring-time check: the tag editor validates one blob without the driver config.
Still open: items 1 (ODBC backend scope), 2 (write scope), 4 (SELECT-only login docs), 6 (Oracle suite run) — decisions/live.
Owner decision 2026-11-09: item 1 ODBC: (a) keep narrow + document (ANSI SQL:2008 only, native providers for SQL Server/MySQL/DB2, ops note on driver manager + vendor driver on every Odbc/AdminUI host). Item 2 writes: build the full design write block (Insert / Upsert / stored procedure, plus timestamp-stamp option); keep BadNoData / BadCommunicationError mapping and WriteIdempotent=false. Item 4: document the SELECT-only login requirement. Item 6 (Oracle suite) remains live work.
Items 1, 2, 4 and 6 done on master
f5c30ed0(rebased onto2da58d03). With items 3 and 5 (241318e7), that covers the whole issue, so I'm closing it.Item 2: the
writeblock. The driver builds every statement itself; there is no authored SQL, every identifier passes the catalog gate, and every value is a bound parameter.mode:Updateis the default and is unchanged — a writable tag with nowriteblock produces byte-identical SQL to before, and 0 rows is still BadNoData.Insertwrites key + value (+ stamp). On a WideRowtopByTimestamptag it appends a row.Upsertis dialect-specific: SQL ServerMERGE … WITH (HOLDLOCK), PostgreSQLINSERT … ON CONFLICT (key) DO UPDATE, MySQLON DUPLICATE KEY UPDATE(1 or 2 rows count as success), OracleMERGE … USING (SELECT 1 FROM dual).Procedurecalls a named stored procedure. The catalog gate resolves the name againstINFORMATION_SCHEMA.ROUTINES/ALL_PROCEDURESand substitutes the catalog's spelling.valueParam/keyParam/timestampParambind by name; MySQL binds positionally in the order value, key, timestamp. Works on Query tags too.stampTimestamp: truesets the tag'stimestampColumn(ortopByTimestamp) to the write time in UTC.SqlWriteSupporttable read by both the driver and the deploy gate.WriteIdempotentstays false.DraftValidatorraises blocking SqlWriteBlockInvalid with the parser's own message, plus SqlWriteModeUnsupported when the mode doesn't fit the mergedprovider(Upsert or Procedure on Odbc). The AdminUI editor shows the same message.Items 1 and 4 (docs/drivers/Sql.md).
Evidence (Release, 0 warnings,
AKKA_TEST_TIMEFACTOR=3):SqlWriteModesLiveTests, 7 tests × SQL Server, PostgreSQL, MySQL and Oracle: Update+stamp, absent key → BadNoData, Insert then duplicate → BadEntryExists, NULL into a NOT NULL column → BadOutOfRange, Upsert insert-then-update-then-same-value, procedure call, unknown procedure → gate-rejected. Rows checked directly on each server.OracleReadTests6/6 passed (plus 7 Oracle write tests).run-odbc-gate.sh, unixODBC + psqlODBC): 4/4, including a new Odbc write leg covering positional Update+stamp, Insert, SQLSTATE 23505 → BadEntryExists, and Upsert → BadNotSupported. The script also gained a startup sleep, because it hit the postgres post-initdb restart once.SqlWriteModeTests, 13 inDraftValidatorSqlWriteBlockTests, 2 AdminUI round-trip tests.Fixture changes. The seeds gained
write_targets(PK) andset_target(p_value, p_key, p_ts)→proc_calls. Grants are scoped: INSERT/UPDATE onwrite_targetsand EXECUTE on the procedure only. PG and Oracle were re-seeded in place. MySQL was brought up from~/otopcua-sql-mysql(/optisn't writable for my user), run, then torn down withdown -v.