- Add sql/00_schema.sql: create dedicated ProductionDataBaseSync schema (idempotent) - Move SyncQueue and usp_SyncApply from dbo into ProductionDataBaseSync - Add sql/03_sync_log_archive.sql: permanent, append-only SyncLogArchive that records both OriginalOperateType and ProcessedOperateType plus the Access log OriginalTime, so pipeline divergences (e.g. Insert applied as Delete) stay reconstructible forever (SyncQueue is transient and only keeps processed type) - config.py: inject sync_queue_table / archive_table / apply_proc (default to the new schema); SqlWriter takes these names instead of hardcoding dbo - sql_writer.py: add ArchiveRow + insert_archive_row (dedup on source keys), parametrize queue/archive/proc names throughout - capture.py: archive every consumed log row before enqueue (preserves evidence before cleanup deletes the Access log) - service.py: pass the three names into SqlWriter - tests: read queue/proc names from config instead of hardcoding dbo.SyncQueue
54 lines
2.2 KiB
Transact-SQL
54 lines
2.2 KiB
Transact-SQL
-- SyncQueue: staging table for the Access -> SQL Server one-way sync.
|
|
-- Lives under the ProductionDataBaseSync schema (run 00_schema.sql first).
|
|
-- Idempotent: safe to re-run (table created only if absent; indexes only if absent).
|
|
|
|
IF OBJECT_ID('ProductionDataBaseSync.SyncQueue', 'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE ProductionDataBaseSync.SyncQueue (
|
|
QueueID bigint IDENTITY(1,1) NOT NULL,
|
|
SourceFile nvarchar(255) NOT NULL,
|
|
SourceTable nvarchar(255) NOT NULL,
|
|
SourceLogID bigint NOT NULL,
|
|
TargetSchema nvarchar(128) NOT NULL,
|
|
TargetTable nvarchar(255) NOT NULL,
|
|
RecordID nvarchar(50) NOT NULL,
|
|
OperateType varchar(10) NOT NULL,
|
|
RowData nvarchar(max) NULL,
|
|
Status varchar(10) NOT NULL CONSTRAINT DF_SyncQueue_Status DEFAULT 'pending',
|
|
RetryCount int NOT NULL CONSTRAINT DF_SyncQueue_Retry DEFAULT 0,
|
|
ErrorMsg nvarchar(max) NULL,
|
|
CapturedAt datetime2 NOT NULL CONSTRAINT DF_SyncQueue_Captured DEFAULT sysdatetime(),
|
|
AppliedAt datetime2 NULL,
|
|
CleanedAt datetime2 NULL,
|
|
CONSTRAINT PK_SyncQueue PRIMARY KEY CLUSTERED (QueueID)
|
|
);
|
|
END
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes
|
|
WHERE name = 'UX_SyncQueue_Dedup'
|
|
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
|
|
BEGIN
|
|
CREATE UNIQUE INDEX UX_SyncQueue_Dedup
|
|
ON ProductionDataBaseSync.SyncQueue(SourceFile, SourceTable, SourceLogID);
|
|
END
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes
|
|
WHERE name = 'IX_SyncQueue_Pending'
|
|
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
|
|
BEGIN
|
|
CREATE INDEX IX_SyncQueue_Pending
|
|
ON ProductionDataBaseSync.SyncQueue(Status, TargetSchema, TargetTable);
|
|
END
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes
|
|
WHERE name = 'IX_SyncQueue_Cleaned'
|
|
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
|
|
BEGIN
|
|
CREATE INDEX IX_SyncQueue_Cleaned
|
|
ON ProductionDataBaseSync.SyncQueue(Status, CleanedAt);
|
|
END
|
|
GO
|