feat(sql): MySql provider over MySqlConnector, one shared provider map (lmxopcua#753)
SqlProvider.MySql is now a constructed backend instead of a reserved name. - MySqlDialect (Driver.Sql.Dialect): MySqlConnector 2.6.2 (MIT, fully managed, no vulnerable packages), backtick quoting with MySQL's 64-char / no-trailing- space rules, information_schema catalog SQL with the database as the schema (system databases excluded, aliases pinned), trailing LIMIT, a type map that also reads the reader-side spellings (INT UNSIGNED -> UInt32, BOOL -> Boolean). - SqlDialects: the one provider -> dialect map. The factory, the Test Connect probe and the browse picker each carried their own switch (and drifted once); they now all read this, and their refusals list the supported/reserved sets from it. - AdminUI driver form offers MySql. - Tests: MySqlDialectTests (golden quoting/catalog/type map/planner SQL); the provider-gate tests in factory, probe, browser and form now treat MySql as supported and Odbc as the reserved control. MySqlReadTests + Docker/mysql (throwaway mysql:8.4, SELECT-only login): liveness, KV/wide-row/topByTimestamp, INT keys vs authored text, case folding against case-sensitive table names, catalog rejection, browse walk, factory+probe, named query with a bound parameter, and a writing stored function whose effect the rolled-back transaction discards. (lmxopcua#753) Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+21
-10
@@ -15,11 +15,21 @@ An ordinary SQL database — typically a MES / LIMS / historian staging table th
|
||||
already writes to. There is no protocol here and nothing to "connect" to a machine: the driver runs
|
||||
`SELECT`s on a timer and turns cells into tag values.
|
||||
|
||||
**Three providers construct**: `SqlServer` (`Microsoft.Data.SqlClient`), `Postgres`
|
||||
(`Npgsql`) and `Oracle` (ODP.NET managed — **Oracle 12.2 or later**, the floor the
|
||||
`FETCH FIRST` paging needs). `SqlProvider` also declares `MySql` and `Odbc`, but those remain
|
||||
**reserved names, not working backends** — authoring one fails the driver rather than silently
|
||||
falling back. → #753
|
||||
**Four providers construct**: `SqlServer` (`Microsoft.Data.SqlClient`), `Postgres`
|
||||
(`Npgsql`), `Oracle` (ODP.NET managed — **Oracle 12.2 or later**, the floor the
|
||||
`FETCH FIRST` paging needs) and `MySql` (`MySqlConnector`, MIT, fully managed — MySQL 8 /
|
||||
MariaDB). `SqlProvider` also declares `Odbc`, which remains a **reserved name, not a working
|
||||
backend** — authoring it fails the driver rather than silently falling back. The provider → dialect
|
||||
map is one shared table (`SqlDialects`) that the factory, the Test Connect probe and the browse
|
||||
picker all read, so the three can no longer disagree about what a build supports.
|
||||
|
||||
MySQL specifics: a MySQL **database is the schema** (`schema.table` = `database.table`; the default
|
||||
schema is the connection's `Database`), identifiers are backtick-quoted (64-character ceiling, no
|
||||
trailing space), and table names are case-sensitive on Linux — the catalog gate's spelling
|
||||
substitution is what lets `PLANT.TAG_VALUES` bind to `plant.tag_values`. `TINYINT(1)` reads as a
|
||||
boolean under MySqlConnector's default `TreatTinyAsBoolean`; `BIT(n)`, `TIME`, `JSON` and binary
|
||||
types infer as `String`. Live gate: `Docker/mysql/docker-compose.yml` + `MySqlReadTests`
|
||||
(`SQL_MYSQL_ENDPOINT` / `SQL_MYSQL_PASSWORD`).
|
||||
|
||||
Two provider-specific things are worth knowing before you author against them:
|
||||
|
||||
@@ -185,7 +195,7 @@ existed in the parser — nothing reads it.)
|
||||
|
||||
| Key | Default | Notes |
|
||||
|---|---|---|
|
||||
| `provider` | `SqlServer` | `SqlServer`, `Postgres` or `Oracle`. `MySql` / `Odbc` are reserved and rejected. |
|
||||
| `provider` | `SqlServer` | `SqlServer`, `Postgres`, `Oracle` or `MySql`. `Odbc` is reserved and rejected. |
|
||||
| `connectionStringRef` | — | **A name, never a connection string.** See below. |
|
||||
| `defaultPollInterval` | `00:00:05` | Applied to any group without its own interval. |
|
||||
| `operationTimeout` | `00:00:15` | Client-side wall-clock deadline; a breach surfaces `BadTimeout`. |
|
||||
@@ -243,7 +253,8 @@ The two classes of authored string are handled differently, and deliberately so:
|
||||
- **Identifier-bearing** fields (`table`, `keyColumn`, `valueColumn`, `timestampColumn`,
|
||||
`columnName`, `whereColumn`, `topByTimestamp`) cannot be parameterised in SQL. They are validated
|
||||
against the live catalog — **`INFORMATION_SCHEMA`** on SQL Server and Postgres, the **`ALL_*`**
|
||||
views (`ALL_USERS` / `ALL_TABLES` ∪ `ALL_VIEWS` / `ALL_TAB_COLUMNS`) on Oracle — and
|
||||
views (`ALL_USERS` / `ALL_TABLES` ∪ `ALL_VIEWS` / `ALL_TAB_COLUMNS`) on Oracle (MySQL also uses
|
||||
`information_schema`) — and
|
||||
dialect-quoted before they reach a command.
|
||||
|
||||
The parser deliberately does **not** sanitise either kind — sanitising a value would corrupt
|
||||
@@ -264,9 +275,9 @@ can commit columns as tags. A committed leaf carries **schema, table and column*
|
||||
cannot address a SQL tag, and an earlier version that emitted only the column produced tags whose
|
||||
typed editor opened empty.
|
||||
|
||||
Browse works against all three constructed providers; the catalog query is the dialect's own
|
||||
(`INFORMATION_SCHEMA` on SQL Server and Postgres — with `pg_catalog` / `information_schema`
|
||||
themselves excluded — and the `ALL_*` views on Oracle).
|
||||
Browse works against every constructed provider; the catalog query is the dialect's own
|
||||
(`INFORMATION_SCHEMA` on SQL Server, Postgres and MySQL — with `pg_catalog` / `information_schema`
|
||||
and MySQL's `mysql` / `performance_schema` / `sys` excluded — and the `ALL_*` views on Oracle).
|
||||
|
||||
### The committed row selector (Gitea #552)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user