using System.Data.Common;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts;
namespace ZB.MOM.WW.OtOpcUa.Driver.Sql;
///
/// The provider seam (design §2.2). ADO.NET's base types abstract
/// connections, commands, parameters and readers; they do not abstract the three things that differ
/// per backend — identifier quoting, the metadata-catalog SQL, and row-limit syntax.
/// Those live here, so no dialect assumption leaks into the reader, the poll planner, or the schema browser.
/// This interface is the driver's SQL-injection boundary. Every runtime value is
/// bound as a and never reaches a command text. Identifiers cannot be
/// parameterized in SQL, so the few that must appear as text are emitted through
/// . Any code path that builds SQL by concatenating an authored tag field
/// without going through is a defect (design §8.1).
/// Quoting is the backstop, not the only defence. Design §8.1's catalog gate is
/// implemented: resolves every authored table/column against the live
/// catalog at Initialize and replaces it with the catalog's own spelling, so an identifier
/// reaching on the poll path is a string this driver read back out of
/// / / — not
/// operator input. An identifier that resolves to nothing rejects its tag (which then publishes
/// BadNodeIdUnknown) rather than being quoted into a query against a nonexistent object.
/// Public because Driver.Sql.Browser consumes it — the catalog SQL is the browse
/// engine, so it is shared rather than duplicated. Implementations own their provider package;
/// no provider-specific type appears in this signature ( is the abstract
/// ).
///
public interface ISqlDialect
{
/// Which backend this dialect speaks. v1 constructs only.
SqlProvider Provider { get; }
///
/// The provider's factory singleton, used to create connections/commands/parameters. Deliberately the
/// abstract base type so consumers never bind to a concrete provider package.
///
DbProviderFactory Factory { get; }
///
/// Quotes one identifier part so it can be safely embedded in a command text, escaping the
/// dialect's own quote character. Rejects anything that cannot be a real catalog identifier rather
/// than emitting it.
/// One part only. A multi-part name such as dbo.TagValues must be split by the
/// caller and each part quoted separately; passing the dotted form yields a single (nonexistent)
/// identifier — safe, but not what the caller meant.
///
/// The bare, unquoted identifier — as returned by the catalog, not pre-quoted.
/// The quoted identifier, ready to concatenate into a command text.
/// The value cannot be a valid catalog identifier.
string QuoteIdentifier(string ident);
/// Cheapest statement that proves the connection is alive (SELECT 1; Oracle needs FROM DUAL).
string LivenessSql { get; }
///
/// The fragment that goes immediately after SELECT to limit a statement to one row —
/// T-SQL's "TOP 1 ". Empty for a dialect that spells the limit at the end of the statement (see
/// ).
/// Include the trailing space when non-empty: the planner concatenates
/// "SELECT " + SingleRowLimitPrefix + columns with no separator of its own, so an empty fragment
/// must leave the statement byte-identical to an unlimited one.
///
///
/// Why a prefix/suffix pair rather than one ApplyRowLimit(sql, n) method. The two
/// spellings sit at opposite ends of the statement — SQL Server writes
/// SELECT TOP 1 … ORDER BY … DESC, while SQLite/Postgres/MySQL write
/// SELECT … ORDER BY … DESC LIMIT 1. A single method taking the SELECT list could only serve the
/// prefix position; a single method taking the whole statement would move statement assembly out of the
/// planner and into every dialect. Two fragments keep the planner the sole author of statement shape
/// and let each dialect fill in the end it uses.
/// Why "single row" rather than a row count. The only limit v1 emits is the wide-row
/// topByTimestamp selector's newest-row pick. Modelling an arbitrary n would be untested
/// surface, and FETCH FIRST/ROWNUM dialects need more than a substituted number anyway.
/// Widen this to a method when a feature actually needs n > 1.
///
string SingleRowLimitPrefix { get; }
///
/// The fragment appended to the very end of a statement to limit it to one row —
/// " LIMIT 1" for SQLite/Postgres/MySQL. Empty for a dialect that limits after SELECT
/// (see ).
/// Include the leading space when non-empty, for the same reason the prefix carries a
/// trailing one: the planner appends it directly to the finished statement.
/// Exactly one of the two fragments is non-empty in every dialect modelled so far, but the planner
/// emits both unconditionally — a dialect needing both ends (or neither) is expressible without
/// touching the call site.
///
string SingleRowLimitSuffix { get; }
/// Catalog query listing schemas — the browser's RootAsync level. Takes no parameters.
string ListSchemasSql { get; }
///
/// Scalar query returning the schema an unqualified object name resolves to for the connecting
/// principal — T-SQL's SELECT SCHEMA_NAME(). Takes no parameters.
///
///
/// Needed by : a tag authored as TagValues rather than
/// dbo.TagValues has to be looked up in some schema, and guessing dbo would be a
/// silent lie on any estate that maps its service accounts to their own default schema. Asking the
/// server is the only answer that matches how the poll query itself will resolve the name.
/// It is a query rather than a name because the answer is per-connection, not per-dialect
/// — the same dialect resolves differently for two different logins.
///
string DefaultSchemaSql { get; }
/// Catalog query listing tables + views in one schema — the browser's schema-expand level. Bind @schema.
string ListTablesSql { get; }
/// Catalog query listing columns of one table — the browser's table-expand / attributes level. Bind @schema and @table.
string ListColumnsSql { get; }
///
/// Folds a catalog data-type family name (as INFORMATION_SCHEMA.COLUMNS.DATA_TYPE reports it —
/// e.g. nvarchar, never nvarchar(50)) onto a .
/// Must never throw: a browse over a table holding one exotic column must still render.
/// An unrecognised family falls back to .
///
/// The bare SQL type family name; matched case-insensitively.
/// The mapped driver data type, or when unrecognised.
DriverDataType MapColumnType(string sqlDataType);
}