@powersync/service-module-mssql 0.0.1 → 0.1.1
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 +28 -0
- package/dist/api/MSSQLRouteAPIAdapter.js +16 -51
- package/dist/api/MSSQLRouteAPIAdapter.js.map +1 -1
- package/dist/common/LSN.js +2 -2
- package/dist/common/LSN.js.map +1 -1
- package/dist/common/MSSQLSourceTable.js +4 -4
- package/dist/common/MSSQLSourceTable.js.map +1 -1
- package/dist/common/mssqls-to-sqlite.js +4 -4
- package/dist/common/mssqls-to-sqlite.js.map +1 -1
- package/dist/module/MSSQLModule.js +1 -1
- package/dist/module/MSSQLModule.js.map +1 -1
- package/dist/replication/CDCPoller.d.ts +2 -2
- package/dist/replication/CDCPoller.js +12 -8
- package/dist/replication/CDCPoller.js.map +1 -1
- package/dist/replication/CDCReplicationJob.d.ts +2 -2
- package/dist/replication/CDCReplicationJob.js +1 -1
- package/dist/replication/CDCReplicationJob.js.map +1 -1
- package/dist/replication/CDCReplicator.d.ts +2 -2
- package/dist/replication/CDCReplicator.js +1 -1
- package/dist/replication/CDCReplicator.js.map +1 -1
- package/dist/replication/CDCStream.d.ts +2 -2
- package/dist/replication/CDCStream.js +32 -37
- package/dist/replication/CDCStream.js.map +1 -1
- package/dist/replication/MSSQLConnectionManager.js +1 -1
- package/dist/replication/MSSQLConnectionManager.js.map +1 -1
- package/dist/replication/MSSQLSnapshotQuery.d.ts +0 -17
- package/dist/replication/MSSQLSnapshotQuery.js +0 -47
- package/dist/replication/MSSQLSnapshotQuery.js.map +1 -1
- package/dist/types/types.d.ts +80 -23
- package/dist/types/types.js +24 -24
- package/dist/types/types.js.map +1 -1
- package/dist/utils/mssql.js +26 -14
- package/dist/utils/mssql.js.map +1 -1
- package/dist/utils/schema.js +31 -15
- package/dist/utils/schema.js.map +1 -1
- package/package.json +10 -10
- package/src/api/MSSQLRouteAPIAdapter.ts +16 -51
- package/src/common/LSN.ts +2 -2
- package/src/common/MSSQLSourceTable.ts +4 -4
- package/src/common/mssqls-to-sqlite.ts +11 -4
- package/src/module/MSSQLModule.ts +1 -1
- package/src/replication/CDCPoller.ts +15 -10
- package/src/replication/CDCReplicationJob.ts +3 -3
- package/src/replication/CDCReplicator.ts +3 -3
- package/src/replication/CDCStream.ts +36 -49
- package/src/replication/MSSQLConnectionManager.ts +1 -1
- package/src/replication/MSSQLSnapshotQuery.ts +0 -54
- package/src/types/types.ts +41 -45
- package/src/utils/mssql.ts +33 -15
- package/src/utils/schema.ts +31 -17
- package/test/src/CDCStream.test.ts +9 -17
- package/test/src/CDCStreamTestContext.ts +5 -5
- package/test/src/CDCStream_resumable_snapshot.test.ts +15 -9
- package/test/src/mssql-to-sqlite.test.ts +28 -27
- package/test/src/util.ts +27 -14
- package/tsconfig.tsbuildinfo +1 -1
- package/ci/init-mssql.sql +0 -50
- package/test/tsconfig.tsbuildinfo +0 -1
|
@@ -12,7 +12,6 @@ import { clearTestDb, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './uti
|
|
|
12
12
|
import { CDCStream, CDCStreamOptions } from '@module/replication/CDCStream.js';
|
|
13
13
|
import { MSSQLConnectionManager } from '@module/replication/MSSQLConnectionManager.js';
|
|
14
14
|
import timers from 'timers/promises';
|
|
15
|
-
import { CDCPollingOptions } from '@module/types/types.js';
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Tests operating on the change data capture need to configure the stream and manage asynchronous
|
|
@@ -110,10 +109,11 @@ export class CDCStreamTestContext implements AsyncDisposable {
|
|
|
110
109
|
metrics: METRICS_HELPER.metricsEngine,
|
|
111
110
|
connections: this.connectionManager,
|
|
112
111
|
abortSignal: this.abortController.signal,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
additionalConfig: {
|
|
113
|
+
pollingBatchSize: 10,
|
|
114
|
+
pollingIntervalMs: 1000,
|
|
115
|
+
trustServerCertificate: true
|
|
116
|
+
},
|
|
117
117
|
...this.cdcStreamOptions
|
|
118
118
|
};
|
|
119
119
|
this._cdcStream = new CDCStream(options);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import { env } from './env.js';
|
|
3
|
-
import {
|
|
3
|
+
import { createTestTableWithBasicId, describeWithStorage, waitForPendingCDCChanges } from './util.js';
|
|
4
4
|
import { TestStorageFactory } from '@powersync/service-core';
|
|
5
5
|
import { METRICS_HELPER } from '@powersync/service-core-tests';
|
|
6
6
|
import { ReplicationMetric } from '@powersync/service-types';
|
|
7
7
|
import * as timers from 'node:timers/promises';
|
|
8
|
-
import { ReplicationAbortedError } from '@powersync/lib-services-framework';
|
|
8
|
+
import { logger, ReplicationAbortedError } from '@powersync/lib-services-framework';
|
|
9
9
|
import { CDCStreamTestContext } from './CDCStreamTestContext.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getLatestLSN } from '@module/utils/mssql.js';
|
|
11
11
|
|
|
12
12
|
describe.skipIf(!(env.CI || env.SLOW_TESTS))('batch replication', function () {
|
|
13
13
|
describeWithStorage({ timeout: 240_000 }, function (factory) {
|
|
@@ -46,10 +46,9 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
|
|
|
46
46
|
await createTestTableWithBasicId(connectionManager, 'test_data1');
|
|
47
47
|
await createTestTableWithBasicId(connectionManager, 'test_data2');
|
|
48
48
|
|
|
49
|
-
let beforeLSN = await getLatestReplicatedLSN(connectionManager);
|
|
50
49
|
await connectionManager.query(`INSERT INTO test_data1(description) SELECT 'value' FROM GENERATE_SERIES(1, 1000, 1)`);
|
|
50
|
+
let beforeLSN = await getLatestLSN(connectionManager);
|
|
51
51
|
await connectionManager.query(`INSERT INTO test_data2(description) SELECT 'value' FROM GENERATE_SERIES(1, 10000, 1)`);
|
|
52
|
-
|
|
53
52
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
54
53
|
|
|
55
54
|
const p = context.replicateSnapshot();
|
|
@@ -64,6 +63,7 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
|
|
|
64
63
|
((await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0) - startRowCount;
|
|
65
64
|
|
|
66
65
|
if (count >= stopAfter) {
|
|
66
|
+
logger.info(`Stopped initial replication after replicating ${count} rows.`);
|
|
67
67
|
break;
|
|
68
68
|
}
|
|
69
69
|
await timers.setTimeout(1);
|
|
@@ -85,22 +85,28 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
|
|
|
85
85
|
cdcStreamOptions: { snapshotBatchSize: 1000 }
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
beforeLSN = await getLatestReplicatedLSN(context2.connectionManager);
|
|
89
88
|
// This delete should be using one of the ids already replicated
|
|
90
89
|
const {
|
|
91
|
-
recordset: [
|
|
90
|
+
recordset: [deleteResult]
|
|
92
91
|
} = await context2.connectionManager.query(`DELETE TOP (1) FROM test_data2 OUTPUT DELETED.id`);
|
|
93
92
|
// This update should also be using one of the ids already replicated
|
|
93
|
+
const id1 = deleteResult.id;
|
|
94
|
+
logger.info(`Deleted row with id: ${id1}`);
|
|
94
95
|
const {
|
|
95
|
-
recordset: [
|
|
96
|
+
recordset: [updateResult]
|
|
96
97
|
} = await context2.connectionManager.query(
|
|
97
98
|
`UPDATE test_data2 SET description = 'update1' OUTPUT INSERTED.id WHERE id = (SELECT TOP 1 id FROM test_data2)`
|
|
98
99
|
);
|
|
100
|
+
const id2 = updateResult.id;
|
|
101
|
+
logger.info(`Updated row with id: ${id2}`);
|
|
102
|
+
beforeLSN = await getLatestLSN(context2.connectionManager);
|
|
99
103
|
const {
|
|
100
|
-
recordset: [
|
|
104
|
+
recordset: [insertResult]
|
|
101
105
|
} = await context2.connectionManager.query(
|
|
102
106
|
`INSERT INTO test_data2(description) OUTPUT INSERTED.id VALUES ('insert1')`
|
|
103
107
|
);
|
|
108
|
+
const id3 = insertResult.id;
|
|
109
|
+
logger.info(`Inserted row with id: ${id3}`);
|
|
104
110
|
await waitForPendingCDCChanges(beforeLSN, context2.connectionManager);
|
|
105
111
|
|
|
106
112
|
await context2.loadNextSyncRules();
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { SqliteInputRow } from '@powersync/service-sync-rules';
|
|
1
|
+
import { SQLITE_TRUE, SqliteInputRow } from '@powersync/service-sync-rules';
|
|
2
2
|
import { afterAll, beforeEach, describe, expect, test } from 'vitest';
|
|
3
3
|
import { clearTestDb, createUpperCaseUUID, TEST_CONNECTION_OPTIONS, waitForPendingCDCChanges } from './util.js';
|
|
4
4
|
import { CDCToSqliteRow, toSqliteInputRow } from '@module/common/mssqls-to-sqlite.js';
|
|
5
5
|
import { MSSQLConnectionManager } from '@module/replication/MSSQLConnectionManager.js';
|
|
6
6
|
import {
|
|
7
7
|
enableCDCForTable,
|
|
8
|
+
escapeIdentifier,
|
|
8
9
|
getCaptureInstance,
|
|
10
|
+
getLatestLSN,
|
|
9
11
|
getLatestReplicatedLSN,
|
|
10
12
|
getMinLSN,
|
|
11
13
|
toQualifiedTableName
|
|
@@ -25,7 +27,7 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
25
27
|
|
|
26
28
|
async function setupTestTable() {
|
|
27
29
|
await connectionManager.query(`
|
|
28
|
-
CREATE TABLE ${connectionManager.schema}.test_data (
|
|
30
|
+
CREATE TABLE ${escapeIdentifier(connectionManager.schema)}.test_data (
|
|
29
31
|
id INT IDENTITY(1,1) PRIMARY KEY,
|
|
30
32
|
tinyint_col TINYINT,
|
|
31
33
|
smallint_col SMALLINT,
|
|
@@ -74,9 +76,9 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
test('Number types mappings', async () => {
|
|
77
|
-
const beforeLSN = await
|
|
79
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
78
80
|
await connectionManager.query(`
|
|
79
|
-
INSERT INTO ${connectionManager.schema}.test_data(
|
|
81
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(
|
|
80
82
|
tinyint_col,
|
|
81
83
|
smallint_col,
|
|
82
84
|
int_col,
|
|
@@ -118,16 +120,16 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
118
120
|
numeric_col: 12345.67,
|
|
119
121
|
money_col: 12345.67,
|
|
120
122
|
smallmoney_col: 123.45,
|
|
121
|
-
bit_col:
|
|
123
|
+
bit_col: SQLITE_TRUE
|
|
122
124
|
};
|
|
123
125
|
expect(databaseRows[0]).toMatchObject(expectedResult);
|
|
124
126
|
expect(replicatedRows[0]).toMatchObject(expectedResult);
|
|
125
127
|
});
|
|
126
128
|
|
|
127
129
|
test('Character types mappings', async () => {
|
|
128
|
-
const beforeLSN = await
|
|
130
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
129
131
|
await connectionManager.query(`
|
|
130
|
-
INSERT INTO
|
|
132
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data (
|
|
131
133
|
char_col,
|
|
132
134
|
varchar_col,
|
|
133
135
|
varchar_max_col,
|
|
@@ -167,11 +169,11 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
167
169
|
});
|
|
168
170
|
|
|
169
171
|
test('Binary types mappings', async () => {
|
|
170
|
-
const beforeLSN = await
|
|
172
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
171
173
|
const binaryData = Buffer.from('BinaryData');
|
|
172
174
|
await connectionManager.query(
|
|
173
175
|
`
|
|
174
|
-
INSERT INTO
|
|
176
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data (
|
|
175
177
|
binary_col,
|
|
176
178
|
varbinary_col,
|
|
177
179
|
varbinary_max_col,
|
|
@@ -211,11 +213,11 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
211
213
|
});
|
|
212
214
|
|
|
213
215
|
test('Date types mappings', async () => {
|
|
214
|
-
const beforeLSN = await
|
|
216
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
215
217
|
const testDate = new Date('2023-03-06T15:47:00.000Z');
|
|
216
218
|
await connectionManager.query(
|
|
217
219
|
`
|
|
218
|
-
INSERT INTO
|
|
220
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(
|
|
219
221
|
date_col,
|
|
220
222
|
datetime_col,
|
|
221
223
|
datetime2_col,
|
|
@@ -255,22 +257,21 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
255
257
|
});
|
|
256
258
|
|
|
257
259
|
test('Date types edge cases mappings', async () => {
|
|
258
|
-
const beforeLSN = await getLatestReplicatedLSN(connectionManager);
|
|
259
|
-
|
|
260
260
|
await connectionManager.query(`
|
|
261
|
-
INSERT INTO
|
|
261
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(datetime2_col)
|
|
262
262
|
VALUES ('0001-01-01 00:00:00.000')
|
|
263
263
|
`);
|
|
264
264
|
await connectionManager.query(`
|
|
265
|
-
INSERT INTO
|
|
265
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(datetime2_col)
|
|
266
266
|
VALUES ('9999-12-31 23:59:59.999')
|
|
267
267
|
`);
|
|
268
268
|
await connectionManager.query(`
|
|
269
|
-
INSERT INTO
|
|
269
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(datetime_col)
|
|
270
270
|
VALUES ('1753-01-01 00:00:00')
|
|
271
271
|
`);
|
|
272
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
272
273
|
await connectionManager.query(`
|
|
273
|
-
INSERT INTO
|
|
274
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(datetime_col)
|
|
274
275
|
VALUES ('9999-12-31 23:59:59.997')
|
|
275
276
|
`);
|
|
276
277
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
@@ -292,10 +293,10 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
292
293
|
});
|
|
293
294
|
|
|
294
295
|
test('DateTimeOffset type mapping', async () => {
|
|
295
|
-
const beforeLSN = await
|
|
296
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
296
297
|
// DateTimeOffset preserves timezone information
|
|
297
298
|
await connectionManager.query(`
|
|
298
|
-
INSERT INTO
|
|
299
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(datetimeoffset_col)
|
|
299
300
|
VALUES ('2023-03-06 15:47:00.000 +05:00')
|
|
300
301
|
`);
|
|
301
302
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
@@ -313,12 +314,12 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
313
314
|
});
|
|
314
315
|
|
|
315
316
|
test('UniqueIdentifier type mapping', async () => {
|
|
316
|
-
const beforeLSN = await
|
|
317
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
317
318
|
|
|
318
319
|
const testGuid = createUpperCaseUUID();
|
|
319
320
|
await connectionManager.query(
|
|
320
321
|
`
|
|
321
|
-
INSERT INTO
|
|
322
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(uniqueidentifier_col)
|
|
322
323
|
VALUES (@guid)
|
|
323
324
|
`,
|
|
324
325
|
[{ name: 'guid', type: sql.UniqueIdentifier, value: testGuid }]
|
|
@@ -334,11 +335,11 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
334
335
|
});
|
|
335
336
|
|
|
336
337
|
test('JSON type mapping', async () => {
|
|
337
|
-
const beforeLSN = await
|
|
338
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
338
339
|
const expectedJSON = { name: 'John Doe', age: 30, married: true };
|
|
339
340
|
await connectionManager.query(
|
|
340
341
|
`
|
|
341
|
-
INSERT INTO
|
|
342
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(json_col)
|
|
342
343
|
VALUES (@json)
|
|
343
344
|
`,
|
|
344
345
|
[{ name: 'json', type: sql.NVarChar(sql.MAX), value: JSON.stringify(expectedJSON) }]
|
|
@@ -355,11 +356,11 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
355
356
|
});
|
|
356
357
|
|
|
357
358
|
test('XML type mapping', async () => {
|
|
358
|
-
const beforeLSN = await
|
|
359
|
+
const beforeLSN = await getLatestLSN(connectionManager);
|
|
359
360
|
const xmlData = '<root><item>value</item></root>';
|
|
360
361
|
await connectionManager.query(
|
|
361
362
|
`
|
|
362
|
-
INSERT INTO
|
|
363
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(xml_col)
|
|
363
364
|
VALUES (@xml)
|
|
364
365
|
`,
|
|
365
366
|
[{ name: 'xml', type: sql.Xml, value: xmlData }]
|
|
@@ -375,7 +376,7 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
375
376
|
|
|
376
377
|
// TODO: Update test when properly converting spatial types
|
|
377
378
|
// test('Spatial types mappings', async () => {
|
|
378
|
-
// const beforeLSN = await
|
|
379
|
+
// const beforeLSN = await getLatestLSN(connectionManager);
|
|
379
380
|
// // Geometry and Geography types are stored as binary/WKT strings
|
|
380
381
|
// await connectionManager.query(`
|
|
381
382
|
// INSERT INTO [${connectionManager.schema}].test_data(geometry_col, geography_col)
|
|
@@ -399,7 +400,7 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
399
400
|
// TODO: Enable when HierarchyID type is properly supported
|
|
400
401
|
// test('HierarchyID type mapping', async () => {
|
|
401
402
|
// const hierarchyid = '/1/';
|
|
402
|
-
// const beforeLSN = await
|
|
403
|
+
// const beforeLSN = await getLatestLSN(connectionManager);
|
|
403
404
|
// await connectionManager.query(`
|
|
404
405
|
// INSERT INTO [${connectionManager.schema}].test_data(hierarchyid_col)
|
|
405
406
|
// VALUES (@hierarchyid)
|
package/test/src/util.ts
CHANGED
|
@@ -8,7 +8,7 @@ import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
|
8
8
|
import { describe, TestOptions } from 'vitest';
|
|
9
9
|
import { env } from './env.js';
|
|
10
10
|
import { MSSQLConnectionManager } from '@module/replication/MSSQLConnectionManager.js';
|
|
11
|
-
import { createCheckpoint, enableCDCForTable, getLatestLSN } from '@module/utils/mssql.js';
|
|
11
|
+
import { createCheckpoint, enableCDCForTable, escapeIdentifier, getLatestLSN } from '@module/utils/mssql.js';
|
|
12
12
|
import sql from 'mssql';
|
|
13
13
|
import { v4 as uuid } from 'uuid';
|
|
14
14
|
import { LSN } from '@module/common/LSN.js';
|
|
@@ -37,7 +37,11 @@ export function describeWithStorage(options: TestOptions, fn: (factory: TestStor
|
|
|
37
37
|
export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({
|
|
38
38
|
type: 'mssql',
|
|
39
39
|
uri: TEST_URI,
|
|
40
|
-
|
|
40
|
+
additionalConfig: {
|
|
41
|
+
pollingBatchSize: 10,
|
|
42
|
+
pollingIntervalMs: 1000,
|
|
43
|
+
trustServerCertificate: true
|
|
44
|
+
}
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
/**
|
|
@@ -62,6 +66,16 @@ export async function clearTestDb(connectionManager: MSSQLConnectionManager) {
|
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
export async function resetTestTable(connectionManager: MSSQLConnectionManager, tableName: string) {
|
|
70
|
+
await connectionManager.execute('sys.sp_cdc_disable_table', [
|
|
71
|
+
{ name: 'source_schema', value: connectionManager.schema },
|
|
72
|
+
{ name: 'source_name', value: tableName },
|
|
73
|
+
{ name: 'capture_instance', value: 'all' }
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
await connectionManager.query(`DROP TABLE [${tableName}]`);
|
|
77
|
+
}
|
|
78
|
+
|
|
65
79
|
/**
|
|
66
80
|
* Create a new database for testing and enables CDC on it.
|
|
67
81
|
* @param connectionManager
|
|
@@ -80,7 +94,7 @@ export async function createTestDb(connectionManager: MSSQLConnectionManager, db
|
|
|
80
94
|
|
|
81
95
|
export async function createTestTable(connectionManager: MSSQLConnectionManager, tableName: string): Promise<void> {
|
|
82
96
|
await connectionManager.query(`
|
|
83
|
-
CREATE TABLE ${connectionManager.schema}.${tableName} (
|
|
97
|
+
CREATE TABLE ${escapeIdentifier(connectionManager.schema)}.${escapeIdentifier(tableName)} (
|
|
84
98
|
id UNIQUEIDENTIFIER PRIMARY KEY,
|
|
85
99
|
description VARCHAR(MAX)
|
|
86
100
|
)
|
|
@@ -93,7 +107,7 @@ export async function createTestTableWithBasicId(
|
|
|
93
107
|
tableName: string
|
|
94
108
|
): Promise<void> {
|
|
95
109
|
await connectionManager.query(`
|
|
96
|
-
CREATE TABLE ${connectionManager.schema}.${tableName} (
|
|
110
|
+
CREATE TABLE ${escapeIdentifier(connectionManager.schema)}.${escapeIdentifier(tableName)} (
|
|
97
111
|
id INT IDENTITY(1,1) PRIMARY KEY,
|
|
98
112
|
description VARCHAR(MAX)
|
|
99
113
|
)
|
|
@@ -110,7 +124,7 @@ export async function insertTestData(connectionManager: MSSQLConnectionManager,
|
|
|
110
124
|
const description = `description_${id}`;
|
|
111
125
|
await connectionManager.query(
|
|
112
126
|
`
|
|
113
|
-
INSERT INTO ${connectionManager.schema}.${tableName} (id, description) VALUES (@id, @description)
|
|
127
|
+
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.${escapeIdentifier(tableName)} (id, description) VALUES (@id, @description)
|
|
114
128
|
`,
|
|
115
129
|
[
|
|
116
130
|
{ name: 'id', type: sql.UniqueIdentifier, value: id },
|
|
@@ -137,8 +151,8 @@ export async function waitForPendingCDCChanges(
|
|
|
137
151
|
);
|
|
138
152
|
|
|
139
153
|
if (result.length === 0) {
|
|
140
|
-
logger.info(`CDC changes pending. Waiting for
|
|
141
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
154
|
+
logger.info(`CDC changes pending. Waiting for 200ms...`);
|
|
155
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
142
156
|
} else {
|
|
143
157
|
logger.info(`Found LSN: ${LSN.fromBinary(result[0].start_lsn).toString()}`);
|
|
144
158
|
return;
|
|
@@ -166,13 +180,12 @@ export async function getClientCheckpoint(
|
|
|
166
180
|
while (Date.now() - start < timeout) {
|
|
167
181
|
const storage = await storageFactory.getActiveStorage();
|
|
168
182
|
const cp = await storage?.getCheckpoint();
|
|
169
|
-
if (cp
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return cp.checkpoint;
|
|
183
|
+
if (cp != null) {
|
|
184
|
+
lastCp = cp;
|
|
185
|
+
if (cp.lsn != null && cp.lsn >= lsn.toString()) {
|
|
186
|
+
logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`);
|
|
187
|
+
return cp.checkpoint;
|
|
188
|
+
}
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
await new Promise((resolve) => setTimeout(resolve, 30));
|