@powersync/service-module-mysql 0.7.4 → 0.9.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 +61 -0
- package/LICENSE +3 -3
- package/dev/docker/mysql/init-scripts/my.cnf +1 -3
- package/dist/api/MySQLRouteAPIAdapter.js +12 -4
- 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.d.ts +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/BinLogReplicationJob.js +4 -1
- package/dist/replication/BinLogReplicationJob.js.map +1 -1
- package/dist/replication/BinLogStream.d.ts +9 -6
- package/dist/replication/BinLogStream.js +117 -73
- package/dist/replication/BinLogStream.js.map +1 -1
- package/dist/replication/zongji/BinLogListener.d.ts +60 -6
- package/dist/replication/zongji/BinLogListener.js +347 -89
- package/dist/replication/zongji/BinLogListener.js.map +1 -1
- package/dist/replication/zongji/zongji-utils.d.ts +4 -1
- package/dist/replication/zongji/zongji-utils.js +9 -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 +12 -11
- package/src/api/MySQLRouteAPIAdapter.ts +15 -4
- package/src/common/ReplicatedGTID.ts +6 -1
- package/src/common/common-index.ts +1 -2
- package/src/common/mysql-to-sqlite.ts +7 -1
- package/src/common/{get-replication-columns.ts → schema-utils.ts} +96 -37
- package/src/replication/BinLogReplicationJob.ts +4 -1
- package/src/replication/BinLogStream.ts +139 -94
- package/src/replication/zongji/BinLogListener.ts +421 -100
- package/src/replication/zongji/zongji-utils.ts +16 -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 +421 -77
- package/test/src/BinLogStream.test.ts +128 -52
- package/test/src/BinlogStreamUtils.ts +12 -2
- package/test/src/mysql-to-sqlite.test.ts +5 -5
- package/test/src/parser-utils.test.ts +24 -0
- package/test/src/schema-changes.test.ts +659 -0
- package/test/src/util.ts +87 -1
- 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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import 'node-sql-parser';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Missing Type definitions for the node-sql-parser
|
|
5
|
+
*/
|
|
6
|
+
declare module 'node-sql-parser' {
|
|
7
|
+
interface RenameStatement {
|
|
8
|
+
type: 'rename';
|
|
9
|
+
table: { db: string | null; table: string }[][];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TruncateStatement {
|
|
13
|
+
type: 'truncate';
|
|
14
|
+
keyword: 'table'; // There are more keywords possible, but we only care about 'table'
|
|
15
|
+
name: { db: string | null; table: string; as: string | null }[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// This custom type more accurately describes what the structure of a Drop statement looks like for indexes.
|
|
19
|
+
interface DropIndexStatement {
|
|
20
|
+
type: 'drop';
|
|
21
|
+
keyword: 'index';
|
|
22
|
+
table: { db: string | null; table: string };
|
|
23
|
+
name: any[];
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/utils/mysql-utils.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { logger } from '@powersync/lib-services-framework';
|
|
|
2
2
|
import mysql from 'mysql2';
|
|
3
3
|
import mysqlPromise from 'mysql2/promise';
|
|
4
4
|
import * as types from '../types/types.js';
|
|
5
|
-
import { coerce, gte } from 'semver';
|
|
6
|
-
import {
|
|
5
|
+
import { coerce, gte, satisfies } from 'semver';
|
|
6
|
+
import { SourceEntityDescriptor } from '@powersync/service-core';
|
|
7
7
|
|
|
8
8
|
export type RetriedQueryOptions = {
|
|
9
9
|
connection: mysqlPromise.Connection;
|
|
@@ -86,6 +86,21 @@ export function isVersionAtLeast(version: string, minimumVersion: string): boole
|
|
|
86
86
|
return gte(coercedVersion!, coercedMinimumVersion!, { loose: true });
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function
|
|
90
|
-
|
|
89
|
+
export function satisfiesVersion(version: string, targetVersion: string): boolean {
|
|
90
|
+
const coercedVersion = coerce(version);
|
|
91
|
+
|
|
92
|
+
return satisfies(coercedVersion!, targetVersion!, { loose: true });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function qualifiedMySQLTable(table: SourceEntityDescriptor): string;
|
|
96
|
+
export function qualifiedMySQLTable(table: string, schema: string): string;
|
|
97
|
+
|
|
98
|
+
export function qualifiedMySQLTable(table: SourceEntityDescriptor | string, schema?: string): string {
|
|
99
|
+
if (typeof table === 'object') {
|
|
100
|
+
return `\`${table.schema.replaceAll('`', '``')}\`.\`${table.name.replaceAll('`', '``')}\``;
|
|
101
|
+
} else if (schema) {
|
|
102
|
+
return `\`${schema.replaceAll('`', '``')}\`.\`${table.replaceAll('`', '``')}\``;
|
|
103
|
+
} else {
|
|
104
|
+
return `\`${table.replaceAll('`', '``')}\``;
|
|
105
|
+
}
|
|
91
106
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Alter, AST, Create, Drop, TruncateStatement, RenameStatement, DropIndexStatement } from 'node-sql-parser';
|
|
2
|
+
|
|
3
|
+
// We ignore create table statements, since even in the worst case we will pick up the changes when row events for that
|
|
4
|
+
// table are received.
|
|
5
|
+
const DDL_KEYWORDS = ['alter table', 'drop table', 'truncate table', 'rename table'];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if a query is a DDL statement that applies to tables matching any of the provided matcher functions.
|
|
9
|
+
* @param query
|
|
10
|
+
* @param matchers
|
|
11
|
+
*/
|
|
12
|
+
export function matchedSchemaChangeQuery(query: string, matchers: ((table: string) => boolean)[]) {
|
|
13
|
+
// Normalize case and remove backticks for matching
|
|
14
|
+
const normalizedQuery = query.toLowerCase().replace(/`/g, '');
|
|
15
|
+
|
|
16
|
+
const isDDLQuery = DDL_KEYWORDS.some((keyword) => normalizedQuery.includes(keyword));
|
|
17
|
+
if (isDDLQuery) {
|
|
18
|
+
const tokens = normalizedQuery.split(/[^a-zA-Z0-9_`]+/);
|
|
19
|
+
// Check if any matched table names appear in the query
|
|
20
|
+
for (const token of tokens) {
|
|
21
|
+
const matchFound = matchers.some((matcher) => matcher(token));
|
|
22
|
+
if (matchFound) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
export function isTruncate(statement: AST): statement is TruncateStatement {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
return statement.type === 'truncate';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
export function isRenameTable(statement: AST): statement is RenameStatement {
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
return statement.type === 'rename';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isAlterTable(statement: AST): statement is Alter {
|
|
44
|
+
return statement.type === 'alter';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isRenameExpression(expression: any): boolean {
|
|
48
|
+
return expression.resource === 'table' && expression.action === 'rename';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isColumnExpression(expression: any): boolean {
|
|
52
|
+
return expression.resource === 'column';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isConstraintExpression(expression: any): boolean {
|
|
56
|
+
return (
|
|
57
|
+
(expression.resource === 'key' && expression.keyword === 'primary key') ||
|
|
58
|
+
expression.resource === 'constraint' ||
|
|
59
|
+
(expression.resource === 'index' && expression.action === 'drop')
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function isDropTable(statement: AST): statement is Drop {
|
|
64
|
+
return statement.type === 'drop' && statement.keyword === 'table';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isDropIndex(statement: AST): statement is DropIndexStatement {
|
|
68
|
+
return statement.type === 'drop' && statement.keyword === 'index';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function isCreateUniqueIndex(statement: AST): statement is Create {
|
|
72
|
+
return statement.type === 'create' && statement.keyword === 'index' && statement.index_type === 'unique';
|
|
73
|
+
}
|