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:
@@ -353,4 +353,27 @@ public sealed class SqlTagConfigModelTests
|
||||
def.RowSelectorColumn.ShouldBeNull();
|
||||
def.RowSelectorValue.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ---- lmxopcua#781 item 2: the authored write block ------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void A_write_block_survives_load_save_and_parses_to_the_same_spec()
|
||||
{
|
||||
const string blob =
|
||||
"""{"model":"KeyValue","table":"t","keyColumn":"k","keyValue":"a","valueColumn":"v","timestampColumn":"ts","writable":true,"write":{"mode":"Upsert","stampTimestamp":true}}""";
|
||||
|
||||
var m = SqlTagConfigModel.FromJson(blob);
|
||||
m.Validate().ShouldBeNull();
|
||||
SqlEquipmentTagParser.TryParse(m.ToJson(), "raw/path", out var def).ShouldBeTrue();
|
||||
def.Write.ShouldBe(new SqlWriteSpec(SqlWriteMode.Upsert, StampTimestamp: true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_malformed_write_block_fails_validation_with_the_runtime_parsers_reason()
|
||||
{
|
||||
var m = SqlTagConfigModel.FromJson(
|
||||
"""{"model":"KeyValue","table":"t","keyColumn":"k","keyValue":"a","valueColumn":"v","writable":true,"write":{"mode":"Procedure"}}""");
|
||||
|
||||
m.Validate().ShouldNotBeNull().ShouldContain("needs 'write.procedure'");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user