@voltro/sql-mssql 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1801 -0
- package/dist/index.d.ts +61 -3
- package/dist/index.js +232 -183
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,34 +1,61 @@
|
|
|
1
1
|
import { MssqlClient as e } from "@effect/sql-mssql";
|
|
2
|
-
import { Config as t,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { Config as t, Duration as n, Effect as r, Layer as i, ManagedRuntime as a, Redacted as o } from "effect";
|
|
3
|
+
import { CDC_OFFSETS_TABLE as s, DEFAULT_ACQUIRE_TIMEOUT_MS as c, EagerCardinalityError as l, _voltroMssqlCdcOffsetsTable as u, attachEagerLoads as d, attributionFields as f, bulkInsertLimitsFor as p, chunkRowsForInsert as m, compileEagerJson as h, compilePredicate as g, compileRawFragment as _, compileSelect as v, decodeRowsFromSchema as y, hasEagerLoads as b, isTableReactive as x, makeEagerFallbackReporter as S, observeDbOp as C, qualifyTable as w, raiseChangeListenerCeiling as T, recordsTable as E, requireTable as ee, runStoreTransaction as te, runWriteRecorders as D, stampGeneratedId as O, stampGeneratedIds as k, withCapturedAttribution as A } from "@voltro/database";
|
|
4
|
+
import { EventEmitter as j } from "node:events";
|
|
5
|
+
import { createLogger as M } from "@voltro/logger";
|
|
6
|
+
import { SqlClient as N, TransactionConnection as P } from "@effect/sql/SqlClient";
|
|
7
7
|
//#region src/sqlLayer.ts
|
|
8
|
-
var
|
|
8
|
+
var F = {
|
|
9
9
|
...e.defaultParameterTypes,
|
|
10
10
|
null: e.defaultParameterTypes.object
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
11
|
+
}, I = (e) => {
|
|
12
|
+
let t = e.acquireTimeoutMs ?? c;
|
|
13
|
+
return t > 0 ? t : void 0;
|
|
14
|
+
}, L = (r) => {
|
|
15
|
+
let i = I(r);
|
|
16
|
+
return e.layerConfig({
|
|
17
|
+
server: t.succeed(r.server),
|
|
18
|
+
parameterTypes: t.succeed(F),
|
|
19
|
+
...i === void 0 ? {} : { connectTimeout: t.succeed(n.millis(i)) },
|
|
20
|
+
...r.port === void 0 ? {} : { port: t.succeed(r.port) },
|
|
21
|
+
...r.database === void 0 ? {} : { database: t.succeed(r.database) },
|
|
22
|
+
...r.username === void 0 ? {} : { username: t.succeed(r.username) },
|
|
23
|
+
...r.password === void 0 ? {} : { password: t.succeed(o.make(r.password)) },
|
|
24
|
+
...r.encrypt === void 0 ? {} : { encrypt: t.succeed(r.encrypt) },
|
|
25
|
+
...r.trustServer === void 0 ? {} : { trustServer: t.succeed(r.trustServer) },
|
|
26
|
+
...r.maxConnections === void 0 ? {} : { maxConnections: t.succeed(r.maxConnections) }
|
|
27
|
+
});
|
|
28
|
+
}, ne = (e) => {
|
|
29
|
+
let t = e.get("sslmode");
|
|
30
|
+
if (t !== null) {
|
|
31
|
+
if (t === "require") return !0;
|
|
32
|
+
if (t === "disable") return !1;
|
|
33
|
+
throw Error(`DB_URL '?sslmode=${t}' is not supported by the mssql dialect — use 'require' (TLS without certificate verification) or 'disable' (plaintext).`);
|
|
34
|
+
}
|
|
35
|
+
for (let t of ["ssl", "encrypt"]) {
|
|
36
|
+
let n = e.get(t);
|
|
37
|
+
if (n !== null) {
|
|
38
|
+
if (n === "true" || n === "1") return !0;
|
|
39
|
+
if (n === "false" || n === "0") return !1;
|
|
40
|
+
throw Error(`DB_URL '?${t}=${n}' is not supported by the mssql dialect — use 'true'/'1' or 'false'/'0'.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, R = (e) => {
|
|
44
|
+
let t = e.acquireTimeoutMs === void 0 ? {} : { acquireTimeoutMs: e.acquireTimeoutMs }, n = (e) => e === void 0 ? {} : {
|
|
45
|
+
encrypt: e,
|
|
46
|
+
...e ? { trustServer: !0 } : {}
|
|
47
|
+
};
|
|
22
48
|
if (e.url) {
|
|
23
|
-
let
|
|
49
|
+
let r = new URL(e.url);
|
|
24
50
|
return {
|
|
25
|
-
server:
|
|
26
|
-
port:
|
|
27
|
-
username: decodeURIComponent(
|
|
28
|
-
password: decodeURIComponent(
|
|
29
|
-
database:
|
|
30
|
-
|
|
31
|
-
...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections }
|
|
51
|
+
server: r.hostname || "localhost",
|
|
52
|
+
port: r.port ? Number(r.port) : 1433,
|
|
53
|
+
username: decodeURIComponent(r.username || "sa"),
|
|
54
|
+
password: decodeURIComponent(r.password || ""),
|
|
55
|
+
database: r.pathname.replace(/^\//, "") || "master",
|
|
56
|
+
...n(e.ssl ?? ne(r.searchParams)),
|
|
57
|
+
...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
|
|
58
|
+
...t
|
|
32
59
|
};
|
|
33
60
|
}
|
|
34
61
|
return {
|
|
@@ -37,10 +64,11 @@ var P = {
|
|
|
37
64
|
username: e.username ?? "sa",
|
|
38
65
|
password: e.password ?? "",
|
|
39
66
|
database: e.database ?? "master",
|
|
40
|
-
|
|
41
|
-
...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections }
|
|
67
|
+
...n(e.ssl),
|
|
68
|
+
...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
|
|
69
|
+
...t
|
|
42
70
|
};
|
|
43
|
-
},
|
|
71
|
+
}, z = (e) => L(R(e)), B = /* @__PURE__ */ new Set(["1205"]), V = (e) => {
|
|
44
72
|
let t = e;
|
|
45
73
|
for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
|
|
46
74
|
let e = t.number;
|
|
@@ -49,37 +77,37 @@ var P = {
|
|
|
49
77
|
if (typeof n == "string") return n;
|
|
50
78
|
t = t.cause;
|
|
51
79
|
}
|
|
52
|
-
},
|
|
53
|
-
let t =
|
|
54
|
-
return t !== void 0 &&
|
|
55
|
-
},
|
|
56
|
-
let t =
|
|
57
|
-
return t !== void 0 &&
|
|
58
|
-
},
|
|
80
|
+
}, H = (e) => {
|
|
81
|
+
let t = V(e);
|
|
82
|
+
return t !== void 0 && B.has(t);
|
|
83
|
+
}, U = /* @__PURE__ */ new Set(["2601", "2627"]), W = (e) => {
|
|
84
|
+
let t = V(e);
|
|
85
|
+
return t !== void 0 && U.has(t);
|
|
86
|
+
}, G = (e) => H(e) ? "retry" : "noRetry", K = {
|
|
59
87
|
I: "insert",
|
|
60
88
|
U: "update",
|
|
61
89
|
D: "delete"
|
|
62
|
-
},
|
|
63
|
-
let e = yield*
|
|
90
|
+
}, q = (e, t) => e(r.gen(function* () {
|
|
91
|
+
let e = yield* N;
|
|
64
92
|
if (((yield* e`
|
|
65
93
|
SELECT COUNT(*) AS ${e("on")}
|
|
66
94
|
FROM sys.change_tracking_databases
|
|
67
|
-
WHERE database_id = DB_ID()`)[0]?.on ?? 0) === 0) return yield*
|
|
95
|
+
WHERE database_id = DB_ID()`)[0]?.on ?? 0) === 0) return yield* r.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."));
|
|
68
96
|
for (let n of t) yield* e.unsafe(`IF NOT EXISTS (
|
|
69
97
|
SELECT 1 FROM sys.change_tracking_tables
|
|
70
|
-
WHERE object_id = OBJECT_ID(${
|
|
98
|
+
WHERE object_id = OBJECT_ID(${J(n)})
|
|
71
99
|
)
|
|
72
|
-
ALTER TABLE ${
|
|
73
|
-
})).then(() => void 0),
|
|
74
|
-
let t =
|
|
75
|
-
await
|
|
76
|
-
let i = async () => (await e.run(
|
|
77
|
-
return yield* (yield*
|
|
78
|
-
})))[0]?.v ?? "0", a = e.startVersion ?? await i(), o = !1, s = null, c = !1,
|
|
79
|
-
let s = await e.run(
|
|
80
|
-
let e = yield*
|
|
81
|
-
SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(${e.unsafe(
|
|
82
|
-
return
|
|
100
|
+
ALTER TABLE ${Y(n)} ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = OFF)`);
|
|
101
|
+
})).then(() => void 0), J = (e) => `'${e.replace(/'/g, "''")}'`, Y = (e) => `[${e.replace(/\]/g, "]]")}]`, X = async (e) => {
|
|
102
|
+
let t = M({ scope: "voltro:mssql:cdc" }), n = e.pollIntervalMs ?? 500;
|
|
103
|
+
await q(e.run, e.tables);
|
|
104
|
+
let i = async () => (await e.run(r.gen(function* () {
|
|
105
|
+
return yield* (yield* N)`SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_CURRENT_VERSION()) AS v`;
|
|
106
|
+
})))[0]?.v ?? "0", a = e.startVersion ?? await i(), o = !1, s = null, c = !1, l = (e, t) => BigInt(e) > BigInt(t), u = async (n, o) => {
|
|
107
|
+
let s = await e.run(r.gen(function* () {
|
|
108
|
+
let e = yield* N, t = e.unsafe(Y(n)), r = (yield* e`
|
|
109
|
+
SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(${e.unsafe(J(n))}))) AS floor`)[0]?.floor;
|
|
110
|
+
return r != null && BigInt(o) < BigInt(r) ? {
|
|
83
111
|
resync: !0,
|
|
84
112
|
rows: []
|
|
85
113
|
} : {
|
|
@@ -92,53 +120,53 @@ var P = {
|
|
|
92
120
|
};
|
|
93
121
|
}));
|
|
94
122
|
if (s.resync) {
|
|
95
|
-
t.warn("cdc: CT retention floor passed our cursor — resyncing to current version", { table:
|
|
123
|
+
t.warn("cdc: CT retention floor passed our cursor — resyncing to current version", { table: n }), a = await i(), e.onResync?.(), e.onVersion?.(a);
|
|
96
124
|
return;
|
|
97
125
|
}
|
|
98
126
|
for (let t of s.rows) {
|
|
99
|
-
let
|
|
100
|
-
if (!
|
|
127
|
+
let r = K[t.__ct_op];
|
|
128
|
+
if (!r) continue;
|
|
101
129
|
let { __ct_op: i, __ct_id: a, ...o } = t;
|
|
102
|
-
if (
|
|
103
|
-
table:
|
|
130
|
+
if (r === "delete") e.onChange({
|
|
131
|
+
table: n,
|
|
104
132
|
op: "delete",
|
|
105
133
|
old: { id: a },
|
|
106
134
|
new: null
|
|
107
135
|
});
|
|
108
136
|
else {
|
|
109
|
-
let t =
|
|
137
|
+
let t = y([o], n, "mssql")[0];
|
|
110
138
|
e.onChange({
|
|
111
|
-
table:
|
|
112
|
-
op:
|
|
139
|
+
table: n,
|
|
140
|
+
op: r,
|
|
113
141
|
old: null,
|
|
114
142
|
new: t
|
|
115
143
|
});
|
|
116
144
|
}
|
|
117
145
|
}
|
|
118
|
-
},
|
|
146
|
+
}, d = async () => {
|
|
119
147
|
if (!(o || c)) {
|
|
120
148
|
c = !0;
|
|
121
149
|
try {
|
|
122
150
|
let t = await i();
|
|
123
|
-
if (
|
|
151
|
+
if (l(t, a)) {
|
|
124
152
|
for (let t of e.tables) {
|
|
125
153
|
if (o) break;
|
|
126
|
-
await
|
|
154
|
+
await u(t, a);
|
|
127
155
|
}
|
|
128
|
-
a =
|
|
156
|
+
a = l(t, a) ? t : a, e.onVersion?.(a);
|
|
129
157
|
}
|
|
130
158
|
} catch (n) {
|
|
131
159
|
t.warn("cdc: poll cycle failed — retrying next tick", {}, n), e.onError?.(n);
|
|
132
160
|
} finally {
|
|
133
161
|
c = !1, o || (s = setTimeout(() => {
|
|
134
|
-
|
|
135
|
-
},
|
|
162
|
+
d();
|
|
163
|
+
}, n));
|
|
136
164
|
}
|
|
137
165
|
}
|
|
138
166
|
};
|
|
139
167
|
return s = setTimeout(() => {
|
|
140
|
-
|
|
141
|
-
},
|
|
168
|
+
d();
|
|
169
|
+
}, n), s.unref && s.unref(), t.info("cdc: Change Tracking reader attached", {
|
|
142
170
|
tables: e.tables.length,
|
|
143
171
|
from: e.startVersion ?? a
|
|
144
172
|
}), {
|
|
@@ -147,28 +175,26 @@ var P = {
|
|
|
147
175
|
},
|
|
148
176
|
currentVersion: () => a
|
|
149
177
|
};
|
|
150
|
-
},
|
|
178
|
+
}, Z = M({ scope: "voltro:mssql" }), Q = async (e) => {
|
|
151
179
|
let t = e.changeStrategy ?? "inline", n = t;
|
|
152
|
-
t === "cdc" && !e.cdcConfig && (
|
|
153
|
-
let
|
|
180
|
+
t === "cdc" && !e.cdcConfig && (Z.warn("changeStrategy='cdc' requires cdcConfig (replicaId + includeTables); falling back to 'inline'."), n = "inline");
|
|
181
|
+
let r = e.tracerLayer ? i.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, o = a.make(r), s = new re(await o.runPromise(N), o, n);
|
|
154
182
|
return n === "cdc" && e.cdcConfig && await s.startCdcConsumer(e.cdcConfig), s;
|
|
155
|
-
},
|
|
156
|
-
let e = x();
|
|
157
|
-
return e === void 0 ? (e) => e() : (t) => O(e, t);
|
|
158
|
-
}, Q = class e {
|
|
183
|
+
}, re = class e {
|
|
159
184
|
sql;
|
|
160
185
|
runtime;
|
|
161
186
|
changeStrategy;
|
|
162
187
|
namespace;
|
|
163
188
|
emitter;
|
|
164
189
|
inflightTxns = 0;
|
|
190
|
+
reportEagerFallback = S(Z);
|
|
165
191
|
cdcHandle = null;
|
|
166
192
|
cdcCheckpointTimer = null;
|
|
167
193
|
cdcPendingVersion = null;
|
|
168
194
|
cdcReplicaId = "";
|
|
169
195
|
cdcStreamName = "default";
|
|
170
196
|
constructor(e, t, n = "inline", r = null, i) {
|
|
171
|
-
this.sql = e, this.runtime = t, this.changeStrategy = n, this.namespace = r, this.emitter = i ?? new
|
|
197
|
+
this.sql = e, this.runtime = t, this.changeStrategy = n, this.namespace = r, this.emitter = i ?? new j(), T(this.emitter);
|
|
172
198
|
}
|
|
173
199
|
get inlineEmit() {
|
|
174
200
|
return this.changeStrategy === "inline";
|
|
@@ -181,22 +207,22 @@ var P = {
|
|
|
181
207
|
}
|
|
182
208
|
__mssqlReplicationFriend = { runEffect: (e) => this.runtime.runPromise(e) };
|
|
183
209
|
async executeQuery(e, t) {
|
|
184
|
-
let
|
|
185
|
-
return
|
|
210
|
+
let n = v(e, this.sql, this.namespace), i = t ? r.provideService(n, P, t) : n;
|
|
211
|
+
return y(await C("mssql", "select", () => this.runtime.runPromise(i)), e.table, "mssql");
|
|
186
212
|
}
|
|
187
|
-
async executeInsert(e, t,
|
|
188
|
-
t =
|
|
189
|
-
let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t).returning("*")}`, c =
|
|
213
|
+
async executeInsert(e, t, n, i, a) {
|
|
214
|
+
t = O(e, t);
|
|
215
|
+
let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t).returning("*")}`, c = n ? r.provideService(s, P, n) : s, l = (await this.runtime.runPromise(c))[0];
|
|
190
216
|
if (!l) throw Error(`MssqlStore.insert: row not returned post-insert in '${e}'`);
|
|
191
217
|
return await this.routeEvent({
|
|
192
218
|
table: e,
|
|
193
219
|
op: "insert",
|
|
194
220
|
old: null,
|
|
195
221
|
new: l
|
|
196
|
-
}, i,
|
|
222
|
+
}, i, n, a), l;
|
|
197
223
|
}
|
|
198
|
-
async executeUpdate(e, t,
|
|
199
|
-
let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(
|
|
224
|
+
async executeUpdate(e, t, n, i, a, o) {
|
|
225
|
+
let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(n).returning("*")} WHERE ${s("id")} = ${t}`, l = i ? r.provideService(c, P, i) : c, u = (await this.runtime.runPromise(l))[0];
|
|
200
226
|
return u ? (await this.routeEvent({
|
|
201
227
|
table: e,
|
|
202
228
|
op: "update",
|
|
@@ -204,48 +230,57 @@ var P = {
|
|
|
204
230
|
new: u
|
|
205
231
|
}, a, i, o), u) : null;
|
|
206
232
|
}
|
|
207
|
-
async executeInsertMany(e, t,
|
|
208
|
-
if (t =
|
|
209
|
-
let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t).returning("*")}`,
|
|
210
|
-
|
|
233
|
+
async executeInsertMany(e, t, n, i, a) {
|
|
234
|
+
if (t = k(e, t), t.length === 0) return [];
|
|
235
|
+
let o = this.sql, s = m(t, p("mssql")), c = (t) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t).returning("*")}`, l;
|
|
236
|
+
if (s.length === 1) {
|
|
237
|
+
let e = c(s[0]), t = n ? r.provideService(e, P, n) : e;
|
|
238
|
+
l = await this.runtime.runPromise(t);
|
|
239
|
+
} else if (n) {
|
|
240
|
+
let e = [];
|
|
241
|
+
for (let t of s) e.push(...await this.runtime.runPromise(r.provideService(c(t), P, n)));
|
|
242
|
+
l = e;
|
|
243
|
+
} else l = await this.runtime.runPromise(o.withTransaction(r.map(r.forEach(s, c, { concurrency: 1 }), (e) => e.flat())));
|
|
244
|
+
let u = t.map((e) => e.id), d = new Map(l.map((e) => [e.id, e])), f = u.map((e) => d.get(e)).filter((e) => e !== void 0), h = f.length === l.length ? f : [...l];
|
|
245
|
+
for (let t of h) await this.routeEvent({
|
|
211
246
|
table: e,
|
|
212
247
|
op: "insert",
|
|
213
248
|
old: null,
|
|
214
249
|
new: t
|
|
215
|
-
}, i,
|
|
216
|
-
return
|
|
250
|
+
}, i, n, a);
|
|
251
|
+
return h;
|
|
217
252
|
}
|
|
218
|
-
async executePatchJson(e, t,
|
|
219
|
-
let c = this.sql, l =
|
|
220
|
-
return
|
|
253
|
+
async executePatchJson(e, t, n, i, a, o, s) {
|
|
254
|
+
let c = this.sql, l = n.split("."), u = l[0], d = l.slice(1), f = d.length === 0 ? "$" : `$.${d.join(".")}`, p = JSON.stringify(i ?? null), m = c`UPDATE ${c(this.nsT(e))} SET ${c(u)} = JSON_MODIFY(${c(u)}, ${f}, JSON_QUERY(${p})) OUTPUT INSERTED.* WHERE ${c("id")} = ${t}`, h = a ? r.provideService(m, P, a) : m, g = (await this.runtime.runPromise(h))[0];
|
|
255
|
+
return g ? (await this.routeEvent({
|
|
221
256
|
table: e,
|
|
222
257
|
op: "update",
|
|
223
258
|
old: null,
|
|
224
|
-
new:
|
|
225
|
-
}, o, a, s),
|
|
259
|
+
new: g
|
|
260
|
+
}, o, a, s), g) : null;
|
|
226
261
|
}
|
|
227
|
-
async executeDelete(e, t,
|
|
228
|
-
let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${o("id")} = ${t}`, c =
|
|
262
|
+
async executeDelete(e, t, n, i, a) {
|
|
263
|
+
let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${o("id")} = ${t}`, c = n ? r.provideService(s, P, n) : s, l = (await this.runtime.runPromise(c))[0];
|
|
229
264
|
return l ? (await this.routeEvent({
|
|
230
265
|
table: e,
|
|
231
266
|
op: "delete",
|
|
232
267
|
old: l,
|
|
233
268
|
new: null
|
|
234
|
-
}, i,
|
|
269
|
+
}, i, n, a), !0) : !1;
|
|
235
270
|
}
|
|
236
|
-
async appendInTxn(e, t,
|
|
271
|
+
async appendInTxn(e, t, n) {
|
|
237
272
|
let i = this.sql, a = i`INSERT INTO ${i(this.nsT(e))} ${i.insert(t)}`;
|
|
238
|
-
await this.runtime.runPromise(
|
|
273
|
+
await this.runtime.runPromise(n ? r.provideService(a, P, n) : a);
|
|
239
274
|
}
|
|
240
|
-
async maxInTxn(e, t,
|
|
241
|
-
let a = this.sql, o = Object.entries(
|
|
275
|
+
async maxInTxn(e, t, n, i) {
|
|
276
|
+
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(i ? r.provideService(s, P, i) : s))[0]?.m;
|
|
242
277
|
return c == null ? null : Number(c);
|
|
243
278
|
}
|
|
244
279
|
async routeEvent(e, t, n = null, r) {
|
|
245
280
|
e = {
|
|
246
|
-
...
|
|
281
|
+
...f(r),
|
|
247
282
|
...e
|
|
248
|
-
}, E(e.table) && await
|
|
283
|
+
}, E(e.table) && await D({
|
|
249
284
|
append: (e, t) => this.appendInTxn(e, t, n),
|
|
250
285
|
maxOf: (e, t, r) => this.maxInTxn(e, t, r, n)
|
|
251
286
|
}, {
|
|
@@ -255,47 +290,60 @@ var P = {
|
|
|
255
290
|
prev: e.old,
|
|
256
291
|
traceId: e.traceId,
|
|
257
292
|
subjectId: e.subjectId
|
|
258
|
-
}),
|
|
293
|
+
}), x(e.table) && (t === null ? this.inlineEmit && this.emitter.emit("change", e) : t.push(e));
|
|
259
294
|
}
|
|
260
295
|
query(e) {
|
|
261
296
|
return this.runWithEager(e, null);
|
|
262
297
|
}
|
|
263
298
|
raw(e, t) {
|
|
264
|
-
let n =
|
|
265
|
-
return this.runtime.runPromise(n);
|
|
299
|
+
let n = _(e, this.sql);
|
|
300
|
+
return C("mssql", "raw", () => this.runtime.runPromise(n));
|
|
266
301
|
}
|
|
267
302
|
async runWithEager(e, t) {
|
|
268
|
-
if (!
|
|
269
|
-
let
|
|
270
|
-
if (
|
|
271
|
-
let e = t ?
|
|
272
|
-
return
|
|
273
|
-
} catch (
|
|
274
|
-
if (
|
|
275
|
-
|
|
303
|
+
if (!b(e)) return this.executeQuery(e, t);
|
|
304
|
+
let n = this.namespace === null ? h(e, this.sql, "mssql") : null;
|
|
305
|
+
if (n !== null) try {
|
|
306
|
+
let e = t ? r.provideService(n.fragment, P, t) : n.fragment, i = await C("mssql", "select", () => this.runtime.runPromise(e));
|
|
307
|
+
return n.decode(i);
|
|
308
|
+
} catch (t) {
|
|
309
|
+
if (t instanceof l) throw t;
|
|
310
|
+
this.reportEagerFallback({
|
|
311
|
+
dialect: "mssql",
|
|
312
|
+
table: e.table,
|
|
313
|
+
reason: "execute-failed",
|
|
314
|
+
error: t
|
|
315
|
+
});
|
|
276
316
|
}
|
|
277
|
-
|
|
317
|
+
else this.reportEagerFallback({
|
|
318
|
+
dialect: "mssql",
|
|
319
|
+
table: e.table,
|
|
320
|
+
reason: "not-compilable"
|
|
321
|
+
});
|
|
322
|
+
return d(await this.executeQuery(e, t), e.eager, e.sourceTable ?? ee(e.table), (e) => this.executeQuery(e, t));
|
|
278
323
|
}
|
|
279
324
|
getInternalRunWithEager() {
|
|
280
325
|
return this.runWithEager.bind(this);
|
|
281
326
|
}
|
|
327
|
+
localWrite(e, t) {
|
|
328
|
+
return C("mssql", e, () => A(t));
|
|
329
|
+
}
|
|
282
330
|
insert(e, t) {
|
|
283
|
-
return
|
|
331
|
+
return this.localWrite("insert", (n) => this.executeInsert(e, t, null, null, n));
|
|
284
332
|
}
|
|
285
333
|
insertMany(e, t) {
|
|
286
|
-
return
|
|
334
|
+
return this.localWrite("insert", (n) => this.executeInsertMany(e, t, null, null, n));
|
|
287
335
|
}
|
|
288
336
|
patchJson(e, t, n, r) {
|
|
289
|
-
return
|
|
337
|
+
return this.localWrite("update", (i) => this.executePatchJson(e, t, n, r, null, null, i));
|
|
290
338
|
}
|
|
291
339
|
update(e, t, n) {
|
|
292
|
-
return
|
|
340
|
+
return this.localWrite("update", (r) => this.executeUpdate(e, t, n, null, null, r));
|
|
293
341
|
}
|
|
294
342
|
delete(e, t) {
|
|
295
|
-
return
|
|
343
|
+
return this.localWrite("delete", (n) => this.executeDelete(e, t, null, null, n));
|
|
296
344
|
}
|
|
297
345
|
async updateMany(e, t, n) {
|
|
298
|
-
let r = this.sql, i =
|
|
346
|
+
let r = this.sql, i = g(n.where, r, this.namespace), a = r`UPDATE ${r(this.nsT(e))} SET ${r.update(t)} OUTPUT INSERTED.* WHERE ${i}`, o = await C("mssql", "update", () => this.runtime.runPromise(a));
|
|
299
347
|
for (let t of o) await this.routeEvent({
|
|
300
348
|
table: e,
|
|
301
349
|
op: "update",
|
|
@@ -305,7 +353,7 @@ var P = {
|
|
|
305
353
|
return o.length;
|
|
306
354
|
}
|
|
307
355
|
async deleteMany(e, t) {
|
|
308
|
-
let n = this.sql, r =
|
|
356
|
+
let n = this.sql, r = g(t.where, n, this.namespace), i = n`DELETE FROM ${n(this.nsT(e))} OUTPUT DELETED.* WHERE ${r}`, a = await C("mssql", "delete", () => this.runtime.runPromise(i));
|
|
309
357
|
for (let t of a) await this.routeEvent({
|
|
310
358
|
table: e,
|
|
311
359
|
op: "delete",
|
|
@@ -315,10 +363,10 @@ var P = {
|
|
|
315
363
|
return a.length;
|
|
316
364
|
}
|
|
317
365
|
upsert(e, t, n) {
|
|
318
|
-
return
|
|
366
|
+
return this.localWrite("upsert", (r) => this.executeUpsert(e, t, n, null, null, r));
|
|
319
367
|
}
|
|
320
368
|
insertIgnore(e, t, n) {
|
|
321
|
-
return
|
|
369
|
+
return this.localWrite("upsert", (r) => this.executeInsertIgnore(e, t, n, null, null, r));
|
|
322
370
|
}
|
|
323
371
|
upsertPatch(e, t, n) {
|
|
324
372
|
if (typeof n.update == "function") return n.update(t);
|
|
@@ -341,45 +389,45 @@ var P = {
|
|
|
341
389
|
try {
|
|
342
390
|
return await this.executeInsert(e, t, r, i);
|
|
343
391
|
} catch (a) {
|
|
344
|
-
if (!
|
|
392
|
+
if (!W(a)) throw a;
|
|
345
393
|
let o = await this.findByConflict(e, t, n.conflictColumns, r);
|
|
346
394
|
if (!o) throw a;
|
|
347
395
|
return await this.executeUpdate(e, o.id, this.upsertPatch(t, o, n), r, i) ?? o;
|
|
348
396
|
}
|
|
349
397
|
}
|
|
350
|
-
async mergeUpsert(e, t,
|
|
351
|
-
let o = this.sql, s = Object.keys(t), c = Array.isArray(
|
|
398
|
+
async mergeUpsert(e, t, n, i, a) {
|
|
399
|
+
let o = this.sql, s = Object.keys(t), c = Array.isArray(n.update) ? n.update.filter((e) => s.includes(e)) : s.filter((e) => e !== "id" && !n.conflictColumns.includes(e)), l = s.map((e) => o`${t[e]}`), u = s.map((e) => o`${o(e)}`), d = n.conflictColumns.map((e) => o`tgt.${o(e)} = src.${o(e)}`), f = s.map((e) => o`${o(e)}`), p = s.map((e) => o`src.${o(e)}`), m = (c.length > 0 ? c : [n.conflictColumns[0]]).map((e) => o`tgt.${o(e)} = src.${o(e)}`), h = o`
|
|
352
400
|
MERGE ${o(this.nsT(e))} WITH (HOLDLOCK) AS tgt
|
|
353
401
|
USING (VALUES (${o.csv(l)})) AS src (${o.csv(u)})
|
|
354
|
-
ON ${o.and(
|
|
355
|
-
WHEN MATCHED THEN UPDATE SET ${o.csv(
|
|
356
|
-
WHEN NOT MATCHED THEN INSERT (${o.csv(
|
|
357
|
-
OUTPUT $action AS ${o("__action")}, INSERTED.*;`,
|
|
358
|
-
if (!
|
|
359
|
-
let
|
|
402
|
+
ON ${o.and(d)}
|
|
403
|
+
WHEN MATCHED THEN UPDATE SET ${o.csv(m)}
|
|
404
|
+
WHEN NOT MATCHED THEN INSERT (${o.csv(f)}) VALUES (${o.csv(p)})
|
|
405
|
+
OUTPUT $action AS ${o("__action")}, INSERTED.*;`, g = i ? r.provideService(h, P, i) : h, _ = (await this.runtime.runPromise(g))[0];
|
|
406
|
+
if (!_) throw Error(`MssqlStore.upsert: MERGE returned no row for '${e}'`);
|
|
407
|
+
let v = _.__action, { __action: y, ...b } = _;
|
|
360
408
|
return this.routeEvent({
|
|
361
409
|
table: e,
|
|
362
|
-
op:
|
|
410
|
+
op: v === "INSERT" ? "insert" : "update",
|
|
363
411
|
old: null,
|
|
364
|
-
new:
|
|
365
|
-
}, a),
|
|
412
|
+
new: b
|
|
413
|
+
}, a), b;
|
|
366
414
|
}
|
|
367
415
|
async executeInsertIgnore(e, t, n, r, i, a) {
|
|
368
|
-
t =
|
|
416
|
+
t = O(e, t);
|
|
369
417
|
let o = await this.findByConflict(e, t, n.conflictColumns, r);
|
|
370
418
|
if (o) return o;
|
|
371
419
|
try {
|
|
372
420
|
return await this.executeInsert(e, t, r, i);
|
|
373
421
|
} catch (i) {
|
|
374
|
-
if (!
|
|
422
|
+
if (!W(i)) throw i;
|
|
375
423
|
let a = await this.findByConflict(e, t, n.conflictColumns, r);
|
|
376
424
|
if (!a) throw i;
|
|
377
425
|
return a;
|
|
378
426
|
}
|
|
379
427
|
}
|
|
380
|
-
async findByConflict(e, t,
|
|
381
|
-
if (
|
|
382
|
-
let a = this.sql, o =
|
|
428
|
+
async findByConflict(e, t, n, i) {
|
|
429
|
+
if (n.length === 0) return;
|
|
430
|
+
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 = i ? r.provideService(s, P, i) : s;
|
|
383
431
|
return (await this.runtime.runPromise(c))[0];
|
|
384
432
|
}
|
|
385
433
|
emitChange(e) {
|
|
@@ -389,9 +437,9 @@ var P = {
|
|
|
389
437
|
if (this.cdcHandle) return;
|
|
390
438
|
this.cdcReplicaId = e.replicaId;
|
|
391
439
|
let t = e.includeTables ?? [];
|
|
392
|
-
t.length === 0 &&
|
|
440
|
+
t.length === 0 && Z.warn("cdc: no reactive tables to tail; Change Tracking reader idle. Set includeTables to enable.");
|
|
393
441
|
let n = await this.readCdcOffset(e.replicaId);
|
|
394
|
-
this.cdcHandle = await
|
|
442
|
+
this.cdcHandle = await X({
|
|
395
443
|
run: this.__mssqlReplicationFriend.runEffect,
|
|
396
444
|
tables: t,
|
|
397
445
|
startVersion: n,
|
|
@@ -399,9 +447,9 @@ var P = {
|
|
|
399
447
|
onVersion: (e) => {
|
|
400
448
|
this.cdcPendingVersion = e;
|
|
401
449
|
},
|
|
402
|
-
onError: (e) =>
|
|
450
|
+
onError: (e) => Z.warn("cdc: consumer error", {}, e),
|
|
403
451
|
onResync: () => {
|
|
404
|
-
|
|
452
|
+
Z.warn("cdc: resynced to current CT version after retention gap — subscriptions self-heal on the next change");
|
|
405
453
|
}
|
|
406
454
|
}), this.cdcCheckpointTimer = setInterval(() => {
|
|
407
455
|
this.flushCdcOffset();
|
|
@@ -410,14 +458,14 @@ var P = {
|
|
|
410
458
|
async readCdcOffset(e) {
|
|
411
459
|
try {
|
|
412
460
|
let t = this.sql, n = (await this.runtime.runPromise(t`
|
|
413
|
-
SELECT TOP 1 ${t("ctVersion")} FROM ${t(
|
|
461
|
+
SELECT TOP 1 ${t("ctVersion")} FROM ${t(s)}
|
|
414
462
|
WHERE ${t("id")} = ${e}`))[0]?.ctVersion;
|
|
415
|
-
return n ? (
|
|
463
|
+
return n ? (Z.info("cdc: resuming from persisted CT version", {
|
|
416
464
|
replicaId: e,
|
|
417
465
|
version: n
|
|
418
466
|
}), n) : null;
|
|
419
467
|
} catch (t) {
|
|
420
|
-
return
|
|
468
|
+
return Z.warn("cdc: no readable offset; starting at current CT version", { replicaId: e }, t), null;
|
|
421
469
|
}
|
|
422
470
|
}
|
|
423
471
|
async flushCdcOffset() {
|
|
@@ -426,7 +474,7 @@ var P = {
|
|
|
426
474
|
this.cdcPendingVersion = null;
|
|
427
475
|
try {
|
|
428
476
|
let t = this.cdcReplicaId;
|
|
429
|
-
await this.executeUpsert(
|
|
477
|
+
await this.executeUpsert(s, {
|
|
430
478
|
id: t,
|
|
431
479
|
replicaId: t,
|
|
432
480
|
streamName: this.cdcStreamName,
|
|
@@ -437,7 +485,7 @@ var P = {
|
|
|
437
485
|
update: ["ctVersion", "updatedAt"]
|
|
438
486
|
}, null, null);
|
|
439
487
|
} catch (t) {
|
|
440
|
-
this.cdcPendingVersion = e,
|
|
488
|
+
this.cdcPendingVersion = e, Z.warn("cdc: checkpoint write failed", {}, t);
|
|
441
489
|
}
|
|
442
490
|
}
|
|
443
491
|
}
|
|
@@ -467,31 +515,32 @@ var P = {
|
|
|
467
515
|
}
|
|
468
516
|
async transactional(e) {
|
|
469
517
|
this.inflightTxns++;
|
|
470
|
-
let t = x(), r = Z(), i = 0, o = n.suspend(() => {
|
|
471
|
-
let o = ++i;
|
|
472
|
-
return this.sql.withTransaction(n.flatMap(n.serviceOption(d), (i) => {
|
|
473
|
-
if (a.isNone(i)) return n.fail(/* @__PURE__ */ Error("MssqlStore.transactional: TransactionConnection missing."));
|
|
474
|
-
let s = new te(this, i.value, t);
|
|
475
|
-
return n.tryPromise({
|
|
476
|
-
try: () => r(() => e(s)).then((e) => ({
|
|
477
|
-
result: e,
|
|
478
|
-
view: s,
|
|
479
|
-
attempt: o
|
|
480
|
-
})),
|
|
481
|
-
catch: (e) => e
|
|
482
|
-
});
|
|
483
|
-
}));
|
|
484
|
-
}), c = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(B)), l = o.pipe(n.retry(c), n.withSpan("store.transactional", { attributes: {
|
|
485
|
-
"db.system": "mssql",
|
|
486
|
-
"db.operation": "transaction"
|
|
487
|
-
} }));
|
|
488
518
|
try {
|
|
489
|
-
|
|
490
|
-
|
|
519
|
+
return await te({
|
|
520
|
+
...this.txnSpec("MssqlStore.transactional"),
|
|
521
|
+
work: e,
|
|
522
|
+
makeView: (e, t) => new ie(this, e, t)
|
|
523
|
+
});
|
|
491
524
|
} finally {
|
|
492
525
|
this.inflightTxns--;
|
|
493
526
|
}
|
|
494
527
|
}
|
|
528
|
+
txnSpec(e) {
|
|
529
|
+
return {
|
|
530
|
+
label: e,
|
|
531
|
+
dialect: "mssql",
|
|
532
|
+
withTransaction: (e) => this.sql.withTransaction(e),
|
|
533
|
+
runPromiseExit: (e) => this.runtime.runPromiseExit(e),
|
|
534
|
+
isRetryable: H,
|
|
535
|
+
span: {
|
|
536
|
+
name: "store.transactional",
|
|
537
|
+
attributes: {
|
|
538
|
+
"db.system": "mssql",
|
|
539
|
+
"db.operation": "transaction"
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
495
544
|
onChange(e) {
|
|
496
545
|
return this.emitter.on("change", e), () => {
|
|
497
546
|
this.emitter.off("change", e);
|
|
@@ -501,7 +550,7 @@ var P = {
|
|
|
501
550
|
return this.changeStrategy === "cdc" ? "fleet" : "local";
|
|
502
551
|
}
|
|
503
552
|
injectExternalChange(e) {
|
|
504
|
-
|
|
553
|
+
x(e.table) && this.emitter.emit("change", {
|
|
505
554
|
...e,
|
|
506
555
|
origin: "injected"
|
|
507
556
|
});
|
|
@@ -517,7 +566,7 @@ var P = {
|
|
|
517
566
|
if (this.inflightTxns > 0) {
|
|
518
567
|
let t = Date.now() + e;
|
|
519
568
|
for (; this.inflightTxns > 0 && Date.now() < t;) await new Promise((e) => setTimeout(e, 25));
|
|
520
|
-
this.inflightTxns > 0 &&
|
|
569
|
+
this.inflightTxns > 0 && Z.warn("close: grace period expired with in-flight transactions", {
|
|
521
570
|
gracePeriodMs: e,
|
|
522
571
|
inflight: this.inflightTxns
|
|
523
572
|
});
|
|
@@ -527,7 +576,7 @@ var P = {
|
|
|
527
576
|
async ping() {
|
|
528
577
|
await this.runtime.runPromise(this.sql`SELECT 1`);
|
|
529
578
|
}
|
|
530
|
-
},
|
|
579
|
+
}, ie = class {
|
|
531
580
|
parent;
|
|
532
581
|
txn;
|
|
533
582
|
attr;
|
|
@@ -600,12 +649,12 @@ var P = {
|
|
|
600
649
|
this.events.length = 0;
|
|
601
650
|
}
|
|
602
651
|
}
|
|
603
|
-
}, $ = (e) => e.__mssqlReplicationFriend ?? null,
|
|
652
|
+
}, $ = (e) => e.__mssqlReplicationFriend ?? null, ae = (e, t) => e === t ? 0 : e < t ? -1 : 1, oe = () => ({
|
|
604
653
|
async capturePrimaryPosition(e) {
|
|
605
654
|
let t = $(e);
|
|
606
655
|
if (t === null) throw Error("mssqlReplicationAdapter: primary is not an MssqlStore (missing __mssqlReplicationFriend).");
|
|
607
|
-
return t.runEffect(
|
|
608
|
-
return (yield* (yield*
|
|
656
|
+
return t.runEffect(r.gen(function* () {
|
|
657
|
+
return (yield* (yield* N)`
|
|
609
658
|
SELECT CONVERT(VARCHAR(40), end_of_log_lsn) AS lsn
|
|
610
659
|
FROM sys.dm_hadr_database_replica_states
|
|
611
660
|
WHERE database_id = DB_ID()
|
|
@@ -617,8 +666,8 @@ var P = {
|
|
|
617
666
|
async probeReplicaPosition(e) {
|
|
618
667
|
let t = $(e);
|
|
619
668
|
if (t === null) throw Error("mssqlReplicationAdapter: replica is not an MssqlStore.");
|
|
620
|
-
return t.runEffect(
|
|
621
|
-
return (yield* (yield*
|
|
669
|
+
return t.runEffect(r.gen(function* () {
|
|
670
|
+
return (yield* (yield* N)`
|
|
622
671
|
SELECT CONVERT(VARCHAR(40), last_hardened_lsn) AS lsn
|
|
623
672
|
FROM sys.dm_hadr_database_replica_states
|
|
624
673
|
WHERE database_id = DB_ID()
|
|
@@ -627,14 +676,14 @@ var P = {
|
|
|
627
676
|
}));
|
|
628
677
|
},
|
|
629
678
|
compare(e, t) {
|
|
630
|
-
return
|
|
679
|
+
return ae(t, e) >= 0 ? "caught-up" : "behind";
|
|
631
680
|
}
|
|
632
|
-
}),
|
|
681
|
+
}), se = {
|
|
633
682
|
id: "mssql",
|
|
634
|
-
makeSqlLayer: (e) =>
|
|
635
|
-
makeStore: (e) =>
|
|
683
|
+
makeSqlLayer: (e) => z(e),
|
|
684
|
+
makeStore: (e) => Q(e),
|
|
636
685
|
compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_[\]]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
|
|
637
|
-
retryFilter:
|
|
686
|
+
retryFilter: G
|
|
638
687
|
};
|
|
639
688
|
//#endregion
|
|
640
|
-
export {
|
|
689
|
+
export { s as CDC_OFFSETS_TABLE, e as MssqlClient, u as _voltroMssqlCdcOffsetsTable, R as connectionFromConfig, q as ensureChangeTracking, L as makeMssqlSqlLayer, z as makeMssqlSqlLayerFromConfig, Q as makeMssqlStore, se as mssqlDialect, oe as mssqlReplicationAdapter, G as mssqlRetryFilter, X as startChangeTrackingCdc };
|