@delali/sirannon-db 0.3.5-next.69 → 0.3.5-next.71
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/README.md +2 -0
- package/dist/backup/index.d.ts +5 -5
- package/dist/backup/index.mjs +4 -3
- package/dist/backup-scheduler/index.d.ts +2 -2
- package/dist/backup-scheduler/index.mjs +5 -4
- package/dist/{change-tracker-DqCdeEyx.d.ts → change-tracker-Dpo99EI-.d.ts} +24 -7
- package/dist/{chunk-ADNWEHGZ.mjs → chunk-3VTT3VU6.mjs} +15 -7
- package/dist/chunk-54MAOILS.mjs +149 -0
- package/dist/{chunk-ESTZZPT5.mjs → chunk-56HOLQKD.mjs} +391 -33
- package/dist/chunk-75IBRE73.mjs +55 -0
- package/dist/chunk-BGJ5JVYL.mjs +62 -0
- package/dist/{chunk-R7GBXNTO.mjs → chunk-BUWJH3D3.mjs} +2 -55
- package/dist/{chunk-NVSQ2T5Q.mjs → chunk-C53DPOR5.mjs} +16 -5
- package/dist/chunk-D3YF2G7X.mjs +147 -0
- package/dist/chunk-E2GRX4ZD.mjs +30 -0
- package/dist/{chunk-2O3PP4CF.mjs → chunk-EUNHTFR7.mjs} +42 -16
- package/dist/{chunk-QBBLSQHQ.mjs → chunk-HO2XTXVX.mjs} +1 -8
- package/dist/{chunk-KJLNYOOL.mjs → chunk-IWS4PJIO.mjs} +1 -1
- package/dist/{chunk-IEKIYO4V.mjs → chunk-JAQ2I4OT.mjs} +5 -5
- package/dist/{chunk-2ES3DT4A.mjs → chunk-JZL64CZZ.mjs} +66 -6
- package/dist/chunk-K43WJORM.mjs +23 -0
- package/dist/{chunk-55YRLSM2.mjs → chunk-KOTBP4AW.mjs} +27 -4
- package/dist/{chunk-3BZLZ47R.mjs → chunk-L6EX6K27.mjs} +2 -2
- package/dist/chunk-LHH4PMRV.mjs +197 -0
- package/dist/{chunk-6AOJCNRU.mjs → chunk-LRUC2GSU.mjs} +3 -3
- package/dist/chunk-N4EXO2VH.mjs +117 -0
- package/dist/{chunk-NZGSDALX.mjs → chunk-NBPLGMPP.mjs} +217 -76
- package/dist/chunk-PJRCMG7L.mjs +232 -0
- package/dist/{chunk-VO3ZWA2G.mjs → chunk-RASMCXBN.mjs} +208 -41
- package/dist/{chunk-HR7K3GZW.mjs → chunk-RLBKZZ24.mjs} +5 -1
- package/dist/chunk-SEL4A4WT.mjs +156 -0
- package/dist/chunk-UGGWPZIP.mjs +1374 -0
- package/dist/{chunk-4KASJKKK.mjs → chunk-UIDQGPJY.mjs} +147 -6
- package/dist/{chunk-FJPR2CJX.mjs → chunk-YGFNJBF3.mjs} +2 -2
- package/dist/chunk-YRNLEOSU.mjs +1080 -0
- package/dist/chunk-ZFFUVYYE.mjs +439 -0
- package/dist/client/index.d.ts +26 -18
- package/dist/client/index.mjs +105 -17
- package/dist/client/topology.d.ts +12 -10
- package/dist/client/topology.mjs +17 -7
- package/dist/{client-base-BxTocU1x.d.ts → client-base-CCY9go0g.d.ts} +22 -9
- package/dist/{client-streams-YKANXMWO.mjs → client-streams-MKPE4MXW.mjs} +6 -6
- package/dist/codegen/cli.mjs +3 -2
- package/dist/codegen/index.mjs +3 -2
- package/dist/core/index.d.ts +59 -16
- package/dist/core/index.mjs +1141 -435
- package/dist/core/writer-worker.mjs +57 -9
- package/dist/{database-CGDsXW1K.d.ts → database-D4oNUy_2.d.ts} +355 -89
- package/dist/{database-backup-DcLhxoa1.d.ts → database-backup-BwTNiBm2.d.ts} +1 -1
- package/dist/driver/better-sqlite3.d.ts +20 -2
- package/dist/driver/better-sqlite3.mjs +212 -36
- package/dist/driver/bun.d.ts +1 -1
- package/dist/driver/bun.mjs +20 -33
- package/dist/driver/expo.d.ts +1 -1
- package/dist/driver/expo.mjs +34 -5
- package/dist/driver/node.d.ts +1 -1
- package/dist/driver/node.mjs +26 -16
- package/dist/driver/wa-sqlite.d.ts +1 -1
- package/dist/driver/wa-sqlite.mjs +160 -13
- package/dist/file-migrations/index.d.ts +1 -1
- package/dist/file-migrations/index.mjs +13 -8
- package/dist/{forward-rpc-VRWFZXHN.mjs → forward-rpc-S7NMLBX7.mjs} +1 -1
- package/dist/{primary-wins-CHNOYuN7.d.ts → primary-wins-BnNAEjwB.d.ts} +7 -6
- package/dist/{protocol-DFpNNsIT.d.ts → protocol-DZSYSv0S.d.ts} +1 -1
- package/dist/replication/coordinator/etcd.d.ts +22 -5
- package/dist/replication/coordinator/etcd.mjs +349 -271
- package/dist/replication/index.d.ts +269 -41
- package/dist/replication/index.mjs +2197 -1155
- package/dist/{restore-options-CH8mE-Tb.d.ts → restore-options-CcMukOLO.d.ts} +1 -1
- package/dist/server/index.d.ts +55 -19
- package/dist/server/index.mjs +858 -307
- package/dist/{server-options-aYtCkefX.d.ts → server-options-B9CsAZp5.d.ts} +9 -7
- package/dist/{server-streams-24XL4T43.mjs → server-streams-SBYCHWWM.mjs} +36 -59
- package/dist/{sirannon-J3EgPVD7.d.ts → sirannon-B6sPKDye.d.ts} +2 -2
- package/dist/transport/grpc.d.ts +26 -7
- package/dist/transport/grpc.mjs +45 -23
- package/dist/transport/memory.d.ts +5 -5
- package/dist/transport/memory.mjs +9 -3
- package/dist/{types-DqODKeOq.d.ts → types-BG31TtrQ.d.ts} +17 -0
- package/dist/{types-BrAiDGOb.d.ts → types-BlZ9GG1a.d.ts} +33 -8
- package/dist/{types-Dc8oWlxj.d.ts → types-DTmi7pXp.d.ts} +198 -15
- package/dist/{types-guEKSzMr.d.ts → types-MczwKtul.d.ts} +214 -135
- package/package.json +18 -7
- package/dist/chunk-52XW3Z76.mjs +0 -256
- package/dist/chunk-A3PHCO6W.mjs +0 -645
- package/dist/chunk-ER7ODTDA.mjs +0 -23
- package/dist/chunk-GVYLZVUZ.mjs +0 -58
- package/dist/chunk-MVSES6MS.mjs +0 -22
- package/dist/chunk-ZN4HI5FK.mjs +0 -907
- package/dist/chunk-ZYWI45EN.mjs +0 -299
- package/dist/errors-CJUkQ-KL.d.ts +0 -199
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReplicationError } from './chunk-BLMRNSUG.mjs';
|
|
2
|
-
import {
|
|
1
|
+
import { ReplicationError, BatchValidationError } from './chunk-BLMRNSUG.mjs';
|
|
2
|
+
import { CHANGES_TABLE, META_TABLE, MIGRATIONS_TABLE, REPLACED_ROWS_TABLE, APPLIED_CHANGES_TABLE, STAGED_PUSH_CHANGES_TABLE, COLUMN_VERSIONS_TABLE, DEVICE_CURSORS_TABLE, PEER_STATE_TABLE, SYNC_STATE_TABLE, STAGED_CHANGES_TABLE, isReservedIdentifier } from './chunk-EUNHTFR7.mjs';
|
|
3
3
|
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
4
4
|
|
|
5
5
|
// src/core/sync/hlc.ts
|
|
@@ -89,6 +89,12 @@ var HLC = class _HLC {
|
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
// src/core/system-catalog/sqlite-catalog.ts
|
|
92
|
+
var TABLE_XINFO_HIDDEN = {
|
|
93
|
+
ordinary: 0,
|
|
94
|
+
virtualTableColumn: 1,
|
|
95
|
+
generatedVirtual: 2,
|
|
96
|
+
generatedStored: 3
|
|
97
|
+
};
|
|
92
98
|
async function selectTableExists(conn, table) {
|
|
93
99
|
const stmt = await conn.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?");
|
|
94
100
|
return Boolean(await stmt.get(table));
|
|
@@ -97,17 +103,29 @@ async function tableInfoRows(conn, table) {
|
|
|
97
103
|
const stmt = await conn.prepare(`PRAGMA table_info("${table}")`);
|
|
98
104
|
return await stmt.all();
|
|
99
105
|
}
|
|
106
|
+
async function tableColumnsWithGenerated(conn, table) {
|
|
107
|
+
const stmt = await conn.prepare(`PRAGMA table_xinfo("${table}")`);
|
|
108
|
+
return (await stmt.all()).map((row) => ({ ...row, hidden: Number(row.hidden) }));
|
|
109
|
+
}
|
|
100
110
|
async function tableColumnNames(conn, table) {
|
|
101
111
|
return (await tableInfoRows(conn, table)).map((col) => col.name);
|
|
102
112
|
}
|
|
113
|
+
function primaryKeyColumns(columns) {
|
|
114
|
+
return columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk).map((column) => column.name);
|
|
115
|
+
}
|
|
103
116
|
async function tablePkColumns(conn, table) {
|
|
104
|
-
return (await tableInfoRows(conn, table))
|
|
117
|
+
return primaryKeyColumns(await tableInfoRows(conn, table));
|
|
105
118
|
}
|
|
106
119
|
async function selectTableSql(conn, table) {
|
|
107
120
|
const stmt = await conn.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?");
|
|
108
121
|
const row = await stmt.get(table);
|
|
109
122
|
return row?.sql ?? null;
|
|
110
123
|
}
|
|
124
|
+
async function selectTriggerSql(conn, trigger) {
|
|
125
|
+
const stmt = await conn.prepare("SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = ?");
|
|
126
|
+
const row = await stmt.get(trigger);
|
|
127
|
+
return row?.sql ?? null;
|
|
128
|
+
}
|
|
111
129
|
async function selectCountTableRows(conn, table) {
|
|
112
130
|
const stmt = await conn.prepare(`SELECT COUNT(*) AS cnt FROM "${table}"`);
|
|
113
131
|
const row = await stmt.get();
|
|
@@ -135,6 +153,34 @@ async function selectUserTableNames(conn, excludePrefix) {
|
|
|
135
153
|
const rows = await stmt.all(`${excludePrefix}%`);
|
|
136
154
|
return rows.map((row) => row.name);
|
|
137
155
|
}
|
|
156
|
+
async function selectOrdinaryTableName(conn, table) {
|
|
157
|
+
const stmt = await conn.prepare(
|
|
158
|
+
`SELECT name FROM sqlite_master
|
|
159
|
+
WHERE type = 'table' AND name = ? COLLATE NOCASE AND sql NOT LIKE 'CREATE VIRTUAL TABLE%'`
|
|
160
|
+
);
|
|
161
|
+
const row = await stmt.get(table);
|
|
162
|
+
return typeof row?.name === "string" ? row.name : null;
|
|
163
|
+
}
|
|
164
|
+
async function selectIndexSql(conn, index) {
|
|
165
|
+
const stmt = await conn.prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ?");
|
|
166
|
+
const row = await stmt.get(index);
|
|
167
|
+
return row?.sql ?? null;
|
|
168
|
+
}
|
|
169
|
+
async function tableIndexList(conn, table) {
|
|
170
|
+
const stmt = await conn.prepare('SELECT name, "unique", origin, partial FROM pragma_index_list(?) ORDER BY seq');
|
|
171
|
+
const rows = await stmt.all(table);
|
|
172
|
+
return rows.map((row) => ({
|
|
173
|
+
name: row.name,
|
|
174
|
+
unique: Number(row.unique),
|
|
175
|
+
origin: row.origin,
|
|
176
|
+
partial: Number(row.partial)
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
async function indexColumns(conn, index) {
|
|
180
|
+
const stmt = await conn.prepare('SELECT cid, name, coll, "key" FROM pragma_index_xinfo(?) ORDER BY seqno');
|
|
181
|
+
const rows = await stmt.all(index);
|
|
182
|
+
return rows.map((row) => ({ cid: Number(row.cid), name: row.name, collation: row.coll, key: Number(row.key) }));
|
|
183
|
+
}
|
|
138
184
|
|
|
139
185
|
// src/core/system-catalog/columns.ts
|
|
140
186
|
var SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -192,14 +238,29 @@ async function deleteMetaValue(conn, key) {
|
|
|
192
238
|
}
|
|
193
239
|
var UPSERT_META_VALUE_SQL = `INSERT INTO "${META_TABLE}" (key, value) VALUES (?, ?)
|
|
194
240
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`;
|
|
241
|
+
function upsertMetaMaxChangeSeqSql(changesTable) {
|
|
242
|
+
return `INSERT INTO "${META_TABLE}" (key, value)
|
|
243
|
+
VALUES (?, (SELECT CAST(COALESCE(MAX(seq), 0) AS TEXT) FROM "${changesTable}"))
|
|
244
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`;
|
|
245
|
+
}
|
|
246
|
+
function upsertMetaValueAndMaxChangeSeqSql(changesTable) {
|
|
247
|
+
return `INSERT INTO "${META_TABLE}" (key, value)
|
|
248
|
+
VALUES (?, ?), (?, (SELECT CAST(COALESCE(MAX(seq), 0) AS TEXT) FROM "${changesTable}"))
|
|
249
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`;
|
|
250
|
+
}
|
|
195
251
|
async function upsertMetaValue(conn, key, value) {
|
|
196
252
|
const stmt = await conn.prepare(UPSERT_META_VALUE_SQL);
|
|
197
253
|
await stmt.run(key, value);
|
|
198
254
|
}
|
|
199
255
|
|
|
200
256
|
// src/core/system-catalog/changes-table.ts
|
|
201
|
-
var
|
|
257
|
+
var SQLITE_SEQUENCE_TABLE = "sqlite_sequence";
|
|
202
258
|
var DDL_CHANGE_ROW_TABLE = "__ddl__";
|
|
259
|
+
var CHANGE_LOG_COLUMNS = "seq, table_name, operation, row_id, changed_at, old_data, new_data, node_id, tx_id, hlc";
|
|
260
|
+
function seqFromColumn(value) {
|
|
261
|
+
if (value === void 0 || value === null) return 0n;
|
|
262
|
+
return typeof value === "bigint" ? value : BigInt(String(value));
|
|
263
|
+
}
|
|
203
264
|
function selectChangesAfterSeqSql(tableName) {
|
|
204
265
|
return `SELECT ${CHANGE_LOG_COLUMNS} FROM "${tableName}" WHERE seq > ? ORDER BY seq ASC LIMIT ?`;
|
|
205
266
|
}
|
|
@@ -237,15 +298,13 @@ async function insertDdlChange(conn, tableName, entry) {
|
|
|
237
298
|
function updateChangeStampsAfterSeqSql(tableName) {
|
|
238
299
|
return `UPDATE "${tableName}" SET node_id = ?, tx_id = ?, hlc = ? WHERE seq > ? AND node_id = ''`;
|
|
239
300
|
}
|
|
240
|
-
function
|
|
241
|
-
return `UPDATE "${tableName}" SET node_id = ?, tx_id = ?, hlc = ?
|
|
301
|
+
function updateChangeStampsAboveMetaSeqSql(tableName) {
|
|
302
|
+
return `UPDATE "${tableName}" SET node_id = ?, tx_id = ?, hlc = ?
|
|
303
|
+
WHERE node_id = '' AND seq > COALESCE((SELECT CAST(value AS INTEGER) FROM "${META_TABLE}" WHERE key = ?), 0)`;
|
|
242
304
|
}
|
|
243
305
|
function selectMinChangeSeqSql(tableName) {
|
|
244
306
|
return `SELECT MIN(seq) AS seq FROM "${tableName}"`;
|
|
245
307
|
}
|
|
246
|
-
function deleteChangesBeforeSql(tableName) {
|
|
247
|
-
return `DELETE FROM "${tableName}" WHERE changed_at < ?`;
|
|
248
|
-
}
|
|
249
308
|
function deleteChangesBeforeUpToSeqSql(tableName) {
|
|
250
309
|
return `DELETE FROM "${tableName}" WHERE changed_at < ? AND seq <= ?`;
|
|
251
310
|
}
|
|
@@ -259,9 +318,20 @@ async function selectMaxRowChangeHlc(conn, tableName, rowTable, rowId) {
|
|
|
259
318
|
async function selectMaxChangeSeq(conn, tableName = CHANGES_TABLE) {
|
|
260
319
|
const stmt = await conn.prepare(`SELECT COALESCE(MAX(seq), 0) AS seq FROM "${tableName}"`);
|
|
261
320
|
const row = await stmt.get();
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
321
|
+
return seqFromColumn(row?.seq);
|
|
322
|
+
}
|
|
323
|
+
async function selectMaxAssignedChangeSeq(conn, tableName = CHANGES_TABLE) {
|
|
324
|
+
const logged = await selectMaxChangeSeq(conn, tableName);
|
|
325
|
+
if (!await selectTableExists(conn, SQLITE_SEQUENCE_TABLE)) return logged;
|
|
326
|
+
const stmt = await conn.prepare(`SELECT seq FROM ${SQLITE_SEQUENCE_TABLE} WHERE name = ?`);
|
|
327
|
+
const row = await stmt.get(tableName);
|
|
328
|
+
const assigned = seqFromColumn(row?.seq);
|
|
329
|
+
return assigned > logged ? assigned : logged;
|
|
330
|
+
}
|
|
331
|
+
async function selectMaxNodeChangeSeq(conn, tableName, nodeId) {
|
|
332
|
+
const stmt = await conn.prepare(`SELECT COALESCE(MAX(seq), 0) AS seq FROM "${tableName}" WHERE node_id = ?`);
|
|
333
|
+
const row = await stmt.get(nodeId);
|
|
334
|
+
return seqFromColumn(row?.seq);
|
|
265
335
|
}
|
|
266
336
|
async function selectMaxChangeHlc(conn, tableName = CHANGES_TABLE) {
|
|
267
337
|
const stmt = await conn.prepare(`SELECT MAX(hlc) AS hlc FROM "${tableName}" WHERE hlc != ''`);
|
|
@@ -340,11 +410,31 @@ function highestMigrationVersion(rows) {
|
|
|
340
410
|
return highest;
|
|
341
411
|
}
|
|
342
412
|
|
|
343
|
-
// src/core/system-catalog/
|
|
344
|
-
function
|
|
345
|
-
|
|
346
|
-
|
|
413
|
+
// src/core/system-catalog/replaced-rows-table.ts
|
|
414
|
+
async function ensureReplacedRowsTable(conn) {
|
|
415
|
+
await conn.exec(
|
|
416
|
+
`CREATE TABLE IF NOT EXISTS "${REPLACED_ROWS_TABLE}" (
|
|
417
|
+
table_name TEXT NOT NULL,
|
|
418
|
+
row_key NOT NULL,
|
|
419
|
+
match_key TEXT NOT NULL,
|
|
420
|
+
row_id NOT NULL,
|
|
421
|
+
old_data TEXT,
|
|
422
|
+
UNIQUE (table_name, row_key)
|
|
423
|
+
)`
|
|
424
|
+
);
|
|
425
|
+
await conn.exec(
|
|
426
|
+
`CREATE INDEX IF NOT EXISTS "${REPLACED_ROWS_TABLE}_by_match" ON "${REPLACED_ROWS_TABLE}" (table_name, match_key)`
|
|
427
|
+
);
|
|
347
428
|
}
|
|
429
|
+
async function deleteReplacedRowsOfTable(conn, table) {
|
|
430
|
+
const stmt = await conn.prepare(`DELETE FROM "${REPLACED_ROWS_TABLE}" WHERE table_name = ?`);
|
|
431
|
+
await stmt.run(table);
|
|
432
|
+
}
|
|
433
|
+
async function deleteAllReplacedRows(conn) {
|
|
434
|
+
await conn.exec(`DELETE FROM "${REPLACED_ROWS_TABLE}"`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/core/system-catalog/applied-changes-table.ts
|
|
348
438
|
async function ensureAppliedChangesTable(conn) {
|
|
349
439
|
await conn.exec(`
|
|
350
440
|
CREATE TABLE IF NOT EXISTS "${APPLIED_CHANGES_TABLE}" (
|
|
@@ -359,12 +449,23 @@ function prepareInsertAppliedChange(conn) {
|
|
|
359
449
|
`INSERT OR IGNORE INTO ${APPLIED_CHANGES_TABLE} (source_node_id, source_seq, applied_at) VALUES (?, ?, ?)`
|
|
360
450
|
);
|
|
361
451
|
}
|
|
452
|
+
async function insertAppliedChangesFromStagedPush(conn, sourceNodeId, appliedAt) {
|
|
453
|
+
const stmt = await conn.prepare(
|
|
454
|
+
`INSERT OR IGNORE INTO "${APPLIED_CHANGES_TABLE}" (source_node_id, source_seq, applied_at)
|
|
455
|
+
SELECT source_node_id, source_seq, ? FROM "${STAGED_PUSH_CHANGES_TABLE}" WHERE source_node_id = ?`
|
|
456
|
+
);
|
|
457
|
+
await stmt.run(appliedAt, sourceNodeId);
|
|
458
|
+
}
|
|
459
|
+
async function deleteAppliedChangesAfterSeq(conn, sourceNodeId, seq) {
|
|
460
|
+
const stmt = await conn.prepare(`DELETE FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ? AND source_seq > ?`);
|
|
461
|
+
await stmt.run(sourceNodeId, seq.toString());
|
|
462
|
+
}
|
|
362
463
|
async function selectMaxAppliedSourceSeq(conn, sourceNodeId) {
|
|
363
464
|
const stmt = await conn.prepare(
|
|
364
465
|
`SELECT MAX(source_seq) AS max_seq FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ?`
|
|
365
466
|
);
|
|
366
467
|
const row = await stmt.get(sourceNodeId);
|
|
367
|
-
return
|
|
468
|
+
return seqFromColumn(row?.max_seq);
|
|
368
469
|
}
|
|
369
470
|
async function selectMaxAppliedSourceSeqByNode(conn) {
|
|
370
471
|
const stmt = await conn.prepare(
|
|
@@ -374,7 +475,7 @@ async function selectMaxAppliedSourceSeqByNode(conn) {
|
|
|
374
475
|
const byNode = /* @__PURE__ */ new Map();
|
|
375
476
|
for (const row of rows) {
|
|
376
477
|
if (row.max_seq === null || row.max_seq === void 0) continue;
|
|
377
|
-
byNode.set(row.source_node_id,
|
|
478
|
+
byNode.set(row.source_node_id, seqFromColumn(row.max_seq));
|
|
378
479
|
}
|
|
379
480
|
return byNode;
|
|
380
481
|
}
|
|
@@ -390,6 +491,73 @@ async function selectAppliedSourceSeqsInRange(conn, sourceNodeId, fromSeq, toSeq
|
|
|
390
491
|
return seqs;
|
|
391
492
|
}
|
|
392
493
|
|
|
494
|
+
// src/core/system-catalog/changes-history.ts
|
|
495
|
+
function originPlaceholders(originCount) {
|
|
496
|
+
return Array.from({ length: originCount }, () => "?").join(", ");
|
|
497
|
+
}
|
|
498
|
+
function selectOutboundHistoryChangesSql(tableName, originCount) {
|
|
499
|
+
return `SELECT ${CHANGE_LOG_COLUMNS} FROM "${tableName}"
|
|
500
|
+
WHERE seq > ? AND hlc != '' AND node_id IN (${originPlaceholders(originCount)})
|
|
501
|
+
ORDER BY seq ASC LIMIT ?`;
|
|
502
|
+
}
|
|
503
|
+
async function selectNewestHistoryChange(conn, tableName, origins, bounds = {}) {
|
|
504
|
+
if (origins.length === 0) return null;
|
|
505
|
+
const seqFilter = bounds.maxSeq === void 0 ? "" : "AND seq <= ?";
|
|
506
|
+
const hlcFilter = bounds.beforeHlc === void 0 ? "" : "AND hlc < ?";
|
|
507
|
+
const stmt = await conn.prepare(
|
|
508
|
+
`SELECT seq, hlc, tx_id FROM "${tableName}"
|
|
509
|
+
WHERE hlc != '' AND node_id IN (${originPlaceholders(origins.length)}) ${seqFilter} ${hlcFilter}
|
|
510
|
+
ORDER BY hlc DESC, seq DESC LIMIT 1`
|
|
511
|
+
);
|
|
512
|
+
const params = [...origins];
|
|
513
|
+
if (bounds.maxSeq !== void 0) params.push(bounds.maxSeq.toString());
|
|
514
|
+
if (bounds.beforeHlc !== void 0) params.push(bounds.beforeHlc);
|
|
515
|
+
const row = await stmt.get(...params);
|
|
516
|
+
if (!row) return null;
|
|
517
|
+
return await selectHistoryTransaction(conn, tableName, row.tx_id) ?? {
|
|
518
|
+
seq: seqFromColumn(row.seq),
|
|
519
|
+
hlc: row.hlc,
|
|
520
|
+
txId: row.tx_id
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
async function selectMaxHistoryChangeSeq(conn, tableName, origins) {
|
|
524
|
+
if (origins.length === 0) return 0n;
|
|
525
|
+
const stmt = await conn.prepare(
|
|
526
|
+
`SELECT COALESCE(MAX(seq), 0) AS seq FROM "${tableName}"
|
|
527
|
+
WHERE hlc != '' AND node_id IN (${originPlaceholders(origins.length)})`
|
|
528
|
+
);
|
|
529
|
+
const row = await stmt.get(...origins);
|
|
530
|
+
return seqFromColumn(row?.seq);
|
|
531
|
+
}
|
|
532
|
+
async function selectHistoryTransaction(conn, tableName, txId) {
|
|
533
|
+
const stmt = await conn.prepare(
|
|
534
|
+
`SELECT MAX(seq) AS seq, MAX(hlc) AS hlc FROM "${tableName}" WHERE tx_id = ? AND hlc != ''`
|
|
535
|
+
);
|
|
536
|
+
const row = await stmt.get(txId);
|
|
537
|
+
if (row?.seq === void 0 || row.seq === null || typeof row.hlc !== "string") return null;
|
|
538
|
+
return { seq: seqFromColumn(row.seq), hlc: row.hlc, txId };
|
|
539
|
+
}
|
|
540
|
+
async function selectHistoryChangesAfterHlc(conn, tableName, origins, hlc) {
|
|
541
|
+
if (origins.length === 0) return [];
|
|
542
|
+
const stmt = await conn.prepare(
|
|
543
|
+
`SELECT seq, table_name, operation, row_id, old_data, new_data FROM "${tableName}"
|
|
544
|
+
WHERE hlc > ? AND node_id IN (${originPlaceholders(origins.length)})
|
|
545
|
+
ORDER BY seq DESC`
|
|
546
|
+
);
|
|
547
|
+
return await stmt.all(hlc, ...origins);
|
|
548
|
+
}
|
|
549
|
+
async function deleteHistoryChangesAfterHlc(conn, tableName, origins, hlc) {
|
|
550
|
+
if (origins.length === 0) return;
|
|
551
|
+
const stmt = await conn.prepare(
|
|
552
|
+
`DELETE FROM "${tableName}" WHERE hlc > ? AND node_id IN (${originPlaceholders(origins.length)})`
|
|
553
|
+
);
|
|
554
|
+
await stmt.run(hlc, ...origins);
|
|
555
|
+
}
|
|
556
|
+
async function deleteStampedChangesUpToSeq(conn, tableName, seq) {
|
|
557
|
+
const stmt = await conn.prepare(`DELETE FROM "${tableName}" WHERE seq <= ? AND hlc != ''`);
|
|
558
|
+
await stmt.run(seq.toString());
|
|
559
|
+
}
|
|
560
|
+
|
|
393
561
|
// src/core/system-catalog/column-versions-table.ts
|
|
394
562
|
var UPSERT_COLUMN_VERSION = `INSERT INTO ${COLUMN_VERSIONS_TABLE} (table_name, row_id, column_name, hlc, node_id)
|
|
395
563
|
VALUES (?, ?, ?, ?, ?)
|
|
@@ -418,6 +586,13 @@ async function selectMaxColumnVersionHlcForRow(conn, tableName, rowId) {
|
|
|
418
586
|
const row = await stmt.get(tableName, rowId);
|
|
419
587
|
return row?.max_hlc ?? null;
|
|
420
588
|
}
|
|
589
|
+
async function deleteColumnVersionsAfterHlc(conn, hlc) {
|
|
590
|
+
const stmt = await conn.prepare(`DELETE FROM ${COLUMN_VERSIONS_TABLE} WHERE hlc > ?`);
|
|
591
|
+
await stmt.run(hlc);
|
|
592
|
+
}
|
|
593
|
+
async function deleteColumnVersions(conn) {
|
|
594
|
+
await conn.exec(`DELETE FROM ${COLUMN_VERSIONS_TABLE}`);
|
|
595
|
+
}
|
|
421
596
|
function prepareUpsertColumnVersion(conn) {
|
|
422
597
|
return conn.prepare(UPSERT_COLUMN_VERSION);
|
|
423
598
|
}
|
|
@@ -428,9 +603,6 @@ function prepareUpsertNewerColumnVersion(conn) {
|
|
|
428
603
|
function prepareDeleteRowColumnVersions(conn) {
|
|
429
604
|
return conn.prepare(`DELETE FROM ${COLUMN_VERSIONS_TABLE} WHERE table_name = ? AND row_id = ?`);
|
|
430
605
|
}
|
|
431
|
-
function prepareDeleteRowColumnVersionsUpToHlc(conn) {
|
|
432
|
-
return conn.prepare(`DELETE FROM ${COLUMN_VERSIONS_TABLE} WHERE table_name = ? AND row_id = ? AND hlc <= ?`);
|
|
433
|
-
}
|
|
434
606
|
|
|
435
607
|
// src/core/system-catalog/device-cursors-table.ts
|
|
436
608
|
async function ensureDeviceCursorsTable(conn) {
|
|
@@ -451,6 +623,19 @@ async function upsertDeviceCursor(conn, deviceId, ackedSeq, updatedAt) {
|
|
|
451
623
|
);
|
|
452
624
|
await stmt.run(deviceId, ackedSeq.toString(), updatedAt);
|
|
453
625
|
}
|
|
626
|
+
async function updateDeviceCursor(conn, deviceId, ackedSeq, updatedAt) {
|
|
627
|
+
const stmt = await conn.prepare(
|
|
628
|
+
`UPDATE "${DEVICE_CURSORS_TABLE}"
|
|
629
|
+
SET acked_seq = max(acked_seq, CAST(? AS INTEGER)), updated_at = ?
|
|
630
|
+
WHERE device_id = ?`
|
|
631
|
+
);
|
|
632
|
+
const result = await stmt.run(ackedSeq.toString(), updatedAt, deviceId);
|
|
633
|
+
return result.changes;
|
|
634
|
+
}
|
|
635
|
+
async function selectDeviceCursorExists(conn, deviceId) {
|
|
636
|
+
const stmt = await conn.prepare(`SELECT 1 AS present FROM "${DEVICE_CURSORS_TABLE}" WHERE device_id = ?`);
|
|
637
|
+
return await stmt.get(deviceId) !== void 0;
|
|
638
|
+
}
|
|
454
639
|
async function deleteExpiredDeviceCursors(conn, changesTable, bounds) {
|
|
455
640
|
assertSafeIdentifier(changesTable);
|
|
456
641
|
const stmt = await conn.prepare(
|
|
@@ -521,20 +706,98 @@ async function upsertPeerAckedSeq(conn, peerNodeId, seq, updatedAt) {
|
|
|
521
706
|
);
|
|
522
707
|
await stmt.run(peerNodeId, seq.toString(), updatedAt);
|
|
523
708
|
}
|
|
709
|
+
async function deletePeerStates(conn) {
|
|
710
|
+
await conn.exec(`DELETE FROM ${PEER_STATE_TABLE}`);
|
|
711
|
+
}
|
|
712
|
+
async function deletePeerState(conn, peerNodeId) {
|
|
713
|
+
const stmt = await conn.prepare(`DELETE FROM ${PEER_STATE_TABLE} WHERE peer_node_id = ?`);
|
|
714
|
+
await stmt.run(peerNodeId);
|
|
715
|
+
}
|
|
524
716
|
async function selectPeerAckedSeq(conn, peerNodeId) {
|
|
525
717
|
const stmt = await conn.prepare(`SELECT last_acked_seq FROM ${PEER_STATE_TABLE} WHERE peer_node_id = ?`);
|
|
526
718
|
const row = await stmt.get(peerNodeId);
|
|
527
|
-
|
|
528
|
-
if (seq === null || seq === void 0) return 0n;
|
|
529
|
-
return typeof seq === "bigint" ? seq : BigInt(String(seq));
|
|
719
|
+
return seqFromColumn(row?.last_acked_seq);
|
|
530
720
|
}
|
|
531
721
|
async function selectMinPeerAckedSeq(conn) {
|
|
532
722
|
const stmt = await conn.prepare(`SELECT MIN(last_acked_seq) AS min_seq, COUNT(*) AS cnt FROM ${PEER_STATE_TABLE}`);
|
|
533
723
|
const row = await stmt.get();
|
|
534
724
|
if (row === void 0 || Number(row.cnt ?? 0) === 0) return null;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
725
|
+
return seqFromColumn(row.min_seq);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/core/system-catalog/replication-history-meta.ts
|
|
729
|
+
var PRIMARY_TERM_START_META_KEY = "replication_primary_term_start";
|
|
730
|
+
var ANCHORED_TERM_META_KEY = "replication_anchored_term";
|
|
731
|
+
var HISTORY_BASE_META_KEY = "replication_history_base";
|
|
732
|
+
var QUARANTINED_META_KEY = "replication_quarantined";
|
|
733
|
+
var DECIMAL_DIGITS = /^\d+$/;
|
|
734
|
+
function parseTermStart(raw) {
|
|
735
|
+
if (raw === null) return null;
|
|
736
|
+
try {
|
|
737
|
+
const parsed = JSON.parse(raw);
|
|
738
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
739
|
+
const term = Reflect.get(parsed, "term");
|
|
740
|
+
const seq = Reflect.get(parsed, "seq");
|
|
741
|
+
if (typeof term !== "string" || typeof seq !== "string") return null;
|
|
742
|
+
if (!DECIMAL_DIGITS.test(term) || !DECIMAL_DIGITS.test(seq)) return null;
|
|
743
|
+
return { term: BigInt(term), seq: BigInt(seq) };
|
|
744
|
+
} catch {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function parseHistoryPoint(raw) {
|
|
749
|
+
if (raw === null) return null;
|
|
750
|
+
try {
|
|
751
|
+
const parsed = JSON.parse(raw);
|
|
752
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
753
|
+
const hlc = Reflect.get(parsed, "hlc");
|
|
754
|
+
const txId = Reflect.get(parsed, "txId");
|
|
755
|
+
if (typeof hlc !== "string" || typeof txId !== "string" || hlc === "") return null;
|
|
756
|
+
return { hlc, txId };
|
|
757
|
+
} catch {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async function selectPrimaryTermStart(conn) {
|
|
762
|
+
await ensureMetaTable(conn);
|
|
763
|
+
return parseTermStart(await selectMetaValue(conn, PRIMARY_TERM_START_META_KEY));
|
|
764
|
+
}
|
|
765
|
+
async function upsertPrimaryTermStart(conn, termStart) {
|
|
766
|
+
await ensureMetaTable(conn);
|
|
767
|
+
await upsertMetaValue(
|
|
768
|
+
conn,
|
|
769
|
+
PRIMARY_TERM_START_META_KEY,
|
|
770
|
+
JSON.stringify({ term: termStart.term.toString(), seq: termStart.seq.toString() })
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
async function deletePrimaryTermStart(conn) {
|
|
774
|
+
await ensureMetaTable(conn);
|
|
775
|
+
await deleteMetaValue(conn, PRIMARY_TERM_START_META_KEY);
|
|
776
|
+
}
|
|
777
|
+
async function selectAnchoredTerm(conn) {
|
|
778
|
+
await ensureMetaTable(conn);
|
|
779
|
+
const raw = await selectMetaValue(conn, ANCHORED_TERM_META_KEY);
|
|
780
|
+
return raw !== null && DECIMAL_DIGITS.test(raw) ? BigInt(raw) : null;
|
|
781
|
+
}
|
|
782
|
+
async function upsertAnchoredTerm(conn, term) {
|
|
783
|
+
await ensureMetaTable(conn);
|
|
784
|
+
await upsertMetaValue(conn, ANCHORED_TERM_META_KEY, term.toString());
|
|
785
|
+
}
|
|
786
|
+
async function selectHistoryBase(conn) {
|
|
787
|
+
await ensureMetaTable(conn);
|
|
788
|
+
return parseHistoryPoint(await selectMetaValue(conn, HISTORY_BASE_META_KEY));
|
|
789
|
+
}
|
|
790
|
+
async function upsertHistoryBase(conn, point) {
|
|
791
|
+
await ensureMetaTable(conn);
|
|
792
|
+
await upsertMetaValue(conn, HISTORY_BASE_META_KEY, point ? JSON.stringify({ hlc: point.hlc, txId: point.txId }) : "");
|
|
793
|
+
}
|
|
794
|
+
async function selectQuarantined(conn) {
|
|
795
|
+
await ensureMetaTable(conn);
|
|
796
|
+
return await selectMetaValue(conn, QUARANTINED_META_KEY) === "true";
|
|
797
|
+
}
|
|
798
|
+
async function upsertQuarantined(conn, quarantined) {
|
|
799
|
+
await ensureMetaTable(conn);
|
|
800
|
+
await upsertMetaValue(conn, QUARANTINED_META_KEY, quarantined ? "true" : "false");
|
|
538
801
|
}
|
|
539
802
|
|
|
540
803
|
// src/core/system-catalog/sync-state-table.ts
|
|
@@ -620,7 +883,7 @@ async function ensureReplicationStateTables(conn) {
|
|
|
620
883
|
}
|
|
621
884
|
|
|
622
885
|
// src/core/system-catalog/staged-changes-table.ts
|
|
623
|
-
function
|
|
886
|
+
function toSeq(value) {
|
|
624
887
|
if (value === null || value === void 0) return null;
|
|
625
888
|
return typeof value === "bigint" ? value : BigInt(String(value));
|
|
626
889
|
}
|
|
@@ -662,14 +925,14 @@ function upsertStagedChangeSql() {
|
|
|
662
925
|
async function selectMaxStagedSeq(conn) {
|
|
663
926
|
const stmt = await conn.prepare(`SELECT MAX(seq) AS seq FROM "${STAGED_CHANGES_TABLE}"`);
|
|
664
927
|
const row = await stmt.get();
|
|
665
|
-
return
|
|
928
|
+
return toSeq(row?.seq);
|
|
666
929
|
}
|
|
667
930
|
async function selectFirstStagedTransactionEnd(conn) {
|
|
668
931
|
const stmt = await conn.prepare(
|
|
669
932
|
`SELECT seq, tx_id FROM "${STAGED_CHANGES_TABLE}" WHERE tx_end = 1 ORDER BY seq ASC LIMIT 1`
|
|
670
933
|
);
|
|
671
934
|
const row = await stmt.get();
|
|
672
|
-
const seq =
|
|
935
|
+
const seq = toSeq(row?.seq);
|
|
673
936
|
if (seq === null) return null;
|
|
674
937
|
return { seq, txId: row?.tx_id ?? "" };
|
|
675
938
|
}
|
|
@@ -688,6 +951,51 @@ async function deleteAllStagedChanges(conn) {
|
|
|
688
951
|
await conn.exec(`DELETE FROM "${STAGED_CHANGES_TABLE}"`);
|
|
689
952
|
}
|
|
690
953
|
|
|
954
|
+
// src/core/system-catalog/staged-push-changes-table.ts
|
|
955
|
+
async function ensureStagedPushChangesTable(conn) {
|
|
956
|
+
await conn.exec(`
|
|
957
|
+
CREATE TABLE IF NOT EXISTS "${STAGED_PUSH_CHANGES_TABLE}" (
|
|
958
|
+
source_node_id TEXT NOT NULL,
|
|
959
|
+
source_seq INTEGER NOT NULL,
|
|
960
|
+
tx_id TEXT NOT NULL,
|
|
961
|
+
change TEXT NOT NULL,
|
|
962
|
+
PRIMARY KEY (source_node_id, source_seq)
|
|
963
|
+
)`);
|
|
964
|
+
}
|
|
965
|
+
function prepareInsertStagedPushChange(conn) {
|
|
966
|
+
return conn.prepare(
|
|
967
|
+
`INSERT OR IGNORE INTO "${STAGED_PUSH_CHANGES_TABLE}" (source_node_id, source_seq, tx_id, change) VALUES (?, ?, ?, ?)`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
async function selectStagedPushTail(conn, sourceNodeId) {
|
|
971
|
+
const stmt = await conn.prepare(
|
|
972
|
+
`SELECT tx_id, source_seq FROM "${STAGED_PUSH_CHANGES_TABLE}"
|
|
973
|
+
WHERE source_node_id = ? ORDER BY source_seq DESC LIMIT 1`
|
|
974
|
+
);
|
|
975
|
+
const row = await stmt.get(sourceNodeId);
|
|
976
|
+
if (row === void 0 || row.source_seq === null || row.source_seq === void 0) return null;
|
|
977
|
+
return { txId: row.tx_id, maxSeq: BigInt(String(row.source_seq)) };
|
|
978
|
+
}
|
|
979
|
+
async function selectStagedPushSeqsInRange(conn, sourceNodeId, fromSeq, toSeq2) {
|
|
980
|
+
const stmt = await conn.prepare(
|
|
981
|
+
`SELECT source_seq FROM "${STAGED_PUSH_CHANGES_TABLE}"
|
|
982
|
+
WHERE source_node_id = ? AND source_seq >= ? AND source_seq <= ?`
|
|
983
|
+
);
|
|
984
|
+
const rows = await stmt.all(sourceNodeId, fromSeq.toString(), toSeq2.toString());
|
|
985
|
+
return new Set(rows.map((row) => String(row.source_seq)));
|
|
986
|
+
}
|
|
987
|
+
async function selectStagedPushChangesAfter(conn, sourceNodeId, afterSeq, limit) {
|
|
988
|
+
const stmt = await conn.prepare(
|
|
989
|
+
`SELECT source_seq, change FROM "${STAGED_PUSH_CHANGES_TABLE}"
|
|
990
|
+
WHERE source_node_id = ? AND source_seq > ? ORDER BY source_seq ASC LIMIT ?`
|
|
991
|
+
);
|
|
992
|
+
return await stmt.all(sourceNodeId, afterSeq.toString(), limit);
|
|
993
|
+
}
|
|
994
|
+
async function deleteStagedPushChanges(conn, sourceNodeId) {
|
|
995
|
+
const stmt = await conn.prepare(`DELETE FROM "${STAGED_PUSH_CHANGES_TABLE}" WHERE source_node_id = ?`);
|
|
996
|
+
await stmt.run(sourceNodeId);
|
|
997
|
+
}
|
|
998
|
+
|
|
691
999
|
// src/core/sync/canonicalise.ts
|
|
692
1000
|
function canonicaliseForChecksum(value) {
|
|
693
1001
|
return canonicaliseValue(value);
|
|
@@ -750,12 +1058,12 @@ var PkResolver = class {
|
|
|
750
1058
|
// src/core/sync/validators.ts
|
|
751
1059
|
var IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
752
1060
|
var SEQ_STRING_RE = /^\d{1,19}$/;
|
|
753
|
-
var SAFE_DDL_RE = /^\s*(CREATE\s+TABLE|ALTER\s+TABLE\s+\S+\s+ADD\s+COLUMN|DROP\s+TABLE|CREATE\s+INDEX|DROP\s+INDEX)\b/i;
|
|
1061
|
+
var SAFE_DDL_RE = /^\s*(CREATE\s+TABLE|ALTER\s+TABLE\s+\S+\s+ADD\s+COLUMN|DROP\s+TABLE|CREATE\s+(?:UNIQUE\s+)?INDEX|DROP\s+INDEX)\b/i;
|
|
754
1062
|
var DDL_DENY_RE = /\b(load_extension|ATTACH|randomblob|zeroblob|writefile|readfile|fts3_tokenizer)\b/i;
|
|
755
1063
|
function validateIdentifier(name) {
|
|
756
1064
|
return IDENTIFIER_RE.test(name);
|
|
757
1065
|
}
|
|
758
|
-
var CREATE_INDEX_TARGETS_RE = /^\s*CREATE\s+INDEX(?:\s+IF\s+NOT\s+EXISTS)?\s+(?:["`[]?[A-Za-z_][A-Za-z0-9_]*["`\]]?\s*\.\s*)?["`[]?([A-Za-z_][A-Za-z0-9_]*)["`\]]?\s+ON\s+(?:["`[]?[A-Za-z_][A-Za-z0-9_]*["`\]]?\s*\.\s*)?["`[]?([A-Za-z_][A-Za-z0-9_]*)/i;
|
|
1066
|
+
var CREATE_INDEX_TARGETS_RE = /^\s*CREATE\s+(?:UNIQUE\s+)?INDEX(?:\s+IF\s+NOT\s+EXISTS)?\s+(?:["`[]?[A-Za-z_][A-Za-z0-9_]*["`\]]?\s*\.\s*)?["`[]?([A-Za-z_][A-Za-z0-9_]*)["`\]]?\s+ON\s+(?:["`[]?[A-Za-z_][A-Za-z0-9_]*["`\]]?\s*\.\s*)?["`[]?([A-Za-z_][A-Za-z0-9_]*)/i;
|
|
759
1067
|
var DDL_TARGET_RE = /^\s*(?:CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?|ALTER\s+TABLE|DROP\s+TABLE(?:\s+IF\s+EXISTS)?|DROP\s+INDEX(?:\s+IF\s+EXISTS)?)\s+(?:["`[]?[A-Za-z_][A-Za-z0-9_]*["`\]]?\s*\.\s*)?["`[]?([A-Za-z_][A-Za-z0-9_]*)/i;
|
|
760
1068
|
function ddlTargetsReservedIdentifier(sql) {
|
|
761
1069
|
const indexTargets = CREATE_INDEX_TARGETS_RE.exec(sql);
|
|
@@ -782,4 +1090,54 @@ function extractDroppedTable(sql) {
|
|
|
782
1090
|
return m?.[1] ?? null;
|
|
783
1091
|
}
|
|
784
1092
|
|
|
785
|
-
|
|
1093
|
+
// src/core/sync/change-seqs.ts
|
|
1094
|
+
var MAX_CHANGE_SEQ = 9223372036854775807n;
|
|
1095
|
+
function assertSeqInRange(seq, label) {
|
|
1096
|
+
if (seq < 0n || seq > MAX_CHANGE_SEQ) {
|
|
1097
|
+
throw new BatchValidationError(`${label} must be a sequence from 0 to ${MAX_CHANGE_SEQ}`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
function explicitSeqs(batch, changeSeqs) {
|
|
1101
|
+
if (changeSeqs.length !== batch.changes.length) {
|
|
1102
|
+
throw new BatchValidationError("changeSeqs must hold one sequence for each change");
|
|
1103
|
+
}
|
|
1104
|
+
const seqs = [];
|
|
1105
|
+
let previous = null;
|
|
1106
|
+
for (const [index, seq] of changeSeqs.entries()) {
|
|
1107
|
+
assertSeqInRange(seq, `changeSeqs[${index}]`);
|
|
1108
|
+
if (previous !== null && seq <= previous) {
|
|
1109
|
+
throw new BatchValidationError("changeSeqs must rise strictly");
|
|
1110
|
+
}
|
|
1111
|
+
previous = seq;
|
|
1112
|
+
seqs.push([seq]);
|
|
1113
|
+
}
|
|
1114
|
+
if (changeSeqs[0] !== batch.fromSeq || previous !== batch.toSeq) {
|
|
1115
|
+
throw new BatchValidationError("changeSeqs must start at fromSeq and end at toSeq");
|
|
1116
|
+
}
|
|
1117
|
+
return seqs;
|
|
1118
|
+
}
|
|
1119
|
+
function attributeChangeSeqs(batch) {
|
|
1120
|
+
assertSeqInRange(batch.fromSeq, "fromSeq");
|
|
1121
|
+
assertSeqInRange(batch.toSeq, "toSeq");
|
|
1122
|
+
if (batch.fromSeq > batch.toSeq) {
|
|
1123
|
+
throw new BatchValidationError("fromSeq must not exceed toSeq");
|
|
1124
|
+
}
|
|
1125
|
+
if (batch.changeSeqs !== void 0) {
|
|
1126
|
+
return explicitSeqs(batch, batch.changeSeqs);
|
|
1127
|
+
}
|
|
1128
|
+
if (batch.txContinues === true) {
|
|
1129
|
+
throw new BatchValidationError("txContinues requires changeSeqs");
|
|
1130
|
+
}
|
|
1131
|
+
const count = batch.changes.length;
|
|
1132
|
+
if (batch.toSeq - batch.fromSeq + 1n === BigInt(count)) {
|
|
1133
|
+
return batch.changes.map((_, index) => [batch.fromSeq + BigInt(index)]);
|
|
1134
|
+
}
|
|
1135
|
+
return batch.changes.map((_, index) => {
|
|
1136
|
+
const seqs = [];
|
|
1137
|
+
if (index === 0) seqs.push(batch.fromSeq);
|
|
1138
|
+
if (index === count - 1 && (index !== 0 || batch.toSeq !== batch.fromSeq)) seqs.push(batch.toSeq);
|
|
1139
|
+
return seqs;
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
export { HLC, IDENTIFIER_RE, MAX_CHANGE_SEQ, PkResolver, SEQ_STRING_RE, TABLE_XINFO_HIDDEN, assertSafeIdentifier, attributeChangeSeqs, canonicaliseForChecksum, deleteAllReplacedRows, deleteAllStagedChanges, deleteAppliedChangesAfterSeq, deleteChangesBeforeUpToSeqSql, deleteColumnVersions, deleteColumnVersionsAfterHlc, deleteExpiredDeviceCursors, deleteHistoryChangesAfterHlc, deleteMetaValue, deletePeerState, deletePeerStates, deletePrimaryTermStart, deleteReplacedRowsOfTable, deleteStagedChangesUpToSeq, deleteStagedPushChanges, deleteStampedChangesUpToSeq, deleteSyncTableStates, ensureBatchApplyTables, ensureChangesTable, ensureDeviceCursorsTable, ensureMetaTable, ensureMigrationsTable, ensureReplacedRowsTable, ensureReplicationStateTables, ensureStagedChangesTable, ensureStagedPushChangesTable, extractDroppedTable, highestMigrationVersion, indexColumns, insertAppliedChangesFromStagedPush, insertDdlChange, insertMetaValueIfAbsent, prepareDeleteMigration, prepareDeleteRowColumnVersions, prepareInsertAppliedChange, prepareInsertMigration, prepareInsertStagedPushChange, prepareUpdateMigrationChecksum, prepareUpsertColumnVersion, prepareUpsertNewerColumnVersion, primaryKeyColumns, referencedTables, replaceMigrationHistory, selectAnchoredTerm, selectAppliedMigrations, selectAppliedMigrationsNewestFirst, selectAppliedSourceSeqsInRange, selectChangesAfterSeqSql, selectCompletedSyncTableNames, selectCountOutboundChanges, selectCountTableRows, selectDeviceCursorExists, selectFirstStagedTransactionEnd, selectHistoryBase, selectHistoryChangesAfterHlc, selectHistoryTransaction, selectIndexSql, selectMaxAppliedSourceSeq, selectMaxAppliedSourceSeqByNode, selectMaxAssignedChangeSeq, selectMaxChangeHlc, selectMaxChangeSeq, selectMaxColumnVersionHlc, selectMaxColumnVersionHlcForRow, selectMaxHistoryChangeSeq, selectMaxNodeChangeSeq, selectMaxRowChangeHlc, selectMaxStagedSeq, selectMetaValue, selectMinChangeSeqSql, selectMinDeviceCursorBoundary, selectMinPeerAckedSeq, selectNewestHistoryChange, selectNodeChangesAfterSeqSql, selectOrdinaryTableName, selectOutboundChangesSql, selectOutboundHistoryChangesSql, selectPeerAckedSeq, selectPrimaryTermStart, selectQuarantined, selectStagedChangesInRange, selectStagedPushChangesAfter, selectStagedPushSeqsInRange, selectStagedPushTail, selectSyncMetaRow, selectTableExists, selectTableSql, selectTablesChangesInRangeSql, selectTriggerSql, selectUserSchemaObjects, selectUserTableNames, setForeignKeysEnabled, tableColumnsWithGenerated, tableIndexList, tableInfoRows, tablePkColumns, updateChangeStampsAboveMetaSeqSql, updateChangeStampsAfterSeqSql, updateDeviceCursor, upsertAnchoredTerm, upsertDeviceCursor, upsertHistoryBase, upsertMetaMaxChangeSeqSql, upsertMetaValue, upsertMetaValueAndMaxChangeSeqSql, upsertPeerAckedSeq, upsertPrimaryTermStart, upsertQuarantined, upsertStagedChangeSql, upsertSyncMeta, upsertSyncTableStatus, validateDdlSafety, validateIdentifier };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { withRowCounts, rowCountsOf } from './chunk-E2GRX4ZD.mjs';
|
|
2
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
3
|
+
|
|
4
|
+
// src/core/worker/protocol.ts
|
|
5
|
+
function toWorkerGroupStatement(statement) {
|
|
6
|
+
return {
|
|
7
|
+
sql: statement.sql,
|
|
8
|
+
params: statement.params ? [...statement.params] : [],
|
|
9
|
+
...statement.trusted === true ? { trusted: true } : {}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
var WORKER_CANCELLED_CODE = "WRITER_WORKER_CANCELLED";
|
|
13
|
+
var WORKER_BEGIN_FAILED_CODE = "WRITER_WORKER_BEGIN_FAILED";
|
|
14
|
+
var KINDS_THAT_CARRY_BEGIN = /* @__PURE__ */ new Set([
|
|
15
|
+
"exec",
|
|
16
|
+
"run",
|
|
17
|
+
"get",
|
|
18
|
+
"all",
|
|
19
|
+
"allRaw",
|
|
20
|
+
"allCounted",
|
|
21
|
+
"getCounted",
|
|
22
|
+
"allRawCounted",
|
|
23
|
+
"runBatch",
|
|
24
|
+
"runBatchSummary"
|
|
25
|
+
]);
|
|
26
|
+
function canCarryBegin(kind) {
|
|
27
|
+
return KINDS_THAT_CARRY_BEGIN.has(kind);
|
|
28
|
+
}
|
|
29
|
+
function leftTransactionUnbegun(error) {
|
|
30
|
+
return error.code === WORKER_BEGIN_FAILED_CODE || error.code === WORKER_CANCELLED_CODE;
|
|
31
|
+
}
|
|
32
|
+
var WORKER_COPY_STOPPED_CODE = "WRITER_WORKER_COPY_STOPPED";
|
|
33
|
+
function serializeError(err) {
|
|
34
|
+
if (err instanceof Error) {
|
|
35
|
+
const { code, sql } = err;
|
|
36
|
+
return withRowCounts(
|
|
37
|
+
{
|
|
38
|
+
message: err.message,
|
|
39
|
+
name: err.name,
|
|
40
|
+
...typeof code === "string" ? { code } : {},
|
|
41
|
+
...typeof sql === "string" ? { sql } : {},
|
|
42
|
+
...err instanceof SirannonError ? { sirannonError: true } : {}
|
|
43
|
+
},
|
|
44
|
+
rowCountsOf(err)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return { message: String(err) };
|
|
48
|
+
}
|
|
49
|
+
function deserializeError(error) {
|
|
50
|
+
const err = error.sirannonError === true && error.code !== void 0 ? new SirannonError(error.message, error.code) : Object.assign(new Error(error.message), error.code === void 0 ? {} : { code: error.code });
|
|
51
|
+
if (error.name) err.name = error.name;
|
|
52
|
+
return withRowCounts(err, rowCountsOf(error));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { WORKER_BEGIN_FAILED_CODE, WORKER_CANCELLED_CODE, WORKER_COPY_STOPPED_CODE, canCarryBegin, deserializeError, leftTransactionUnbegun, serializeError, toWorkerGroupStatement };
|