@raft-hlc-sync-protocol/raft-sync-lib 1.0.3 → 1.0.4
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/dialects.js +135 -5
- package/index.js +14 -5
- package/package.json +2 -2
- package/sync-engine.js +0 -3
- package/sync-protocol.js +0 -108
package/dialects.js
CHANGED
|
@@ -17,8 +17,6 @@
|
|
|
17
17
|
* 若不指定 dialect 或指定不认识的值,所有方言方法必须由 db 自行实现,否则报错。
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { getInfraSchemaSQL, getTriggersSQL, dropTriggersSQL } from './sync-protocol.js';
|
|
21
|
-
|
|
22
20
|
// ═══════════════════════════════════════════════════
|
|
23
21
|
// SQLite
|
|
24
22
|
// ═══════════════════════════════════════════════════
|
|
@@ -34,9 +32,101 @@ const SQLITE = {
|
|
|
34
32
|
return `INSERT OR REPLACE INTO ${table} (${cols}) VALUES (${ph})`;
|
|
35
33
|
},
|
|
36
34
|
|
|
37
|
-
infraSchemaSQL
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
infraSchemaSQL() {
|
|
36
|
+
return [
|
|
37
|
+
`CREATE TABLE IF NOT EXISTS _sync_log (
|
|
38
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
+
table_name TEXT NOT NULL,
|
|
40
|
+
operation TEXT NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
41
|
+
row_key TEXT NOT NULL,
|
|
42
|
+
row_data TEXT,
|
|
43
|
+
_hlc TEXT NOT NULL,
|
|
44
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
45
|
+
)`,
|
|
46
|
+
`CREATE INDEX IF NOT EXISTS idx_sync_log_id ON _sync_log (id)`,
|
|
47
|
+
`CREATE TABLE IF NOT EXISTS _sync_peers (
|
|
48
|
+
peer_key TEXT NOT NULL,
|
|
49
|
+
last_sync_id INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
last_sync_at TEXT,
|
|
51
|
+
PRIMARY KEY (peer_key)
|
|
52
|
+
)`,
|
|
53
|
+
`CREATE TABLE IF NOT EXISTS _tombstones (
|
|
54
|
+
table_name TEXT NOT NULL,
|
|
55
|
+
row_key TEXT NOT NULL,
|
|
56
|
+
_hlc TEXT NOT NULL,
|
|
57
|
+
PRIMARY KEY (table_name, row_key)
|
|
58
|
+
)`,
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
triggersSQL(tableName, def) {
|
|
63
|
+
const blobSet = new Set(def.blobColumns || []);
|
|
64
|
+
|
|
65
|
+
const keyExpr = (ref) => {
|
|
66
|
+
const parts = def.keyColumns.map((col) => `'${col}', ${ref}.${col}`);
|
|
67
|
+
return `json_object(${parts.join(', ')})`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const dataExpr = (ref) => {
|
|
71
|
+
const parts = def.dataColumns.map((col) => {
|
|
72
|
+
if (blobSet.has(col)) return `'${col}', hex(${ref}.${col})`;
|
|
73
|
+
return `'${col}', ${ref}.${col}`;
|
|
74
|
+
});
|
|
75
|
+
return `json_object(${parts.join(', ')})`;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return [
|
|
79
|
+
// BEFORE INSERT:强制要求 _hlc 非空且非默认值 '0'
|
|
80
|
+
`
|
|
81
|
+
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_hlc_insert
|
|
82
|
+
BEFORE INSERT ON ${tableName}
|
|
83
|
+
BEGIN
|
|
84
|
+
SELECT RAISE(ABORT, 'sync-lib: _hlc is required for INSERT on ${tableName}')
|
|
85
|
+
WHERE NEW._hlc IS NULL OR NEW._hlc = '' OR NEW._hlc = '0';
|
|
86
|
+
END`,
|
|
87
|
+
// BEFORE UPDATE:强制要求 _hlc 非空且非默认值 '0',且必须严格大于旧值(防止时钟回退)
|
|
88
|
+
`
|
|
89
|
+
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_hlc_update
|
|
90
|
+
BEFORE UPDATE ON ${tableName}
|
|
91
|
+
BEGIN
|
|
92
|
+
SELECT RAISE(ABORT, 'sync-lib: _hlc is required for UPDATE on ${tableName}')
|
|
93
|
+
WHERE NEW._hlc IS NULL OR NEW._hlc = '' OR NEW._hlc = '0';
|
|
94
|
+
SELECT RAISE(ABORT, 'sync-lib: _hlc must advance on UPDATE on ${tableName}')
|
|
95
|
+
WHERE NEW._hlc <= OLD._hlc;
|
|
96
|
+
END`,
|
|
97
|
+
`
|
|
98
|
+
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_insert
|
|
99
|
+
AFTER INSERT ON ${tableName}
|
|
100
|
+
BEGIN
|
|
101
|
+
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
102
|
+
VALUES ('${tableName}', 'INSERT', ${keyExpr('NEW')}, ${dataExpr('NEW')}, NEW._hlc);
|
|
103
|
+
END`,
|
|
104
|
+
`
|
|
105
|
+
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_update
|
|
106
|
+
AFTER UPDATE ON ${tableName}
|
|
107
|
+
BEGIN
|
|
108
|
+
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
109
|
+
VALUES ('${tableName}', 'UPDATE', ${keyExpr('NEW')}, ${dataExpr('NEW')}, NEW._hlc);
|
|
110
|
+
END`,
|
|
111
|
+
`
|
|
112
|
+
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_delete
|
|
113
|
+
AFTER DELETE ON ${tableName}
|
|
114
|
+
BEGIN
|
|
115
|
+
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
116
|
+
VALUES ('${tableName}', 'DELETE', ${keyExpr('OLD')}, NULL, OLD._hlc);
|
|
117
|
+
END`,
|
|
118
|
+
];
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
dropTriggersSQL(tableName) {
|
|
122
|
+
return [
|
|
123
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc_insert`,
|
|
124
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc_update`,
|
|
125
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_insert`,
|
|
126
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_update`,
|
|
127
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_delete`,
|
|
128
|
+
];
|
|
129
|
+
},
|
|
40
130
|
};
|
|
41
131
|
|
|
42
132
|
// ═══════════════════════════════════════════════════
|
|
@@ -98,6 +188,22 @@ const POSTGRESQL = {
|
|
|
98
188
|
}).join(', ');
|
|
99
189
|
|
|
100
190
|
return [
|
|
191
|
+
// HLC 校验函数:INSERT/UPDATE 时强制要求 _hlc 有效且单调递增
|
|
192
|
+
`CREATE OR REPLACE FUNCTION _sync_trg_${tableName}_hlc_fn() RETURNS TRIGGER AS $$
|
|
193
|
+
BEGIN
|
|
194
|
+
IF NEW._hlc IS NULL OR NEW._hlc = '' OR NEW._hlc = '0' THEN
|
|
195
|
+
RAISE EXCEPTION 'sync-lib: _hlc is required for % on ${tableName}', TG_OP;
|
|
196
|
+
END IF;
|
|
197
|
+
IF TG_OP = 'UPDATE' AND NEW._hlc <= OLD._hlc THEN
|
|
198
|
+
RAISE EXCEPTION 'sync-lib: _hlc must advance on UPDATE on ${tableName}';
|
|
199
|
+
END IF;
|
|
200
|
+
RETURN NEW;
|
|
201
|
+
END; $$ LANGUAGE plpgsql`,
|
|
202
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc ON ${tableName}`,
|
|
203
|
+
`CREATE TRIGGER _sync_trg_${tableName}_hlc
|
|
204
|
+
BEFORE INSERT OR UPDATE ON ${tableName}
|
|
205
|
+
FOR EACH ROW EXECUTE FUNCTION _sync_trg_${tableName}_hlc_fn()`,
|
|
206
|
+
// 变更记录函数:写入 _sync_log
|
|
101
207
|
`CREATE OR REPLACE FUNCTION _sync_trg_${tableName}_fn() RETURNS TRIGGER AS $$
|
|
102
208
|
BEGIN
|
|
103
209
|
IF TG_OP = 'DELETE' THEN
|
|
@@ -119,6 +225,8 @@ FOR EACH ROW EXECUTE FUNCTION _sync_trg_${tableName}_fn()`,
|
|
|
119
225
|
|
|
120
226
|
dropTriggersSQL(tableName) {
|
|
121
227
|
return [
|
|
228
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc ON ${tableName}`,
|
|
229
|
+
`DROP FUNCTION IF EXISTS _sync_trg_${tableName}_hlc_fn`,
|
|
122
230
|
`DROP TRIGGER IF EXISTS _sync_trg_${tableName} ON ${tableName}`,
|
|
123
231
|
`DROP FUNCTION IF EXISTS _sync_trg_${tableName}_fn`,
|
|
124
232
|
];
|
|
@@ -180,6 +288,26 @@ const MYSQL = {
|
|
|
180
288
|
}).join(', ');
|
|
181
289
|
|
|
182
290
|
return [
|
|
291
|
+
// HLC 校验触发器:INSERT 时强制要求 _hlc 有效
|
|
292
|
+
`CREATE TRIGGER _sync_trg_${tableName}_hlc_insert
|
|
293
|
+
BEFORE INSERT ON ${tableName} FOR EACH ROW
|
|
294
|
+
BEGIN
|
|
295
|
+
IF NEW._hlc IS NULL OR NEW._hlc = '' OR NEW._hlc = '0' THEN
|
|
296
|
+
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'sync-lib: _hlc is required for INSERT on ${tableName}';
|
|
297
|
+
END IF;
|
|
298
|
+
END`,
|
|
299
|
+
// HLC 校验触发器:UPDATE 时强制要求 _hlc 有效且单调递增
|
|
300
|
+
`CREATE TRIGGER _sync_trg_${tableName}_hlc_update
|
|
301
|
+
BEFORE UPDATE ON ${tableName} FOR EACH ROW
|
|
302
|
+
BEGIN
|
|
303
|
+
IF NEW._hlc IS NULL OR NEW._hlc = '' OR NEW._hlc = '0' THEN
|
|
304
|
+
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'sync-lib: _hlc is required for UPDATE on ${tableName}';
|
|
305
|
+
END IF;
|
|
306
|
+
IF NEW._hlc <= OLD._hlc THEN
|
|
307
|
+
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'sync-lib: _hlc must advance on UPDATE on ${tableName}';
|
|
308
|
+
END IF;
|
|
309
|
+
END`,
|
|
310
|
+
// 变更记录触发器
|
|
183
311
|
`CREATE TRIGGER _sync_trg_${tableName}_insert
|
|
184
312
|
AFTER INSERT ON ${tableName} FOR EACH ROW
|
|
185
313
|
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
@@ -197,6 +325,8 @@ VALUES ('${tableName}', 'DELETE', JSON_OBJECT(${keyExpr('OLD')}), NULL, OLD._hlc
|
|
|
197
325
|
|
|
198
326
|
dropTriggersSQL(tableName) {
|
|
199
327
|
return [
|
|
328
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc_insert`,
|
|
329
|
+
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_hlc_update`,
|
|
200
330
|
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_insert`,
|
|
201
331
|
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_update`,
|
|
202
332
|
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_delete`,
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* import { SyncEngine } from './sync-lib/index.js';
|
|
12
12
|
*
|
|
13
|
-
* // 1. 外部实现 DatabaseAdapter
|
|
13
|
+
* // 1. 外部实现 DatabaseAdapter 基础查询接口
|
|
14
14
|
* const db = {
|
|
15
15
|
* run(sql, params) { ... }, // → { changes: number }
|
|
16
16
|
* get(sql, params) { ... }, // → Object | null
|
|
@@ -23,6 +23,18 @@
|
|
|
23
23
|
* nodeId: 'node-1',
|
|
24
24
|
* db,
|
|
25
25
|
*
|
|
26
|
+
* // ── 方言(推荐)──
|
|
27
|
+
* // 指定内置方言后,db 上缺失的 SQL 方法由 dialects.js 自动填充:
|
|
28
|
+
* // beginTransaction / commit / rollback
|
|
29
|
+
* // upsertSQL / infraSchemaSQL / triggersSQL / dropTriggersSQL
|
|
30
|
+
* // 支持:'sqlite' | 'postgresql' | 'postgres' | 'pg' | 'mysql'
|
|
31
|
+
* dialect: 'sqlite',
|
|
32
|
+
*
|
|
33
|
+
* // ── 方言方法优先级 ──
|
|
34
|
+
* // db 上已有的方法 > dialect 自动填充 > 报错(未指定 dialect 且方法缺失时)
|
|
35
|
+
* // 即:用户在 db 上手动注入的方法始终优先于内置方言实现。
|
|
36
|
+
* // 例如:db.upsertSQL = (table, cols, keys) => '...' // 覆盖内置 SQLite upsert
|
|
37
|
+
*
|
|
26
38
|
* // ── 必须回调 ──
|
|
27
39
|
* onSendToPeer: (peerId, msg) => transport.send(peerId, msg),
|
|
28
40
|
* onClosePeer: (peerId, reason) => transport.close(peerId),
|
|
@@ -106,6 +118,7 @@
|
|
|
106
118
|
* ├── hlc.js ← HLC 混合逻辑时钟(纯算法)
|
|
107
119
|
* ├── leader-election.js ← Raft 简化版选举状态机(onXXX 回调)
|
|
108
120
|
* ├── sync-protocol.js ← 消息编解码 + 幂等数据应用(DatabaseAdapter 接口)
|
|
121
|
+
* ├── dialects.js ← 内置 SQL 方言(SQLite/PostgreSQL/MySQL),所有 SQL 逻辑的唯一来源
|
|
109
122
|
* └── sync-engine.js ← 主引擎(组合以上模块,对外统一入口)
|
|
110
123
|
*/
|
|
111
124
|
|
|
@@ -151,8 +164,4 @@ export {
|
|
|
151
164
|
updatePeerSyncId,
|
|
152
165
|
// 墓碑
|
|
153
166
|
cleanTombstones,
|
|
154
|
-
// Schema / Trigger SQL 生成
|
|
155
|
-
getInfraSchemaSQL,
|
|
156
|
-
getTriggersSQL,
|
|
157
|
-
dropTriggersSQL,
|
|
158
167
|
} from './sync-protocol.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raft-hlc-sync-protocol/raft-sync-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "pure javascript raft and hlc sync protocol",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"raft",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"database",
|
|
10
10
|
"sqlite",
|
|
11
11
|
"mysql",
|
|
12
|
-
"
|
|
12
|
+
"postgresql"
|
|
13
13
|
],
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"author": "xnet",
|
package/sync-engine.js
CHANGED
package/sync-protocol.js
CHANGED
|
@@ -424,111 +424,3 @@ export function logChange(db, tableName, operation, rowKeyJson, rowDataJson, hlc
|
|
|
424
424
|
);
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
// ╔═══════════════════════════════════════╗
|
|
428
|
-
// ║ Schema / Trigger SQL 生成 ║
|
|
429
|
-
// ╚═══════════════════════════════════════╝
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* 返回同步基础设施表的建表 SQL(SQLite 语法,作为默认降级实现)
|
|
433
|
-
* 包含:_sync_log, _sync_peers, _tombstones
|
|
434
|
-
*
|
|
435
|
-
* 若需其他数据库方言,请在 DatabaseAdapter 上实现 infraSchemaSQL() 方法覆盖此默认值。
|
|
436
|
-
* SyncEngine.initSchema() 优先调用 db.infraSchemaSQL(),不存在时才使用本函数。
|
|
437
|
-
*
|
|
438
|
-
* @returns {string[]} SQL 语句数组(SQLite 方言)
|
|
439
|
-
*/
|
|
440
|
-
export function getInfraSchemaSQL() {
|
|
441
|
-
return [
|
|
442
|
-
`CREATE TABLE IF NOT EXISTS _sync_log (
|
|
443
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
444
|
-
table_name TEXT NOT NULL,
|
|
445
|
-
operation TEXT NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
446
|
-
row_key TEXT NOT NULL,
|
|
447
|
-
row_data TEXT,
|
|
448
|
-
_hlc TEXT NOT NULL,
|
|
449
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
450
|
-
)`,
|
|
451
|
-
`CREATE INDEX IF NOT EXISTS idx_sync_log_id ON _sync_log (id)`,
|
|
452
|
-
|
|
453
|
-
`CREATE TABLE IF NOT EXISTS _sync_peers (
|
|
454
|
-
peer_key TEXT NOT NULL,
|
|
455
|
-
last_sync_id INTEGER NOT NULL DEFAULT 0,
|
|
456
|
-
last_sync_at TEXT,
|
|
457
|
-
PRIMARY KEY (peer_key)
|
|
458
|
-
)`,
|
|
459
|
-
|
|
460
|
-
`CREATE TABLE IF NOT EXISTS _tombstones (
|
|
461
|
-
table_name TEXT NOT NULL,
|
|
462
|
-
row_key TEXT NOT NULL,
|
|
463
|
-
_hlc TEXT NOT NULL,
|
|
464
|
-
PRIMARY KEY (table_name, row_key)
|
|
465
|
-
)`,
|
|
466
|
-
];
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* 为指定表生成 SQLite 同步触发器 SQL
|
|
471
|
-
* 触发器自动将 INSERT/UPDATE/DELETE 变更写入 _sync_log
|
|
472
|
-
*
|
|
473
|
-
* 注意:这是 SQLite 专用语法。其他数据库请使用 logChange() 手动记录。
|
|
474
|
-
*
|
|
475
|
-
* @param {string} tableName
|
|
476
|
-
* @param {TableDef} def - 表定义
|
|
477
|
-
* @returns {string[]} SQL 语句数组
|
|
478
|
-
*/
|
|
479
|
-
export function getTriggersSQL(tableName, def) {
|
|
480
|
-
const sqls = [];
|
|
481
|
-
|
|
482
|
-
const keyExpr = (ref) => {
|
|
483
|
-
const parts = def.keyColumns.map((col) => `'${col}', ${ref}.${col}`);
|
|
484
|
-
return `json_object(${parts.join(', ')})`;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const dataExpr = (ref) => {
|
|
488
|
-
const blobSet = new Set(def.blobColumns || []);
|
|
489
|
-
const parts = def.dataColumns.map((col) => {
|
|
490
|
-
if (blobSet.has(col)) return `'${col}', hex(${ref}.${col})`;
|
|
491
|
-
return `'${col}', ${ref}.${col}`;
|
|
492
|
-
});
|
|
493
|
-
return `json_object(${parts.join(', ')})`;
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
sqls.push(`
|
|
497
|
-
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_insert
|
|
498
|
-
AFTER INSERT ON ${tableName}
|
|
499
|
-
BEGIN
|
|
500
|
-
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
501
|
-
VALUES ('${tableName}', 'INSERT', ${keyExpr('NEW')}, ${dataExpr('NEW')}, NEW._hlc);
|
|
502
|
-
END`);
|
|
503
|
-
|
|
504
|
-
sqls.push(`
|
|
505
|
-
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_update
|
|
506
|
-
AFTER UPDATE ON ${tableName}
|
|
507
|
-
BEGIN
|
|
508
|
-
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
509
|
-
VALUES ('${tableName}', 'UPDATE', ${keyExpr('NEW')}, ${dataExpr('NEW')}, NEW._hlc);
|
|
510
|
-
END`);
|
|
511
|
-
|
|
512
|
-
sqls.push(`
|
|
513
|
-
CREATE TRIGGER IF NOT EXISTS _sync_trg_${tableName}_delete
|
|
514
|
-
AFTER DELETE ON ${tableName}
|
|
515
|
-
BEGIN
|
|
516
|
-
INSERT INTO _sync_log (table_name, operation, row_key, row_data, _hlc)
|
|
517
|
-
VALUES ('${tableName}', 'DELETE', ${keyExpr('OLD')}, NULL, OLD._hlc);
|
|
518
|
-
END`);
|
|
519
|
-
|
|
520
|
-
return sqls;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* 返回删除指定表同步触发器的 SQL
|
|
525
|
-
* @param {string} tableName
|
|
526
|
-
* @returns {string[]}
|
|
527
|
-
*/
|
|
528
|
-
export function dropTriggersSQL(tableName) {
|
|
529
|
-
return [
|
|
530
|
-
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_insert`,
|
|
531
|
-
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_update`,
|
|
532
|
-
`DROP TRIGGER IF EXISTS _sync_trg_${tableName}_delete`,
|
|
533
|
-
];
|
|
534
|
-
}
|