@prisma/client-engine-runtime 6.6.0-dev.9 → 6.6.0-dev.90
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/QueryPlan.d.ts +31 -2
- package/dist/crypto.d.ts +1 -0
- package/dist/index.d.mts +55 -21
- package/dist/index.d.ts +55 -21
- package/dist/index.js +248 -240
- package/dist/index.mjs +244 -238
- package/dist/interpreter/QueryInterpreter.d.ts +11 -4
- package/dist/interpreter/generators.d.ts +21 -0
- package/dist/interpreter/renderQuery.d.ts +3 -2
- package/dist/interpreter/renderQuery.test.d.ts +1 -0
- package/dist/transactionManager/Transaction.d.ts +1 -7
- package/dist/transactionManager/TransactionManager.d.ts +7 -6
- package/package.json +9 -4
- package/dist/interpreter/renderQueryTemplate.d.ts +0 -10
- /package/dist/interpreter/{renderQueryTemplate.test.d.ts → generators.test.d.ts} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,121 +1,119 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/interpreter/generators.ts
|
|
2
|
+
import cuid1 from "@bugsnag/cuid";
|
|
3
|
+
import { createId as cuid2 } from "@paralleldrive/cuid2";
|
|
4
|
+
import { nanoid } from "nanoid";
|
|
5
|
+
import { ulid } from "ulid";
|
|
6
|
+
import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
|
|
7
|
+
var GeneratorRegistry = class {
|
|
8
|
+
#generators = {};
|
|
9
|
+
constructor() {
|
|
10
|
+
this.register("now", new NowGenerator());
|
|
11
|
+
this.register("uuid", new UuidGenerator());
|
|
12
|
+
this.register("cuid", new CuidGenerator());
|
|
13
|
+
this.register("ulid", new UlidGenerator());
|
|
14
|
+
this.register("nanoid", new NanoIdGenerator());
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns a snapshot of the generator registry. It's 'frozen' in time at the moment of this
|
|
18
|
+
* method being called, meaning that the built-in time-based generators will always return
|
|
19
|
+
* the same value on repeated calls as long as the same snapshot is used.
|
|
20
|
+
*/
|
|
21
|
+
snapshot() {
|
|
22
|
+
return Object.create(this.#generators, {
|
|
23
|
+
now: { value: new NowGenerator() }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Registers a new generator with the given name.
|
|
28
|
+
*/
|
|
29
|
+
register(name, generator) {
|
|
30
|
+
this.#generators[name] = generator;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var NowGenerator = class {
|
|
34
|
+
#now = /* @__PURE__ */ new Date();
|
|
35
|
+
generate() {
|
|
36
|
+
return this.#now.toISOString();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var UuidGenerator = class {
|
|
40
|
+
generate(arg) {
|
|
41
|
+
if (arg === 4) {
|
|
42
|
+
return uuidv4();
|
|
43
|
+
} else if (arg === 7) {
|
|
44
|
+
return uuidv7();
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error("Invalid UUID generator arguments");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var CuidGenerator = class {
|
|
51
|
+
generate(arg) {
|
|
52
|
+
if (arg === 1) {
|
|
53
|
+
return cuid1();
|
|
54
|
+
} else if (arg === 2) {
|
|
55
|
+
return cuid2();
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error("Invalid CUID generator arguments");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var UlidGenerator = class {
|
|
62
|
+
generate() {
|
|
63
|
+
return ulid();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var NanoIdGenerator = class {
|
|
67
|
+
generate(arg) {
|
|
68
|
+
if (typeof arg === "number") {
|
|
69
|
+
return nanoid(arg);
|
|
70
|
+
} else if (arg === void 0) {
|
|
71
|
+
return nanoid();
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error("Invalid Nanoid generator arguments");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
4
76
|
};
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
8
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
9
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
10
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
11
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
12
77
|
|
|
13
78
|
// src/QueryPlan.ts
|
|
14
79
|
function isPrismaValuePlaceholder(value) {
|
|
15
80
|
return typeof value === "object" && value !== null && value["prisma__type"] === "param";
|
|
16
81
|
}
|
|
82
|
+
function isPrismaValueGenerator(value) {
|
|
83
|
+
return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
|
|
84
|
+
}
|
|
17
85
|
|
|
18
|
-
// src/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function renderQueryTemplate({
|
|
22
|
-
query,
|
|
23
|
-
params
|
|
24
|
-
}) {
|
|
25
|
-
if (!query.includes(BEGIN_REPEAT)) {
|
|
26
|
-
return { query, params };
|
|
27
|
-
}
|
|
28
|
-
const flattenedParams = [];
|
|
29
|
-
let lastParamId = 1;
|
|
30
|
-
let result = "";
|
|
31
|
-
let templatePos = 0;
|
|
32
|
-
let state = 0 /* Normal */;
|
|
33
|
-
let stateBeforeQuote = 0 /* Normal */;
|
|
34
|
-
while (templatePos < query.length) {
|
|
35
|
-
const nextChar = query[templatePos];
|
|
36
|
-
if (state === 1 /* Quoted */ && nextChar !== '"') {
|
|
37
|
-
result += nextChar;
|
|
38
|
-
templatePos++;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (nextChar === '"') {
|
|
42
|
-
if (state === 1 /* Quoted */) {
|
|
43
|
-
state = stateBeforeQuote;
|
|
44
|
-
} else {
|
|
45
|
-
stateBeforeQuote = state;
|
|
46
|
-
state = 1 /* Quoted */;
|
|
47
|
-
}
|
|
48
|
-
result += nextChar;
|
|
49
|
-
templatePos++;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (query.slice(templatePos, templatePos + BEGIN_REPEAT.length) === BEGIN_REPEAT) {
|
|
53
|
-
if (state === 2 /* Repeating */) {
|
|
54
|
-
throw new Error("Nested repetition is not allowed");
|
|
55
|
-
}
|
|
56
|
-
state = 2 /* Repeating */;
|
|
57
|
-
templatePos += BEGIN_REPEAT.length;
|
|
58
|
-
result += "(";
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (query.slice(templatePos, templatePos + END_REPEAT.length) === END_REPEAT) {
|
|
62
|
-
if (state === 0 /* Normal */) {
|
|
63
|
-
throw new Error("Unmatched repetition end");
|
|
64
|
-
}
|
|
65
|
-
state = 0 /* Normal */;
|
|
66
|
-
templatePos += END_REPEAT.length;
|
|
67
|
-
result += ")";
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (nextChar === "$") {
|
|
71
|
-
const paramMatch = query.slice(templatePos + 1).match(/^\d+/);
|
|
72
|
-
if (!paramMatch) {
|
|
73
|
-
result += "$";
|
|
74
|
-
templatePos++;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
templatePos += paramMatch[0].length + 1;
|
|
78
|
-
const originalParamIdx = parseInt(paramMatch[0]);
|
|
79
|
-
const paramValue = params[originalParamIdx - 1];
|
|
80
|
-
switch (state) {
|
|
81
|
-
case 0 /* Normal */: {
|
|
82
|
-
flattenedParams.push(paramValue);
|
|
83
|
-
result += `$${lastParamId++}`;
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
case 2 /* Repeating */: {
|
|
87
|
-
const paramArray = Array.isArray(paramValue) ? paramValue : [paramValue];
|
|
88
|
-
if (paramArray.length === 0) {
|
|
89
|
-
result += "NULL";
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
paramArray.forEach((value, idx) => {
|
|
93
|
-
flattenedParams.push(value);
|
|
94
|
-
result += `$${lastParamId++}`;
|
|
95
|
-
if (idx !== paramArray.length - 1) {
|
|
96
|
-
result += ", ";
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
default: {
|
|
102
|
-
throw new Error(`Unexpected state: ${state}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
result += nextChar;
|
|
108
|
-
templatePos++;
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
query: result,
|
|
112
|
-
params: flattenedParams
|
|
113
|
-
};
|
|
86
|
+
// src/utils.ts
|
|
87
|
+
function assertNever(_, message) {
|
|
88
|
+
throw new Error(message);
|
|
114
89
|
}
|
|
115
90
|
|
|
116
91
|
// src/interpreter/renderQuery.ts
|
|
117
|
-
function renderQuery(
|
|
118
|
-
const
|
|
92
|
+
function renderQuery(dbQuery, scope, generators) {
|
|
93
|
+
const queryType = dbQuery.type;
|
|
94
|
+
switch (queryType) {
|
|
95
|
+
case "rawSql":
|
|
96
|
+
return renderRawSql(dbQuery.sql, substituteParams(dbQuery.params, scope, generators));
|
|
97
|
+
case "templateSql":
|
|
98
|
+
return renderTemplateSql(
|
|
99
|
+
dbQuery.fragments,
|
|
100
|
+
dbQuery.placeholderFormat,
|
|
101
|
+
substituteParams(dbQuery.params, scope, generators)
|
|
102
|
+
);
|
|
103
|
+
default:
|
|
104
|
+
assertNever(queryType, `Invalid query type`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function substituteParams(params, scope, generators) {
|
|
108
|
+
return params.map((param) => {
|
|
109
|
+
if (isPrismaValueGenerator(param)) {
|
|
110
|
+
const { name, args } = param.prisma__value;
|
|
111
|
+
const generator = generators[name];
|
|
112
|
+
if (!generator) {
|
|
113
|
+
throw new Error(`Encountered an unknown generator '${name}'`);
|
|
114
|
+
}
|
|
115
|
+
return generator.generate(...args);
|
|
116
|
+
}
|
|
119
117
|
if (!isPrismaValuePlaceholder(param)) {
|
|
120
118
|
return param;
|
|
121
119
|
}
|
|
@@ -125,11 +123,48 @@ function renderQuery({ query, params }, scope) {
|
|
|
125
123
|
}
|
|
126
124
|
return value;
|
|
127
125
|
});
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
}
|
|
127
|
+
function renderTemplateSql(fragments, placeholderFormat, params) {
|
|
128
|
+
let paramIndex = 0;
|
|
129
|
+
let placeholderNumber = 1;
|
|
130
|
+
const flattenedParams = [];
|
|
131
|
+
const sql = fragments.map((fragment) => {
|
|
132
|
+
const fragmentType = fragment.type;
|
|
133
|
+
switch (fragmentType) {
|
|
134
|
+
case "parameter":
|
|
135
|
+
if (paramIndex >= params.length) {
|
|
136
|
+
throw new Error(`Malformed query template. Fragments attempt to read over ${params.length} parameters.`);
|
|
137
|
+
}
|
|
138
|
+
flattenedParams.push(params[paramIndex++]);
|
|
139
|
+
return formatPlaceholder(placeholderFormat, placeholderNumber++);
|
|
140
|
+
case "stringChunk":
|
|
141
|
+
return fragment.value;
|
|
142
|
+
case "parameterTuple": {
|
|
143
|
+
if (paramIndex >= params.length) {
|
|
144
|
+
throw new Error(`Malformed query template. Fragments attempt to read over ${params.length} parameters.`);
|
|
145
|
+
}
|
|
146
|
+
const paramValue = params[paramIndex++];
|
|
147
|
+
const paramArray = Array.isArray(paramValue) ? paramValue : [paramValue];
|
|
148
|
+
const placeholders = paramArray.length == 0 ? "NULL" : paramArray.map((value) => {
|
|
149
|
+
flattenedParams.push(value);
|
|
150
|
+
return formatPlaceholder(placeholderFormat, placeholderNumber++);
|
|
151
|
+
}).join(",");
|
|
152
|
+
return `(${placeholders})`;
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
assertNever(fragmentType, "Invalid fragment type");
|
|
156
|
+
}
|
|
157
|
+
}).join("");
|
|
158
|
+
return renderRawSql(sql, flattenedParams);
|
|
159
|
+
}
|
|
160
|
+
function formatPlaceholder(placeholderFormat, placeholderNumber) {
|
|
161
|
+
return placeholderFormat.hasNumbering ? `${placeholderFormat.prefix}${placeholderNumber}` : placeholderFormat.prefix;
|
|
162
|
+
}
|
|
163
|
+
function renderRawSql(sql, params) {
|
|
164
|
+
const argTypes = params.map((param) => toArgType(param));
|
|
130
165
|
return {
|
|
131
|
-
sql
|
|
132
|
-
args:
|
|
166
|
+
sql,
|
|
167
|
+
args: params,
|
|
133
168
|
argTypes
|
|
134
169
|
};
|
|
135
170
|
}
|
|
@@ -198,24 +233,23 @@ function serialize(resultSet) {
|
|
|
198
233
|
}
|
|
199
234
|
|
|
200
235
|
// src/interpreter/QueryInterpreter.ts
|
|
201
|
-
var _queryable, _placeholderValues, _onQuery, _QueryInterpreter_instances, withQueryEvent_fn;
|
|
202
236
|
var QueryInterpreter = class {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
async run(queryPlan) {
|
|
213
|
-
return this.interpretNode(queryPlan,
|
|
214
|
-
}
|
|
215
|
-
async interpretNode(node, scope) {
|
|
237
|
+
#transactionManager;
|
|
238
|
+
#placeholderValues;
|
|
239
|
+
#onQuery;
|
|
240
|
+
#generators = new GeneratorRegistry();
|
|
241
|
+
constructor({ transactionManager, placeholderValues, onQuery }) {
|
|
242
|
+
this.#transactionManager = transactionManager;
|
|
243
|
+
this.#placeholderValues = placeholderValues;
|
|
244
|
+
this.#onQuery = onQuery;
|
|
245
|
+
}
|
|
246
|
+
async run(queryPlan, queryable) {
|
|
247
|
+
return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot());
|
|
248
|
+
}
|
|
249
|
+
async interpretNode(node, queryable, scope, generators) {
|
|
216
250
|
switch (node.type) {
|
|
217
251
|
case "seq": {
|
|
218
|
-
const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, scope)));
|
|
252
|
+
const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
|
|
219
253
|
return results[results.length - 1];
|
|
220
254
|
}
|
|
221
255
|
case "get": {
|
|
@@ -225,10 +259,10 @@ var QueryInterpreter = class {
|
|
|
225
259
|
const nestedScope = Object.create(scope);
|
|
226
260
|
await Promise.all(
|
|
227
261
|
node.args.bindings.map(async (binding) => {
|
|
228
|
-
nestedScope[binding.name] = await this.interpretNode(binding.expr, scope);
|
|
262
|
+
nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, scope, generators);
|
|
229
263
|
})
|
|
230
264
|
);
|
|
231
|
-
return this.interpretNode(node.args.expr, nestedScope);
|
|
265
|
+
return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
|
|
232
266
|
}
|
|
233
267
|
case "getFirstNonEmpty": {
|
|
234
268
|
for (const name of node.args.names) {
|
|
@@ -240,41 +274,31 @@ var QueryInterpreter = class {
|
|
|
240
274
|
return [];
|
|
241
275
|
}
|
|
242
276
|
case "concat": {
|
|
243
|
-
const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, scope)));
|
|
277
|
+
const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
|
|
244
278
|
return parts.reduce((acc, part) => acc.concat(asList(part)), []);
|
|
245
279
|
}
|
|
246
280
|
case "sum": {
|
|
247
|
-
const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, scope)));
|
|
281
|
+
const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
|
|
248
282
|
return parts.reduce((acc, part) => asNumber(acc) + asNumber(part));
|
|
249
283
|
}
|
|
250
284
|
case "execute": {
|
|
251
|
-
const query = renderQuery(node.args, scope);
|
|
252
|
-
return
|
|
253
|
-
|
|
254
|
-
if (result.ok) {
|
|
255
|
-
return result.value;
|
|
256
|
-
} else {
|
|
257
|
-
throw result.error;
|
|
258
|
-
}
|
|
285
|
+
const query = renderQuery(node.args, scope, generators);
|
|
286
|
+
return this.#withQueryEvent(query, async () => {
|
|
287
|
+
return await queryable.executeRaw(query);
|
|
259
288
|
});
|
|
260
289
|
}
|
|
261
290
|
case "query": {
|
|
262
|
-
const query = renderQuery(node.args, scope);
|
|
263
|
-
return
|
|
264
|
-
|
|
265
|
-
if (result.ok) {
|
|
266
|
-
return serialize(result.value);
|
|
267
|
-
} else {
|
|
268
|
-
throw result.error;
|
|
269
|
-
}
|
|
291
|
+
const query = renderQuery(node.args, scope, generators);
|
|
292
|
+
return this.#withQueryEvent(query, async () => {
|
|
293
|
+
return serialize(await queryable.queryRaw(query));
|
|
270
294
|
});
|
|
271
295
|
}
|
|
272
296
|
case "reverse": {
|
|
273
|
-
const value = await this.interpretNode(node.args, scope);
|
|
297
|
+
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
274
298
|
return Array.isArray(value) ? value.reverse() : value;
|
|
275
299
|
}
|
|
276
300
|
case "unique": {
|
|
277
|
-
const value = await this.interpretNode(node.args, scope);
|
|
301
|
+
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
278
302
|
if (!Array.isArray(value)) {
|
|
279
303
|
return value;
|
|
280
304
|
}
|
|
@@ -284,22 +308,22 @@ var QueryInterpreter = class {
|
|
|
284
308
|
return value[0] ?? null;
|
|
285
309
|
}
|
|
286
310
|
case "required": {
|
|
287
|
-
const value = await this.interpretNode(node.args, scope);
|
|
311
|
+
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
288
312
|
if (isEmpty(value)) {
|
|
289
313
|
throw new Error("Required value is empty");
|
|
290
314
|
}
|
|
291
315
|
return value;
|
|
292
316
|
}
|
|
293
317
|
case "mapField": {
|
|
294
|
-
const value = await this.interpretNode(node.args.records, scope);
|
|
318
|
+
const value = await this.interpretNode(node.args.records, queryable, scope, generators);
|
|
295
319
|
return mapField(value, node.args.field);
|
|
296
320
|
}
|
|
297
321
|
case "join": {
|
|
298
|
-
const parent = await this.interpretNode(node.args.parent, scope);
|
|
322
|
+
const parent = await this.interpretNode(node.args.parent, queryable, scope, generators);
|
|
299
323
|
const children = await Promise.all(
|
|
300
324
|
node.args.children.map(async (joinExpr) => ({
|
|
301
325
|
joinExpr,
|
|
302
|
-
childRecords: await this.interpretNode(joinExpr.child, scope)
|
|
326
|
+
childRecords: await this.interpretNode(joinExpr.child, queryable, scope, generators)
|
|
303
327
|
}))
|
|
304
328
|
);
|
|
305
329
|
if (Array.isArray(parent)) {
|
|
@@ -310,30 +334,41 @@ var QueryInterpreter = class {
|
|
|
310
334
|
}
|
|
311
335
|
return attachChildrenToParent(asRecord(parent), children);
|
|
312
336
|
}
|
|
337
|
+
case "transaction": {
|
|
338
|
+
if (!this.#transactionManager.enabled) {
|
|
339
|
+
return this.interpretNode(node.args, queryable, scope, generators);
|
|
340
|
+
}
|
|
341
|
+
const transactionManager = this.#transactionManager.manager;
|
|
342
|
+
const transactionInfo = await transactionManager.startTransaction();
|
|
343
|
+
const transaction = transactionManager.getTransaction(transactionInfo, "new");
|
|
344
|
+
try {
|
|
345
|
+
const value = await this.interpretNode(node.args, transaction, scope, generators);
|
|
346
|
+
await transactionManager.commitTransaction(transactionInfo.id);
|
|
347
|
+
return value;
|
|
348
|
+
} catch (e) {
|
|
349
|
+
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
350
|
+
throw e;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
313
353
|
default: {
|
|
314
354
|
node;
|
|
315
355
|
throw new Error(`Unexpected node type: ${node.type}`);
|
|
316
356
|
}
|
|
317
357
|
}
|
|
318
358
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
duration: endInstant - startInstant,
|
|
333
|
-
query: query.sql,
|
|
334
|
-
params: query.args
|
|
335
|
-
});
|
|
336
|
-
return result;
|
|
359
|
+
async #withQueryEvent(query, execute) {
|
|
360
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
361
|
+
const startInstant = performance.now();
|
|
362
|
+
const result = await execute();
|
|
363
|
+
const endInstant = performance.now();
|
|
364
|
+
this.#onQuery?.({
|
|
365
|
+
timestamp,
|
|
366
|
+
duration: endInstant - startInstant,
|
|
367
|
+
query: query.sql,
|
|
368
|
+
params: query.args
|
|
369
|
+
});
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
337
372
|
};
|
|
338
373
|
function isEmpty(value) {
|
|
339
374
|
if (Array.isArray(value)) {
|
|
@@ -391,22 +426,16 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
|
|
|
391
426
|
return true;
|
|
392
427
|
}
|
|
393
428
|
|
|
394
|
-
// src/transactionManager/Transaction.ts
|
|
395
|
-
var IsolationLevel = /* @__PURE__ */ ((IsolationLevel2) => {
|
|
396
|
-
IsolationLevel2["ReadUncommitted"] = "ReadUncommitted";
|
|
397
|
-
IsolationLevel2["ReadCommitted"] = "ReadCommitted";
|
|
398
|
-
IsolationLevel2["RepeatableRead"] = "RepeatableRead";
|
|
399
|
-
IsolationLevel2["Snapshot"] = "Snapshot";
|
|
400
|
-
IsolationLevel2["Serializable"] = "Serializable";
|
|
401
|
-
return IsolationLevel2;
|
|
402
|
-
})(IsolationLevel || {});
|
|
403
|
-
|
|
404
429
|
// src/transactionManager/TransactionManager.ts
|
|
405
|
-
import Debug from "@prisma/debug";
|
|
430
|
+
import { Debug } from "@prisma/debug";
|
|
406
431
|
|
|
407
|
-
// src/
|
|
408
|
-
function
|
|
409
|
-
|
|
432
|
+
// src/crypto.ts
|
|
433
|
+
async function getCrypto() {
|
|
434
|
+
return globalThis.crypto ?? await import("node:crypto");
|
|
435
|
+
}
|
|
436
|
+
async function randomUUID() {
|
|
437
|
+
const crypto = await getCrypto();
|
|
438
|
+
return crypto.randomUUID();
|
|
410
439
|
}
|
|
411
440
|
|
|
412
441
|
// src/transactionManager/TransactionManagerErrors.ts
|
|
@@ -414,8 +443,8 @@ var TransactionManagerError = class extends Error {
|
|
|
414
443
|
constructor(message, meta) {
|
|
415
444
|
super("Transaction API error: " + message);
|
|
416
445
|
this.meta = meta;
|
|
417
|
-
__publicField(this, "code", "P2028");
|
|
418
446
|
}
|
|
447
|
+
code = "P2028";
|
|
419
448
|
};
|
|
420
449
|
var TransactionDriverAdapterError = class extends TransactionManagerError {
|
|
421
450
|
constructor(message, errorParams) {
|
|
@@ -465,35 +494,25 @@ var InvalidTransactionIsolationLevelError = class extends TransactionManagerErro
|
|
|
465
494
|
|
|
466
495
|
// src/transactionManager/TransactionManager.ts
|
|
467
496
|
var MAX_CLOSED_TRANSACTIONS = 100;
|
|
468
|
-
var isolationLevelMap = {
|
|
469
|
-
ReadUncommitted: "READ UNCOMMITTED",
|
|
470
|
-
ReadCommitted: "READ COMMITTED",
|
|
471
|
-
RepeatableRead: "REPEATABLE READ",
|
|
472
|
-
Snapshot: "SNAPSHOT",
|
|
473
|
-
Serializable: "SERIALIZABLE"
|
|
474
|
-
};
|
|
475
497
|
var debug = Debug("prisma:client:transactionManager");
|
|
476
498
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
477
499
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
478
|
-
var ISOLATION_LEVEL_QUERY = (isolationLevel) => ({
|
|
479
|
-
sql: "SET TRANSACTION ISOLATION LEVEL " + isolationLevelMap[isolationLevel],
|
|
480
|
-
args: [],
|
|
481
|
-
argTypes: []
|
|
482
|
-
});
|
|
483
500
|
var TransactionManager = class {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
501
|
+
// The map of active transactions.
|
|
502
|
+
transactions = /* @__PURE__ */ new Map();
|
|
503
|
+
// List of last closed transactions. Max MAX_CLOSED_TRANSACTIONS entries.
|
|
504
|
+
// Used to provide better error messages than a generic "transaction not found".
|
|
505
|
+
closedTransactions = [];
|
|
506
|
+
driverAdapter;
|
|
507
|
+
transactionOptions;
|
|
508
|
+
constructor({ driverAdapter, transactionOptions }) {
|
|
491
509
|
this.driverAdapter = driverAdapter;
|
|
510
|
+
this.transactionOptions = transactionOptions;
|
|
492
511
|
}
|
|
493
512
|
async startTransaction(options) {
|
|
494
|
-
const validatedOptions = this.validateOptions(options);
|
|
513
|
+
const validatedOptions = options !== void 0 ? this.validateOptions(options) : this.transactionOptions;
|
|
495
514
|
const transaction = {
|
|
496
|
-
id:
|
|
515
|
+
id: await randomUUID(),
|
|
497
516
|
status: "waiting",
|
|
498
517
|
timer: void 0,
|
|
499
518
|
timeout: validatedOptions.timeout,
|
|
@@ -502,28 +521,17 @@ var TransactionManager = class {
|
|
|
502
521
|
};
|
|
503
522
|
this.transactions.set(transaction.id, transaction);
|
|
504
523
|
transaction.timer = this.startTransactionTimeout(transaction.id, validatedOptions.maxWait);
|
|
505
|
-
|
|
506
|
-
|
|
524
|
+
let startedTransaction;
|
|
525
|
+
try {
|
|
526
|
+
startedTransaction = await this.driverAdapter.startTransaction(validatedOptions.isolationLevel);
|
|
527
|
+
} catch (error) {
|
|
507
528
|
throw new TransactionDriverAdapterError("Failed to start transaction.", {
|
|
508
|
-
driverAdapterError:
|
|
529
|
+
driverAdapterError: error
|
|
509
530
|
});
|
|
510
|
-
if (this.requiresSettingIsolationLevelFirst() && validatedOptions.isolationLevel) {
|
|
511
|
-
await txContext.value.executeRaw(ISOLATION_LEVEL_QUERY(validatedOptions.isolationLevel));
|
|
512
|
-
}
|
|
513
|
-
const startedTransaction = await txContext.value.startTransaction();
|
|
514
|
-
if (!startedTransaction.ok)
|
|
515
|
-
throw new TransactionDriverAdapterError("Failed to start transaction.", {
|
|
516
|
-
driverAdapterError: startedTransaction.error
|
|
517
|
-
});
|
|
518
|
-
if (!startedTransaction.value.options.usePhantomQuery) {
|
|
519
|
-
await startedTransaction.value.executeRaw({ sql: "BEGIN", args: [], argTypes: [] });
|
|
520
|
-
if (!this.requiresSettingIsolationLevelFirst() && validatedOptions.isolationLevel) {
|
|
521
|
-
await txContext.value.executeRaw(ISOLATION_LEVEL_QUERY(validatedOptions.isolationLevel));
|
|
522
|
-
}
|
|
523
531
|
}
|
|
524
532
|
switch (transaction.status) {
|
|
525
533
|
case "waiting":
|
|
526
|
-
transaction.transaction = startedTransaction
|
|
534
|
+
transaction.transaction = startedTransaction;
|
|
527
535
|
clearTimeout(transaction.timer);
|
|
528
536
|
transaction.timer = void 0;
|
|
529
537
|
transaction.status = "running";
|
|
@@ -603,20 +611,24 @@ var TransactionManager = class {
|
|
|
603
611
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
604
612
|
tx.status = status;
|
|
605
613
|
if (tx.transaction && status === "committed") {
|
|
606
|
-
|
|
607
|
-
|
|
614
|
+
try {
|
|
615
|
+
await tx.transaction.commit();
|
|
616
|
+
} catch (error) {
|
|
608
617
|
throw new TransactionDriverAdapterError("Failed to commit transaction.", {
|
|
609
|
-
driverAdapterError:
|
|
618
|
+
driverAdapterError: error
|
|
610
619
|
});
|
|
620
|
+
}
|
|
611
621
|
if (!tx.transaction.options.usePhantomQuery) {
|
|
612
622
|
await tx.transaction.executeRaw(COMMIT_QUERY());
|
|
613
623
|
}
|
|
614
624
|
} else if (tx.transaction) {
|
|
615
|
-
|
|
616
|
-
|
|
625
|
+
try {
|
|
626
|
+
await tx.transaction.rollback();
|
|
627
|
+
} catch (error) {
|
|
617
628
|
throw new TransactionDriverAdapterError("Failed to rollback transaction.", {
|
|
618
|
-
driverAdapterError:
|
|
629
|
+
driverAdapterError: error
|
|
619
630
|
});
|
|
631
|
+
}
|
|
620
632
|
if (!tx.transaction.options.usePhantomQuery) {
|
|
621
633
|
await tx.transaction.executeRaw(ROLLBACK_QUERY());
|
|
622
634
|
}
|
|
@@ -632,24 +644,18 @@ var TransactionManager = class {
|
|
|
632
644
|
validateOptions(options) {
|
|
633
645
|
if (!options.timeout) throw new TransactionManagerError("timeout is required");
|
|
634
646
|
if (!options.maxWait) throw new TransactionManagerError("maxWait is required");
|
|
635
|
-
if (options.isolationLevel === "
|
|
636
|
-
throw new InvalidTransactionIsolationLevelError(options.isolationLevel);
|
|
637
|
-
if (this.driverAdapter.provider === "sqlite" && options.isolationLevel && options.isolationLevel !== "Serializable" /* Serializable */)
|
|
638
|
-
throw new InvalidTransactionIsolationLevelError(options.isolationLevel);
|
|
647
|
+
if (options.isolationLevel === "SNAPSHOT") throw new InvalidTransactionIsolationLevelError(options.isolationLevel);
|
|
639
648
|
return {
|
|
640
649
|
...options,
|
|
641
650
|
timeout: options.timeout,
|
|
642
651
|
maxWait: options.maxWait
|
|
643
652
|
};
|
|
644
653
|
}
|
|
645
|
-
requiresSettingIsolationLevelFirst() {
|
|
646
|
-
return this.driverAdapter.provider === "mysql";
|
|
647
|
-
}
|
|
648
654
|
};
|
|
649
655
|
export {
|
|
650
|
-
IsolationLevel,
|
|
651
656
|
QueryInterpreter,
|
|
652
657
|
TransactionManager,
|
|
653
658
|
TransactionManagerError,
|
|
659
|
+
isPrismaValueGenerator,
|
|
654
660
|
isPrismaValuePlaceholder
|
|
655
661
|
};
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SqlQueryable } from '@prisma/driver-adapter-utils';
|
|
2
2
|
import { QueryEvent } from '../events';
|
|
3
3
|
import { QueryPlanNode } from '../QueryPlan';
|
|
4
|
+
import { type TransactionManager } from '../transactionManager/TransactionManager';
|
|
5
|
+
export type QueryInterpreterTransactionManager = {
|
|
6
|
+
enabled: true;
|
|
7
|
+
manager: TransactionManager;
|
|
8
|
+
} | {
|
|
9
|
+
enabled: false;
|
|
10
|
+
};
|
|
4
11
|
export type QueryInterpreterOptions = {
|
|
5
|
-
|
|
12
|
+
transactionManager: QueryInterpreterTransactionManager;
|
|
6
13
|
placeholderValues: Record<string, unknown>;
|
|
7
14
|
onQuery?: (event: QueryEvent) => void;
|
|
8
15
|
};
|
|
9
16
|
export declare class QueryInterpreter {
|
|
10
17
|
#private;
|
|
11
|
-
constructor({
|
|
12
|
-
run(queryPlan: QueryPlanNode): Promise<unknown>;
|
|
18
|
+
constructor({ transactionManager, placeholderValues, onQuery }: QueryInterpreterOptions);
|
|
19
|
+
run(queryPlan: QueryPlanNode, queryable: SqlQueryable): Promise<unknown>;
|
|
13
20
|
private interpretNode;
|
|
14
21
|
}
|