@typed-sql/mysql 1.0.0-rc.0 → 2.0.0-rc.1
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 +136 -0
- package/README.md +45 -56
- package/dist/packages/mysql/src/bulk.d.ts +22 -0
- package/dist/packages/mysql/src/bulk.d.ts.map +1 -0
- package/dist/packages/mysql/src/bulk.js +205 -0
- package/dist/packages/mysql/src/bulk.js.map +1 -0
- package/dist/packages/mysql/src/decoding.d.ts +26 -0
- package/dist/packages/mysql/src/decoding.d.ts.map +1 -0
- package/dist/packages/mysql/src/decoding.js +172 -0
- package/dist/packages/mysql/src/decoding.js.map +1 -0
- package/dist/packages/mysql/src/index.d.ts +5 -1
- package/dist/packages/mysql/src/index.d.ts.map +1 -1
- package/dist/packages/mysql/src/index.js +16 -3
- package/dist/packages/mysql/src/index.js.map +1 -1
- package/dist/packages/mysql/src/mysql2.d.ts +60 -1
- package/dist/packages/mysql/src/mysql2.d.ts.map +1 -1
- package/dist/packages/mysql/src/mysql2.js +402 -1
- package/dist/packages/mysql/src/mysql2.js.map +1 -1
- package/dist/packages/mysql/src/prepared.d.ts +20 -0
- package/dist/packages/mysql/src/prepared.d.ts.map +1 -0
- package/dist/packages/mysql/src/prepared.js +47 -0
- package/dist/packages/mysql/src/prepared.js.map +1 -0
- package/dist/packages/mysql/src/provider.d.ts.map +1 -1
- package/dist/packages/mysql/src/provider.js +8 -1
- package/dist/packages/mysql/src/provider.js.map +1 -1
- package/dist/packages/mysql/src/resolver.d.ts.map +1 -1
- package/dist/packages/mysql/src/resolver.js +20 -1
- package/dist/packages/mysql/src/resolver.js.map +1 -1
- package/dist/packages/mysql/src/routing.d.ts +22 -0
- package/dist/packages/mysql/src/routing.d.ts.map +1 -0
- package/dist/packages/mysql/src/routing.js +72 -0
- package/dist/packages/mysql/src/routing.js.map +1 -0
- package/dist/packages/mysql/src/runtime.d.ts +22 -8
- package/dist/packages/mysql/src/runtime.d.ts.map +1 -1
- package/dist/packages/mysql/src/runtime.js +530 -70
- package/dist/packages/mysql/src/runtime.js.map +1 -1
- package/dist/packages/mysql/src/semantics.d.ts +5 -0
- package/dist/packages/mysql/src/semantics.d.ts.map +1 -0
- package/dist/packages/mysql/src/semantics.js +203 -0
- package/dist/packages/mysql/src/semantics.js.map +1 -0
- package/dist/packages/mysql/src/stream.d.ts +32 -0
- package/dist/packages/mysql/src/stream.d.ts.map +1 -0
- package/dist/packages/mysql/src/stream.js +175 -0
- package/dist/packages/mysql/src/stream.js.map +1 -0
- package/dist/packages/mysql/src/version.d.ts +2 -0
- package/dist/packages/mysql/src/version.d.ts.map +1 -0
- package/dist/packages/mysql/src/version.js +2 -0
- package/dist/packages/mysql/src/version.js.map +1 -0
- package/package.json +4 -4
|
@@ -1,118 +1,578 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import {
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { adapterCapabilities, assertExecutionCapabilities, createAdapterCapabilityResolver, databaseErrorCompletion, hasQueryResultValidator, observeQueryStream, QueryCardinalityError, queryResultValidationSource, renderQuery, runControlledExecution, startDatabaseObservation, UnsupportedExecutionCapabilityError, validateQueryResultRows, validateQueryResultStream, } from "@typed-sql/core";
|
|
4
|
+
import { createMySqlBulkCapability, mysqlBulk } from "./bulk.js";
|
|
5
|
+
import { decodeMySqlRows, encodeMySqlValue, MySqlDecoderPlanCache, } from "./decoding.js";
|
|
6
|
+
import { createMySqlPreparedQueryState, prepareMySqlQuery, } from "./prepared.js";
|
|
7
|
+
import { createMySqlQueryStream, validateMySqlStreamBatchSize } from "./stream.js";
|
|
3
8
|
import { defaultMySqlTypePolicy } from "./type-policy.js";
|
|
4
|
-
|
|
5
|
-
const
|
|
9
|
+
import { MYSQL_DIALECT_VERSION } from "./version.js";
|
|
10
|
+
const defaultRuntimeTypePolicy = defaultMySqlTypePolicy;
|
|
11
|
+
const emptyBatchResults = Object.freeze([]);
|
|
12
|
+
function createMySqlObservationState(observer) {
|
|
13
|
+
return { observer, fingerprints: observer === undefined ? undefined : new WeakMap() };
|
|
14
|
+
}
|
|
15
|
+
function mysqlQueryFingerprint(state, query) {
|
|
16
|
+
const key = query;
|
|
17
|
+
const cached = state.fingerprints?.get(key);
|
|
18
|
+
if (cached !== undefined)
|
|
19
|
+
return cached;
|
|
20
|
+
const text = renderQuery(query, mysqlRenderer).text;
|
|
21
|
+
const fingerprint = `sha256:${createHash("sha256").update(`mysql\0${MYSQL_DIALECT_VERSION}\0${text}`).digest("hex")}`;
|
|
22
|
+
state.fingerprints?.set(key, fingerprint);
|
|
23
|
+
return fingerprint;
|
|
24
|
+
}
|
|
6
25
|
export const mysqlRenderer = Object.freeze({
|
|
7
26
|
placeholder: () => "?",
|
|
8
27
|
quoteIdentifier: (identifier) => `\`${identifier.replaceAll("`", "``")}\``,
|
|
9
28
|
});
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new RangeError(`${label} value ${value} cannot be represented as a finite number`);
|
|
17
|
-
return result;
|
|
18
|
-
}
|
|
19
|
-
function decode(value, field, policy, decimal) {
|
|
20
|
-
if (value === null || value === undefined)
|
|
21
|
-
return value;
|
|
22
|
-
if (field.columnType === mysqlTypes.longlong) {
|
|
23
|
-
if (policy.bigint === "bigint")
|
|
24
|
-
return BigInt(String(value));
|
|
25
|
-
if (policy.bigint === "number") {
|
|
26
|
-
const result = finite(String(value), "bigint");
|
|
27
|
-
if (!Number.isSafeInteger(result))
|
|
28
|
-
throw new RangeError(`bigint value ${value} exceeds JavaScript's safe integer range`);
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
return String(value);
|
|
32
|
-
}
|
|
33
|
-
if (field.columnType === mysqlTypes.decimal) {
|
|
34
|
-
if (policy.decimal === "number")
|
|
35
|
-
return finite(String(value), "decimal");
|
|
36
|
-
if (policy.decimal === "Decimal") {
|
|
37
|
-
if (decimal === undefined)
|
|
38
|
-
throw new TypeError("decimal=Decimal requires a decimal(value) codec");
|
|
39
|
-
return decimal(String(value));
|
|
40
|
-
}
|
|
41
|
-
return String(value);
|
|
42
|
-
}
|
|
43
|
-
if ([mysqlTypes.date, mysqlTypes.datetime, mysqlTypes.timestamp].includes(field.columnType)) {
|
|
44
|
-
return policy.date === "string" ? String(value) : value instanceof Date ? value : new Date(String(value));
|
|
45
|
-
}
|
|
46
|
-
if (field.columnType === mysqlTypes.json && policy.json === "string")
|
|
47
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
48
|
-
if (field.columnType === mysqlTypes.tiny && field.columnLength === 1 && policy.tinyint1 === "boolean")
|
|
49
|
-
return value === true || value === 1 || value === "1";
|
|
50
|
-
return value;
|
|
29
|
+
function createMySqlConnectionOperation() {
|
|
30
|
+
let finish;
|
|
31
|
+
const completion = new Promise((resolve) => {
|
|
32
|
+
finish = resolve;
|
|
33
|
+
});
|
|
34
|
+
return { completion, finish };
|
|
51
35
|
}
|
|
52
36
|
class MySqlDatabaseImplementation {
|
|
53
37
|
#pool;
|
|
54
38
|
#connection;
|
|
55
39
|
#ownsPool;
|
|
56
|
-
#policy;
|
|
57
|
-
#decimal;
|
|
58
40
|
#depth;
|
|
59
|
-
|
|
41
|
+
#prepared;
|
|
42
|
+
#observation;
|
|
43
|
+
#decoderPlans;
|
|
44
|
+
#executes;
|
|
45
|
+
#streams;
|
|
46
|
+
#transactionState;
|
|
47
|
+
#scopeOpen = true;
|
|
48
|
+
executionCapabilities;
|
|
49
|
+
[adapterCapabilities];
|
|
50
|
+
constructor(pool, connection, ownsPool, depth, prepared, decoderPlans, observation, transactionState) {
|
|
60
51
|
this.#pool = pool;
|
|
61
52
|
this.#connection = connection;
|
|
62
53
|
this.#ownsPool = ownsPool;
|
|
63
|
-
this.#policy = policy;
|
|
64
|
-
this.#decimal = decimal;
|
|
65
54
|
this.#depth = depth;
|
|
55
|
+
this.#prepared = prepared;
|
|
56
|
+
this.#decoderPlans = decoderPlans;
|
|
57
|
+
this.#observation = observation;
|
|
58
|
+
this.#executes = connection === undefined ? undefined : new Set();
|
|
59
|
+
this.#streams = connection === undefined ? undefined : new Set();
|
|
60
|
+
this.#transactionState = transactionState;
|
|
61
|
+
this.executionCapabilities = Object.freeze({
|
|
62
|
+
cancellation: pool.executionCapabilities?.cancellation ?? false,
|
|
63
|
+
deadlines: pool.executionCapabilities?.deadlines ?? false,
|
|
64
|
+
});
|
|
65
|
+
const bulkTransport = {
|
|
66
|
+
loadData: (statement, chunks, options) => this.#loadData(statement, chunks, options),
|
|
67
|
+
};
|
|
68
|
+
this[adapterCapabilities] = createAdapterCapabilityResolver(pool.bulkLoad === true ? [[mysqlBulk, createMySqlBulkCapability(bulkTransport)]] : []);
|
|
66
69
|
}
|
|
67
70
|
async execute(query) {
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
if (this.#observation.observer === undefined) {
|
|
72
|
+
return this.#validateRows(query, await this.#executeUnobserved(query));
|
|
73
|
+
}
|
|
74
|
+
return this.#observeQuery(query, "many", async () => this.#validateRows(query, await this.#executeUnobserved(query)));
|
|
75
|
+
}
|
|
76
|
+
async #executeUnobserved(query) {
|
|
77
|
+
this.#assertScopeOpen();
|
|
78
|
+
this.#assertConnectionAvailable("execute a query");
|
|
79
|
+
if (this.#connection === undefined)
|
|
80
|
+
return this.#executeOn(this.#pool, query);
|
|
81
|
+
const operation = createMySqlConnectionOperation();
|
|
82
|
+
this.#executes.add(operation);
|
|
83
|
+
this.#transactionState.execute = operation;
|
|
84
|
+
try {
|
|
85
|
+
return await this.#executeOn(this.#connection, query);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
this.#executes.delete(operation);
|
|
89
|
+
if (this.#transactionState?.execute === operation)
|
|
90
|
+
this.#transactionState.execute = undefined;
|
|
91
|
+
operation.finish();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async all(query, options) {
|
|
95
|
+
if (this.#observation.observer === undefined) {
|
|
96
|
+
return this.#validateRows(query, await this.#allUnobserved(query, options));
|
|
97
|
+
}
|
|
98
|
+
return this.#observeQuery(query, "many", async () => this.#validateRows(query, await this.#allUnobserved(query, options)));
|
|
99
|
+
}
|
|
100
|
+
async #allUnobserved(query, options) {
|
|
101
|
+
if (options === undefined || (options.signal === undefined && options.deadline === undefined))
|
|
102
|
+
return this.#executeUnobserved(query);
|
|
103
|
+
assertExecutionCapabilities(this.executionCapabilities, options);
|
|
104
|
+
this.#assertScopeOpen();
|
|
105
|
+
this.#assertConnectionAvailable("execute a query");
|
|
106
|
+
if (options.signal?.aborted || (options.deadline !== undefined && Number(options.deadline) <= Date.now())) {
|
|
107
|
+
return runControlledExecution(options, async () => [], () => undefined);
|
|
108
|
+
}
|
|
109
|
+
if (this.#connection === undefined) {
|
|
110
|
+
const connection = await this.#pool.getConnection();
|
|
111
|
+
if (connection.destroy === undefined) {
|
|
112
|
+
connection.release();
|
|
113
|
+
throw new UnsupportedExecutionCapabilityError(options.signal === undefined ? "deadlines" : "cancellation");
|
|
114
|
+
}
|
|
115
|
+
let discarded = false;
|
|
116
|
+
try {
|
|
117
|
+
return await this.#executeControlledOn(connection, query, options, () => {
|
|
118
|
+
connection.destroy();
|
|
119
|
+
discarded = true;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
if (!discarded)
|
|
124
|
+
connection.release();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const connection = this.#connection;
|
|
128
|
+
if (connection.destroy === undefined) {
|
|
129
|
+
throw new UnsupportedExecutionCapabilityError(options.signal === undefined ? "deadlines" : "cancellation");
|
|
130
|
+
}
|
|
131
|
+
const operationState = createMySqlConnectionOperation();
|
|
132
|
+
this.#executes.add(operationState);
|
|
133
|
+
this.#transactionState.execute = operationState;
|
|
134
|
+
try {
|
|
135
|
+
return await this.#executeControlledOn(connection, query, options, () => {
|
|
136
|
+
this.#transactionState.usable = false;
|
|
137
|
+
connection.destroy();
|
|
138
|
+
this.#transactionState.discarded = true;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
this.#executes.delete(operationState);
|
|
143
|
+
if (this.#transactionState?.execute === operationState)
|
|
144
|
+
this.#transactionState.execute = undefined;
|
|
145
|
+
operationState.finish();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async #executeControlledOn(connection, query, options, cancel) {
|
|
149
|
+
return runControlledExecution(options, () => this.#executeOn(connection, query), cancel);
|
|
150
|
+
}
|
|
151
|
+
async one(query, options) {
|
|
152
|
+
if (this.#observation.observer === undefined) {
|
|
153
|
+
const rows = await this.#validateRows(query, await this.#allUnobserved(query, options));
|
|
154
|
+
if (rows.length !== 1)
|
|
155
|
+
throw new QueryCardinalityError("one", rows.length);
|
|
156
|
+
return rows[0];
|
|
157
|
+
}
|
|
158
|
+
const rows = await this.#observeQuery(query, "one", async () => {
|
|
159
|
+
const result = await this.#validateRows(query, await this.#allUnobserved(query, options));
|
|
160
|
+
if (result.length !== 1)
|
|
161
|
+
throw new QueryCardinalityError("one", result.length);
|
|
162
|
+
return result;
|
|
163
|
+
});
|
|
164
|
+
return rows[0];
|
|
165
|
+
}
|
|
166
|
+
async maybeOne(query, options) {
|
|
167
|
+
if (this.#observation.observer === undefined) {
|
|
168
|
+
const rows = await this.#validateRows(query, await this.#allUnobserved(query, options));
|
|
169
|
+
if (rows.length > 1)
|
|
170
|
+
throw new QueryCardinalityError("maybeOne", rows.length);
|
|
171
|
+
return rows[0];
|
|
172
|
+
}
|
|
173
|
+
const rows = await this.#observeQuery(query, "maybeOne", async () => {
|
|
174
|
+
const result = await this.#validateRows(query, await this.#allUnobserved(query, options));
|
|
175
|
+
if (result.length > 1)
|
|
176
|
+
throw new QueryCardinalityError("maybeOne", result.length);
|
|
177
|
+
return result;
|
|
178
|
+
});
|
|
179
|
+
return rows[0];
|
|
180
|
+
}
|
|
181
|
+
async #observeQuery(query, cardinality, operation) {
|
|
182
|
+
if (this.#observation.observer === undefined)
|
|
183
|
+
return operation();
|
|
184
|
+
const observation = startDatabaseObservation(this.#observation.observer, {
|
|
185
|
+
kind: "query",
|
|
186
|
+
dialect: "mysql",
|
|
187
|
+
grammarVersion: MYSQL_DIALECT_VERSION,
|
|
188
|
+
transactionDepth: this.#depth,
|
|
189
|
+
fingerprint: mysqlQueryFingerprint(this.#observation, query),
|
|
190
|
+
cardinality,
|
|
191
|
+
prepared: this.#prepared.queries.has(queryResultValidationSource(query)),
|
|
192
|
+
});
|
|
193
|
+
if (observation === undefined)
|
|
194
|
+
return operation();
|
|
195
|
+
try {
|
|
196
|
+
const rows = await observation.run(operation);
|
|
197
|
+
observation.end({ status: "success", rowCount: rows.length });
|
|
198
|
+
return rows;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
observation.end(databaseErrorCompletion(error), error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async #executeOn(executor, query) {
|
|
206
|
+
const prepared = this.#prepared.queries.get(queryResultValidationSource(query));
|
|
207
|
+
const rendered = prepared?.rendered ?? renderQuery(query, mysqlRenderer);
|
|
208
|
+
const result = await executor.execute(rendered.text, rendered.values.map(encodeMySqlValue));
|
|
70
209
|
if (!Array.isArray(result.rows))
|
|
71
210
|
return [];
|
|
72
|
-
const
|
|
73
|
-
return result.rows
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
211
|
+
const decoders = this.#decoderPlans.get(result.fields ?? []);
|
|
212
|
+
return decodeMySqlRows(result.rows, decoders);
|
|
213
|
+
}
|
|
214
|
+
async batch(queries) {
|
|
215
|
+
if (queries.length === 0)
|
|
216
|
+
return emptyBatchResults;
|
|
217
|
+
if (this.#observation.observer === undefined) {
|
|
218
|
+
return this.#validateBatch(queries, await this.#batchUnobserved(queries));
|
|
219
|
+
}
|
|
220
|
+
return this.#observeBatch(queries, async () => this.#validateBatch(queries, await this.#batchUnobserved(queries)));
|
|
221
|
+
}
|
|
222
|
+
async #batchUnobserved(queries) {
|
|
223
|
+
this.#assertScopeOpen();
|
|
224
|
+
this.#assertConnectionAvailable("execute a batch");
|
|
225
|
+
if (queries.length === 0)
|
|
226
|
+
return emptyBatchResults;
|
|
227
|
+
const orderedQueries = [...queries];
|
|
228
|
+
if (this.#connection !== undefined) {
|
|
229
|
+
const operation = createMySqlConnectionOperation();
|
|
230
|
+
this.#transactionState.batch = operation;
|
|
231
|
+
try {
|
|
232
|
+
return await this.#executeBatchOn(this.#connection, orderedQueries, () => this.#assertBatchCanContinue(operation));
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
if (this.#transactionState?.batch === operation)
|
|
236
|
+
this.#transactionState.batch = undefined;
|
|
237
|
+
operation.finish();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const connection = await this.#pool.getConnection();
|
|
241
|
+
let results;
|
|
242
|
+
try {
|
|
243
|
+
results = await this.#executeBatchOn(connection, orderedQueries);
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
try {
|
|
247
|
+
connection.release();
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
/* Preserve the batch execution failure. */
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
connection.release();
|
|
255
|
+
return results;
|
|
256
|
+
}
|
|
257
|
+
async #observeBatch(queries, operation) {
|
|
258
|
+
if (this.#observation.observer === undefined)
|
|
259
|
+
return operation();
|
|
260
|
+
const fingerprints = Object.freeze([...queries].map((query) => mysqlQueryFingerprint(this.#observation, query)));
|
|
261
|
+
const observation = startDatabaseObservation(this.#observation.observer, {
|
|
262
|
+
kind: "batch",
|
|
263
|
+
dialect: "mysql",
|
|
264
|
+
grammarVersion: MYSQL_DIALECT_VERSION,
|
|
265
|
+
transactionDepth: this.#depth,
|
|
266
|
+
fingerprints,
|
|
267
|
+
size: queries.length,
|
|
268
|
+
});
|
|
269
|
+
if (observation === undefined)
|
|
270
|
+
return operation();
|
|
271
|
+
try {
|
|
272
|
+
const results = await observation.run(operation);
|
|
273
|
+
observation.end({ status: "success" });
|
|
274
|
+
return results;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
observation.end(databaseErrorCompletion(error), error);
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async #executeBatchOn(connection, queries, beforeEach) {
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const query of queries) {
|
|
284
|
+
beforeEach?.();
|
|
285
|
+
results.push(await this.#executeOn(connection, query));
|
|
286
|
+
}
|
|
287
|
+
return results;
|
|
288
|
+
}
|
|
289
|
+
stream(query, options = {}) {
|
|
290
|
+
this.#assertScopeOpen();
|
|
291
|
+
const prepared = this.#prepared.queries.get(queryResultValidationSource(query));
|
|
292
|
+
const rendered = prepared?.rendered ?? renderQuery(query, mysqlRenderer);
|
|
293
|
+
const batchSize = validateMySqlStreamBatchSize(options.batchSize);
|
|
294
|
+
let exposedStream;
|
|
295
|
+
const queryStream = createMySqlQueryStream({
|
|
296
|
+
openConnection: async () => {
|
|
297
|
+
if (this.#connection !== undefined) {
|
|
298
|
+
this.#assertScopeOpen();
|
|
299
|
+
this.#assertConnectionAvailable("start another query stream");
|
|
300
|
+
this.#transactionState.active = exposedStream;
|
|
301
|
+
return { connection: this.#connection, release: false };
|
|
302
|
+
}
|
|
303
|
+
return { connection: await this.#pool.getConnection(), release: true };
|
|
304
|
+
},
|
|
305
|
+
text: rendered.text,
|
|
306
|
+
values: rendered.values.map(encodeMySqlValue),
|
|
307
|
+
batchSize,
|
|
308
|
+
decoderPlans: this.#decoderPlans,
|
|
309
|
+
onClose: () => {
|
|
310
|
+
this.#streams?.delete(exposedStream);
|
|
311
|
+
if (this.#transactionState?.active === exposedStream)
|
|
312
|
+
this.#transactionState.active = undefined;
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
const validatedStream = hasQueryResultValidator(query)
|
|
316
|
+
? validateQueryResultStream(query, queryStream, mysqlQueryFingerprint(this.#observation, query))
|
|
317
|
+
: queryStream;
|
|
318
|
+
exposedStream =
|
|
319
|
+
this.#observation.observer === undefined
|
|
320
|
+
? validatedStream
|
|
321
|
+
: observeQueryStream(validatedStream, this.#observation.observer, {
|
|
322
|
+
kind: "stream",
|
|
323
|
+
dialect: "mysql",
|
|
324
|
+
grammarVersion: MYSQL_DIALECT_VERSION,
|
|
325
|
+
transactionDepth: this.#depth,
|
|
326
|
+
fingerprint: mysqlQueryFingerprint(this.#observation, query),
|
|
327
|
+
prepared: prepared !== undefined,
|
|
328
|
+
});
|
|
329
|
+
this.#streams?.add(exposedStream);
|
|
330
|
+
return exposedStream;
|
|
331
|
+
}
|
|
332
|
+
async #validateRows(query, rows) {
|
|
333
|
+
if (!hasQueryResultValidator(query))
|
|
334
|
+
return rows;
|
|
335
|
+
return validateQueryResultRows(query, rows, mysqlQueryFingerprint(this.#observation, query));
|
|
336
|
+
}
|
|
337
|
+
async #validateBatch(queries, results) {
|
|
338
|
+
let validated;
|
|
339
|
+
const queryList = queries;
|
|
340
|
+
const resultList = results;
|
|
341
|
+
for (let index = 0; index < queryList.length; index += 1) {
|
|
342
|
+
const query = queryList[index];
|
|
343
|
+
if (!hasQueryResultValidator(query))
|
|
344
|
+
continue;
|
|
345
|
+
validated ??= [...resultList];
|
|
346
|
+
validated[index] = await validateQueryResultRows(query, resultList[index], mysqlQueryFingerprint(this.#observation, query));
|
|
347
|
+
}
|
|
348
|
+
return (validated ?? results);
|
|
349
|
+
}
|
|
350
|
+
async #loadData(statement, chunks, options) {
|
|
351
|
+
assertExecutionCapabilities(this.executionCapabilities, options);
|
|
352
|
+
this.#assertScopeOpen();
|
|
353
|
+
this.#assertConnectionAvailable("start LOAD DATA");
|
|
354
|
+
const connection = this.#connection ?? (await this.#pool.getConnection());
|
|
355
|
+
if (connection.loadData === undefined) {
|
|
356
|
+
if (this.#connection === undefined)
|
|
357
|
+
connection.release();
|
|
358
|
+
throw new Error("MySQL LOAD DATA requires an adapter with an application-owned local infile stream implementation");
|
|
359
|
+
}
|
|
360
|
+
const state = createMySqlConnectionOperation();
|
|
361
|
+
if (this.#connection !== undefined)
|
|
362
|
+
this.#transactionState.bulk = state;
|
|
363
|
+
let discarded = false;
|
|
364
|
+
const discard = () => {
|
|
365
|
+
if (discarded || connection.destroy === undefined)
|
|
366
|
+
return;
|
|
367
|
+
discarded = true;
|
|
368
|
+
connection.destroy();
|
|
369
|
+
if (this.#transactionState !== undefined) {
|
|
370
|
+
this.#transactionState.usable = false;
|
|
371
|
+
this.#transactionState.discarded = true;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
try {
|
|
375
|
+
await runControlledExecution(options, () => connection.loadData(statement, chunks), discard);
|
|
376
|
+
if (this.#connection === undefined)
|
|
377
|
+
connection.release();
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
discard();
|
|
381
|
+
if (!discarded && this.#connection === undefined)
|
|
382
|
+
connection.release();
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
if (this.#transactionState?.bulk === state)
|
|
387
|
+
this.#transactionState.bulk = undefined;
|
|
388
|
+
state.finish();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
prepare(statementName, factory) {
|
|
392
|
+
this.#assertScopeOpen();
|
|
393
|
+
return prepareMySqlQuery(this.#prepared, mysqlRenderer, statementName, factory);
|
|
77
394
|
}
|
|
78
395
|
async transaction(fn) {
|
|
396
|
+
if (this.#observation.observer === undefined)
|
|
397
|
+
return this.#transactionUnobserved(fn);
|
|
398
|
+
const observation = startDatabaseObservation(this.#observation.observer, {
|
|
399
|
+
kind: "transaction",
|
|
400
|
+
dialect: "mysql",
|
|
401
|
+
grammarVersion: MYSQL_DIALECT_VERSION,
|
|
402
|
+
transactionDepth: this.#depth + 1,
|
|
403
|
+
});
|
|
404
|
+
if (observation === undefined)
|
|
405
|
+
return this.#transactionUnobserved(fn);
|
|
406
|
+
try {
|
|
407
|
+
const result = await observation.run(() => this.#transactionUnobserved(fn));
|
|
408
|
+
observation.end({ status: "success" });
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
observation.end(databaseErrorCompletion(error), error);
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async #transactionUnobserved(fn) {
|
|
417
|
+
this.#assertScopeOpen();
|
|
79
418
|
if (this.#connection !== undefined)
|
|
80
419
|
return this.#nested(fn);
|
|
81
420
|
const connection = await this.#pool.getConnection();
|
|
421
|
+
let transaction;
|
|
422
|
+
let result;
|
|
82
423
|
try {
|
|
83
424
|
await connection.beginTransaction();
|
|
84
|
-
|
|
425
|
+
transaction = new _a(this.#pool, connection, false, 1, this.#prepared, this.#decoderPlans, this.#observation, { active: undefined, batch: undefined, bulk: undefined, execute: undefined, discarded: false, usable: true });
|
|
426
|
+
result = await fn(transaction);
|
|
427
|
+
transaction.#scopeOpen = false;
|
|
428
|
+
transaction.#transactionState.usable = false;
|
|
429
|
+
await transaction.#assertTransactionReadyForFinalize(true);
|
|
85
430
|
await connection.commit();
|
|
86
|
-
return result;
|
|
87
431
|
}
|
|
88
432
|
catch (error) {
|
|
89
|
-
|
|
90
|
-
|
|
433
|
+
if (transaction !== undefined) {
|
|
434
|
+
transaction.#scopeOpen = false;
|
|
435
|
+
transaction.#transactionState.usable = false;
|
|
436
|
+
await transaction.#invalidateScope();
|
|
91
437
|
}
|
|
92
|
-
|
|
93
|
-
|
|
438
|
+
if (transaction === undefined || transaction.#transactionState?.discarded !== true) {
|
|
439
|
+
try {
|
|
440
|
+
await connection.rollback();
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
/* Preserve the original failure. */
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
connection.release();
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
/* Preserve the original failure. */
|
|
450
|
+
}
|
|
94
451
|
}
|
|
95
452
|
throw error;
|
|
96
453
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
454
|
+
connection.release();
|
|
455
|
+
return result;
|
|
100
456
|
}
|
|
101
457
|
async #nested(fn) {
|
|
458
|
+
this.#assertScopeOpen();
|
|
459
|
+
this.#assertConnectionAvailable("start a nested transaction");
|
|
102
460
|
const connection = this.#connection;
|
|
103
461
|
const depth = this.#depth + 1;
|
|
104
462
|
const savepoint = `typed_sql_${depth}`;
|
|
105
463
|
await connection.query(`SAVEPOINT ${savepoint}`);
|
|
464
|
+
let transaction;
|
|
106
465
|
try {
|
|
107
|
-
|
|
466
|
+
transaction = new _a(this.#pool, connection, false, depth, this.#prepared, this.#decoderPlans, this.#observation, this.#transactionState);
|
|
467
|
+
const result = await fn(transaction);
|
|
468
|
+
transaction.#scopeOpen = false;
|
|
469
|
+
await transaction.#assertTransactionReadyForFinalize();
|
|
108
470
|
await connection.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
109
471
|
return result;
|
|
110
472
|
}
|
|
111
473
|
catch (error) {
|
|
112
|
-
|
|
474
|
+
if (transaction !== undefined)
|
|
475
|
+
await transaction.#invalidateScope();
|
|
476
|
+
if (this.#transactionState?.usable !== false) {
|
|
477
|
+
try {
|
|
478
|
+
await connection.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
/* Preserve the original failure. */
|
|
482
|
+
}
|
|
483
|
+
}
|
|
113
484
|
throw error;
|
|
114
485
|
}
|
|
115
486
|
}
|
|
487
|
+
async #rejectActiveStreams() {
|
|
488
|
+
if (this.#streams === undefined || this.#streams.size === 0)
|
|
489
|
+
return;
|
|
490
|
+
const active = [...this.#streams];
|
|
491
|
+
await Promise.allSettled(active.map((stream) => stream.close()));
|
|
492
|
+
throw new Error(`MySQL transaction callback returned with ${active.length} active query stream${active.length === 1 ? "" : "s"}; consume or close every stream before returning`);
|
|
493
|
+
}
|
|
494
|
+
async #closeStreams() {
|
|
495
|
+
const streams = new Set(this.#streams);
|
|
496
|
+
if (this.#transactionState?.active !== undefined)
|
|
497
|
+
streams.add(this.#transactionState.active);
|
|
498
|
+
if (streams.size === 0)
|
|
499
|
+
return;
|
|
500
|
+
await Promise.allSettled([...streams].map((stream) => stream.close()));
|
|
501
|
+
}
|
|
502
|
+
async #invalidateScope() {
|
|
503
|
+
this.#scopeOpen = false;
|
|
504
|
+
await this.#closeStreams();
|
|
505
|
+
await this.#settleExecutes();
|
|
506
|
+
await this.#settleBatch();
|
|
507
|
+
await this.#settleBulk();
|
|
508
|
+
}
|
|
509
|
+
async #assertTransactionReadyForFinalize(connectionFinalizing = false) {
|
|
510
|
+
const leakedExecutes = new Set(this.#executes);
|
|
511
|
+
if (this.#transactionState?.execute !== undefined)
|
|
512
|
+
leakedExecutes.add(this.#transactionState.execute);
|
|
513
|
+
const leakedBatch = this.#transactionState?.batch;
|
|
514
|
+
const leakedBulk = this.#transactionState?.bulk;
|
|
515
|
+
await this.#rejectActiveStreams();
|
|
516
|
+
const active = this.#transactionState?.active;
|
|
517
|
+
if (active !== undefined) {
|
|
518
|
+
await Promise.allSettled([active.close()]);
|
|
519
|
+
throw new Error("MySQL transaction callback returned while a nested query stream owned its connection; await nested work and close every stream before returning");
|
|
520
|
+
}
|
|
521
|
+
if (leakedExecutes.size > 0) {
|
|
522
|
+
await this.#settleExecuteOperations(leakedExecutes);
|
|
523
|
+
throw new Error("MySQL transaction callback returned while an execute operation owned its connection; await execute before returning");
|
|
524
|
+
}
|
|
525
|
+
if (leakedBatch !== undefined) {
|
|
526
|
+
await leakedBatch.completion;
|
|
527
|
+
throw new Error("MySQL transaction callback returned while an ordered batch owned its connection; await the batch before returning");
|
|
528
|
+
}
|
|
529
|
+
if (leakedBulk !== undefined) {
|
|
530
|
+
await leakedBulk.completion;
|
|
531
|
+
throw new Error("MySQL transaction callback returned while LOAD DATA owned its connection; await LOAD DATA before returning");
|
|
532
|
+
}
|
|
533
|
+
if (!connectionFinalizing && this.#transactionState?.usable === false)
|
|
534
|
+
throw new Error("This MySQL transaction connection is no longer active");
|
|
535
|
+
}
|
|
536
|
+
#assertConnectionAvailable(action) {
|
|
537
|
+
if (this.#transactionState?.active !== undefined)
|
|
538
|
+
throw new Error(`Cannot ${action} while a MySQL query stream owns the transaction connection`);
|
|
539
|
+
if (this.#transactionState?.batch !== undefined)
|
|
540
|
+
throw new Error(`Cannot ${action} while a MySQL ordered batch owns the transaction connection`);
|
|
541
|
+
if (this.#transactionState?.bulk !== undefined)
|
|
542
|
+
throw new Error(`Cannot ${action} while MySQL LOAD DATA owns the transaction connection`);
|
|
543
|
+
if (this.#transactionState?.execute !== undefined)
|
|
544
|
+
throw new Error(`Cannot ${action} while a MySQL execute operation owns the transaction connection`);
|
|
545
|
+
}
|
|
546
|
+
#assertBatchCanContinue(operation) {
|
|
547
|
+
this.#assertScopeOpen();
|
|
548
|
+
if (this.#transactionState?.batch !== operation)
|
|
549
|
+
throw new Error("This MySQL ordered batch no longer owns the transaction connection");
|
|
550
|
+
}
|
|
551
|
+
async #settleBatch() {
|
|
552
|
+
const batch = this.#transactionState?.batch;
|
|
553
|
+
if (batch !== undefined)
|
|
554
|
+
await batch.completion;
|
|
555
|
+
}
|
|
556
|
+
async #settleBulk() {
|
|
557
|
+
const bulk = this.#transactionState?.bulk;
|
|
558
|
+
if (bulk !== undefined)
|
|
559
|
+
await bulk.completion;
|
|
560
|
+
}
|
|
561
|
+
async #settleExecutes() {
|
|
562
|
+
const operations = new Set(this.#executes);
|
|
563
|
+
if (this.#transactionState?.execute !== undefined)
|
|
564
|
+
operations.add(this.#transactionState.execute);
|
|
565
|
+
await this.#settleExecuteOperations(operations);
|
|
566
|
+
}
|
|
567
|
+
async #settleExecuteOperations(operations) {
|
|
568
|
+
for (const operation of operations) {
|
|
569
|
+
await operation.completion;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
#assertScopeOpen() {
|
|
573
|
+
if (this.#connection !== undefined && (!this.#scopeOpen || this.#transactionState?.usable === false))
|
|
574
|
+
throw new Error("This MySQL transaction scope is no longer active");
|
|
575
|
+
}
|
|
116
576
|
async close() {
|
|
117
577
|
if (this.#connection !== undefined)
|
|
118
578
|
throw new Error("Cannot close a database from inside a transaction");
|
|
@@ -122,9 +582,9 @@ class MySqlDatabaseImplementation {
|
|
|
122
582
|
}
|
|
123
583
|
_a = MySqlDatabaseImplementation;
|
|
124
584
|
export function createMySqlDatabase(options) {
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
585
|
+
const typePolicy = { ...defaultRuntimeTypePolicy, ...options.typePolicy };
|
|
586
|
+
if (typePolicy.decimal === "Decimal" && options.decimal === undefined)
|
|
127
587
|
throw new TypeError("decimal=Decimal requires a decimal(value) codec");
|
|
128
|
-
return new MySqlDatabaseImplementation(options.pool, undefined, options.ownsPool ?? false,
|
|
588
|
+
return new MySqlDatabaseImplementation(options.pool, undefined, options.ownsPool ?? false, 0, createMySqlPreparedQueryState(), new MySqlDecoderPlanCache(typePolicy, options.decimal), createMySqlObservationState(options.observer));
|
|
129
589
|
}
|
|
130
590
|
//# sourceMappingURL=runtime.js.map
|