@prisma/client-engine-runtime 6.9.0-dev.4 → 6.9.0-dev.40
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 +50 -1
- package/dist/UserFacingError.d.ts +3 -3
- package/dist/index.d.mts +88 -13
- package/dist/index.d.ts +88 -13
- package/dist/index.js +586 -187
- package/dist/index.mjs +580 -186
- package/dist/interpreter/DataMapper.d.ts +3 -0
- package/dist/interpreter/QueryInterpreter.d.ts +2 -1
- package/dist/interpreter/generators.d.ts +2 -1
- package/dist/interpreter/renderQuery.d.ts +2 -1
- package/dist/interpreter/serializeSql.d.ts +2 -1
- package/dist/tracing.d.ts +10 -2
- package/dist/transactionManager/TransactionManager.d.ts +3 -1
- package/dist/transactionManager/TransactionManagerErrors.d.ts +4 -4
- package/dist/utils.d.ts +15 -0
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,244 @@
|
|
|
1
|
-
// src/interpreter/
|
|
2
|
-
import
|
|
1
|
+
// src/interpreter/DataMapper.ts
|
|
2
|
+
import Decimal2 from "decimal.js";
|
|
3
3
|
|
|
4
4
|
// src/utils.ts
|
|
5
|
+
import Decimal from "decimal.js";
|
|
5
6
|
function assertNever(_, message) {
|
|
6
7
|
throw new Error(message);
|
|
7
8
|
}
|
|
9
|
+
function isDeepStrictEqual(a, b) {
|
|
10
|
+
return a === b || a !== null && b !== null && typeof a === "object" && typeof b === "object" && Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((key) => isDeepStrictEqual(a[key], b[key]));
|
|
11
|
+
}
|
|
12
|
+
function doKeysMatch(lhs, rhs) {
|
|
13
|
+
const lhsKeys = Object.keys(lhs);
|
|
14
|
+
const rhsKeys = Object.keys(rhs);
|
|
15
|
+
const smallerKeyList = lhsKeys.length < rhsKeys.length ? lhsKeys : rhsKeys;
|
|
16
|
+
return smallerKeyList.every((key) => {
|
|
17
|
+
if (typeof lhs[key] !== typeof rhs[key]) {
|
|
18
|
+
if (typeof lhs[key] === "number" || typeof rhs[key] === "number") {
|
|
19
|
+
return `${lhs[key]}` === `${rhs[key]}`;
|
|
20
|
+
} else if (typeof lhs[key] === "bigint" || typeof rhs[key] === "bigint") {
|
|
21
|
+
return BigInt(`${lhs[key]}`.replace(/n$/, "")) === BigInt(`${rhs[key]}`.replace(/n$/, ""));
|
|
22
|
+
} else if (lhs[key] instanceof Date || rhs[key] instanceof Date) {
|
|
23
|
+
return (/* @__PURE__ */ new Date(`${lhs[key]}`)).getTime() === (/* @__PURE__ */ new Date(`${rhs[key]}`)).getTime();
|
|
24
|
+
} else if (Decimal.isDecimal(lhs[key]) || Decimal.isDecimal(rhs[key])) {
|
|
25
|
+
return new Decimal(`${lhs[key]}`).equals(new Decimal(`${rhs[key]}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return isDeepStrictEqual(lhs[key], rhs[key]);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function safeJsonStringify(obj) {
|
|
32
|
+
return JSON.stringify(obj, (_key, val) => {
|
|
33
|
+
if (typeof val === "bigint") {
|
|
34
|
+
return val.toString();
|
|
35
|
+
} else if (val instanceof Uint8Array) {
|
|
36
|
+
return Buffer.from(val).toString("base64");
|
|
37
|
+
}
|
|
38
|
+
return val;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/interpreter/DataMapper.ts
|
|
43
|
+
var DataMapperError = class extends Error {
|
|
44
|
+
name = "DataMapperError";
|
|
45
|
+
};
|
|
46
|
+
function applyDataMap(data, structure) {
|
|
47
|
+
switch (structure.type) {
|
|
48
|
+
case "AffectedRows":
|
|
49
|
+
if (typeof data !== "number") {
|
|
50
|
+
throw new DataMapperError(`Expected an affected rows count, got: ${typeof data} (${data})`);
|
|
51
|
+
}
|
|
52
|
+
return { count: data };
|
|
53
|
+
case "Object":
|
|
54
|
+
return mapArrayOrObject(data, structure.fields);
|
|
55
|
+
case "Value":
|
|
56
|
+
return mapValue(data, "<result>", structure.resultType);
|
|
57
|
+
default:
|
|
58
|
+
assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function mapArrayOrObject(data, fields) {
|
|
62
|
+
if (data === null) return null;
|
|
63
|
+
if (Array.isArray(data)) {
|
|
64
|
+
const rows = data;
|
|
65
|
+
return rows.map((row) => mapObject(row, fields));
|
|
66
|
+
}
|
|
67
|
+
if (typeof data === "object") {
|
|
68
|
+
const row = data;
|
|
69
|
+
return mapObject(row, fields);
|
|
70
|
+
}
|
|
71
|
+
if (typeof data === "string") {
|
|
72
|
+
let decodedData;
|
|
73
|
+
try {
|
|
74
|
+
decodedData = JSON.parse(data);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new DataMapperError(`Expected an array or object, got a string that is not valid JSON`, {
|
|
77
|
+
cause: error
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return mapArrayOrObject(decodedData, fields);
|
|
81
|
+
}
|
|
82
|
+
throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
|
|
83
|
+
}
|
|
84
|
+
function mapObject(data, fields) {
|
|
85
|
+
if (typeof data !== "object") {
|
|
86
|
+
throw new DataMapperError(`Expected an object, but got '${typeof data}'`);
|
|
87
|
+
}
|
|
88
|
+
const result = {};
|
|
89
|
+
for (const [name, node] of Object.entries(fields)) {
|
|
90
|
+
switch (node.type) {
|
|
91
|
+
case "AffectedRows": {
|
|
92
|
+
throw new DataMapperError(`Unexpected 'AffectedRows' node in data mapping for field '${name}'`);
|
|
93
|
+
}
|
|
94
|
+
case "Object": {
|
|
95
|
+
if (!node.flattened && !Object.hasOwn(data, name)) {
|
|
96
|
+
throw new DataMapperError(
|
|
97
|
+
`Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const target = node.flattened ? data : data[name];
|
|
101
|
+
result[name] = mapArrayOrObject(target, node.fields);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "Value":
|
|
105
|
+
{
|
|
106
|
+
const dbName = node.dbName;
|
|
107
|
+
if (Object.hasOwn(data, dbName)) {
|
|
108
|
+
result[name] = mapValue(data[dbName], dbName, node.resultType);
|
|
109
|
+
} else {
|
|
110
|
+
throw new DataMapperError(
|
|
111
|
+
`Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
default:
|
|
117
|
+
assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
function mapValue(value, columnName, resultType) {
|
|
123
|
+
if (value === null) {
|
|
124
|
+
return resultType.type === "Array" ? [] : null;
|
|
125
|
+
}
|
|
126
|
+
switch (resultType.type) {
|
|
127
|
+
case "Any":
|
|
128
|
+
return value;
|
|
129
|
+
case "String": {
|
|
130
|
+
if (typeof value !== "string") {
|
|
131
|
+
throw new DataMapperError(`Expected a string in column '${columnName}', got ${typeof value}: ${value}`);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
case "Int": {
|
|
136
|
+
switch (typeof value) {
|
|
137
|
+
case "number": {
|
|
138
|
+
return Math.trunc(value);
|
|
139
|
+
}
|
|
140
|
+
case "string": {
|
|
141
|
+
const numberValue = Math.trunc(Number(value));
|
|
142
|
+
if (Number.isNaN(numberValue) || !Number.isFinite(numberValue)) {
|
|
143
|
+
throw new DataMapperError(`Expected an integer in column '${columnName}', got string: ${value}`);
|
|
144
|
+
}
|
|
145
|
+
if (!Number.isSafeInteger(numberValue)) {
|
|
146
|
+
throw new DataMapperError(
|
|
147
|
+
`Integer value in column '${columnName}' is too large to represent as a JavaScript number without loss of precision, got: ${value}. Consider using BigInt type.`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return numberValue;
|
|
151
|
+
}
|
|
152
|
+
default:
|
|
153
|
+
throw new DataMapperError(`Expected an integer in column '${columnName}', got ${typeof value}: ${value}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
case "BigInt": {
|
|
157
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
158
|
+
throw new DataMapperError(`Expected a bigint in column '${columnName}', got ${typeof value}: ${value}`);
|
|
159
|
+
}
|
|
160
|
+
return { $type: "BigInt", value };
|
|
161
|
+
}
|
|
162
|
+
case "Float": {
|
|
163
|
+
if (typeof value === "number") return value;
|
|
164
|
+
if (typeof value === "string") {
|
|
165
|
+
const parsedValue = Number(value);
|
|
166
|
+
if (Number.isNaN(parsedValue) && !/^[-+]?nan$/.test(value.toLowerCase())) {
|
|
167
|
+
throw new DataMapperError(`Expected a float in column '${columnName}', got string: ${value}`);
|
|
168
|
+
}
|
|
169
|
+
return parsedValue;
|
|
170
|
+
}
|
|
171
|
+
throw new DataMapperError(`Expected a float in column '${columnName}', got ${typeof value}: ${value}`);
|
|
172
|
+
}
|
|
173
|
+
case "Boolean": {
|
|
174
|
+
if (typeof value === "boolean") return value;
|
|
175
|
+
if (typeof value === "number") return value === 1;
|
|
176
|
+
if (typeof value === "string") {
|
|
177
|
+
if (value === "true" || value === "TRUE" || value === "1") {
|
|
178
|
+
return true;
|
|
179
|
+
} else if (value === "false" || value === "FALSE" || value === "0") {
|
|
180
|
+
return false;
|
|
181
|
+
} else {
|
|
182
|
+
throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
|
|
186
|
+
}
|
|
187
|
+
case "Decimal":
|
|
188
|
+
if (typeof value !== "number" && typeof value !== "string" && !Decimal2.isDecimal(value)) {
|
|
189
|
+
throw new DataMapperError(`Expected a decimal in column '${columnName}', got ${typeof value}: ${value}`);
|
|
190
|
+
}
|
|
191
|
+
return { $type: "Decimal", value };
|
|
192
|
+
case "Date": {
|
|
193
|
+
if (typeof value === "string") {
|
|
194
|
+
return { $type: "DateTime", value: ensureTimezoneInIsoString(value) };
|
|
195
|
+
}
|
|
196
|
+
if (typeof value === "number" || value instanceof Date) {
|
|
197
|
+
return { $type: "DateTime", value };
|
|
198
|
+
}
|
|
199
|
+
throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
|
|
200
|
+
}
|
|
201
|
+
case "Time": {
|
|
202
|
+
if (typeof value === "string") {
|
|
203
|
+
return { $type: "DateTime", value: `1970-01-01T${ensureTimezoneInIsoString(value)}` };
|
|
204
|
+
}
|
|
205
|
+
throw new DataMapperError(`Expected a time in column '${columnName}', got ${typeof value}: ${value}`);
|
|
206
|
+
}
|
|
207
|
+
case "Array": {
|
|
208
|
+
const values = value;
|
|
209
|
+
return values.map((v, i) => mapValue(v, `${columnName}[${i}]`, resultType.inner));
|
|
210
|
+
}
|
|
211
|
+
case "Object": {
|
|
212
|
+
const jsonValue = typeof value === "string" ? value : safeJsonStringify(value);
|
|
213
|
+
return { $type: "Json", value: jsonValue };
|
|
214
|
+
}
|
|
215
|
+
case "Bytes": {
|
|
216
|
+
if (typeof value === "string" && value.startsWith("\\x")) {
|
|
217
|
+
return { $type: "Bytes", value: Buffer.from(value.slice(2), "hex").toString("base64") };
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(value)) {
|
|
220
|
+
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
221
|
+
}
|
|
222
|
+
throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
|
|
223
|
+
}
|
|
224
|
+
default:
|
|
225
|
+
assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
var TIMEZONE_PATTERN = /Z$|(?<!\d{4}-\d{2})[+-]\d{2}(:?\d{2})?$/;
|
|
229
|
+
function ensureTimezoneInIsoString(dt) {
|
|
230
|
+
const results = TIMEZONE_PATTERN.exec(dt);
|
|
231
|
+
if (results === null) {
|
|
232
|
+
return `${dt}Z`;
|
|
233
|
+
} else if (results[0] !== "Z" && results[1] === void 0) {
|
|
234
|
+
return `${dt}:00`;
|
|
235
|
+
} else {
|
|
236
|
+
return dt;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
8
239
|
|
|
9
240
|
// src/tracing.ts
|
|
241
|
+
import { SpanKind } from "@opentelemetry/api";
|
|
10
242
|
var noopTracingHelper = {
|
|
11
243
|
runInChildSpan(_, callback) {
|
|
12
244
|
return callback();
|
|
@@ -24,6 +256,37 @@ function providerToOtelSystem(provider) {
|
|
|
24
256
|
assertNever(provider, `Unknown provider: ${provider}`);
|
|
25
257
|
}
|
|
26
258
|
}
|
|
259
|
+
async function withQuerySpanAndEvent({
|
|
260
|
+
query,
|
|
261
|
+
queryable,
|
|
262
|
+
tracingHelper,
|
|
263
|
+
onQuery,
|
|
264
|
+
execute
|
|
265
|
+
}) {
|
|
266
|
+
return await tracingHelper.runInChildSpan(
|
|
267
|
+
{
|
|
268
|
+
name: "db_query",
|
|
269
|
+
kind: SpanKind.CLIENT,
|
|
270
|
+
attributes: {
|
|
271
|
+
"db.query.text": query.sql,
|
|
272
|
+
"db.system.name": providerToOtelSystem(queryable.provider)
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
async () => {
|
|
276
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
277
|
+
const startInstant = performance.now();
|
|
278
|
+
const result = await execute();
|
|
279
|
+
const endInstant = performance.now();
|
|
280
|
+
onQuery?.({
|
|
281
|
+
timestamp,
|
|
282
|
+
duration: endInstant - startInstant,
|
|
283
|
+
query: query.sql,
|
|
284
|
+
params: query.args
|
|
285
|
+
});
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
}
|
|
27
290
|
|
|
28
291
|
// src/UserFacingError.ts
|
|
29
292
|
import { isDriverAdapterError } from "@prisma/driver-adapter-utils";
|
|
@@ -34,7 +297,7 @@ var UserFacingError = class extends Error {
|
|
|
34
297
|
constructor(message, code, meta) {
|
|
35
298
|
super(message);
|
|
36
299
|
this.code = code;
|
|
37
|
-
this.meta = meta;
|
|
300
|
+
this.meta = meta ?? {};
|
|
38
301
|
}
|
|
39
302
|
toQueryResponseErrorObject() {
|
|
40
303
|
return {
|
|
@@ -57,7 +320,7 @@ function rethrowAsUserFacing(error) {
|
|
|
57
320
|
if (!code || !message) {
|
|
58
321
|
throw error;
|
|
59
322
|
}
|
|
60
|
-
throw new UserFacingError(message, code, error);
|
|
323
|
+
throw new UserFacingError(message, code, { driverAdapterError: error });
|
|
61
324
|
}
|
|
62
325
|
function getErrorCode(err) {
|
|
63
326
|
switch (err.cause.kind) {
|
|
@@ -168,101 +431,6 @@ function renderConstraint(constraint) {
|
|
|
168
431
|
return "(not available)";
|
|
169
432
|
}
|
|
170
433
|
|
|
171
|
-
// src/interpreter/DataMapper.ts
|
|
172
|
-
import Decimal from "decimal.js";
|
|
173
|
-
function applyDataMap(data, structure) {
|
|
174
|
-
switch (structure.type) {
|
|
175
|
-
case "Object":
|
|
176
|
-
return mapArrayOrObject(data, structure.fields);
|
|
177
|
-
case "Value":
|
|
178
|
-
return mapValue(data, structure.resultType);
|
|
179
|
-
default:
|
|
180
|
-
assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
function mapArrayOrObject(data, fields) {
|
|
184
|
-
if (data === null) return null;
|
|
185
|
-
if (Array.isArray(data)) {
|
|
186
|
-
const rows = data;
|
|
187
|
-
return rows.map((row) => mapObject(row, fields));
|
|
188
|
-
}
|
|
189
|
-
if (typeof data === "object") {
|
|
190
|
-
const row = data;
|
|
191
|
-
return mapObject(row, fields);
|
|
192
|
-
}
|
|
193
|
-
throw new Error(`DataMapper: Expected an array or an object, got: ${typeof data}`);
|
|
194
|
-
}
|
|
195
|
-
function mapObject(data, fields) {
|
|
196
|
-
if (typeof data !== "object") {
|
|
197
|
-
throw new Error(`DataMapper: Expected an object, but got '${typeof data}'`);
|
|
198
|
-
}
|
|
199
|
-
const result = {};
|
|
200
|
-
for (const [name, node] of Object.entries(fields)) {
|
|
201
|
-
switch (node.type) {
|
|
202
|
-
case "Object": {
|
|
203
|
-
if (!node.flattened && !Object.hasOwn(data, name)) {
|
|
204
|
-
throw new Error(
|
|
205
|
-
`DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
const target = node.flattened ? data : data[name];
|
|
209
|
-
result[name] = mapArrayOrObject(target, node.fields);
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
case "Value":
|
|
213
|
-
{
|
|
214
|
-
const dbName = node.dbName;
|
|
215
|
-
if (Object.hasOwn(data, dbName)) {
|
|
216
|
-
result[name] = mapValue(data[dbName], node.resultType);
|
|
217
|
-
} else {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
default:
|
|
225
|
-
assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return result;
|
|
229
|
-
}
|
|
230
|
-
function mapValue(value, resultType) {
|
|
231
|
-
if (value === null) return null;
|
|
232
|
-
switch (resultType.type) {
|
|
233
|
-
case "Any":
|
|
234
|
-
return value;
|
|
235
|
-
case "String":
|
|
236
|
-
return typeof value === "string" ? value : `${value}`;
|
|
237
|
-
case "Int":
|
|
238
|
-
return typeof value === "number" ? value : parseInt(`${value}`, 10);
|
|
239
|
-
case "BigInt":
|
|
240
|
-
return typeof value === "bigint" ? value : BigInt(`${value}`);
|
|
241
|
-
case "Float":
|
|
242
|
-
return typeof value === "number" ? value : parseFloat(`${value}`);
|
|
243
|
-
case "Boolean":
|
|
244
|
-
return typeof value === "boolean" ? value : value !== "0";
|
|
245
|
-
case "Decimal":
|
|
246
|
-
return typeof value === "number" ? new Decimal(value) : new Decimal(`${value}`);
|
|
247
|
-
case "Date":
|
|
248
|
-
return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
|
|
249
|
-
case "Array": {
|
|
250
|
-
const values = value;
|
|
251
|
-
return values.map((v) => mapValue(v, resultType.inner));
|
|
252
|
-
}
|
|
253
|
-
case "Object":
|
|
254
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
255
|
-
case "Bytes": {
|
|
256
|
-
if (!Array.isArray(value)) {
|
|
257
|
-
throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
|
|
258
|
-
}
|
|
259
|
-
return new Uint8Array(value);
|
|
260
|
-
}
|
|
261
|
-
default:
|
|
262
|
-
assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
434
|
// src/interpreter/generators.ts
|
|
267
435
|
import cuid1 from "@bugsnag/cuid";
|
|
268
436
|
import { createId as cuid2 } from "@paralleldrive/cuid2";
|
|
@@ -272,7 +440,6 @@ import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
|
|
|
272
440
|
var GeneratorRegistry = class {
|
|
273
441
|
#generators = {};
|
|
274
442
|
constructor() {
|
|
275
|
-
this.register("now", new NowGenerator());
|
|
276
443
|
this.register("uuid", new UuidGenerator());
|
|
277
444
|
this.register("cuid", new CuidGenerator());
|
|
278
445
|
this.register("ulid", new UlidGenerator());
|
|
@@ -284,9 +451,11 @@ var GeneratorRegistry = class {
|
|
|
284
451
|
* method being called, meaning that the built-in time-based generators will always return
|
|
285
452
|
* the same value on repeated calls as long as the same snapshot is used.
|
|
286
453
|
*/
|
|
287
|
-
snapshot() {
|
|
454
|
+
snapshot(provider) {
|
|
288
455
|
return Object.create(this.#generators, {
|
|
289
|
-
now: {
|
|
456
|
+
now: {
|
|
457
|
+
value: provider === "mysql" ? new MysqlNowGenerator() : new NowGenerator()
|
|
458
|
+
}
|
|
290
459
|
});
|
|
291
460
|
}
|
|
292
461
|
/**
|
|
@@ -302,6 +471,12 @@ var NowGenerator = class {
|
|
|
302
471
|
return this.#now.toISOString();
|
|
303
472
|
}
|
|
304
473
|
};
|
|
474
|
+
var MysqlNowGenerator = class {
|
|
475
|
+
#now = /* @__PURE__ */ new Date();
|
|
476
|
+
generate() {
|
|
477
|
+
return this.#now.toISOString().replace("T", " ").replace("Z", "");
|
|
478
|
+
}
|
|
479
|
+
};
|
|
305
480
|
var UuidGenerator = class {
|
|
306
481
|
generate(arg) {
|
|
307
482
|
if (arg === 4) {
|
|
@@ -367,6 +542,9 @@ function isPrismaValueGenerator(value) {
|
|
|
367
542
|
function isPrismaValueBytes(value) {
|
|
368
543
|
return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
|
|
369
544
|
}
|
|
545
|
+
function isPrismaValueBigInt(value) {
|
|
546
|
+
return typeof value === "object" && value !== null && value["prisma__type"] === "bigint";
|
|
547
|
+
}
|
|
370
548
|
|
|
371
549
|
// src/interpreter/renderQuery.ts
|
|
372
550
|
function renderQuery(dbQuery, scope, generators) {
|
|
@@ -409,9 +587,10 @@ function evaluateParam(param, scope, generators) {
|
|
|
409
587
|
}
|
|
410
588
|
if (Array.isArray(value)) {
|
|
411
589
|
value = value.map((el) => evaluateParam(el, scope, generators));
|
|
412
|
-
}
|
|
413
|
-
if (isPrismaValueBytes(value)) {
|
|
590
|
+
} else if (isPrismaValueBytes(value)) {
|
|
414
591
|
value = Buffer.from(value.prisma__value, "base64");
|
|
592
|
+
} else if (isPrismaValueBigInt(value)) {
|
|
593
|
+
value = BigInt(value.prisma__value);
|
|
415
594
|
}
|
|
416
595
|
return value;
|
|
417
596
|
}
|
|
@@ -505,6 +684,7 @@ function doesRequireEvaluation(param) {
|
|
|
505
684
|
}
|
|
506
685
|
|
|
507
686
|
// src/interpreter/serializeSql.ts
|
|
687
|
+
import { ColumnTypeEnum } from "@prisma/driver-adapter-utils";
|
|
508
688
|
function serializeSql(resultSet) {
|
|
509
689
|
return resultSet.rows.map(
|
|
510
690
|
(row) => row.reduce((acc, value, index) => {
|
|
@@ -525,6 +705,100 @@ function serializeSql(resultSet) {
|
|
|
525
705
|
}, {})
|
|
526
706
|
);
|
|
527
707
|
}
|
|
708
|
+
function serializeRawSql(resultSet) {
|
|
709
|
+
const types = resultSet.columnTypes.map((type) => serializeColumnType(type));
|
|
710
|
+
const mappers = types.map((type) => {
|
|
711
|
+
switch (type) {
|
|
712
|
+
case "int":
|
|
713
|
+
return (value) => value === null ? null : typeof value === "number" ? value : parseInt(`${value}`, 10);
|
|
714
|
+
case "bigint":
|
|
715
|
+
return (value) => value === null ? null : typeof value === "bigint" ? value : BigInt(`${value}`);
|
|
716
|
+
case "json":
|
|
717
|
+
return (value) => typeof value === "string" ? JSON.parse(value) : value;
|
|
718
|
+
case "bool":
|
|
719
|
+
return (value) => typeof value === "string" ? value === "true" || value === "1" : typeof value === "number" ? value === 1 : value;
|
|
720
|
+
default:
|
|
721
|
+
return (value) => value;
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
return {
|
|
725
|
+
columns: resultSet.columnNames,
|
|
726
|
+
types: resultSet.columnTypes.map((type) => serializeColumnType(type)),
|
|
727
|
+
rows: resultSet.rows.map((row) => row.map((value, index) => mappers[index](value)))
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function serializeColumnType(columnType) {
|
|
731
|
+
switch (columnType) {
|
|
732
|
+
case ColumnTypeEnum.Int32:
|
|
733
|
+
return "int";
|
|
734
|
+
case ColumnTypeEnum.Int64:
|
|
735
|
+
return "bigint";
|
|
736
|
+
case ColumnTypeEnum.Float:
|
|
737
|
+
return "float";
|
|
738
|
+
case ColumnTypeEnum.Double:
|
|
739
|
+
return "double";
|
|
740
|
+
case ColumnTypeEnum.Text:
|
|
741
|
+
return "string";
|
|
742
|
+
case ColumnTypeEnum.Enum:
|
|
743
|
+
return "enum";
|
|
744
|
+
case ColumnTypeEnum.Bytes:
|
|
745
|
+
return "bytes";
|
|
746
|
+
case ColumnTypeEnum.Boolean:
|
|
747
|
+
return "bool";
|
|
748
|
+
case ColumnTypeEnum.Character:
|
|
749
|
+
return "char";
|
|
750
|
+
case ColumnTypeEnum.Numeric:
|
|
751
|
+
return "decimal";
|
|
752
|
+
case ColumnTypeEnum.Json:
|
|
753
|
+
return "json";
|
|
754
|
+
case ColumnTypeEnum.Uuid:
|
|
755
|
+
return "uuid";
|
|
756
|
+
case ColumnTypeEnum.DateTime:
|
|
757
|
+
return "datetime";
|
|
758
|
+
case ColumnTypeEnum.Date:
|
|
759
|
+
return "date";
|
|
760
|
+
case ColumnTypeEnum.Time:
|
|
761
|
+
return "time";
|
|
762
|
+
case ColumnTypeEnum.Int32Array:
|
|
763
|
+
return "int-array";
|
|
764
|
+
case ColumnTypeEnum.Int64Array:
|
|
765
|
+
return "bigint-array";
|
|
766
|
+
case ColumnTypeEnum.FloatArray:
|
|
767
|
+
return "float-array";
|
|
768
|
+
case ColumnTypeEnum.DoubleArray:
|
|
769
|
+
return "double-array";
|
|
770
|
+
case ColumnTypeEnum.TextArray:
|
|
771
|
+
return "string-array";
|
|
772
|
+
case ColumnTypeEnum.EnumArray:
|
|
773
|
+
return "string-array";
|
|
774
|
+
case ColumnTypeEnum.BytesArray:
|
|
775
|
+
return "bytes-array";
|
|
776
|
+
case ColumnTypeEnum.BooleanArray:
|
|
777
|
+
return "bool-array";
|
|
778
|
+
case ColumnTypeEnum.CharacterArray:
|
|
779
|
+
return "char-array";
|
|
780
|
+
case ColumnTypeEnum.NumericArray:
|
|
781
|
+
return "decimal-array";
|
|
782
|
+
case ColumnTypeEnum.JsonArray:
|
|
783
|
+
return "json-array";
|
|
784
|
+
case ColumnTypeEnum.UuidArray:
|
|
785
|
+
return "uuid-array";
|
|
786
|
+
case ColumnTypeEnum.DateTimeArray:
|
|
787
|
+
return "datetime-array";
|
|
788
|
+
case ColumnTypeEnum.DateArray:
|
|
789
|
+
return "date-array";
|
|
790
|
+
case ColumnTypeEnum.TimeArray:
|
|
791
|
+
return "time-array";
|
|
792
|
+
case ColumnTypeEnum.UnknownNumber:
|
|
793
|
+
return "unknown";
|
|
794
|
+
/// The following PlanetScale type IDs are mapped into Set:
|
|
795
|
+
/// - SET (SET) -> e.g. `"foo,bar"` (String-encoded, comma-separated)
|
|
796
|
+
case ColumnTypeEnum.Set:
|
|
797
|
+
return "string";
|
|
798
|
+
default:
|
|
799
|
+
assertNever(columnType, `Unexpected column type: ${columnType}`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
528
802
|
|
|
529
803
|
// src/interpreter/validation.ts
|
|
530
804
|
function performValidation(data, rules, error) {
|
|
@@ -552,6 +826,8 @@ function doesSatisfyRule(data, rule) {
|
|
|
552
826
|
return rule.args !== 0;
|
|
553
827
|
}
|
|
554
828
|
return rule.args !== 1;
|
|
829
|
+
case "affectedRowCountEq":
|
|
830
|
+
return data === rule.args;
|
|
555
831
|
case "never":
|
|
556
832
|
return false;
|
|
557
833
|
default:
|
|
@@ -569,7 +845,9 @@ function renderMessage(data, error) {
|
|
|
569
845
|
return `An operation failed because it depends on one or more records that were required but not found. No '${error.context.model}' record${hint} was found for ${error.context.operation} on ${error.context.relationType} relation '${error.context.relation}'.`;
|
|
570
846
|
}
|
|
571
847
|
case "INCOMPLETE_CONNECT_INPUT":
|
|
572
|
-
return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length :
|
|
848
|
+
return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length : data}.`;
|
|
849
|
+
case "INCOMPLETE_CONNECT_OUTPUT":
|
|
850
|
+
return `The required connected records were not found. Expected ${error.context.expectedRows} records to be connected after connect operation on ${error.context.relationType} relation '${error.context.relation}', found ${Array.isArray(data) ? data.length : data}.`;
|
|
573
851
|
case "RECORDS_NOT_CONNECTED":
|
|
574
852
|
return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
|
|
575
853
|
default:
|
|
@@ -582,6 +860,8 @@ function getErrorCode2(error) {
|
|
|
582
860
|
return "P2014";
|
|
583
861
|
case "RECORDS_NOT_CONNECTED":
|
|
584
862
|
return "P2017";
|
|
863
|
+
case "INCOMPLETE_CONNECT_OUTPUT":
|
|
864
|
+
return "P2018";
|
|
585
865
|
case "MISSING_RECORD":
|
|
586
866
|
case "MISSING_RELATED_RECORD":
|
|
587
867
|
case "INCOMPLETE_CONNECT_INPUT":
|
|
@@ -599,12 +879,21 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
599
879
|
#generators = new GeneratorRegistry();
|
|
600
880
|
#tracingHelper;
|
|
601
881
|
#serializer;
|
|
602
|
-
|
|
882
|
+
#rawSerializer;
|
|
883
|
+
constructor({
|
|
884
|
+
transactionManager,
|
|
885
|
+
placeholderValues,
|
|
886
|
+
onQuery,
|
|
887
|
+
tracingHelper,
|
|
888
|
+
serializer,
|
|
889
|
+
rawSerializer
|
|
890
|
+
}) {
|
|
603
891
|
this.#transactionManager = transactionManager;
|
|
604
892
|
this.#placeholderValues = placeholderValues;
|
|
605
893
|
this.#onQuery = onQuery;
|
|
606
894
|
this.#tracingHelper = tracingHelper;
|
|
607
895
|
this.#serializer = serializer;
|
|
896
|
+
this.#rawSerializer = rawSerializer ?? serializer;
|
|
608
897
|
}
|
|
609
898
|
static forSql(options) {
|
|
610
899
|
return new _QueryInterpreter({
|
|
@@ -612,27 +901,36 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
612
901
|
placeholderValues: options.placeholderValues,
|
|
613
902
|
onQuery: options.onQuery,
|
|
614
903
|
tracingHelper: options.tracingHelper,
|
|
615
|
-
serializer: serializeSql
|
|
904
|
+
serializer: serializeSql,
|
|
905
|
+
rawSerializer: serializeRawSql
|
|
616
906
|
});
|
|
617
907
|
}
|
|
618
908
|
async run(queryPlan, queryable) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
909
|
+
const { value } = await this.interpretNode(
|
|
910
|
+
queryPlan,
|
|
911
|
+
queryable,
|
|
912
|
+
this.#placeholderValues,
|
|
913
|
+
this.#generators.snapshot(queryable.provider)
|
|
914
|
+
).catch((e) => rethrowAsUserFacing(e));
|
|
915
|
+
return value;
|
|
622
916
|
}
|
|
623
917
|
async interpretNode(node, queryable, scope, generators) {
|
|
624
918
|
switch (node.type) {
|
|
625
919
|
case "seq": {
|
|
626
|
-
|
|
627
|
-
|
|
920
|
+
let result;
|
|
921
|
+
for (const arg of node.args) {
|
|
922
|
+
result = await this.interpretNode(arg, queryable, scope, generators);
|
|
923
|
+
}
|
|
924
|
+
return result ?? { value: void 0 };
|
|
628
925
|
}
|
|
629
926
|
case "get": {
|
|
630
|
-
return scope[node.args.name];
|
|
927
|
+
return { value: scope[node.args.name] };
|
|
631
928
|
}
|
|
632
929
|
case "let": {
|
|
633
930
|
const nestedScope = Object.create(scope);
|
|
634
931
|
for (const binding of node.args.bindings) {
|
|
635
|
-
|
|
932
|
+
const { value } = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
|
|
933
|
+
nestedScope[binding.name] = value;
|
|
636
934
|
}
|
|
637
935
|
return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
|
|
638
936
|
}
|
|
@@ -640,71 +938,87 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
640
938
|
for (const name of node.args.names) {
|
|
641
939
|
const value = scope[name];
|
|
642
940
|
if (!isEmpty(value)) {
|
|
643
|
-
return value;
|
|
941
|
+
return { value };
|
|
644
942
|
}
|
|
645
943
|
}
|
|
646
|
-
return [];
|
|
944
|
+
return { value: [] };
|
|
647
945
|
}
|
|
648
946
|
case "concat": {
|
|
649
|
-
const parts = await Promise.all(
|
|
650
|
-
|
|
947
|
+
const parts = await Promise.all(
|
|
948
|
+
node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators).then((res) => res.value))
|
|
949
|
+
);
|
|
950
|
+
return {
|
|
951
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
952
|
+
};
|
|
651
953
|
}
|
|
652
954
|
case "sum": {
|
|
653
|
-
const parts = await Promise.all(
|
|
654
|
-
|
|
955
|
+
const parts = await Promise.all(
|
|
956
|
+
node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators).then((res) => res.value))
|
|
957
|
+
);
|
|
958
|
+
return {
|
|
959
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber(acc) + asNumber(part)) : 0
|
|
960
|
+
};
|
|
655
961
|
}
|
|
656
962
|
case "execute": {
|
|
657
963
|
const query = renderQuery(node.args, scope, generators);
|
|
658
|
-
return this.#
|
|
659
|
-
return await queryable.executeRaw(query);
|
|
964
|
+
return this.#withQuerySpanAndEvent(query, queryable, async () => {
|
|
965
|
+
return { value: await queryable.executeRaw(query) };
|
|
660
966
|
});
|
|
661
967
|
}
|
|
662
968
|
case "query": {
|
|
663
969
|
const query = renderQuery(node.args, scope, generators);
|
|
664
|
-
return this.#
|
|
665
|
-
|
|
970
|
+
return this.#withQuerySpanAndEvent(query, queryable, async () => {
|
|
971
|
+
const result = await queryable.queryRaw(query);
|
|
972
|
+
if (node.args.type === "rawSql") {
|
|
973
|
+
return { value: this.#rawSerializer(result), lastInsertId: result.lastInsertId };
|
|
974
|
+
} else {
|
|
975
|
+
return { value: this.#serializer(result), lastInsertId: result.lastInsertId };
|
|
976
|
+
}
|
|
666
977
|
});
|
|
667
978
|
}
|
|
668
979
|
case "reverse": {
|
|
669
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
670
|
-
return Array.isArray(value) ? value.reverse() : value;
|
|
980
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
981
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
671
982
|
}
|
|
672
983
|
case "unique": {
|
|
673
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
984
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
674
985
|
if (!Array.isArray(value)) {
|
|
675
|
-
return value;
|
|
986
|
+
return { value, lastInsertId };
|
|
676
987
|
}
|
|
677
988
|
if (value.length > 1) {
|
|
678
989
|
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
679
990
|
}
|
|
680
|
-
return value[0] ?? null;
|
|
991
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
681
992
|
}
|
|
682
993
|
case "required": {
|
|
683
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
994
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
684
995
|
if (isEmpty(value)) {
|
|
685
996
|
throw new Error("Required value is empty");
|
|
686
997
|
}
|
|
687
|
-
return value;
|
|
998
|
+
return { value, lastInsertId };
|
|
688
999
|
}
|
|
689
1000
|
case "mapField": {
|
|
690
|
-
const value = await this.interpretNode(node.args.records, queryable, scope, generators);
|
|
691
|
-
return mapField(value, node.args.field);
|
|
1001
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.records, queryable, scope, generators);
|
|
1002
|
+
return { value: mapField(value, node.args.field), lastInsertId };
|
|
692
1003
|
}
|
|
693
1004
|
case "join": {
|
|
694
|
-
const parent = await this.interpretNode(node.args.parent, queryable, scope, generators);
|
|
1005
|
+
const { value: parent, lastInsertId } = await this.interpretNode(node.args.parent, queryable, scope, generators);
|
|
1006
|
+
if (parent === null) {
|
|
1007
|
+
return { value: null, lastInsertId };
|
|
1008
|
+
}
|
|
695
1009
|
const children = await Promise.all(
|
|
696
1010
|
node.args.children.map(async (joinExpr) => ({
|
|
697
1011
|
joinExpr,
|
|
698
|
-
childRecords: await this.interpretNode(joinExpr.child, queryable, scope, generators)
|
|
1012
|
+
childRecords: (await this.interpretNode(joinExpr.child, queryable, scope, generators)).value
|
|
699
1013
|
}))
|
|
700
1014
|
);
|
|
701
1015
|
if (Array.isArray(parent)) {
|
|
702
1016
|
for (const record of parent) {
|
|
703
1017
|
attachChildrenToParent(asRecord(record), children);
|
|
704
1018
|
}
|
|
705
|
-
return parent;
|
|
1019
|
+
return { value: parent, lastInsertId };
|
|
706
1020
|
}
|
|
707
|
-
return attachChildrenToParent(asRecord(parent), children);
|
|
1021
|
+
return { value: attachChildrenToParent(asRecord(parent), children), lastInsertId };
|
|
708
1022
|
}
|
|
709
1023
|
case "transaction": {
|
|
710
1024
|
if (!this.#transactionManager.enabled) {
|
|
@@ -723,16 +1037,16 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
723
1037
|
}
|
|
724
1038
|
}
|
|
725
1039
|
case "dataMap": {
|
|
726
|
-
const
|
|
727
|
-
return applyDataMap(
|
|
1040
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1041
|
+
return { value: applyDataMap(value, node.args.structure), lastInsertId };
|
|
728
1042
|
}
|
|
729
1043
|
case "validate": {
|
|
730
|
-
const
|
|
731
|
-
performValidation(
|
|
732
|
-
return
|
|
1044
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1045
|
+
performValidation(value, node.args.rules, node.args);
|
|
1046
|
+
return { value, lastInsertId };
|
|
733
1047
|
}
|
|
734
1048
|
case "if": {
|
|
735
|
-
const value = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
1049
|
+
const { value } = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
736
1050
|
if (doesSatisfyRule(value, node.args.rule)) {
|
|
737
1051
|
return await this.interpretNode(node.args.then, queryable, scope, generators);
|
|
738
1052
|
} else {
|
|
@@ -740,42 +1054,73 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
740
1054
|
}
|
|
741
1055
|
}
|
|
742
1056
|
case "unit": {
|
|
743
|
-
return void 0;
|
|
1057
|
+
return { value: void 0 };
|
|
744
1058
|
}
|
|
745
1059
|
case "diff": {
|
|
746
|
-
const from = await this.interpretNode(node.args.from, queryable, scope, generators);
|
|
747
|
-
const to = await this.interpretNode(node.args.to, queryable, scope, generators);
|
|
1060
|
+
const { value: from } = await this.interpretNode(node.args.from, queryable, scope, generators);
|
|
1061
|
+
const { value: to } = await this.interpretNode(node.args.to, queryable, scope, generators);
|
|
748
1062
|
const toSet = new Set(asList(to));
|
|
749
|
-
return asList(from).filter((item) => !toSet.has(item));
|
|
1063
|
+
return { value: asList(from).filter((item) => !toSet.has(item)) };
|
|
1064
|
+
}
|
|
1065
|
+
case "distinctBy": {
|
|
1066
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1067
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1068
|
+
const result = [];
|
|
1069
|
+
for (const item of asList(value)) {
|
|
1070
|
+
const key = getRecordKey(item, node.args.fields);
|
|
1071
|
+
if (!seen.has(key)) {
|
|
1072
|
+
seen.add(key);
|
|
1073
|
+
result.push(item);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return { value: result, lastInsertId };
|
|
1077
|
+
}
|
|
1078
|
+
case "paginate": {
|
|
1079
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1080
|
+
const list = asList(value);
|
|
1081
|
+
const linkingFields = node.args.pagination.linkingFields;
|
|
1082
|
+
if (linkingFields !== null) {
|
|
1083
|
+
const groupedByParent = /* @__PURE__ */ new Map();
|
|
1084
|
+
for (const item of list) {
|
|
1085
|
+
const parentKey = getRecordKey(item, linkingFields);
|
|
1086
|
+
if (!groupedByParent.has(parentKey)) {
|
|
1087
|
+
groupedByParent.set(parentKey, []);
|
|
1088
|
+
}
|
|
1089
|
+
groupedByParent.get(parentKey).push(item);
|
|
1090
|
+
}
|
|
1091
|
+
const groupList = Array.from(groupedByParent.entries());
|
|
1092
|
+
groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
|
|
1093
|
+
return {
|
|
1094
|
+
value: groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination)),
|
|
1095
|
+
lastInsertId
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
return { value: paginate(list, node.args.pagination), lastInsertId };
|
|
1099
|
+
}
|
|
1100
|
+
case "extendRecord": {
|
|
1101
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1102
|
+
const record = value === null ? {} : asRecord(value);
|
|
1103
|
+
for (const [key, entry] of Object.entries(node.args.values)) {
|
|
1104
|
+
if (entry.type === "lastInsertId") {
|
|
1105
|
+
record[key] = lastInsertId;
|
|
1106
|
+
} else {
|
|
1107
|
+
record[key] = evaluateParam(entry.value, scope, generators);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return { value: record, lastInsertId };
|
|
750
1111
|
}
|
|
751
1112
|
default:
|
|
752
1113
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
753
1114
|
}
|
|
754
1115
|
}
|
|
755
|
-
#
|
|
756
|
-
return
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
}
|
|
764
|
-
},
|
|
765
|
-
async () => {
|
|
766
|
-
const timestamp = /* @__PURE__ */ new Date();
|
|
767
|
-
const startInstant = performance.now();
|
|
768
|
-
const result = await execute();
|
|
769
|
-
const endInstant = performance.now();
|
|
770
|
-
this.#onQuery?.({
|
|
771
|
-
timestamp,
|
|
772
|
-
duration: endInstant - startInstant,
|
|
773
|
-
query: query.sql,
|
|
774
|
-
params: query.args
|
|
775
|
-
});
|
|
776
|
-
return result;
|
|
777
|
-
}
|
|
778
|
-
);
|
|
1116
|
+
#withQuerySpanAndEvent(query, queryable, execute) {
|
|
1117
|
+
return withQuerySpanAndEvent({
|
|
1118
|
+
query,
|
|
1119
|
+
queryable,
|
|
1120
|
+
execute,
|
|
1121
|
+
tracingHelper: this.#tracingHelper,
|
|
1122
|
+
onQuery: this.#onQuery
|
|
1123
|
+
});
|
|
779
1124
|
}
|
|
780
1125
|
};
|
|
781
1126
|
function isEmpty(value) {
|
|
@@ -819,7 +1164,12 @@ function attachChildrenToParent(parentRecord, children) {
|
|
|
819
1164
|
}
|
|
820
1165
|
function filterChildRecords(records, parentRecord, joinExpr) {
|
|
821
1166
|
if (Array.isArray(records)) {
|
|
822
|
-
|
|
1167
|
+
const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
|
|
1168
|
+
if (joinExpr.isRelationUnique) {
|
|
1169
|
+
return filtered.length > 0 ? filtered[0] : null;
|
|
1170
|
+
} else {
|
|
1171
|
+
return filtered;
|
|
1172
|
+
}
|
|
823
1173
|
} else if (records === null) {
|
|
824
1174
|
return null;
|
|
825
1175
|
} else {
|
|
@@ -835,6 +1185,18 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
|
|
|
835
1185
|
}
|
|
836
1186
|
return true;
|
|
837
1187
|
}
|
|
1188
|
+
function paginate(list, { cursor, skip, take }) {
|
|
1189
|
+
const cursorIndex = cursor !== null ? list.findIndex((item) => doKeysMatch(item, cursor)) : 0;
|
|
1190
|
+
if (cursorIndex === -1) {
|
|
1191
|
+
return [];
|
|
1192
|
+
}
|
|
1193
|
+
const start = cursorIndex + (skip ?? 0);
|
|
1194
|
+
const end = take !== null ? start + take : list.length;
|
|
1195
|
+
return list.slice(start, end);
|
|
1196
|
+
}
|
|
1197
|
+
function getRecordKey(record, fields) {
|
|
1198
|
+
return JSON.stringify(fields.map((field) => record[field]));
|
|
1199
|
+
}
|
|
838
1200
|
|
|
839
1201
|
// src/transactionManager/TransactionManager.ts
|
|
840
1202
|
import { Debug } from "@prisma/debug";
|
|
@@ -849,12 +1211,11 @@ async function randomUUID() {
|
|
|
849
1211
|
}
|
|
850
1212
|
|
|
851
1213
|
// src/transactionManager/TransactionManagerErrors.ts
|
|
852
|
-
var TransactionManagerError = class extends
|
|
1214
|
+
var TransactionManagerError = class extends UserFacingError {
|
|
1215
|
+
name = "TransactionManagerError";
|
|
853
1216
|
constructor(message, meta) {
|
|
854
|
-
super("Transaction API error: " + message);
|
|
855
|
-
this.meta = meta;
|
|
1217
|
+
super("Transaction API error: " + message, "P2028", meta);
|
|
856
1218
|
}
|
|
857
|
-
code = "P2028";
|
|
858
1219
|
};
|
|
859
1220
|
var TransactionNotFoundError = class extends TransactionManagerError {
|
|
860
1221
|
constructor() {
|
|
@@ -865,12 +1226,12 @@ var TransactionNotFoundError = class extends TransactionManagerError {
|
|
|
865
1226
|
};
|
|
866
1227
|
var TransactionClosedError = class extends TransactionManagerError {
|
|
867
1228
|
constructor(operation) {
|
|
868
|
-
super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction
|
|
1229
|
+
super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction.`);
|
|
869
1230
|
}
|
|
870
1231
|
};
|
|
871
1232
|
var TransactionRolledBackError = class extends TransactionManagerError {
|
|
872
1233
|
constructor(operation) {
|
|
873
|
-
super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back
|
|
1234
|
+
super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back.`);
|
|
874
1235
|
}
|
|
875
1236
|
};
|
|
876
1237
|
var TransactionStartTimeoutError = class extends TransactionManagerError {
|
|
@@ -902,6 +1263,16 @@ var MAX_CLOSED_TRANSACTIONS = 100;
|
|
|
902
1263
|
var debug = Debug("prisma:client:transactionManager");
|
|
903
1264
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
904
1265
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
1266
|
+
var PHANTOM_COMMIT_QUERY = () => ({
|
|
1267
|
+
sql: '-- Implicit "COMMIT" query via underlying driver',
|
|
1268
|
+
args: [],
|
|
1269
|
+
argTypes: []
|
|
1270
|
+
});
|
|
1271
|
+
var PHANTOM_ROLLBACK_QUERY = () => ({
|
|
1272
|
+
sql: '-- Implicit "ROLLBACK" query via underlying driver',
|
|
1273
|
+
args: [],
|
|
1274
|
+
argTypes: []
|
|
1275
|
+
});
|
|
905
1276
|
var TransactionManager = class {
|
|
906
1277
|
// The map of active transactions.
|
|
907
1278
|
transactions = /* @__PURE__ */ new Map();
|
|
@@ -911,14 +1282,17 @@ var TransactionManager = class {
|
|
|
911
1282
|
driverAdapter;
|
|
912
1283
|
transactionOptions;
|
|
913
1284
|
tracingHelper;
|
|
1285
|
+
#onQuery;
|
|
914
1286
|
constructor({
|
|
915
1287
|
driverAdapter,
|
|
916
1288
|
transactionOptions,
|
|
917
|
-
tracingHelper
|
|
1289
|
+
tracingHelper,
|
|
1290
|
+
onQuery
|
|
918
1291
|
}) {
|
|
919
1292
|
this.driverAdapter = driverAdapter;
|
|
920
1293
|
this.transactionOptions = transactionOptions;
|
|
921
1294
|
this.tracingHelper = tracingHelper;
|
|
1295
|
+
this.#onQuery = onQuery;
|
|
922
1296
|
}
|
|
923
1297
|
async startTransaction(options) {
|
|
924
1298
|
return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
|
|
@@ -1022,14 +1396,20 @@ var TransactionManager = class {
|
|
|
1022
1396
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
1023
1397
|
tx.status = status;
|
|
1024
1398
|
if (tx.transaction && status === "committed") {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1399
|
+
if (tx.transaction.options.usePhantomQuery) {
|
|
1400
|
+
await this.#withQuerySpanAndEvent(PHANTOM_COMMIT_QUERY(), tx.transaction, () => tx.transaction.commit());
|
|
1401
|
+
} else {
|
|
1402
|
+
await tx.transaction.commit();
|
|
1403
|
+
const query = COMMIT_QUERY();
|
|
1404
|
+
await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
|
|
1028
1405
|
}
|
|
1029
1406
|
} else if (tx.transaction) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1407
|
+
if (tx.transaction.options.usePhantomQuery) {
|
|
1408
|
+
await this.#withQuerySpanAndEvent(PHANTOM_ROLLBACK_QUERY(), tx.transaction, () => tx.transaction.rollback());
|
|
1409
|
+
} else {
|
|
1410
|
+
await tx.transaction.rollback();
|
|
1411
|
+
const query = ROLLBACK_QUERY();
|
|
1412
|
+
await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
|
|
1033
1413
|
}
|
|
1034
1414
|
}
|
|
1035
1415
|
clearTimeout(tx.timer);
|
|
@@ -1050,14 +1430,28 @@ var TransactionManager = class {
|
|
|
1050
1430
|
maxWait: options.maxWait
|
|
1051
1431
|
};
|
|
1052
1432
|
}
|
|
1433
|
+
#withQuerySpanAndEvent(query, queryable, execute) {
|
|
1434
|
+
return withQuerySpanAndEvent({
|
|
1435
|
+
query,
|
|
1436
|
+
queryable,
|
|
1437
|
+
execute,
|
|
1438
|
+
tracingHelper: this.tracingHelper,
|
|
1439
|
+
onQuery: this.#onQuery
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1053
1442
|
};
|
|
1054
1443
|
export {
|
|
1444
|
+
DataMapperError,
|
|
1055
1445
|
QueryInterpreter,
|
|
1056
1446
|
TransactionManager,
|
|
1057
1447
|
TransactionManagerError,
|
|
1058
1448
|
UserFacingError,
|
|
1449
|
+
doKeysMatch,
|
|
1450
|
+
isDeepStrictEqual,
|
|
1451
|
+
isPrismaValueBigInt,
|
|
1059
1452
|
isPrismaValueBytes,
|
|
1060
1453
|
isPrismaValueGenerator,
|
|
1061
1454
|
isPrismaValuePlaceholder,
|
|
1062
|
-
noopTracingHelper
|
|
1455
|
+
noopTracingHelper,
|
|
1456
|
+
safeJsonStringify
|
|
1063
1457
|
};
|