@voltro/sql-mssql 0.85.0 → 0.86.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/dist/index.js CHANGED
@@ -1,824 +1,16 @@
1
- import { MssqlClient as e } from "@effect/sql-mssql";
2
- import { TYPES as t } from "tedious";
3
- import { Config as n, Duration as r, Effect as i, Layer as a, ManagedRuntime as o, Option as s, Redacted as c } from "effect";
4
- import { resolveParameter as l } from "tedious/lib/data-type";
5
- import { CDC_OFFSETS_TABLE as u, DEFAULT_ACQUIRE_TIMEOUT_MS as d, EagerCardinalityError as f, MSSQL_TRANSIENT_NUMBERS as p, SHARED_TRANSACTION_SERVICE as m, _voltroMssqlCdcOffsetsTable as h, attachEagerLoads as g, attributionFields as _, bulkInsertLimitsFor as v, causeBranches as y, chunkRowsForInsert as b, compileEagerJson as x, compileJsonPatch as S, compilePredicate as C, compileRawFragment as w, compileSelect as T, decodeRowsFromSchema as E, encodeRowForSchema as D, externalChangeEvent as ee, hasEagerLoads as te, isTableReactive as O, jsonPatchAssignments as ne, makeEagerFallbackReporter as re, makeTransactionAbortTracker as ie, observeDbOp as k, onTransaction as ae, qualifyTable as oe, quoteIdent as se, raiseChangeListenerCeiling as ce, recordsTable as A, requireTable as le, rowKeyColumns as j, runSavepoint as ue, runStoreTransaction as de, runWriteRecorders as fe, sqlError as pe, stampGeneratedId as M, stampGeneratedIds as me, upsertUpdatePatch as N, upsertUpdateValues as P, withCapturedAttribution as he } from "@voltro/database";
6
- import { EventEmitter as ge } from "node:events";
7
- import { createLogger as F } from "@voltro/logger";
8
- import { SqlClient as I } from "effect/sql/SqlClient";
9
- //#region src/numberParameter.ts
10
- var L = (e) => typeof e == "bigint" ? t.BigInt : typeof e == "number" && Number.isSafeInteger(e) ? e >= -2147483648 && e <= 2147483647 ? t.Int : t.BigInt : t.Float, _e = {
11
- ...t.Int,
12
- declaration: (e) => L(e.value).declaration(e),
13
- resolve: (e, t, n) => l({
14
- ...e,
15
- type: L(e.value)
16
- }, t, n).data,
17
- writeTypeInfo: (e, t, n) => L(t.value).writeTypeInfo(e, t, n),
18
- writeValue: (e, t, n) => L(t.value).writeValue(e, t, n),
19
- validate: (e, t, n) => {
20
- if (typeof e == "number" && !Number.isFinite(e)) throw TypeError("SQL Server numeric parameters must be finite.");
21
- return L(e).validate(e, t, n);
22
- }
23
- }, ve = {
24
- ...t.VarBinary,
25
- resolveLength: (e) => Math.max(1, t.VarBinary.resolveLength(e)),
26
- validate: (e, n, r) => t.VarBinary.validate(e instanceof Uint8Array && !Buffer.isBuffer(e) ? Buffer.from(e.buffer, e.byteOffset, e.byteLength) : e, n, r)
27
- }, ye = {
28
- ...e.defaultParameterTypes,
29
- null: t.NVarChar,
30
- Date: t.DateTime2,
31
- string: t.NVarChar,
32
- number: _e,
33
- Uint8Array: ve
34
- }, be = (e) => {
35
- let t = e.acquireTimeoutMs ?? d;
36
- return t > 0 ? t : void 0;
37
- }, R = (t) => {
38
- let i = be(t);
39
- return e.layerConfig({
40
- transactionService: n.succeed(m),
41
- server: n.succeed(t.server),
42
- parameterTypes: n.succeed(ye),
43
- ...i === void 0 ? {} : { connectTimeout: n.succeed(r.millis(i)) },
44
- ...t.port === void 0 ? {} : { port: n.succeed(t.port) },
45
- ...t.database === void 0 ? {} : { database: n.succeed(t.database) },
46
- ...t.username === void 0 ? {} : { username: n.succeed(t.username) },
47
- ...t.password === void 0 ? {} : { password: n.succeed(c.make(t.password)) },
48
- encrypt: n.succeed(t.encrypt ?? !1),
49
- ...t.trustServer === void 0 ? {} : { trustServer: n.succeed(t.trustServer) },
50
- ...t.maxConnections === void 0 ? {} : { maxConnections: n.succeed(t.maxConnections) }
51
- });
52
- }, xe = (e) => {
53
- let t = e.get("sslmode");
54
- if (t !== null) {
55
- if (t === "require") return !0;
56
- if (t === "disable") return !1;
57
- throw Error(`DB_URL '?sslmode=${t}' is not supported by the mssql dialect — use 'require' (TLS without certificate verification) or 'disable' (plaintext).`);
58
- }
59
- for (let t of ["ssl", "encrypt"]) {
60
- let n = e.get(t);
61
- if (n !== null) {
62
- if (n === "true" || n === "1") return !0;
63
- if (n === "false" || n === "0") return !1;
64
- throw Error(`DB_URL '?${t}=${n}' is not supported by the mssql dialect — use 'true'/'1' or 'false'/'0'.`);
65
- }
66
- }
67
- }, z = (e) => {
68
- let t = e.acquireTimeoutMs === void 0 ? {} : { acquireTimeoutMs: e.acquireTimeoutMs }, n = (e) => e === void 0 ? {} : {
69
- encrypt: e,
70
- ...e ? { trustServer: !0 } : {}
71
- };
72
- if (e.url) {
73
- let r = new URL(e.url);
74
- return {
75
- server: r.hostname || "localhost",
76
- port: r.port ? Number(r.port) : 1433,
77
- username: decodeURIComponent(r.username || "sa"),
78
- password: decodeURIComponent(r.password || ""),
79
- database: r.pathname.replace(/^\//, "") || "master",
80
- ...n(e.ssl ?? xe(r.searchParams)),
81
- ...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
82
- ...t
83
- };
84
- }
85
- return {
86
- server: e.host ?? "localhost",
87
- port: e.port ?? 1433,
88
- username: e.username ?? "sa",
89
- password: e.password ?? "",
90
- database: e.database ?? "master",
91
- ...n(e.ssl),
92
- ...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
93
- ...t
94
- };
95
- }, B = (e) => R(z(e)), V = (e, t) => i.gen(function* () {
96
- let n = yield* e`
97
- SELECT name, system_type_id FROM sys.columns
98
- WHERE object_id = OBJECT_ID(${e`${e(t)}`.compile()[0]}, 'U')
99
- ORDER BY column_id
100
- `.withoutTransform;
101
- return n.length === 0 ? yield* pe(`Cannot inspect result columns for ${t}`) : n;
102
- }), H = (e, t, n) => e.join(", ", !1)(t.map((t) => {
103
- let r = e.literal(`[${t.name.replaceAll("]", "]]")}]`), i = n ? e`${e(n)}.${r}` : r;
104
- return t.system_type_id === 106 || t.system_type_id === 108 ? e`CONVERT(varchar(80), ${i}) AS ${r}` : i;
105
- })), U = (e, t, n, r = !1) => i.uninterruptible(i.gen(function* () {
106
- let i = e.literal("[#voltro_write_output]"), a = yield* V(e, t), o = e.join(", ", !1)(a.map((t) => {
107
- let n = e.literal(`[${t.name.replaceAll("]", "]]")}]`);
108
- return t.system_type_id === 189 ? e`CONVERT(binary(8), ${n}) AS ${n}` : n;
109
- })), s = H(e, a);
110
- return yield* e`
111
- SELECT TOP (0) ${r ? e.literal("CAST(NULL AS nvarchar(10)) AS [__action],") : e.literal("")} source.* INTO ${i}
112
- FROM (SELECT ${o} FROM ${e(t)} UNION ALL SELECT ${o} FROM ${e(t)}) AS source;
113
- ${n(i)};
114
- SELECT ${r ? e.literal("[__action],") : e.literal("")}${s} FROM ${i};
115
- DROP TABLE ${i};
116
- `;
117
- })), Se = p, W = (e) => {
118
- let t = e;
119
- for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
120
- let e = t.number;
121
- if (typeof e == "number") return String(e);
122
- let n = t.code;
123
- if (typeof n == "string") return n;
124
- t = t.cause;
125
- }
126
- }, G = (e) => y(e).some((e) => {
127
- let t = W(e);
128
- return t !== void 0 && Se.has(t);
129
- }), Ce = /* @__PURE__ */ new Set(["2601", "2627"]), K = (e) => y(e).some((e) => {
130
- let t = W(e);
131
- return t !== void 0 && Ce.has(t);
132
- }), q = (e) => G(e) ? "retry" : "noRetry", we = {
133
- I: "insert",
134
- U: "update",
135
- D: "delete"
136
- }, J = (e, t) => e(i.gen(function* () {
137
- let e = yield* I;
138
- if (((yield* e`
139
- SELECT COUNT(*) AS ${e("on")}
140
- FROM sys.change_tracking_databases
141
- WHERE database_id = DB_ID()`)[0]?.on ?? 0) === 0) return yield* i.fail(/* @__PURE__ */ Error("mssql Change Tracking CDC requires CT enabled on the database. Run `ALTER DATABASE <db> SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)`, or set CDC=0 for inline emit."));
142
- let n = [];
143
- for (let r of t) {
144
- if (((yield* e`
145
- SELECT CASE WHEN OBJECT_ID(${r}, 'U') IS NULL THEN 0 ELSE 1 END AS ${e("present")}`)[0]?.present ?? 0) === 0) {
146
- n.push(r);
147
- continue;
148
- }
149
- yield* e.unsafe(`IF NOT EXISTS (
150
- SELECT 1 FROM sys.change_tracking_tables
151
- WHERE object_id = OBJECT_ID(${Y(r)})
152
- )
153
- ALTER TABLE ${X(r)} ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = OFF)`);
154
- }
155
- return n;
156
- })), Y = (e) => `'${e.replace(/'/g, "''")}'`, X = (e) => `[${e.replace(/\]/g, "]]")}]`, Z = async (e) => {
157
- let t = F({ scope: "voltro:mssql:cdc" }), n = e.pollIntervalMs ?? 500, r = await J(e.run, e.tables), a = /* @__PURE__ */ new Set(), o = async () => (await e.run(i.gen(function* () {
158
- return yield* (yield* I)`SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_CURRENT_VERSION()) AS v`;
159
- })))[0]?.v ?? "0", s = e.startVersion ?? await o(), c = !1, l = null, u = !1, d = (e, t) => BigInt(e) > BigInt(t), f = async (n, r) => {
160
- let c = await e.run(i.gen(function* () {
161
- let e = yield* I, t = e.unsafe(X(n)), i = (yield* e`
162
- SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(${e.unsafe(Y(n))}))) AS floor`)[0]?.floor, o = r;
163
- if (i != null && BigInt(r) < BigInt(i)) {
164
- if (!a.has(n)) return {
165
- resync: !0,
166
- rows: []
167
- };
168
- o = i;
169
- }
170
- return {
171
- resync: !1,
172
- rows: yield* e`
173
- SELECT ct.SYS_CHANGE_OPERATION AS __ct_op, ct.id AS __ct_id, ${H(e, yield* V(e, n), "t")}
174
- FROM CHANGETABLE(CHANGES ${t}, ${BigInt(o)}) AS ct
175
- LEFT JOIN ${t} AS t ON t.id = ct.id
176
- ORDER BY ct.SYS_CHANGE_VERSION`.unprepared
177
- };
178
- }));
179
- if (c.resync) {
180
- t.warn("cdc: CT retention floor passed our cursor — resyncing to current version", { table: n }), s = await o(), e.onResync?.(), e.onVersion?.(s);
181
- return;
182
- }
183
- for (let t of c.rows) {
184
- let r = we[t.__ct_op];
185
- if (!r) continue;
186
- let { __ct_op: i, __ct_id: a, ...o } = t;
187
- if (r === "delete") e.onChange({
188
- table: n,
189
- op: "delete",
190
- old: { id: a },
191
- new: null,
192
- netChange: !0
193
- });
194
- else {
195
- let t = E([o], n, "mssql")[0];
196
- e.onChange({
197
- table: n,
198
- op: r,
199
- old: null,
200
- new: t,
201
- netChange: !0
202
- });
203
- }
204
- }
205
- }, p = async () => {
206
- if (!(c || u)) {
207
- u = !0;
208
- try {
209
- if (r.length > 0) {
210
- let t = r;
211
- r = await J(e.run, t);
212
- for (let e of t) r.includes(e) || a.add(e);
213
- }
214
- let t = await o();
215
- if (d(t, s)) {
216
- for (let t of e.tables) {
217
- if (c) break;
218
- r.includes(t) || (await f(t, s), a.delete(t));
219
- }
220
- s = d(t, s) ? t : s, e.onVersion?.(s);
221
- }
222
- } catch (n) {
223
- c || (t.warn("cdc: poll cycle failed — retrying next tick", {}, n), e.onError?.(n));
224
- } finally {
225
- u = !1, c || (l = setTimeout(() => {
226
- p();
227
- }, n));
228
- }
229
- }
230
- };
231
- return l = setTimeout(() => {
232
- p();
233
- }, n), l.unref && l.unref(), t.info("cdc: Change Tracking reader attached", {
234
- tables: e.tables.length,
235
- from: e.startVersion ?? s
236
- }), {
237
- stop: async () => {
238
- c = !0, l &&= (clearTimeout(l), null);
239
- },
240
- currentVersion: () => s
241
- };
242
- }, Q = F({ scope: "voltro:mssql" }), $ = async (e) => {
243
- let t = e.changeStrategy ?? "inline", n = t;
244
- t === "cdc" && !e.cdcConfig && (Q.warn("changeStrategy='cdc' requires cdcConfig (replicaId + includeTables); falling back to 'inline'."), n = "inline");
245
- let r = e.tracerLayer ? a.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, i = o.make(r), s = new Te(await i.runPromise(I), i, n);
246
- return n === "cdc" && e.cdcConfig && await s.startCdcConsumer(e.cdcConfig), s;
247
- }, Te = class e {
248
- sql;
249
- runtime;
250
- changeStrategy;
251
- namespace;
252
- emitter;
253
- inflightTxns = 0;
254
- reportEagerFallback = re(Q);
255
- cdcHandle = null;
256
- cdcCheckpointTimer = null;
257
- cdcPendingVersion = null;
258
- cdcReplicaId = "";
259
- cdcStreamName = "default";
260
- constructor(e, t, n = "inline", r = null, i) {
261
- this.sql = e, this.runtime = t, this.changeStrategy = n, this.namespace = r, this.emitter = i ?? new ge(), ce(this.emitter);
262
- }
263
- get inlineEmit() {
264
- return this.changeStrategy === "inline";
265
- }
266
- withNamespace(t) {
267
- return t === this.namespace ? this : new e(this.sql, this.runtime, this.changeStrategy, t, this.emitter);
268
- }
269
- nsT(e) {
270
- return oe(this.namespace, e);
271
- }
272
- __mssqlReplicationFriend = { runEffect: (e) => this.runtime.runPromise(e) };
273
- async executeQuery(e, t) {
274
- let n = T(e, this.sql, this.namespace), r = t ? i.provideService(n, this.sql.transactionService, t) : n, a = await k("mssql", "select", () => this.runtime.runPromise(r));
275
- return E(a, e.table, "mssql");
276
- }
277
- decodeWritten(e, t) {
278
- return E([t], e, "mssql")[0] ?? t;
279
- }
280
- async executeInsert(e, t, n, r, a) {
281
- t = M(e, t);
282
- let o = this.sql, s = U(o, this.nsT(e), (n) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(D(t, e)).returning(o`INSERTED.* INTO ${n}`)}`), c = n ? i.provideService(s, this.sql.transactionService, n) : s, l = (await this.runtime.runPromise(c))[0];
283
- if (!l) throw Error(`MssqlStore.insert: row not returned post-insert in '${e}'`);
284
- let u = this.decodeWritten(e, l);
285
- return await this.routeEvent({
286
- table: e,
287
- op: "insert",
288
- old: null,
289
- new: u
290
- }, r, n, a), u;
291
- }
292
- async executeUpdate(e, t, n, r, a, o) {
293
- let s = this.sql, c = U(s, this.nsT(e), (r) => s`UPDATE ${s(this.nsT(e))} SET ${s.update(D(n, e)).returning(s`INSERTED.* INTO ${r}`)} WHERE ${s.and(j(t).map(([e, t]) => s`${s(e)} = ${t}`))}`), l = r ? i.provideService(c, this.sql.transactionService, r) : c, u = (await this.runtime.runPromise(l))[0];
294
- if (!u) return null;
295
- let d = this.decodeWritten(e, u);
296
- return await this.routeEvent({
297
- table: e,
298
- op: "update",
299
- old: null,
300
- new: d
301
- }, a, r, o), d;
302
- }
303
- async executeInsertMany(e, t, n, r, a) {
304
- if (t = me(e, t), t.length === 0) return [];
305
- let o = this.sql, s = b(t, v("mssql")), c = (t) => U(o, this.nsT(e), (n) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t.map((t) => D(t, e))).returning(o`INSERTED.* INTO ${n}`)}`), l;
306
- if (s.length === 1) {
307
- let e = c(s[0]), t = n ? i.provideService(e, this.sql.transactionService, n) : e;
308
- l = await this.runtime.runPromise(t);
309
- } else if (n) {
310
- let e = [];
311
- for (let t of s) e.push(...await this.runtime.runPromise(i.provideService(c(t), this.sql.transactionService, n)));
312
- l = e;
313
- } else l = await this.runtime.runPromise(o.withTransaction(i.map(i.forEach(s, c, { concurrency: 1 }), (e) => e.flat())));
314
- let u = t.map((e) => e.id), d = E(l, e, "mssql"), f = new Map(d.map((e) => [e.id, e])), p = u.map((e) => f.get(e)).filter((e) => e !== void 0), m = p.length === l.length ? p : d;
315
- for (let t of m) await this.routeEvent({
316
- table: e,
317
- op: "insert",
318
- old: null,
319
- new: t
320
- }, r, n, a);
321
- return m;
322
- }
323
- async executePatchJson(e, t, n, r, a, o, s, c) {
324
- let l = this.sql, u = D(ne(t, n, c), e), d = l.csv([S(l, "mssql", n, r), ...Object.entries(u).map(([e, t]) => l`${l(e)} = ${t}`)]), f = U(l, this.nsT(e), (n) => l`UPDATE ${l(this.nsT(e))} SET ${d} OUTPUT INSERTED.* INTO ${n} WHERE ${l.and(j(t).map(([e, t]) => l`${l(e)} = ${t}`))}`), p = a ? i.provideService(f, this.sql.transactionService, a) : f, m = (await this.runtime.runPromise(p))[0];
325
- if (!m) return null;
326
- let h = this.decodeWritten(e, m);
327
- return await this.routeEvent({
328
- table: e,
329
- op: "update",
330
- old: null,
331
- new: h
332
- }, o, a, s), h;
333
- }
334
- async executeDelete(e, t, n, r, a) {
335
- let o = this.sql, s = U(o, this.nsT(e), (n) => o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* INTO ${n} WHERE ${o.and(j(t).map(([e, t]) => o`${o(e)} = ${t}`))}`), c = n ? i.provideService(s, this.sql.transactionService, n) : s, l = (await this.runtime.runPromise(c))[0];
336
- if (!l) return !1;
337
- let u = this.decodeWritten(e, l);
338
- return await this.routeEvent({
339
- table: e,
340
- op: "delete",
341
- old: u,
342
- new: null
343
- }, r, n, a), !0;
344
- }
345
- async appendInTxn(e, t, n) {
346
- let r = this.sql, a = r`INSERT INTO ${r(this.nsT(e))} ${r.insert(D(t, e))}`;
347
- await this.runtime.runPromise(n ? i.provideService(a, this.sql.transactionService, n) : a);
348
- }
349
- async maxInTxn(e, t, n, r) {
350
- let a = this.sql, o = Object.entries(n).map(([e, t]) => a`${a(e)} = ${t}`), s = a`SELECT MAX(${a(t)}) AS ${a("m")} FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = (await this.runtime.runPromise(r ? i.provideService(s, this.sql.transactionService, r) : s))[0]?.m;
351
- return c == null ? null : Number(c);
352
- }
353
- async routeEvent(e, t, n = null, r) {
354
- e = {
355
- ..._(r),
356
- ...e
357
- }, A(e.table) && await fe({
358
- append: (e, t) => this.appendInTxn(e, t, n),
359
- maxOf: (e, t, r) => this.maxInTxn(e, t, r, n)
360
- }, {
361
- table: e.table,
362
- op: e.op,
363
- next: e.new,
364
- prev: e.old,
365
- traceId: e.traceId,
366
- subjectId: e.subjectId
367
- }), O(e.table) && (t === null ? this.inlineEmit && this.emitter.emit("change", e) : t.push(e));
368
- }
369
- query(e) {
370
- return this.runWithEager(e, null);
371
- }
372
- raw(e, t) {
373
- return this.rawOn(e, null);
374
- }
375
- rawOn(e, t) {
376
- let n = w(e, this.sql), r = t ? i.provideService(n, this.sql.transactionService, t) : n;
377
- return k("mssql", "raw", () => this.runtime.runPromise(r));
378
- }
379
- async runWithEager(e, t) {
380
- if (!te(e)) return this.executeQuery(e, t);
381
- let n = this.namespace === null ? x(e, this.sql, "mssql") : null;
382
- if (n !== null) try {
383
- let e = t ? i.provideService(n.fragment, this.sql.transactionService, t) : n.fragment, r = await k("mssql", "select", () => this.runtime.runPromise(e));
384
- return n.decode(r);
385
- } catch (t) {
386
- if (t instanceof f) throw t;
387
- this.reportEagerFallback({
388
- dialect: "mssql",
389
- table: e.table,
390
- reason: "execute-failed",
391
- error: t
392
- });
393
- }
394
- else this.reportEagerFallback({
395
- dialect: "mssql",
396
- table: e.table,
397
- reason: "not-compilable"
398
- });
399
- let r = await this.executeQuery(e, t);
400
- return g(r, e.eager, e.sourceTable ?? le(e.table), (e) => this.executeQuery(e, t));
401
- }
402
- getInternalRunWithEager() {
403
- return this.runWithEager.bind(this);
404
- }
405
- localWrite(e, t, n) {
406
- let r = (e) => A(t) ? this.writeWithRecorders(n, e) : n(e, null, null);
407
- return k("mssql", e, () => he(r));
408
- }
409
- async writeWithRecorders(e, t) {
410
- let n = [], r = this.sql, a = await this.runtime.runPromise(r.withTransaction(i.flatMap(i.serviceOption(this.sql.transactionService), (r) => s.isNone(r) ? i.fail(/* @__PURE__ */ Error("MssqlStore.write: TransactionConnection missing.")) : i.tryPromise({
411
- try: () => e(t, r.value, n),
412
- catch: (e) => e
413
- }))));
414
- for (let e of n) this.emitChange(e);
415
- return a;
416
- }
417
- insert(e, t) {
418
- return this.localWrite("insert", e, (n, r, i) => this.executeInsert(e, t, r, i, n));
419
- }
420
- insertMany(e, t) {
421
- return this.localWrite("insert", e, (n, r, i) => this.executeInsertMany(e, t, r, i, n));
422
- }
423
- patchJson(e, t, n, r, i) {
424
- return this.localWrite("update", e, (a, o, s) => this.executePatchJson(e, t, n, r, o, s, a, i));
425
- }
426
- update(e, t, n) {
427
- return this.localWrite("update", e, (r, i, a) => this.executeUpdate(e, t, n, i, a, r));
428
- }
429
- delete(e, t) {
430
- return this.localWrite("delete", e, (n, r, i) => this.executeDelete(e, t, r, i, n));
431
- }
432
- updateMany(e, t, n) {
433
- return this.executeUpdateMany(e, t, n, null, null);
434
- }
435
- deleteMany(e, t) {
436
- return this.executeDeleteMany(e, t, null, null);
437
- }
438
- async executeUpdateMany(e, t, n, r, a, o) {
439
- let s = this.sql, c = C(n.where, s, this.namespace), l = U(s, this.nsT(e), (n) => s`UPDATE ${s(this.nsT(e))} SET ${s.update(D(t, e))} OUTPUT INSERTED.* INTO ${n} WHERE ${c}`), u = r ? i.provideService(l, this.sql.transactionService, r) : l, d = await k("mssql", "update", () => this.runtime.runPromise(u));
440
- for (let t of E(d, e, "mssql")) await this.routeEvent({
441
- table: e,
442
- op: "update",
443
- old: null,
444
- new: t
445
- }, a, r, o);
446
- return d.length;
447
- }
448
- async executeDeleteMany(e, t, n, r, a) {
449
- let o = this.sql, s = C(t.where, o, this.namespace), c = U(o, this.nsT(e), (t) => o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* INTO ${t} WHERE ${s}`), l = n ? i.provideService(c, this.sql.transactionService, n) : c, u = await k("mssql", "delete", () => this.runtime.runPromise(l));
450
- for (let t of E(u, e, "mssql")) await this.routeEvent({
451
- table: e,
452
- op: "delete",
453
- old: t,
454
- new: null
455
- }, r, n, a);
456
- return u.length;
457
- }
458
- upsert(e, t, n) {
459
- return this.localWrite("upsert", e, (r, i, a) => this.executeUpsert(e, t, n, i, a, r));
460
- }
461
- insertIgnore(e, t, n) {
462
- return this.insertIgnoreWithOutcome(e, t, n).then((e) => e.row);
463
- }
464
- insertIgnoreWithOutcome(e, t, n) {
465
- return this.localWrite("upsert", e, (r, i, a) => this.executeInsertIgnore(e, t, n, i, a, r));
466
- }
467
- async executeUpsert(e, t, n, r, i, a) {
468
- if (P(n), t = M(e, t), typeof n.update != "function" && n.conflictColumns.length > 0) return this.mergeUpsert(e, t, {
469
- conflictColumns: n.conflictColumns,
470
- ...n.update === void 0 ? {} : { update: n.update },
471
- ...n.updateValues === void 0 ? {} : { updateValues: n.updateValues }
472
- }, r, i, a);
473
- let o = await this.findByConflict(e, t, n.conflictColumns, r);
474
- if (o) return await this.executeUpdate(e, Object.fromEntries(n.conflictColumns.map((e) => [e, o[e]])), N(t, o, n), r, i, a) ?? o;
475
- try {
476
- return await this.executeInsert(e, t, r, i, a);
477
- } catch (o) {
478
- if (!K(o)) throw o;
479
- let s = await this.findByConflict(e, t, n.conflictColumns, r);
480
- if (!s) throw o;
481
- return await this.executeUpdate(e, Object.fromEntries(n.conflictColumns.map((e) => [e, s[e]])), N(t, s, n), r, i, a) ?? s;
482
- }
483
- }
484
- async mergeUpsert(e, t, n, r, a, o) {
485
- let s = this.sql, c = Object.keys(t), l = Array.isArray(n.update) ? n.update.filter((e) => c.includes(e)) : c.filter((e) => e !== "id" && !n.conflictColumns.includes(e)), u = D(t, e), d = c.map((e) => s`${u[e]}`), f = c.map((e) => s`${s(e)}`), p = n.conflictColumns.map((e) => s`tgt.${s(e)} = src.${s(e)}`), m = c.map((e) => s`${s(e)}`), h = c.map((e) => s`src.${s(e)}`), g = P(n), _ = D(g, e), v = [...l.filter((e) => !Object.hasOwn(g, e)).map((e) => s`tgt.${s(e)} = src.${s(e)}`), ...Object.entries(_).map(([e, t]) => s`tgt.${s(e)} = ${t}`)];
486
- v.length === 0 && v.push(s`tgt.${s(n.conflictColumns[0])} = src.${s(n.conflictColumns[0])}`);
487
- let y = U(s, this.nsT(e), (t) => s`
488
- MERGE ${s(this.nsT(e))} WITH (HOLDLOCK) AS tgt
489
- USING (VALUES (${s.csv(d)})) AS src (${s.csv(f)})
490
- ON ${s.and(p)}
491
- WHEN MATCHED THEN UPDATE SET ${s.csv(v)}
492
- WHEN NOT MATCHED THEN INSERT (${s.csv(m)}) VALUES (${s.csv(h)})
493
- OUTPUT $action AS ${s("__action")}, INSERTED.* INTO ${t};`, !0), b = r ? i.provideService(y, this.sql.transactionService, r) : y, x = (await this.runtime.runPromise(b))[0];
494
- if (!x) throw Error(`MssqlStore.upsert: MERGE returned no row for '${e}'`);
495
- let S = x.__action, { __action: C, ...w } = x, T = this.decodeWritten(e, w);
496
- return await this.routeEvent({
497
- table: e,
498
- op: S === "INSERT" ? "insert" : "update",
499
- old: null,
500
- new: T
501
- }, a, r, o), T;
502
- }
503
- async executeInsertIgnore(e, t, n, r, i, a) {
504
- t = M(e, t);
505
- let o = await this.findByConflict(e, t, n.conflictColumns, r);
506
- if (o) return {
507
- row: o,
508
- outcome: "ignored"
509
- };
510
- try {
511
- return {
512
- row: await this.executeInsert(e, t, r, i, a),
513
- outcome: "inserted"
514
- };
515
- } catch (i) {
516
- if (!K(i)) throw i;
517
- let a = await this.findByConflict(e, t, n.conflictColumns, r);
518
- if (!a) throw i;
519
- return {
520
- row: a,
521
- outcome: "ignored"
522
- };
523
- }
524
- }
525
- async findByConflict(e, t, n, r) {
526
- if (n.length === 0) return;
527
- let a = this.sql, o = n.map((e) => a`${a(e)} = ${t[e]}`), s = a`SELECT TOP 1 * FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = r ? i.provideService(s, this.sql.transactionService, r) : s, l = (await this.runtime.runPromise(c))[0];
528
- return l ? this.decodeWritten(e, l) : void 0;
529
- }
530
- emitChange(e) {
531
- this.emitter.emit("change", e);
532
- }
533
- async startCdcConsumer(e) {
534
- if (this.cdcHandle) return;
535
- this.cdcReplicaId = e.replicaId;
536
- let t = e.includeTables ?? [];
537
- t.length === 0 && Q.warn("cdc: no reactive tables to tail; Change Tracking reader idle. Set includeTables to enable.");
538
- let n = await this.readCdcOffset(e.replicaId);
539
- this.cdcHandle = await Z({
540
- run: this.__mssqlReplicationFriend.runEffect,
541
- tables: t,
542
- startVersion: n,
543
- onChange: (e) => this.injectExternalChange(e),
544
- onVersion: (e) => {
545
- this.cdcPendingVersion = e;
546
- },
547
- onError: (e) => Q.warn("cdc: consumer error", {}, e),
548
- onResync: () => {
549
- Q.warn("cdc: resynced to current CT version after retention gap — subscriptions self-heal on the next change");
550
- }
551
- }), this.cdcCheckpointTimer = setInterval(() => {
552
- this.flushCdcOffset();
553
- }, 1e3), this.cdcCheckpointTimer.unref && this.cdcCheckpointTimer.unref();
554
- }
555
- async readCdcOffset(e) {
556
- try {
557
- let t = this.sql, n = (await this.runtime.runPromise(t`
558
- SELECT TOP 1 ${t("ctVersion")} FROM ${t(u)}
559
- WHERE ${t("id")} = ${e}`))[0]?.ctVersion;
560
- return n ? (Q.info("cdc: resuming from persisted CT version", {
561
- replicaId: e,
562
- version: n
563
- }), n) : null;
564
- } catch (t) {
565
- return Q.warn("cdc: no readable offset; starting at current CT version", { replicaId: e }, t), null;
566
- }
567
- }
568
- async flushCdcOffset() {
569
- let e = this.cdcPendingVersion;
570
- if (e !== null) {
571
- this.cdcPendingVersion = null;
572
- try {
573
- let t = this.cdcReplicaId;
574
- await this.executeUpsert(u, {
575
- id: t,
576
- replicaId: t,
577
- streamName: this.cdcStreamName,
578
- ctVersion: e,
579
- updatedAt: /* @__PURE__ */ new Date()
580
- }, {
581
- conflictColumns: ["id"],
582
- update: ["ctVersion", "updatedAt"]
583
- }, null, null);
584
- } catch (t) {
585
- this.cdcPendingVersion = e, Q.warn("cdc: checkpoint write failed", {}, t);
586
- }
587
- }
588
- }
589
- getInternalExecuteQuery() {
590
- return this.executeQuery.bind(this);
591
- }
592
- getInternalExecuteInsert() {
593
- return this.executeInsert.bind(this);
594
- }
595
- getInternalExecuteInsertMany() {
596
- return this.executeInsertMany.bind(this);
597
- }
598
- getInternalExecutePatchJson() {
599
- return this.executePatchJson.bind(this);
600
- }
601
- getInternalExecuteUpdate() {
602
- return this.executeUpdate.bind(this);
603
- }
604
- getInternalExecuteUpdateMany() {
605
- return this.executeUpdateMany.bind(this);
606
- }
607
- getInternalExecuteDeleteMany() {
608
- return this.executeDeleteMany.bind(this);
609
- }
610
- getInternalExecuteDelete() {
611
- return this.executeDelete.bind(this);
612
- }
613
- getInternalExecuteUpsert() {
614
- return this.executeUpsert.bind(this);
615
- }
616
- getInternalExecuteInsertIgnore() {
617
- return this.executeInsertIgnore.bind(this);
618
- }
619
- async emptyTables(e) {
620
- await this.emptyTablesOn(e, null);
621
- }
622
- async incomingForeignKeys(e) {
623
- if (e.length === 0) return [];
624
- let t = this.sql;
625
- return (await this.runtime.runPromise(t`
626
- SELECT child.name AS fromTable, col.name AS columnName, parent.name AS toTable
627
- FROM sys.foreign_key_columns fkc
628
- JOIN sys.tables child ON child.object_id = fkc.parent_object_id
629
- JOIN sys.tables parent ON parent.object_id = fkc.referenced_object_id
630
- JOIN sys.columns col ON col.object_id = fkc.parent_object_id AND col.column_id = fkc.parent_column_id
631
- WHERE parent.name IN ${t.in(e)}
632
- AND child.name NOT IN ${t.in(e)}`)).map((e) => ({
633
- from: e.fromTable,
634
- column: e.columnName,
635
- to: e.toTable
636
- }));
637
- }
638
- async emptyTablesOn(e, t) {
639
- if (e.length === 0) return;
640
- let n = this.sql, r = (e) => this.nsT(e), a = (e, t) => n.unsafe(`ALTER TABLE ${se(r(e), "mssql")} ${t ? "WITH CHECK CHECK" : "NOCHECK"} CONSTRAINT ALL`), o = i.acquireUseRelease(i.forEach(e, (e) => a(e, !1), { discard: !0 }), () => i.forEach(e, (e) => n`DELETE FROM ${n(r(e))}`, { discard: !0 }), () => i.orDie(i.ignore(i.forEach(e, (e) => a(e, !0), { discard: !0 })))), s = t === null ? n.withTransaction(o) : i.provideService(o, this.sql.transactionService, t);
641
- await this.runtime.runPromise(s);
642
- }
643
- async transactional(e) {
644
- this.inflightTxns++;
645
- try {
646
- return await de({
647
- ...this.txnSpec("MssqlStore.transactional"),
648
- work: e,
649
- makeView: (e, t) => new Ee(this, e, t)
650
- });
651
- } finally {
652
- this.inflightTxns--;
653
- }
654
- }
655
- get transactionService() {
656
- return this.sql.transactionService;
657
- }
658
- txnSpec(e) {
659
- return {
660
- label: e,
661
- dialect: "mssql",
662
- withTransaction: (e) => this.sql.withTransaction(e),
663
- transactionService: this.sql.transactionService,
664
- runPromiseExit: (e) => this.runtime.runPromiseExit(e),
665
- isRetryable: G,
666
- span: {
667
- name: "store.transactional",
668
- attributes: {
669
- "db.system": "mssql",
670
- "db.operation": "transaction"
671
- }
672
- }
673
- };
674
- }
675
- onChange(e) {
676
- return this.emitter.on("change", e), () => {
677
- this.emitter.off("change", e);
678
- };
679
- }
680
- get changeScope() {
681
- return this.changeStrategy === "cdc" ? "fleet" : "local";
682
- }
683
- injectExternalChange(e) {
684
- O(e.table) && this.emitter.emit("change", ee(e));
685
- }
686
- run(e) {
687
- return this.runtime.runPromise(e);
688
- }
689
- async close(e = 5e3) {
690
- if (this.cdcCheckpointTimer &&= (clearInterval(this.cdcCheckpointTimer), null), this.cdcHandle) {
691
- let e = this.cdcHandle.currentVersion();
692
- e !== null && (this.cdcPendingVersion = e), await this.flushCdcOffset(), await this.cdcHandle.stop(), this.cdcHandle = null;
693
- }
694
- if (this.inflightTxns > 0) {
695
- let t = Date.now() + e;
696
- for (; this.inflightTxns > 0 && Date.now() < t;) await new Promise((e) => setTimeout(e, 25));
697
- this.inflightTxns > 0 && Q.warn("close: grace period expired with in-flight transactions", {
698
- gracePeriodMs: e,
699
- inflight: this.inflightTxns
700
- });
701
- }
702
- await this.runtime.dispose();
703
- }
704
- async ping() {
705
- await this.runtime.runPromise(this.sql`SELECT 1`);
706
- }
707
- }, Ee = class {
708
- parent;
709
- txn;
710
- attr;
711
- events = [];
712
- aborts = ie((e) => G(e));
713
- committed = !1;
714
- constructor(e, t, n = void 0) {
715
- this.parent = e, this.txn = t, this.attr = n;
716
- }
717
- savepoint(e) {
718
- return ue({
719
- tracker: this.aborts,
720
- name: `voltro_sp_${++this.savepointCounter}`,
721
- spelling: "mssql",
722
- exec: (e) => this.parent.run(i.provideService(I.pipe(i.flatMap((t) => t.unsafe(e))), this.parent.transactionService, this.txn))
723
- }, e);
724
- }
725
- savepointCounter = 0;
726
- run(e) {
727
- return this.aborts.track(() => this.parent.run(ae(e, this.parent.transactionService, this.txn)));
728
- }
729
- abortedBy() {
730
- return this.aborts.abortedBy();
731
- }
732
- raw(e, t) {
733
- return this.aborts.track(() => this.parent.rawOn(e, this.txn));
734
- }
735
- query(e) {
736
- return this.aborts.track(() => this.parent.getInternalRunWithEager()(e, this.txn));
737
- }
738
- emptyTables(e) {
739
- return this.aborts.track(() => this.parent.emptyTablesOn(e, this.txn));
740
- }
741
- insert(e, t) {
742
- return this.aborts.track(() => this.parent.getInternalExecuteInsert()(e, t, this.txn, this.events, this.attr));
743
- }
744
- insertMany(e, t) {
745
- return this.aborts.track(() => this.parent.getInternalExecuteInsertMany()(e, t, this.txn, this.events, this.attr));
746
- }
747
- patchJson(e, t, n, r, i) {
748
- return this.aborts.track(() => this.parent.getInternalExecutePatchJson()(e, t, n, r, this.txn, this.events, this.attr, i));
749
- }
750
- update(e, t, n) {
751
- return this.aborts.track(() => this.parent.getInternalExecuteUpdate()(e, t, n, this.txn, this.events, this.attr));
752
- }
753
- delete(e, t) {
754
- return this.aborts.track(() => this.parent.getInternalExecuteDelete()(e, t, this.txn, this.events, this.attr));
755
- }
756
- updateMany(e, t, n) {
757
- return this.aborts.track(() => this.parent.getInternalExecuteUpdateMany()(e, t, n, this.txn, this.events, this.attr));
758
- }
759
- deleteMany(e, t) {
760
- return this.aborts.track(() => this.parent.getInternalExecuteDeleteMany()(e, t, this.txn, this.events, this.attr));
761
- }
762
- upsert(e, t, n) {
763
- return this.aborts.track(() => this.parent.getInternalExecuteUpsert()(e, t, n, this.txn, this.events, this.attr));
764
- }
765
- insertIgnore(e, t, n) {
766
- return this.insertIgnoreWithOutcome(e, t, n).then((e) => e.row);
767
- }
768
- insertIgnoreWithOutcome(e, t, n) {
769
- return this.aborts.track(() => this.parent.getInternalExecuteInsertIgnore()(e, t, n, this.txn, this.events, this.attr));
770
- }
771
- transactional(e) {
772
- return Promise.reject(/* @__PURE__ */ Error("MssqlStore.transactional: nested transactions are not supported."));
773
- }
774
- onChange(e) {
775
- throw Error("MssqlTransactionalView.onChange: not supported inside a transaction.");
776
- }
777
- injectExternalChange(e) {
778
- throw Error("MssqlTransactionalView.injectExternalChange: not supported inside a transaction.");
779
- }
780
- commitEvents() {
781
- if (!this.committed) {
782
- this.committed = !0;
783
- for (let e of this.events) this.parent.emitChange(e);
784
- this.events.length = 0;
785
- }
786
- }
787
- }, De = (e) => e.__mssqlReplicationFriend ?? null, Oe = (e, t) => e === t ? 0 : e < t ? -1 : 1, ke = () => ({
788
- async capturePrimaryPosition(e) {
789
- let t = De(e);
790
- if (t === null) throw Error("mssqlReplicationAdapter: primary is not an MssqlStore (missing __mssqlReplicationFriend).");
791
- return t.runEffect(i.gen(function* () {
792
- return (yield* (yield* I)`
793
- SELECT CONVERT(VARCHAR(40), end_of_log_lsn) AS lsn
794
- FROM sys.dm_hadr_database_replica_states
795
- WHERE database_id = DB_ID()
796
- AND is_local = 1
797
- AND is_primary_replica = 1
798
- `)[0]?.lsn ?? "00000000:00000000:0000";
799
- }));
800
- },
801
- async probeReplicaPosition(e) {
802
- let t = De(e);
803
- if (t === null) throw Error("mssqlReplicationAdapter: replica is not an MssqlStore.");
804
- return t.runEffect(i.gen(function* () {
805
- return (yield* (yield* I)`
806
- SELECT CONVERT(VARCHAR(40), last_hardened_lsn) AS lsn
807
- FROM sys.dm_hadr_database_replica_states
808
- WHERE database_id = DB_ID()
809
- AND is_local = 1
810
- `)[0]?.lsn ?? "00000000:00000000:0000";
811
- }));
812
- },
813
- compare(e, t) {
814
- return Oe(t, e) >= 0 ? "caught-up" : "behind";
815
- }
816
- }), Ae = {
1
+ import { MssqlClient as e, connectionFromConfig as t, makeMssqlSqlLayer as n, makeMssqlSqlLayerFromConfig as r } from "./sqlLayer.js";
2
+ import { mssqlRetryFilter as i } from "./retry.js";
3
+ import { ensureChangeTracking as a, startChangeTrackingCdc as o } from "./changeTrackingCdc.js";
4
+ import { CDC_OFFSETS_TABLE as s, _voltroMssqlCdcOffsetsTable as c } from "./cdcOffsetsTable.js";
5
+ import { makeMssqlStore as l } from "./store.js";
6
+ import { mssqlReplicationAdapter as u } from "./replicationAdapter.js";
7
+ //#region src/index.ts
8
+ var d = {
817
9
  id: "mssql",
818
- makeSqlLayer: (e) => B(e),
819
- makeStore: (e) => $(e),
10
+ makeSqlLayer: (e) => r(e),
11
+ makeStore: (e) => l(e),
820
12
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_[\]]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
821
- retryFilter: q
13
+ retryFilter: i
822
14
  };
823
15
  //#endregion
824
- export { u as CDC_OFFSETS_TABLE, e as MssqlClient, h as _voltroMssqlCdcOffsetsTable, z as connectionFromConfig, J as ensureChangeTracking, R as makeMssqlSqlLayer, B as makeMssqlSqlLayerFromConfig, $ as makeMssqlStore, Ae as mssqlDialect, ke as mssqlReplicationAdapter, q as mssqlRetryFilter, Z as startChangeTrackingCdc };
16
+ export { s as CDC_OFFSETS_TABLE, e as MssqlClient, c as _voltroMssqlCdcOffsetsTable, t as connectionFromConfig, a as ensureChangeTracking, n as makeMssqlSqlLayer, r as makeMssqlSqlLayerFromConfig, l as makeMssqlStore, d as mssqlDialect, u as mssqlReplicationAdapter, i as mssqlRetryFilter, o as startChangeTrackingCdc };