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 the required schema version (SqlServerSchemaBootstrapper.RequiredVersion, currently 2) exists and /// fails every historian call with a clear error otherwise. /// public bool AutoCreateSchema { get; set; } = true; /// /// Days of history to keep, measured on source time. null (default) keeps everything. When set, /// a driver node deletes older hist.Sample/hist.Event rows every . /// public int? RetentionDays { get; set; } /// How often the retention purge runs. Must be positive. public TimeSpan PurgeInterval { get; set; } = TimeSpan.FromHours(1); /// /// Rows per DELETE TOP(n) batch. Default 4000 stays under SQL Server's 5000-lock escalation /// threshold so a purge never escalates to a table lock. /// public int PurgeBatchSize { get; set; } = 4000; [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."); if (RetentionDays is < 1) errors.Add("ServerHistorian:SqlServer:RetentionDays must be at least 1 when set (omit it to keep history forever)."); if (PurgeInterval <= TimeSpan.Zero) errors.Add("ServerHistorian:SqlServer:PurgeInterval must be greater than zero."); if (PurgeBatchSize < 1) errors.Add("ServerHistorian:SqlServer:PurgeBatchSize must be at least 1."); return errors; } }