using System; using System.Data; using Junction.Domain; namespace Junction.Persistence { /// /// Owns the SQLite schema (DDL). Idempotent: safe to run on every startup. /// Only-latest policy — one snapshot row and one item-set per machine — but the /// shape leaves room to add history tables later without breaking these tables. /// public static class SqliteSchema { // machines: one row per configured machine. ConnectionConfig bag stored as JSON text. private const string CreateMachines = @"CREATE TABLE IF NOT EXISTS machines ( Id TEXT NOT NULL PRIMARY KEY, Name TEXT NOT NULL, ProtocolId TEXT NOT NULL, PollIntervalTicks INTEGER NOT NULL, ConnectionConfigJson TEXT NOT NULL );"; // latest_snapshots: exactly one row per machine (PK = MachineId). Upsert overwrites. private const string CreateLatestSnapshots = @"CREATE TABLE IF NOT EXISTS latest_snapshots ( MachineId TEXT NOT NULL PRIMARY KEY, CapturedAt TEXT NOT NULL, ConnectionState INTEGER NOT NULL );"; // snapshot_items: datums of the latest snapshot. Replaced on each SaveSnapshot. private const string CreateSnapshotItems = @"CREATE TABLE IF NOT EXISTS snapshot_items ( MachineId TEXT NOT NULL, ItemId TEXT NOT NULL, Name TEXT NOT NULL, Value TEXT NOT NULL, Category TEXT NOT NULL, Timestamp TEXT NOT NULL, PRIMARY KEY (MachineId, ItemId) );"; /// /// Creates all tables if they do not already exist. Idempotent. /// Returns a failed instead of throwing on store errors. /// public static Result EnsureCreated(IDbConnection connection) { if (connection == null) { return Result.Fail(OperationError.Of("SqliteSchema", "Connection is null.")); } try { if (connection.State != ConnectionState.Open) { connection.Open(); } Execute(connection, CreateMachines); Execute(connection, CreateLatestSnapshots); Execute(connection, CreateSnapshotItems); return Result.Ok(); } catch (Exception ex) { return Result.Fail(OperationError.Of("SqliteSchema", "Schema init failed: " + ex.Message)); } } /// Convenience overload: opens a connection from the factory and runs . public static Result EnsureCreated(IConnectionFactory connectionFactory) { if (connectionFactory == null) { return Result.Fail(OperationError.Of("SqliteSchema", "Connection factory is null.")); } try { using (var connection = connectionFactory.CreateOpenConnection()) { return EnsureCreated(connection); } } catch (Exception ex) { return Result.Fail(OperationError.Of("SqliteSchema", "Schema init failed: " + ex.Message)); } } private static void Execute(IDbConnection connection, string sql) { using (var command = connection.CreateCommand()) { command.CommandText = sql; command.ExecuteNonQuery(); } } } }