using System.Text.RegularExpressions; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer; /// /// Options for the SQL Server historian backend (the ServerHistorian:SqlServer config block). /// public sealed partial class SqlServerHistorianOptions { /// Required. ADO.NET connection string for the historian database (never the ConfigDb). public string ConnectionString { get; set; } = ""; /// SQL schema holding the historian objects. Spliced into DDL/DML, so it is regex-validated. public string Schema { get; set; } = "hist"; /// Per-command timeout. Must be positive. public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromSeconds(30); /// /// true creates the schema and applies missing scripts on first use; false only verifies /// that schema version 1 exists and fails every historian call with a clear error otherwise. /// public bool AutoCreateSchema { get; set; } = true; [GeneratedRegex("^[A-Za-z_][A-Za-z0-9_]{0,127}$")] private static partial Regex IdentifierRegex(); /// True when is a safe SQL identifier. /// Candidate schema name. /// Whether it matches ^[A-Za-z_][A-Za-z0-9_]{0,127}$. public static bool IsValidSchemaName(string? schema) => schema is not null && IdentifierRegex().IsMatch(schema); /// Validates the options without touching SQL. /// One message per problem; empty when valid. public IReadOnlyList Validate() { var errors = new List(); if (string.IsNullOrWhiteSpace(ConnectionString)) errors.Add("ServerHistorian:SqlServer:ConnectionString is required."); if (!IsValidSchemaName(Schema)) errors.Add("ServerHistorian:SqlServer:Schema must be a SQL identifier matching ^[A-Za-z_][A-Za-z0-9_]{0,127}$."); if (CommandTimeout <= TimeSpan.Zero) errors.Add("ServerHistorian:SqlServer:CommandTimeout must be greater than zero."); return errors; } }