@powersync/service-module-mssql 0.0.0-dev-20251128080741
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/ci/init-mssql.sql +50 -0
- package/dist/api/MSSQLRouteAPIAdapter.d.ts +21 -0
- package/dist/api/MSSQLRouteAPIAdapter.js +248 -0
- package/dist/api/MSSQLRouteAPIAdapter.js.map +1 -0
- package/dist/common/LSN.d.ts +37 -0
- package/dist/common/LSN.js +64 -0
- package/dist/common/LSN.js.map +1 -0
- package/dist/common/MSSQLSourceTable.d.ts +27 -0
- package/dist/common/MSSQLSourceTable.js +35 -0
- package/dist/common/MSSQLSourceTable.js.map +1 -0
- package/dist/common/MSSQLSourceTableCache.d.ts +14 -0
- package/dist/common/MSSQLSourceTableCache.js +28 -0
- package/dist/common/MSSQLSourceTableCache.js.map +1 -0
- package/dist/common/mssqls-to-sqlite.d.ts +18 -0
- package/dist/common/mssqls-to-sqlite.js +143 -0
- package/dist/common/mssqls-to-sqlite.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/module/MSSQLModule.d.ts +15 -0
- package/dist/module/MSSQLModule.js +68 -0
- package/dist/module/MSSQLModule.js.map +1 -0
- package/dist/replication/CDCPoller.d.ts +67 -0
- package/dist/replication/CDCPoller.js +188 -0
- package/dist/replication/CDCPoller.js.map +1 -0
- package/dist/replication/CDCReplicationJob.d.ts +17 -0
- package/dist/replication/CDCReplicationJob.js +76 -0
- package/dist/replication/CDCReplicationJob.js.map +1 -0
- package/dist/replication/CDCReplicator.d.ts +18 -0
- package/dist/replication/CDCReplicator.js +55 -0
- package/dist/replication/CDCReplicator.js.map +1 -0
- package/dist/replication/CDCStream.d.ts +106 -0
- package/dist/replication/CDCStream.js +539 -0
- package/dist/replication/CDCStream.js.map +1 -0
- package/dist/replication/MSSQLConnectionManager.d.ts +23 -0
- package/dist/replication/MSSQLConnectionManager.js +97 -0
- package/dist/replication/MSSQLConnectionManager.js.map +1 -0
- package/dist/replication/MSSQLConnectionManagerFactory.d.ts +10 -0
- package/dist/replication/MSSQLConnectionManagerFactory.js +28 -0
- package/dist/replication/MSSQLConnectionManagerFactory.js.map +1 -0
- package/dist/replication/MSSQLErrorRateLimiter.d.ts +10 -0
- package/dist/replication/MSSQLErrorRateLimiter.js +34 -0
- package/dist/replication/MSSQLErrorRateLimiter.js.map +1 -0
- package/dist/replication/MSSQLSnapshotQuery.d.ts +71 -0
- package/dist/replication/MSSQLSnapshotQuery.js +190 -0
- package/dist/replication/MSSQLSnapshotQuery.js.map +1 -0
- package/dist/types/mssql-data-types.d.ts +66 -0
- package/dist/types/mssql-data-types.js +62 -0
- package/dist/types/mssql-data-types.js.map +1 -0
- package/dist/types/types.d.ts +177 -0
- package/dist/types/types.js +141 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/mssql.d.ts +80 -0
- package/dist/utils/mssql.js +329 -0
- package/dist/utils/mssql.js.map +1 -0
- package/dist/utils/schema.d.ts +21 -0
- package/dist/utils/schema.js +131 -0
- package/dist/utils/schema.js.map +1 -0
- package/package.json +51 -0
- package/src/api/MSSQLRouteAPIAdapter.ts +283 -0
- package/src/common/LSN.ts +77 -0
- package/src/common/MSSQLSourceTable.ts +54 -0
- package/src/common/MSSQLSourceTableCache.ts +36 -0
- package/src/common/mssqls-to-sqlite.ts +151 -0
- package/src/index.ts +1 -0
- package/src/module/MSSQLModule.ts +82 -0
- package/src/replication/CDCPoller.ts +247 -0
- package/src/replication/CDCReplicationJob.ts +87 -0
- package/src/replication/CDCReplicator.ts +70 -0
- package/src/replication/CDCStream.ts +691 -0
- package/src/replication/MSSQLConnectionManager.ts +113 -0
- package/src/replication/MSSQLConnectionManagerFactory.ts +33 -0
- package/src/replication/MSSQLErrorRateLimiter.ts +36 -0
- package/src/replication/MSSQLSnapshotQuery.ts +230 -0
- package/src/types/mssql-data-types.ts +79 -0
- package/src/types/types.ts +224 -0
- package/src/utils/mssql.ts +420 -0
- package/src/utils/schema.ts +172 -0
- package/test/src/CDCStream.test.ts +204 -0
- package/test/src/CDCStreamTestContext.ts +212 -0
- package/test/src/CDCStream_resumable_snapshot.test.ts +159 -0
- package/test/src/env.ts +11 -0
- package/test/src/mssql-to-sqlite.test.ts +474 -0
- package/test/src/setup.ts +12 -0
- package/test/src/util.ts +188 -0
- package/test/tsconfig.json +28 -0
- package/tsconfig.json +26 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +15 -0
package/test/src/util.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as types from '@module/types/types.js';
|
|
2
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
3
|
+
import { BucketStorageFactory, InternalOpId, ReplicationCheckpoint, TestStorageFactory } from '@powersync/service-core';
|
|
4
|
+
|
|
5
|
+
import * as mongo_storage from '@powersync/service-module-mongodb-storage';
|
|
6
|
+
import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
7
|
+
|
|
8
|
+
import { describe, TestOptions } from 'vitest';
|
|
9
|
+
import { env } from './env.js';
|
|
10
|
+
import { MSSQLConnectionManager } from '@module/replication/MSSQLConnectionManager.js';
|
|
11
|
+
import { createCheckpoint, enableCDCForTable, getLatestLSN } from '@module/utils/mssql.js';
|
|
12
|
+
import sql from 'mssql';
|
|
13
|
+
import { v4 as uuid } from 'uuid';
|
|
14
|
+
import { LSN } from '@module/common/LSN.js';
|
|
15
|
+
|
|
16
|
+
export const TEST_URI = env.MSSQL_TEST_URI;
|
|
17
|
+
|
|
18
|
+
export const INITIALIZED_MONGO_STORAGE_FACTORY = mongo_storage.test_utils.mongoTestStorageFactoryGenerator({
|
|
19
|
+
url: env.MONGO_TEST_URL,
|
|
20
|
+
isCI: env.CI
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const INITIALIZED_POSTGRES_STORAGE_FACTORY = postgres_storage.test_utils.postgresTestStorageFactoryGenerator({
|
|
24
|
+
url: env.PG_STORAGE_TEST_URL
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export function describeWithStorage(options: TestOptions, fn: (factory: TestStorageFactory) => void) {
|
|
28
|
+
describe.skipIf(!env.TEST_MONGO_STORAGE)(`mongodb storage`, options, function () {
|
|
29
|
+
fn(INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe.skipIf(!env.TEST_POSTGRES_STORAGE)(`postgres storage`, options, function () {
|
|
33
|
+
fn(INITIALIZED_POSTGRES_STORAGE_FACTORY);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({
|
|
38
|
+
type: 'mssql',
|
|
39
|
+
uri: TEST_URI,
|
|
40
|
+
trustServerCertificate: true
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Clears all test tables (those prefixed with 'test_') from the database. Also removes CDC instances for those tables.
|
|
45
|
+
* @param connectionManager
|
|
46
|
+
*/
|
|
47
|
+
export async function clearTestDb(connectionManager: MSSQLConnectionManager) {
|
|
48
|
+
const { recordset: tables } = await connectionManager.query(`
|
|
49
|
+
SELECT TABLE_SCHEMA, TABLE_NAME
|
|
50
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
51
|
+
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME LIKE 'test_%'
|
|
52
|
+
`);
|
|
53
|
+
for (const row of tables) {
|
|
54
|
+
// Disable CDC for the table if enabled
|
|
55
|
+
await connectionManager.execute('sys.sp_cdc_disable_table', [
|
|
56
|
+
{ name: 'source_schema', value: row.TABLE_SCHEMA },
|
|
57
|
+
{ name: 'source_name', value: row.TABLE_NAME },
|
|
58
|
+
{ name: 'capture_instance', value: 'all' }
|
|
59
|
+
]);
|
|
60
|
+
// Drop Tables
|
|
61
|
+
await connectionManager.query(`DROP TABLE [${row.TABLE_NAME}]`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create a new database for testing and enables CDC on it.
|
|
67
|
+
* @param connectionManager
|
|
68
|
+
* @param dbName
|
|
69
|
+
*/
|
|
70
|
+
export async function createTestDb(connectionManager: MSSQLConnectionManager, dbName: string) {
|
|
71
|
+
await connectionManager.query(`DROP DATABASE IF EXISTS ${dbName}`);
|
|
72
|
+
await connectionManager.query(`CREATE DATABASE ${dbName}`);
|
|
73
|
+
await connectionManager.execute(`
|
|
74
|
+
USE ${dbName};
|
|
75
|
+
GO
|
|
76
|
+
|
|
77
|
+
EXEC sys.sp_cdc_enable_db;
|
|
78
|
+
GO`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function createTestTable(connectionManager: MSSQLConnectionManager, tableName: string): Promise<void> {
|
|
82
|
+
await connectionManager.query(`
|
|
83
|
+
CREATE TABLE ${connectionManager.schema}.${tableName} (
|
|
84
|
+
id UNIQUEIDENTIFIER PRIMARY KEY,
|
|
85
|
+
description VARCHAR(MAX)
|
|
86
|
+
)
|
|
87
|
+
`);
|
|
88
|
+
await enableCDCForTable({ connectionManager, table: tableName });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function createTestTableWithBasicId(
|
|
92
|
+
connectionManager: MSSQLConnectionManager,
|
|
93
|
+
tableName: string
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
await connectionManager.query(`
|
|
96
|
+
CREATE TABLE ${connectionManager.schema}.${tableName} (
|
|
97
|
+
id INT IDENTITY(1,1) PRIMARY KEY,
|
|
98
|
+
description VARCHAR(MAX)
|
|
99
|
+
)
|
|
100
|
+
`);
|
|
101
|
+
await enableCDCForTable({ connectionManager, table: tableName });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface TestData {
|
|
105
|
+
id: string;
|
|
106
|
+
description: string;
|
|
107
|
+
}
|
|
108
|
+
export async function insertTestData(connectionManager: MSSQLConnectionManager, tableName: string): Promise<TestData> {
|
|
109
|
+
const id = createUpperCaseUUID();
|
|
110
|
+
const description = `description_${id}`;
|
|
111
|
+
await connectionManager.query(
|
|
112
|
+
`
|
|
113
|
+
INSERT INTO ${connectionManager.schema}.${tableName} (id, description) VALUES (@id, @description)
|
|
114
|
+
`,
|
|
115
|
+
[
|
|
116
|
+
{ name: 'id', type: sql.UniqueIdentifier, value: id },
|
|
117
|
+
{ name: 'description', type: sql.NVarChar(sql.MAX), value: description }
|
|
118
|
+
]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return { id, description };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function waitForPendingCDCChanges(
|
|
125
|
+
beforeLSN: LSN,
|
|
126
|
+
connectionManager: MSSQLConnectionManager
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
while (true) {
|
|
129
|
+
const { recordset: result } = await connectionManager.query(
|
|
130
|
+
`
|
|
131
|
+
SELECT TOP 1 start_lsn
|
|
132
|
+
FROM cdc.lsn_time_mapping
|
|
133
|
+
WHERE start_lsn > @before_lsn
|
|
134
|
+
ORDER BY start_lsn DESC
|
|
135
|
+
`,
|
|
136
|
+
[{ name: 'before_lsn', type: sql.VarBinary, value: beforeLSN.toBinary() }]
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (result.length === 0) {
|
|
140
|
+
logger.info(`CDC changes pending. Waiting for 500ms...`);
|
|
141
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
142
|
+
} else {
|
|
143
|
+
logger.info(`Found LSN: ${LSN.fromBinary(result[0].start_lsn).toString()}`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function getClientCheckpoint(
|
|
150
|
+
connectionManager: MSSQLConnectionManager,
|
|
151
|
+
storageFactory: BucketStorageFactory,
|
|
152
|
+
options?: { timeout?: number }
|
|
153
|
+
): Promise<InternalOpId> {
|
|
154
|
+
const start = Date.now();
|
|
155
|
+
|
|
156
|
+
const lsn = await getLatestLSN(connectionManager);
|
|
157
|
+
await createCheckpoint(connectionManager);
|
|
158
|
+
|
|
159
|
+
// This old API needs a persisted checkpoint id.
|
|
160
|
+
// Since we don't use LSNs anymore, the only way to get that is to wait.
|
|
161
|
+
|
|
162
|
+
const timeout = options?.timeout ?? 50_000;
|
|
163
|
+
let lastCp: ReplicationCheckpoint | null = null;
|
|
164
|
+
|
|
165
|
+
logger.info(`Waiting for LSN checkpoint: ${lsn}`);
|
|
166
|
+
while (Date.now() - start < timeout) {
|
|
167
|
+
const storage = await storageFactory.getActiveStorage();
|
|
168
|
+
const cp = await storage?.getCheckpoint();
|
|
169
|
+
if (cp != null) {
|
|
170
|
+
lastCp = cp;
|
|
171
|
+
if (cp.lsn != null && cp.lsn >= lsn.toString()) {
|
|
172
|
+
logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`);
|
|
173
|
+
return cp.checkpoint;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
throw new Error(`Timeout while waiting for checkpoint ${lsn}. Last checkpoint: ${lastCp?.lsn}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generates a new UUID string in uppercase for testing purposes to match the SQL Server UNIQUEIDENTIFIER format.
|
|
185
|
+
*/
|
|
186
|
+
export function createUpperCaseUUID(): string {
|
|
187
|
+
return uuid().toUpperCase();
|
|
188
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"baseUrl": "./",
|
|
6
|
+
"noEmit": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"paths": {
|
|
11
|
+
"@/*": ["../../../packages/service-core/src/*"],
|
|
12
|
+
"@module/*": ["../src/*"],
|
|
13
|
+
"@core-tests/*": ["../../../packages/service-core/test/src/*"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"],
|
|
17
|
+
"references": [
|
|
18
|
+
{
|
|
19
|
+
"path": "../"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "../../../packages/service-core/test"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "../../../packages/service-core/"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"typeRoots": ["./node_modules/@types"]
|
|
10
|
+
},
|
|
11
|
+
"include": ["src"],
|
|
12
|
+
"references": [
|
|
13
|
+
{
|
|
14
|
+
"path": "../../packages/types"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": "../../packages/sync-rules"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "../../packages/service-core"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": "../../libs/lib-services"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|