fix(sql): chunk the key-value IN list at the dialect's bind ceiling (Driver.Sql-002, Driver.Sql.Dialect-002)

ISqlDialect gains MaxBindParameters — the per-command ceiling the seam was
missing while modelling everything else that differs per backend: 2098 usable
of SQL Server's 2100 (sp_executesql spends two), 1000 for Oracle's IN-list
limit (ORA-01795, tighter than its 64k bind count), 65535 for the Postgres
Bind message.

SqlGroupPlanner.AddKeyValuePlans now emits several plans for one group when
its distinct keys exceed that ceiling, instead of one statement the provider
refuses after the connection opened — which left every tag in an EAV group
publishing BadCommunicationError on every poll, permanently, and reported the
database endpoint as down. Chunks share the group key; members follow their
own key so a shared keyValue never splits. SqlPollReader is unchanged: it runs
one command per plan and slices each plan's own Members back, proven by a
fixture read at a ceiling of 1.
This commit is contained in:
Joseph Doherty
2026-08-10 08:02:44 -04:00
parent 74594e6083
commit 886ce1b87c
14 changed files with 509 additions and 16 deletions
@@ -27,6 +27,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Sql;
/// <param name="GroupKey">
/// The opaque equality key that decided this grouping — a value tuple, so two plans compare equal exactly
/// when they would have folded together. Exposed for diagnostics and plan caching, never for parsing.
/// <para><b>Not unique across plans.</b> A key-value group holding more distinct keys than the dialect's
/// <see cref="ISqlDialect.MaxBindParameters"/> is emitted as several plans carrying <em>this same key</em>
/// — the chunking is an execution detail of one group, not a regrouping.</para>
/// </param>
/// <param name="SqlText">The single parameterized statement to execute for this group.</param>
/// <param name="ParameterNames">