feat(sql): authored write block — Update/Insert/Upsert/Procedure + timestamp stamp; ODBC scope + login docs (lmxopcua#781)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
Item 2: a tag's optional `write` block picks the statement the driver builds from its own catalog-validated identifiers (no authored SQL; values always bound): - Update (default, unchanged back-compat UPDATE ... WHERE key; 0 rows = BadNoData) - Insert (key+value[+stamp]; append for a WideRow topByTimestamp tag) - Upsert: SQL Server MERGE WITH (HOLDLOCK), PostgreSQL INSERT ... ON CONFLICT, MySQL ON DUPLICATE KEY UPDATE (1-or-2 rows), Oracle MERGE from DUAL - Procedure: named stored procedure resolved against the routine catalog by the catalog gate (INFORMATION_SCHEMA.ROUTINES / ALL_PROCEDURES), value/key/timestamp params by name (MySQL positional) - stampTimestamp sets the tag's timestamp column to the write time (UTC) Odbc serves Update/Insert only (shared SqlWriteSupport table). Constraint failures map to BadEntryExists (unique) / BadOutOfRange (other integrity, data exception); everything else, permission denied included, stays BadCommunicationError. WriteIdempotent stays false. The parser refuses malformed blocks (strict keys/enums/param charset); DraftValidator emits blocking SqlWriteBlockInvalid / SqlWriteModeUnsupported from the driver's own parse; the AdminUI editor surfaces the same message. Items 1 + 4 (docs/drivers/Sql.md): ODBC stays the ANSI SQL:2008 profile (native providers for SQL Server/MySQL, DB2 unsupported) with an ops note on driver manager + vendor driver on every Odbc and AdminUI host; read-only drivers require a SELECT-only login, writable drivers a login limited to their write targets. Tests: SqlWriteModeTests (parse, every dialect's SQL, gate, status map, SQLite end-to-end), DraftValidatorSqlWriteBlockTests, AdminUI round-trip; live SqlWriteModesLiveTests on SQL Server, PostgreSQL, MySQL, Oracle (seeds gain write_targets + set_target) and an Odbc write leg. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+28
@@ -94,8 +94,36 @@ INSERT INTO sqlpoll.int_tag_values (tag_id, num_value, sample_ts)
|
||||
VALUES (101, 77.5, TIMESTAMP '2026-07-24 10:00:00'),
|
||||
(102, 12.0, TIMESTAMP '2026-07-24 10:00:01');
|
||||
|
||||
-- Write-mode targets (lmxopcua#781, SqlWriteModesLiveTests): a primary-keyed setpoint table — the unique key is
|
||||
-- what INSERT … ON CONFLICT (tag_name) needs — and a SECURITY DEFINER procedure that journals its arguments, so the
|
||||
-- login needs only EXECUTE on it. p_ts is timestamptz because a procedure argument resolves by IMPLICIT cast and the
|
||||
-- driver binds the write time as timestamptz.
|
||||
CREATE TABLE sqlpoll.write_targets (
|
||||
tag_name varchar(128) NOT NULL PRIMARY KEY,
|
||||
sp_value double precision NOT NULL,
|
||||
stamp_ts timestamp(3) NULL
|
||||
);
|
||||
|
||||
INSERT INTO sqlpoll.write_targets (tag_name, sp_value, stamp_ts) VALUES ('Present', 1.0, NULL);
|
||||
|
||||
CREATE TABLE sqlpoll.proc_calls (
|
||||
tag_name varchar(128) NOT NULL,
|
||||
sp_value double precision NULL,
|
||||
stamp_ts timestamptz NULL
|
||||
);
|
||||
|
||||
CREATE PROCEDURE sqlpoll.set_target(p_value double precision, p_key varchar, p_ts timestamptz)
|
||||
LANGUAGE sql SECURITY DEFINER
|
||||
AS $$ INSERT INTO sqlpoll.proc_calls (tag_name, sp_value, stamp_ts) VALUES (p_key, p_value, p_ts) $$;
|
||||
|
||||
RESET ROLE;
|
||||
|
||||
-- ── grants: USAGE + SELECT and nothing else ───────────────────────────────────────────────────
|
||||
GRANT USAGE ON SCHEMA sqlpoll TO otopcua;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA sqlpoll TO otopcua;
|
||||
|
||||
-- …except the write-mode targets (lmxopcua#781): INSERT + UPDATE on the one setpoint table, EXECUTE on the one
|
||||
-- procedure. Every other table stays SELECT-only, so a write pointed there is refused by the server's own grant.
|
||||
GRANT INSERT, UPDATE ON sqlpoll.write_targets TO otopcua;
|
||||
REVOKE EXECUTE ON PROCEDURE sqlpoll.set_target FROM PUBLIC;
|
||||
GRANT EXECUTE ON PROCEDURE sqlpoll.set_target TO otopcua;
|
||||
|
||||
Reference in New Issue
Block a user