8c6d6aff6a
Moves the database-independent logic of the SQL Server historian backend into a new shared library for the upcoming QuestDB backend (#666): status codes and bits, value/column mapping, StoredSample + quality clamp, the AtTime and aggregate evaluators, BucketPlan, LiveWritePreparer and HistoryUpdatePlanner (both with a resolutionTicks parameter, default 1), tag-name rules, the bootstrap failure cache, health counters, alarm event ids, the event continuation and severity clamp. The SQL Server backend now calls it; its T-SQL, TVP builders, connection, bootstrapper and catalog stay. Tests: StatusCodeOracleTests, ValueWriterPrepareTests, the column half of SampleRowMapperTests and ProcessedReadTests.Every_enum_member_is_either_served_or_refused move to the new Sql.Common.Tests under the same names. SqlServer integration tests get mechanical renames only. Test-name set across the three projects is identical to the baseline (160 tests). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
47 lines
2.2 KiB
C#
47 lines
2.2 KiB
C#
using System.Text.RegularExpressions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer;
|
|
|
|
/// <summary>
|
|
/// Options for the SQL Server historian backend (the <c>ServerHistorian:SqlServer</c> config block).
|
|
/// </summary>
|
|
public sealed partial class SqlServerHistorianOptions
|
|
{
|
|
/// <summary>Required. ADO.NET connection string for the historian database (never the ConfigDb).</summary>
|
|
public string ConnectionString { get; set; } = "";
|
|
|
|
/// <summary>SQL schema holding the historian objects. Spliced into DDL/DML, so it is regex-validated.</summary>
|
|
public string Schema { get; set; } = "hist";
|
|
|
|
/// <summary>Per-command timeout. Must be positive.</summary>
|
|
public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
|
|
|
/// <summary>
|
|
/// <c>true</c> creates the schema and applies missing scripts on first use; <c>false</c> only verifies
|
|
/// that schema version 1 exists and fails every historian call with a clear error otherwise.
|
|
/// </summary>
|
|
public bool AutoCreateSchema { get; set; } = true;
|
|
|
|
[GeneratedRegex("^[A-Za-z_][A-Za-z0-9_]{0,127}$")]
|
|
private static partial Regex IdentifierRegex();
|
|
|
|
/// <summary>True when <paramref name="schema"/> is a safe SQL identifier.</summary>
|
|
/// <param name="schema">Candidate schema name.</param>
|
|
/// <returns>Whether it matches <c>^[A-Za-z_][A-Za-z0-9_]{0,127}$</c>.</returns>
|
|
public static bool IsValidSchemaName(string? schema) => schema is not null && IdentifierRegex().IsMatch(schema);
|
|
|
|
/// <summary>Validates the options without touching SQL.</summary>
|
|
/// <returns>One message per problem; empty when valid.</returns>
|
|
public IReadOnlyList<string> Validate()
|
|
{
|
|
var errors = new List<string>();
|
|
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;
|
|
}
|
|
}
|