@powersync/service-module-mysql 0.7.4 → 0.8.0
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 +30 -0
- package/dev/docker/mysql/init-scripts/my.cnf +1 -3
- package/dist/api/MySQLRouteAPIAdapter.js +11 -3
- package/dist/api/MySQLRouteAPIAdapter.js.map +1 -1
- package/dist/common/ReplicatedGTID.js +4 -0
- package/dist/common/ReplicatedGTID.js.map +1 -1
- package/dist/common/common-index.d.ts +1 -2
- package/dist/common/common-index.js +1 -2
- package/dist/common/common-index.js.map +1 -1
- package/dist/common/mysql-to-sqlite.js +4 -0
- package/dist/common/mysql-to-sqlite.js.map +1 -1
- package/dist/common/schema-utils.d.ts +20 -0
- package/dist/common/{get-replication-columns.js → schema-utils.js} +73 -30
- package/dist/common/schema-utils.js.map +1 -0
- package/dist/replication/BinLogStream.d.ts +9 -6
- package/dist/replication/BinLogStream.js +99 -70
- package/dist/replication/BinLogStream.js.map +1 -1
- package/dist/replication/zongji/BinLogListener.d.ts +52 -5
- package/dist/replication/zongji/BinLogListener.js +302 -85
- package/dist/replication/zongji/BinLogListener.js.map +1 -1
- package/dist/replication/zongji/zongji-utils.d.ts +2 -1
- package/dist/replication/zongji/zongji-utils.js +3 -0
- package/dist/replication/zongji/zongji-utils.js.map +1 -1
- package/dist/types/node-sql-parser-extended-types.d.ts +31 -0
- package/dist/types/node-sql-parser-extended-types.js +2 -0
- package/dist/types/node-sql-parser-extended-types.js.map +1 -0
- package/dist/utils/mysql-utils.d.ts +4 -2
- package/dist/utils/mysql-utils.js +15 -3
- package/dist/utils/mysql-utils.js.map +1 -1
- package/dist/utils/parser-utils.d.ts +16 -0
- package/dist/utils/parser-utils.js +58 -0
- package/dist/utils/parser-utils.js.map +1 -0
- package/package.json +9 -8
- package/src/api/MySQLRouteAPIAdapter.ts +11 -3
- package/src/common/ReplicatedGTID.ts +6 -1
- package/src/common/common-index.ts +1 -2
- package/src/common/mysql-to-sqlite.ts +3 -0
- package/src/common/{get-replication-columns.ts → schema-utils.ts} +96 -37
- package/src/replication/BinLogStream.ts +119 -91
- package/src/replication/zongji/BinLogListener.ts +370 -93
- package/src/replication/zongji/zongji-utils.ts +6 -1
- package/src/types/node-sql-parser-extended-types.ts +25 -0
- package/src/utils/mysql-utils.ts +19 -4
- package/src/utils/parser-utils.ts +73 -0
- package/test/src/BinLogListener.test.ts +415 -32
- package/test/src/BinLogStream.test.ts +128 -52
- package/test/src/BinlogStreamUtils.ts +12 -2
- package/test/src/parser-utils.test.ts +24 -0
- package/test/src/schema-changes.test.ts +663 -0
- package/test/src/util.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/common/get-replication-columns.d.ts +0 -12
- package/dist/common/get-replication-columns.js.map +0 -1
- package/dist/common/get-tables-from-pattern.d.ts +0 -7
- package/dist/common/get-tables-from-pattern.js +0 -28
- package/dist/common/get-tables-from-pattern.js.map +0 -1
- package/src/common/get-tables-from-pattern.ts +0 -44
|
@@ -4,7 +4,8 @@ import { ReplicationMetric } from '@powersync/service-types';
|
|
|
4
4
|
import { v4 as uuid } from 'uuid';
|
|
5
5
|
import { describe, expect, test } from 'vitest';
|
|
6
6
|
import { BinlogStreamTestContext } from './BinlogStreamUtils.js';
|
|
7
|
-
import { describeWithStorage } from './util.js';
|
|
7
|
+
import { createTestDb, describeWithStorage } from './util.js';
|
|
8
|
+
import { qualifiedMySQLTable } from '@module/utils/mysql-utils.js';
|
|
8
9
|
|
|
9
10
|
const BASIC_SYNC_RULES = `
|
|
10
11
|
bucket_definitions:
|
|
@@ -13,7 +14,7 @@ bucket_definitions:
|
|
|
13
14
|
- SELECT id, description FROM "test_data"
|
|
14
15
|
`;
|
|
15
16
|
|
|
16
|
-
describe('
|
|
17
|
+
describe('BinLogStream tests', () => {
|
|
17
18
|
describeWithStorage({ timeout: 20_000 }, defineBinlogStreamTests);
|
|
18
19
|
});
|
|
19
20
|
|
|
@@ -34,7 +35,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
34
35
|
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
35
36
|
const startTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
36
37
|
|
|
37
|
-
context.startStreaming();
|
|
38
|
+
await context.startStreaming();
|
|
38
39
|
const testId = uuid();
|
|
39
40
|
await connectionManager.query(
|
|
40
41
|
`INSERT INTO test_data(id, description, num) VALUES('${testId}', 'test1', 1152921504606846976)`
|
|
@@ -48,7 +49,59 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
48
49
|
expect(endTxCount - startTxCount).toEqual(1);
|
|
49
50
|
});
|
|
50
51
|
|
|
51
|
-
test('
|
|
52
|
+
test('Replicate multi schema sync rules', async () => {
|
|
53
|
+
await using context = await BinlogStreamTestContext.open(factory);
|
|
54
|
+
const { connectionManager } = context;
|
|
55
|
+
await context.updateSyncRules(`
|
|
56
|
+
bucket_definitions:
|
|
57
|
+
default_schema_test_data:
|
|
58
|
+
data:
|
|
59
|
+
- SELECT id, description, num FROM "${connectionManager.databaseName}"."test_data"
|
|
60
|
+
multi_schema_test_data:
|
|
61
|
+
data:
|
|
62
|
+
- SELECT id, description, num FROM "multi_schema"."test_data"
|
|
63
|
+
`);
|
|
64
|
+
|
|
65
|
+
await createTestDb(connectionManager, 'multi_schema');
|
|
66
|
+
|
|
67
|
+
await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description TEXT, num BIGINT)`);
|
|
68
|
+
const testTable = qualifiedMySQLTable('test_data', 'multi_schema');
|
|
69
|
+
await connectionManager.query(
|
|
70
|
+
`CREATE TABLE IF NOT EXISTS ${testTable} (id CHAR(36) PRIMARY KEY,description TEXT);`
|
|
71
|
+
);
|
|
72
|
+
await context.replicateSnapshot();
|
|
73
|
+
|
|
74
|
+
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
75
|
+
const startTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
76
|
+
|
|
77
|
+
await context.startStreaming();
|
|
78
|
+
|
|
79
|
+
const testId = uuid();
|
|
80
|
+
await connectionManager.query(
|
|
81
|
+
`INSERT INTO test_data(id, description, num) VALUES('${testId}', 'test1', 1152921504606846976)`
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const testId2 = uuid();
|
|
85
|
+
await connectionManager.query(`INSERT INTO ${testTable}(id, description) VALUES('${testId2}', 'test2')`);
|
|
86
|
+
|
|
87
|
+
const default_data = await context.getBucketData('default_schema_test_data[]');
|
|
88
|
+
expect(default_data).toMatchObject([
|
|
89
|
+
putOp('test_data', { id: testId, description: 'test1', num: 1152921504606846976n })
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
const multi_schema_data = await context.getBucketData('multi_schema_test_data[]');
|
|
93
|
+
expect(multi_schema_data).toMatchObject([putOp('test_data', { id: testId2, description: 'test2' })]);
|
|
94
|
+
|
|
95
|
+
const endRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
96
|
+
const endTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
97
|
+
expect(endRowCount - startRowCount).toEqual(2);
|
|
98
|
+
expect(endTxCount - startTxCount).toEqual(2);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('Replicate case sensitive table', async () => {
|
|
102
|
+
// MySQL inherits the case sensitivity of the underlying OS filesystem.
|
|
103
|
+
// So Unix-based systems will have case-sensitive tables, but Windows won't.
|
|
104
|
+
// https://dev.mysql.com/doc/refman/8.4/en/identifier-case-sensitivity.html
|
|
52
105
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
53
106
|
const { connectionManager } = context;
|
|
54
107
|
await context.updateSyncRules(`
|
|
@@ -65,7 +118,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
65
118
|
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
66
119
|
const startTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
67
120
|
|
|
68
|
-
context.startStreaming();
|
|
121
|
+
await context.startStreaming();
|
|
69
122
|
|
|
70
123
|
const testId = uuid();
|
|
71
124
|
await connectionManager.query(`INSERT INTO test_DATA(id, description) VALUES('${testId}','test1')`);
|
|
@@ -79,50 +132,73 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
79
132
|
expect(endTxCount - startTxCount).toEqual(1);
|
|
80
133
|
});
|
|
81
134
|
|
|
82
|
-
|
|
83
|
-
// test('replicating TRUNCATE', async () => {
|
|
84
|
-
// await using context = await BinlogStreamTestContext.create(factory);
|
|
85
|
-
// const { connectionManager } = context;
|
|
86
|
-
// const syncRuleContent = `
|
|
87
|
-
// bucket_definitions:
|
|
88
|
-
// global:
|
|
89
|
-
// data:
|
|
90
|
-
// - SELECT id, description FROM "test_data"
|
|
91
|
-
// by_test_data:
|
|
92
|
-
// parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id
|
|
93
|
-
// data: []
|
|
94
|
-
// `;
|
|
95
|
-
// await context.updateSyncRules(syncRuleContent);
|
|
96
|
-
// await connectionManager.query(`DROP TABLE IF EXISTS test_data`);
|
|
97
|
-
// await connectionManager.query(
|
|
98
|
-
// `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`
|
|
99
|
-
// );
|
|
100
|
-
|
|
101
|
-
// await context.replicateSnapshot();
|
|
102
|
-
// context.startStreaming();
|
|
103
|
-
|
|
104
|
-
// const [{ test_id }] = pgwireRows(
|
|
105
|
-
// await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`)
|
|
106
|
-
// );
|
|
107
|
-
// await connectionManager.query(`TRUNCATE test_data`);
|
|
108
|
-
|
|
109
|
-
// const data = await context.getBucketData('global[]');
|
|
110
|
-
|
|
111
|
-
// expect(data).toMatchObject([
|
|
112
|
-
// putOp('test_data', { id: test_id, description: 'test1' }),
|
|
113
|
-
// removeOp('test_data', test_id)
|
|
114
|
-
// ]);
|
|
115
|
-
// });
|
|
116
|
-
|
|
117
|
-
test('replicating changing primary key', async () => {
|
|
135
|
+
test('Replicate matched wild card tables in sync rules', async () => {
|
|
118
136
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
119
137
|
const { connectionManager } = context;
|
|
138
|
+
await context.updateSyncRules(`
|
|
139
|
+
bucket_definitions:
|
|
140
|
+
global:
|
|
141
|
+
data:
|
|
142
|
+
- SELECT id, description FROM "test_data_%"`);
|
|
143
|
+
|
|
144
|
+
await connectionManager.query(`CREATE TABLE test_data_1 (id CHAR(36) PRIMARY KEY, description TEXT)`);
|
|
145
|
+
await connectionManager.query(`CREATE TABLE test_data_2 (id CHAR(36) PRIMARY KEY, description TEXT)`);
|
|
146
|
+
|
|
147
|
+
const testId11 = uuid();
|
|
148
|
+
await connectionManager.query(`INSERT INTO test_data_1(id, description) VALUES('${testId11}','test11')`);
|
|
149
|
+
|
|
150
|
+
const testId21 = uuid();
|
|
151
|
+
await connectionManager.query(`INSERT INTO test_data_2(id, description) VALUES('${testId21}','test21')`);
|
|
152
|
+
|
|
153
|
+
await context.replicateSnapshot();
|
|
154
|
+
await context.startStreaming();
|
|
155
|
+
|
|
156
|
+
const testId12 = uuid();
|
|
157
|
+
await connectionManager.query(`INSERT INTO test_data_1(id, description) VALUES('${testId12}', 'test12')`);
|
|
158
|
+
|
|
159
|
+
const testId22 = uuid();
|
|
160
|
+
await connectionManager.query(`INSERT INTO test_data_2(id, description) VALUES('${testId22}', 'test22')`);
|
|
161
|
+
const data = await context.getBucketData('global[]');
|
|
162
|
+
|
|
163
|
+
expect(data).toMatchObject([
|
|
164
|
+
putOp('test_data_1', { id: testId11, description: 'test11' }),
|
|
165
|
+
putOp('test_data_2', { id: testId21, description: 'test21' }),
|
|
166
|
+
putOp('test_data_1', { id: testId12, description: 'test12' }),
|
|
167
|
+
putOp('test_data_2', { id: testId22, description: 'test22' })
|
|
168
|
+
]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('Handle table TRUNCATE events', async () => {
|
|
172
|
+
await using context = await BinlogStreamTestContext.open(factory);
|
|
173
|
+
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
174
|
+
|
|
175
|
+
const { connectionManager } = context;
|
|
176
|
+
await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description text)`);
|
|
177
|
+
|
|
178
|
+
await context.replicateSnapshot();
|
|
179
|
+
await context.startStreaming();
|
|
180
|
+
|
|
181
|
+
const testId = uuid();
|
|
182
|
+
await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId}','test1')`);
|
|
183
|
+
await connectionManager.query(`TRUNCATE TABLE test_data`);
|
|
184
|
+
|
|
185
|
+
const data = await context.getBucketData('global[]');
|
|
186
|
+
|
|
187
|
+
expect(data).toMatchObject([
|
|
188
|
+
putOp('test_data', { id: testId, description: 'test1' }),
|
|
189
|
+
removeOp('test_data', testId)
|
|
190
|
+
]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Handle changes in a replicated table primary key', async () => {
|
|
194
|
+
await using context = await BinlogStreamTestContext.open(factory);
|
|
120
195
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
121
196
|
|
|
197
|
+
const { connectionManager } = context;
|
|
122
198
|
await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description text)`);
|
|
123
199
|
|
|
124
200
|
await context.replicateSnapshot();
|
|
125
|
-
context.startStreaming();
|
|
201
|
+
await context.startStreaming();
|
|
126
202
|
|
|
127
203
|
const testId1 = uuid();
|
|
128
204
|
await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}','test1')`);
|
|
@@ -154,7 +230,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
154
230
|
]);
|
|
155
231
|
});
|
|
156
232
|
|
|
157
|
-
test('
|
|
233
|
+
test('Initial snapshot sync', async () => {
|
|
158
234
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
159
235
|
const { connectionManager } = context;
|
|
160
236
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -167,7 +243,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
167
243
|
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
168
244
|
|
|
169
245
|
await context.replicateSnapshot();
|
|
170
|
-
context.startStreaming();
|
|
246
|
+
await context.startStreaming();
|
|
171
247
|
|
|
172
248
|
const endRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
173
249
|
const data = await context.getBucketData('global[]');
|
|
@@ -175,7 +251,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
175
251
|
expect(endRowCount - startRowCount).toEqual(1);
|
|
176
252
|
});
|
|
177
253
|
|
|
178
|
-
test('
|
|
254
|
+
test('Snapshot with date values', async () => {
|
|
179
255
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
180
256
|
const { connectionManager } = context;
|
|
181
257
|
await context.updateSyncRules(`
|
|
@@ -195,7 +271,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
195
271
|
`);
|
|
196
272
|
|
|
197
273
|
await context.replicateSnapshot();
|
|
198
|
-
context.startStreaming();
|
|
274
|
+
await context.startStreaming();
|
|
199
275
|
|
|
200
276
|
const data = await context.getBucketData('global[]');
|
|
201
277
|
expect(data).toMatchObject([
|
|
@@ -209,7 +285,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
209
285
|
]);
|
|
210
286
|
});
|
|
211
287
|
|
|
212
|
-
test('
|
|
288
|
+
test('Replication with date values', async () => {
|
|
213
289
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
214
290
|
const { connectionManager } = context;
|
|
215
291
|
await context.updateSyncRules(`
|
|
@@ -228,7 +304,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
228
304
|
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
229
305
|
const startTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
230
306
|
|
|
231
|
-
context.startStreaming();
|
|
307
|
+
await context.startStreaming();
|
|
232
308
|
|
|
233
309
|
const testId = uuid();
|
|
234
310
|
await connectionManager.query(`
|
|
@@ -259,7 +335,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
259
335
|
expect(endTxCount - startTxCount).toEqual(2);
|
|
260
336
|
});
|
|
261
337
|
|
|
262
|
-
test('
|
|
338
|
+
test('Replication for tables not in the sync rules are ignored', async () => {
|
|
263
339
|
await using context = await BinlogStreamTestContext.open(factory);
|
|
264
340
|
const { connectionManager } = context;
|
|
265
341
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -271,7 +347,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
271
347
|
const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED)) ?? 0;
|
|
272
348
|
const startTxCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED)) ?? 0;
|
|
273
349
|
|
|
274
|
-
context.startStreaming();
|
|
350
|
+
await context.startStreaming();
|
|
275
351
|
|
|
276
352
|
await connectionManager.query(`INSERT INTO test_donotsync(id, description) VALUES('${uuid()}','test1')`);
|
|
277
353
|
const data = await context.getBucketData('global[]');
|
|
@@ -300,7 +376,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
300
376
|
await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description TEXT, num BIGINT)`);
|
|
301
377
|
|
|
302
378
|
await context.replicateSnapshot();
|
|
303
|
-
context.startStreaming();
|
|
379
|
+
await context.startStreaming();
|
|
304
380
|
await connectionManager.query(
|
|
305
381
|
`INSERT INTO test_data(id, description, num) VALUES('${testId1}', 'test1', 1152921504606846976)`
|
|
306
382
|
);
|
|
@@ -315,7 +391,7 @@ function defineBinlogStreamTests(factory: storage.TestStorageFactory) {
|
|
|
315
391
|
await context.loadActiveSyncRules();
|
|
316
392
|
// Does not actually do a snapshot again - just does the required intialization.
|
|
317
393
|
await context.replicateSnapshot();
|
|
318
|
-
context.startStreaming();
|
|
394
|
+
await context.startStreaming();
|
|
319
395
|
await connectionManager.query(`INSERT INTO test_data(id, description, num) VALUES('${testId2}', 'test2', 0)`);
|
|
320
396
|
const data = await context.getBucketData('global[]');
|
|
321
397
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
|
|
17
17
|
import mysqlPromise from 'mysql2/promise';
|
|
18
18
|
import { clearTestDb, TEST_CONNECTION_OPTIONS } from './util.js';
|
|
19
|
+
import timers from 'timers/promises';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Tests operating on the binlog stream need to configure the stream and manage asynchronous
|
|
@@ -112,15 +113,24 @@ export class BinlogStreamTestContext {
|
|
|
112
113
|
|
|
113
114
|
async replicateSnapshot() {
|
|
114
115
|
await this.binlogStream.initReplication();
|
|
115
|
-
await this.storage!.autoActivate();
|
|
116
116
|
this.replicationDone = true;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
startStreaming() {
|
|
119
|
+
async startStreaming() {
|
|
120
120
|
if (!this.replicationDone) {
|
|
121
121
|
throw new Error('Call replicateSnapshot() before startStreaming()');
|
|
122
122
|
}
|
|
123
123
|
this.streamPromise = this.binlogStream.streamChanges();
|
|
124
|
+
|
|
125
|
+
// Wait for the replication to start before returning.
|
|
126
|
+
// This avoids a bunch of unpredictable race conditions that appear in testing
|
|
127
|
+
return new Promise<void>(async (resolve) => {
|
|
128
|
+
while (this.binlogStream.isStartingReplication) {
|
|
129
|
+
await timers.setTimeout(50);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
resolve();
|
|
133
|
+
});
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
async getCheckpoint(options?: { timeout?: number }): Promise<InternalOpId> {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { matchedSchemaChangeQuery } from '@module/utils/parser-utils.js';
|
|
3
|
+
|
|
4
|
+
describe('MySQL Parser Util Tests', () => {
|
|
5
|
+
test('matchedSchemaChangeQuery function', () => {
|
|
6
|
+
const matcher = (tableName: string) => tableName === 'users';
|
|
7
|
+
|
|
8
|
+
// DDL matches and table name matches
|
|
9
|
+
expect(matchedSchemaChangeQuery('ALTER TABLE users ADD COLUMN name VARCHAR(255)', [matcher])).toBeTruthy();
|
|
10
|
+
expect(matchedSchemaChangeQuery('DROP TABLE users', [matcher])).toBeTruthy();
|
|
11
|
+
expect(matchedSchemaChangeQuery('TRUNCATE TABLE users', [matcher])).toBeTruthy();
|
|
12
|
+
expect(matchedSchemaChangeQuery('RENAME TABLE new_users TO users', [matcher])).toBeTruthy();
|
|
13
|
+
|
|
14
|
+
// Can handle backticks in table names
|
|
15
|
+
expect(
|
|
16
|
+
matchedSchemaChangeQuery('ALTER TABLE `clientSchema`.`users` ADD COLUMN name VARCHAR(255)', [matcher])
|
|
17
|
+
).toBeTruthy();
|
|
18
|
+
|
|
19
|
+
// DDL matches, but table name does not match
|
|
20
|
+
expect(matchedSchemaChangeQuery('DROP TABLE clientSchema.clients', [matcher])).toBeFalsy();
|
|
21
|
+
// No DDL match
|
|
22
|
+
expect(matchedSchemaChangeQuery('SELECT * FROM users', [matcher])).toBeFalsy();
|
|
23
|
+
});
|
|
24
|
+
});
|