refactor(historian): extract Driver.Historian.Sql.Common from the SQL Server backend (no behaviour change) (1a)

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
This commit is contained in:
Joseph Doherty
2026-10-11 01:26:49 -04:00
parent cdea173bd1
commit 8c6d6aff6a
49 changed files with 1262 additions and 672 deletions
@@ -3,6 +3,7 @@ using Microsoft.Data.SqlClient;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common;
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.IntegrationTests;
@@ -55,11 +56,11 @@ public sealed class WriteTests(SqlHistorianFixture fx)
var rows = await RowsAsync(s, "tag.a");
rows.Count.ShouldBe(6);
for (var i = 0; i < 6; i++) rows[i].T.Ticks.ShouldBe(vals[i].TimestampUtc!.Value.Ticks);
var back = rows.Select(r => SampleRowMapper.FromColumns(r.Kind, r.N, r.I, r.S)).ToList();
var back = rows.Select(r => SampleColumnMapper.FromColumns(r.Kind, r.N, r.I, r.S)).ToList();
back.ShouldBe(vals.Select(v => v.Value).ToList());
(rows[5].N, rows[5].I, rows[5].S).ShouldBe((null, null, null));
rows[1].Status.ShouldBe(unchecked((int)0x40000000u));
SampleRowMapper.FromDbStatus(rows[5].Status).ShouldBe(0x80310000u);
SampleColumnMapper.FromDbStatus(rows[5].Status).ShouldBe(0x80310000u);
}
[Fact]
@@ -101,8 +102,8 @@ public sealed class WriteTests(SqlHistorianFixture fx)
rows.Count.ShouldBe(3);
rows[0].Kind.ShouldBe((byte)HistorianValueKind.Empty);
rows[0].N.ShouldBeNull();
rows[0].Status.ShouldBe(unchecked((int)SqlHistorianStatusCodes.BadOutOfRange)); // Uncertain NaN ⇒ Bad (I-1)
rows[1].Status.ShouldBe(unchecked((int)SqlHistorianStatusCodes.BadOutOfRange)); // Good +Inf ⇒ Bad (I-1)
rows[0].Status.ShouldBe(unchecked((int)HistorianStatusCodes.BadOutOfRange)); // Uncertain NaN ⇒ Bad (I-1)
rows[1].Status.ShouldBe(unchecked((int)HistorianStatusCodes.BadOutOfRange)); // Good +Inf ⇒ Bad (I-1)
rows[2].N.ShouldBe(7);
}