review(Driver.Sql): findings + fix the missing default-schema fallback, the rowSelector doc, and a dead package ref

Driver.Sql-001 (Medium): SqlCatalogLoader.ReadDefaultSchemaAsync documented a
fallback to the sole visible schema when the server reports no default schema and
did not implement it, so a login with no DEFAULT_SCHEMA (or a Postgres search_path
that resolves to nothing) rejected EVERY unqualified authored table at the catalog
gate — a whole-driver BadNodeIdUnknown outage against a healthy database. Fixed;
SqlCatalogDefaultSchemaTests written red first, with a control proving the
ambiguous multi-schema case still refuses to guess.

Driver.Sql-003 (Medium): docs/drivers/Sql.md spelled the WideRow row selector at
the top level, but the parser reads those keys only inside a nested rowSelector —
a tag authored from the doc is refused and reads BadNodeIdUnknown (the #552 class,
via the documentation). Also removed the documented-but-nonexistent pollIntervalMs
per-tag field.

Driver.Sql-007 (Low): dropped the unused Microsoft.Data.SqlClient PackageReference
left behind by the .Dialect extraction (#526).

Open: 002 (no IN-list chunking vs the 2100/1000 provider bind caps), 004, 005, 006,
008, 009, 010.
This commit is contained in:
Joseph Doherty
2026-08-10 06:58:08 -04:00
parent b990adae55
commit 69ad376f0d
5 changed files with 387 additions and 11 deletions
+16 -4
View File
@@ -56,13 +56,18 @@ One row per tag. The driver matches `keyColumn = keyValue` and reads `valueColum
The tag names its `columnName`; the row is picked either by a `whereColumn`/`whereValue` pair or by
newest-timestamp.
⚠️ **The selector keys live inside a nested `rowSelector` object**, not at the top level. A blob that
spells `whereColumn` / `whereValue` / `topByTimestamp` beside `columnName` carries *no* row selector
as far as `SqlEquipmentTagParser` is concerned: the tag is refused, the driver skips it, and it reads
`BadNodeIdUnknown` forever. The typed editor, the browse-commit mapper and the deploy gate all use
the nested form.
```json
{
"model": "WideRow",
"table": "dbo.LineStatus",
"columnName": "OvenTemp",
"whereColumn": "LineId",
"whereValue": "L1"
"rowSelector": { "whereColumn": "LineId", "whereValue": "L1" }
}
```
@@ -71,10 +76,13 @@ newest-timestamp.
"model": "WideRow",
"table": "dbo.Readings",
"columnName": "OvenTemp",
"topByTimestamp": "CapturedUtc"
"rowSelector": { "topByTimestamp": "CapturedUtc" }
}
```
`whereValue` may be authored as a JSON string, number or boolean (it is bound as a parameter either
way). `keyValue` on the `KeyValue` model must be a **string**.
### `Query` — deferred to P3
Named arbitrary-`SELECT`. **The v1 parser rejects it**, and that refusal is enforced end-to-end —
@@ -87,7 +95,11 @@ ignoring it later.
|---|---|
| `timestampColumn` | Source timestamp for the value. Absent ⇒ the driver stamps the poll time. |
| `type` | Explicit OPC UA type override. Absent ⇒ inferred from the result set's column metadata. |
| `pollIntervalMs` | Per-tag override of the instance's `defaultPollInterval`. |
There is **no per-tag poll-interval field**. The interval is the subscriber's, floored by
`PollGroupEngine.DefaultMinInterval`, falling back to the instance's `defaultPollInterval` when the
subscriber names none. (A `pollIntervalMs` per-tag override was documented here for a while and never
existed in the parser — nothing reads it.)
## Driver config