feat: SyncQueue table and set-based apply stored procedure
- sql/01_sync_queue.sql: idempotent DDL for dbo.SyncQueue (PK + unique
dedup index + pending lookup index), safe to re-run.
- sql/02_sync_apply.sql: dbo.usp_SyncApply (@MaxRetries INT=5). Per
distinct (TargetSchema,TargetTable) it builds column projections from
sys.columns (excludes ID key/computed/identity/rowversion) and runs a
dynamic-SQL MERGE (last-write-wins via ROW_NUMBER over SourceLogID DESC)
for Insert/Update plus a DELETE for the last op = Delete.
SET IDENTITY_INSERT ON preserves Access PKs.
- tests/conftest.py: sql_conn fixture reads conn_str from gitignored
config.yaml via load_config; skipped without RUN_INTEGRATION=1.
- tests/test_apply_proc.py: integration test covering IDENTITY-preserving
INSERT, last-write-wins UPDATE, BIT conversion, and DELETE; cleans up.
Deviation from the brief's procedure (root-cause fix, design preserved):
every JSON path key is quoted ('$."col"') so non-ASCII column names
(e.g. Chinese 名字/数量) parse correctly. Without quoting, JSON_VALUE
raises "JSON path format is not correct" on Chinese columns, which is the
real target schema for this Access->SQL Server sync.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
42
sql/01_sync_queue.sql
Normal file
42
sql/01_sync_queue.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- SyncQueue: staging table for the Access -> SQL Server one-way sync.
|
||||
-- Idempotent: safe to re-run (table created only if absent; indexes only if absent).
|
||||
|
||||
IF OBJECT_ID('dbo.SyncQueue', 'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.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,
|
||||
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('dbo.SyncQueue'))
|
||||
BEGIN
|
||||
CREATE UNIQUE INDEX UX_SyncQueue_Dedup
|
||||
ON dbo.SyncQueue(SourceFile, SourceTable, SourceLogID);
|
||||
END
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes
|
||||
WHERE name = 'IX_SyncQueue_Pending'
|
||||
AND object_id = OBJECT_ID('dbo.SyncQueue'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_SyncQueue_Pending
|
||||
ON dbo.SyncQueue(Status, TargetSchema, TargetTable);
|
||||
END
|
||||
GO
|
||||
Reference in New Issue
Block a user