@prisma/client-engine-runtime 6.9.0-dev.5 → 6.9.0-dev.51
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 +54 -1
- package/dist/UserFacingError.d.ts +3 -3
- package/dist/index.d.mts +92 -13
- package/dist/index.d.ts +92 -13
- package/dist/index.js +597 -187
- package/dist/index.mjs +591 -186
- package/dist/interpreter/DataMapper.d.ts +4 -1
- 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,255 @@
|
|
|
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, enums) {
|
|
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, enums);
|
|
55
|
+
case "Value":
|
|
56
|
+
return mapValue(data, "<result>", structure.resultType, enums);
|
|
57
|
+
default:
|
|
58
|
+
assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function mapArrayOrObject(data, fields, enums) {
|
|
62
|
+
if (data === null) return null;
|
|
63
|
+
if (Array.isArray(data)) {
|
|
64
|
+
const rows = data;
|
|
65
|
+
return rows.map((row) => mapObject(row, fields, enums));
|
|
66
|
+
}
|
|
67
|
+
if (typeof data === "object") {
|
|
68
|
+
const row = data;
|
|
69
|
+
return mapObject(row, fields, enums);
|
|
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, enums);
|
|
81
|
+
}
|
|
82
|
+
throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
|
|
83
|
+
}
|
|
84
|
+
function mapObject(data, fields, enums) {
|
|
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, enums);
|
|
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, enums);
|
|
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, enums) {
|
|
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, enums));
|
|
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
|
+
case "Enum": {
|
|
225
|
+
const enumDef = enums[resultType.inner];
|
|
226
|
+
if (enumDef === void 0) {
|
|
227
|
+
throw new DataMapperError(`Unknown enum '${resultType.inner}'`);
|
|
228
|
+
}
|
|
229
|
+
const enumValue = enumDef[`${value}`];
|
|
230
|
+
if (enumValue === void 0) {
|
|
231
|
+
throw new DataMapperError(`Unknown enum value '${value}' for enum '${resultType.inner}'`);
|
|
232
|
+
}
|
|
233
|
+
return enumValue;
|
|
234
|
+
}
|
|
235
|
+
default:
|
|
236
|
+
assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
var TIMEZONE_PATTERN = /Z$|(?<!\d{4}-\d{2})[+-]\d{2}(:?\d{2})?$/;
|
|
240
|
+
function ensureTimezoneInIsoString(dt) {
|
|
241
|
+
const results = TIMEZONE_PATTERN.exec(dt);
|
|
242
|
+
if (results === null) {
|
|
243
|
+
return `${dt}Z`;
|
|
244
|
+
} else if (results[0] !== "Z" && results[1] === void 0) {
|
|
245
|
+
return `${dt}:00`;
|
|
246
|
+
} else {
|
|
247
|
+
return dt;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
8
250
|
|
|
9
251
|
// src/tracing.ts
|
|
252
|
+
import { SpanKind } from "@opentelemetry/api";
|
|
10
253
|
var noopTracingHelper = {
|
|
11
254
|
runInChildSpan(_, callback) {
|
|
12
255
|
return callback();
|
|
@@ -24,6 +267,37 @@ function providerToOtelSystem(provider) {
|
|
|
24
267
|
assertNever(provider, `Unknown provider: ${provider}`);
|
|
25
268
|
}
|
|
26
269
|
}
|
|
270
|
+
async function withQuerySpanAndEvent({
|
|
271
|
+
query,
|
|
272
|
+
queryable,
|
|
273
|
+
tracingHelper,
|
|
274
|
+
onQuery,
|
|
275
|
+
execute
|
|
276
|
+
}) {
|
|
277
|
+
return await tracingHelper.runInChildSpan(
|
|
278
|
+
{
|
|
279
|
+
name: "db_query",
|
|
280
|
+
kind: SpanKind.CLIENT,
|
|
281
|
+
attributes: {
|
|
282
|
+
"db.query.text": query.sql,
|
|
283
|
+
"db.system.name": providerToOtelSystem(queryable.provider)
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
async () => {
|
|
287
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
288
|
+
const startInstant = performance.now();
|
|
289
|
+
const result = await execute();
|
|
290
|
+
const endInstant = performance.now();
|
|
291
|
+
onQuery?.({
|
|
292
|
+
timestamp,
|
|
293
|
+
duration: endInstant - startInstant,
|
|
294
|
+
query: query.sql,
|
|
295
|
+
params: query.args
|
|
296
|
+
});
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
}
|
|
27
301
|
|
|
28
302
|
// src/UserFacingError.ts
|
|
29
303
|
import { isDriverAdapterError } from "@prisma/driver-adapter-utils";
|
|
@@ -34,7 +308,7 @@ var UserFacingError = class extends Error {
|
|
|
34
308
|
constructor(message, code, meta) {
|
|
35
309
|
super(message);
|
|
36
310
|
this.code = code;
|
|
37
|
-
this.meta = meta;
|
|
311
|
+
this.meta = meta ?? {};
|
|
38
312
|
}
|
|
39
313
|
toQueryResponseErrorObject() {
|
|
40
314
|
return {
|
|
@@ -57,7 +331,7 @@ function rethrowAsUserFacing(error) {
|
|
|
57
331
|
if (!code || !message) {
|
|
58
332
|
throw error;
|
|
59
333
|
}
|
|
60
|
-
throw new UserFacingError(message, code, error);
|
|
334
|
+
throw new UserFacingError(message, code, { driverAdapterError: error });
|
|
61
335
|
}
|
|
62
336
|
function getErrorCode(err) {
|
|
63
337
|
switch (err.cause.kind) {
|
|
@@ -168,101 +442,6 @@ function renderConstraint(constraint) {
|
|
|
168
442
|
return "(not available)";
|
|
169
443
|
}
|
|
170
444
|
|
|
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
445
|
// src/interpreter/generators.ts
|
|
267
446
|
import cuid1 from "@bugsnag/cuid";
|
|
268
447
|
import { createId as cuid2 } from "@paralleldrive/cuid2";
|
|
@@ -272,7 +451,6 @@ import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
|
|
|
272
451
|
var GeneratorRegistry = class {
|
|
273
452
|
#generators = {};
|
|
274
453
|
constructor() {
|
|
275
|
-
this.register("now", new NowGenerator());
|
|
276
454
|
this.register("uuid", new UuidGenerator());
|
|
277
455
|
this.register("cuid", new CuidGenerator());
|
|
278
456
|
this.register("ulid", new UlidGenerator());
|
|
@@ -284,9 +462,11 @@ var GeneratorRegistry = class {
|
|
|
284
462
|
* method being called, meaning that the built-in time-based generators will always return
|
|
285
463
|
* the same value on repeated calls as long as the same snapshot is used.
|
|
286
464
|
*/
|
|
287
|
-
snapshot() {
|
|
465
|
+
snapshot(provider) {
|
|
288
466
|
return Object.create(this.#generators, {
|
|
289
|
-
now: {
|
|
467
|
+
now: {
|
|
468
|
+
value: provider === "mysql" ? new MysqlNowGenerator() : new NowGenerator()
|
|
469
|
+
}
|
|
290
470
|
});
|
|
291
471
|
}
|
|
292
472
|
/**
|
|
@@ -302,6 +482,12 @@ var NowGenerator = class {
|
|
|
302
482
|
return this.#now.toISOString();
|
|
303
483
|
}
|
|
304
484
|
};
|
|
485
|
+
var MysqlNowGenerator = class {
|
|
486
|
+
#now = /* @__PURE__ */ new Date();
|
|
487
|
+
generate() {
|
|
488
|
+
return this.#now.toISOString().replace("T", " ").replace("Z", "");
|
|
489
|
+
}
|
|
490
|
+
};
|
|
305
491
|
var UuidGenerator = class {
|
|
306
492
|
generate(arg) {
|
|
307
493
|
if (arg === 4) {
|
|
@@ -367,6 +553,9 @@ function isPrismaValueGenerator(value) {
|
|
|
367
553
|
function isPrismaValueBytes(value) {
|
|
368
554
|
return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
|
|
369
555
|
}
|
|
556
|
+
function isPrismaValueBigInt(value) {
|
|
557
|
+
return typeof value === "object" && value !== null && value["prisma__type"] === "bigint";
|
|
558
|
+
}
|
|
370
559
|
|
|
371
560
|
// src/interpreter/renderQuery.ts
|
|
372
561
|
function renderQuery(dbQuery, scope, generators) {
|
|
@@ -409,9 +598,10 @@ function evaluateParam(param, scope, generators) {
|
|
|
409
598
|
}
|
|
410
599
|
if (Array.isArray(value)) {
|
|
411
600
|
value = value.map((el) => evaluateParam(el, scope, generators));
|
|
412
|
-
}
|
|
413
|
-
if (isPrismaValueBytes(value)) {
|
|
601
|
+
} else if (isPrismaValueBytes(value)) {
|
|
414
602
|
value = Buffer.from(value.prisma__value, "base64");
|
|
603
|
+
} else if (isPrismaValueBigInt(value)) {
|
|
604
|
+
value = BigInt(value.prisma__value);
|
|
415
605
|
}
|
|
416
606
|
return value;
|
|
417
607
|
}
|
|
@@ -505,6 +695,7 @@ function doesRequireEvaluation(param) {
|
|
|
505
695
|
}
|
|
506
696
|
|
|
507
697
|
// src/interpreter/serializeSql.ts
|
|
698
|
+
import { ColumnTypeEnum } from "@prisma/driver-adapter-utils";
|
|
508
699
|
function serializeSql(resultSet) {
|
|
509
700
|
return resultSet.rows.map(
|
|
510
701
|
(row) => row.reduce((acc, value, index) => {
|
|
@@ -525,6 +716,100 @@ function serializeSql(resultSet) {
|
|
|
525
716
|
}, {})
|
|
526
717
|
);
|
|
527
718
|
}
|
|
719
|
+
function serializeRawSql(resultSet) {
|
|
720
|
+
const types = resultSet.columnTypes.map((type) => serializeColumnType(type));
|
|
721
|
+
const mappers = types.map((type) => {
|
|
722
|
+
switch (type) {
|
|
723
|
+
case "int":
|
|
724
|
+
return (value) => value === null ? null : typeof value === "number" ? value : parseInt(`${value}`, 10);
|
|
725
|
+
case "bigint":
|
|
726
|
+
return (value) => value === null ? null : typeof value === "bigint" ? value : BigInt(`${value}`);
|
|
727
|
+
case "json":
|
|
728
|
+
return (value) => typeof value === "string" ? JSON.parse(value) : value;
|
|
729
|
+
case "bool":
|
|
730
|
+
return (value) => typeof value === "string" ? value === "true" || value === "1" : typeof value === "number" ? value === 1 : value;
|
|
731
|
+
default:
|
|
732
|
+
return (value) => value;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
return {
|
|
736
|
+
columns: resultSet.columnNames,
|
|
737
|
+
types: resultSet.columnTypes.map((type) => serializeColumnType(type)),
|
|
738
|
+
rows: resultSet.rows.map((row) => row.map((value, index) => mappers[index](value)))
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function serializeColumnType(columnType) {
|
|
742
|
+
switch (columnType) {
|
|
743
|
+
case ColumnTypeEnum.Int32:
|
|
744
|
+
return "int";
|
|
745
|
+
case ColumnTypeEnum.Int64:
|
|
746
|
+
return "bigint";
|
|
747
|
+
case ColumnTypeEnum.Float:
|
|
748
|
+
return "float";
|
|
749
|
+
case ColumnTypeEnum.Double:
|
|
750
|
+
return "double";
|
|
751
|
+
case ColumnTypeEnum.Text:
|
|
752
|
+
return "string";
|
|
753
|
+
case ColumnTypeEnum.Enum:
|
|
754
|
+
return "enum";
|
|
755
|
+
case ColumnTypeEnum.Bytes:
|
|
756
|
+
return "bytes";
|
|
757
|
+
case ColumnTypeEnum.Boolean:
|
|
758
|
+
return "bool";
|
|
759
|
+
case ColumnTypeEnum.Character:
|
|
760
|
+
return "char";
|
|
761
|
+
case ColumnTypeEnum.Numeric:
|
|
762
|
+
return "decimal";
|
|
763
|
+
case ColumnTypeEnum.Json:
|
|
764
|
+
return "json";
|
|
765
|
+
case ColumnTypeEnum.Uuid:
|
|
766
|
+
return "uuid";
|
|
767
|
+
case ColumnTypeEnum.DateTime:
|
|
768
|
+
return "datetime";
|
|
769
|
+
case ColumnTypeEnum.Date:
|
|
770
|
+
return "date";
|
|
771
|
+
case ColumnTypeEnum.Time:
|
|
772
|
+
return "time";
|
|
773
|
+
case ColumnTypeEnum.Int32Array:
|
|
774
|
+
return "int-array";
|
|
775
|
+
case ColumnTypeEnum.Int64Array:
|
|
776
|
+
return "bigint-array";
|
|
777
|
+
case ColumnTypeEnum.FloatArray:
|
|
778
|
+
return "float-array";
|
|
779
|
+
case ColumnTypeEnum.DoubleArray:
|
|
780
|
+
return "double-array";
|
|
781
|
+
case ColumnTypeEnum.TextArray:
|
|
782
|
+
return "string-array";
|
|
783
|
+
case ColumnTypeEnum.EnumArray:
|
|
784
|
+
return "string-array";
|
|
785
|
+
case ColumnTypeEnum.BytesArray:
|
|
786
|
+
return "bytes-array";
|
|
787
|
+
case ColumnTypeEnum.BooleanArray:
|
|
788
|
+
return "bool-array";
|
|
789
|
+
case ColumnTypeEnum.CharacterArray:
|
|
790
|
+
return "char-array";
|
|
791
|
+
case ColumnTypeEnum.NumericArray:
|
|
792
|
+
return "decimal-array";
|
|
793
|
+
case ColumnTypeEnum.JsonArray:
|
|
794
|
+
return "json-array";
|
|
795
|
+
case ColumnTypeEnum.UuidArray:
|
|
796
|
+
return "uuid-array";
|
|
797
|
+
case ColumnTypeEnum.DateTimeArray:
|
|
798
|
+
return "datetime-array";
|
|
799
|
+
case ColumnTypeEnum.DateArray:
|
|
800
|
+
return "date-array";
|
|
801
|
+
case ColumnTypeEnum.TimeArray:
|
|
802
|
+
return "time-array";
|
|
803
|
+
case ColumnTypeEnum.UnknownNumber:
|
|
804
|
+
return "unknown";
|
|
805
|
+
/// The following PlanetScale type IDs are mapped into Set:
|
|
806
|
+
/// - SET (SET) -> e.g. `"foo,bar"` (String-encoded, comma-separated)
|
|
807
|
+
case ColumnTypeEnum.Set:
|
|
808
|
+
return "string";
|
|
809
|
+
default:
|
|
810
|
+
assertNever(columnType, `Unexpected column type: ${columnType}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
528
813
|
|
|
529
814
|
// src/interpreter/validation.ts
|
|
530
815
|
function performValidation(data, rules, error) {
|
|
@@ -552,6 +837,8 @@ function doesSatisfyRule(data, rule) {
|
|
|
552
837
|
return rule.args !== 0;
|
|
553
838
|
}
|
|
554
839
|
return rule.args !== 1;
|
|
840
|
+
case "affectedRowCountEq":
|
|
841
|
+
return data === rule.args;
|
|
555
842
|
case "never":
|
|
556
843
|
return false;
|
|
557
844
|
default:
|
|
@@ -569,7 +856,9 @@ function renderMessage(data, error) {
|
|
|
569
856
|
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
857
|
}
|
|
571
858
|
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 :
|
|
859
|
+
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}.`;
|
|
860
|
+
case "INCOMPLETE_CONNECT_OUTPUT":
|
|
861
|
+
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
862
|
case "RECORDS_NOT_CONNECTED":
|
|
574
863
|
return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
|
|
575
864
|
default:
|
|
@@ -582,6 +871,8 @@ function getErrorCode2(error) {
|
|
|
582
871
|
return "P2014";
|
|
583
872
|
case "RECORDS_NOT_CONNECTED":
|
|
584
873
|
return "P2017";
|
|
874
|
+
case "INCOMPLETE_CONNECT_OUTPUT":
|
|
875
|
+
return "P2018";
|
|
585
876
|
case "MISSING_RECORD":
|
|
586
877
|
case "MISSING_RELATED_RECORD":
|
|
587
878
|
case "INCOMPLETE_CONNECT_INPUT":
|
|
@@ -599,12 +890,21 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
599
890
|
#generators = new GeneratorRegistry();
|
|
600
891
|
#tracingHelper;
|
|
601
892
|
#serializer;
|
|
602
|
-
|
|
893
|
+
#rawSerializer;
|
|
894
|
+
constructor({
|
|
895
|
+
transactionManager,
|
|
896
|
+
placeholderValues,
|
|
897
|
+
onQuery,
|
|
898
|
+
tracingHelper,
|
|
899
|
+
serializer,
|
|
900
|
+
rawSerializer
|
|
901
|
+
}) {
|
|
603
902
|
this.#transactionManager = transactionManager;
|
|
604
903
|
this.#placeholderValues = placeholderValues;
|
|
605
904
|
this.#onQuery = onQuery;
|
|
606
905
|
this.#tracingHelper = tracingHelper;
|
|
607
906
|
this.#serializer = serializer;
|
|
907
|
+
this.#rawSerializer = rawSerializer ?? serializer;
|
|
608
908
|
}
|
|
609
909
|
static forSql(options) {
|
|
610
910
|
return new _QueryInterpreter({
|
|
@@ -612,27 +912,36 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
612
912
|
placeholderValues: options.placeholderValues,
|
|
613
913
|
onQuery: options.onQuery,
|
|
614
914
|
tracingHelper: options.tracingHelper,
|
|
615
|
-
serializer: serializeSql
|
|
915
|
+
serializer: serializeSql,
|
|
916
|
+
rawSerializer: serializeRawSql
|
|
616
917
|
});
|
|
617
918
|
}
|
|
618
919
|
async run(queryPlan, queryable) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
920
|
+
const { value } = await this.interpretNode(
|
|
921
|
+
queryPlan,
|
|
922
|
+
queryable,
|
|
923
|
+
this.#placeholderValues,
|
|
924
|
+
this.#generators.snapshot(queryable.provider)
|
|
925
|
+
).catch((e) => rethrowAsUserFacing(e));
|
|
926
|
+
return value;
|
|
622
927
|
}
|
|
623
928
|
async interpretNode(node, queryable, scope, generators) {
|
|
624
929
|
switch (node.type) {
|
|
625
930
|
case "seq": {
|
|
626
|
-
|
|
627
|
-
|
|
931
|
+
let result;
|
|
932
|
+
for (const arg of node.args) {
|
|
933
|
+
result = await this.interpretNode(arg, queryable, scope, generators);
|
|
934
|
+
}
|
|
935
|
+
return result ?? { value: void 0 };
|
|
628
936
|
}
|
|
629
937
|
case "get": {
|
|
630
|
-
return scope[node.args.name];
|
|
938
|
+
return { value: scope[node.args.name] };
|
|
631
939
|
}
|
|
632
940
|
case "let": {
|
|
633
941
|
const nestedScope = Object.create(scope);
|
|
634
942
|
for (const binding of node.args.bindings) {
|
|
635
|
-
|
|
943
|
+
const { value } = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
|
|
944
|
+
nestedScope[binding.name] = value;
|
|
636
945
|
}
|
|
637
946
|
return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
|
|
638
947
|
}
|
|
@@ -640,71 +949,87 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
640
949
|
for (const name of node.args.names) {
|
|
641
950
|
const value = scope[name];
|
|
642
951
|
if (!isEmpty(value)) {
|
|
643
|
-
return value;
|
|
952
|
+
return { value };
|
|
644
953
|
}
|
|
645
954
|
}
|
|
646
|
-
return [];
|
|
955
|
+
return { value: [] };
|
|
647
956
|
}
|
|
648
957
|
case "concat": {
|
|
649
|
-
const parts = await Promise.all(
|
|
650
|
-
|
|
958
|
+
const parts = await Promise.all(
|
|
959
|
+
node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators).then((res) => res.value))
|
|
960
|
+
);
|
|
961
|
+
return {
|
|
962
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
963
|
+
};
|
|
651
964
|
}
|
|
652
965
|
case "sum": {
|
|
653
|
-
const parts = await Promise.all(
|
|
654
|
-
|
|
966
|
+
const parts = await Promise.all(
|
|
967
|
+
node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators).then((res) => res.value))
|
|
968
|
+
);
|
|
969
|
+
return {
|
|
970
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber(acc) + asNumber(part)) : 0
|
|
971
|
+
};
|
|
655
972
|
}
|
|
656
973
|
case "execute": {
|
|
657
974
|
const query = renderQuery(node.args, scope, generators);
|
|
658
|
-
return this.#
|
|
659
|
-
return await queryable.executeRaw(query);
|
|
975
|
+
return this.#withQuerySpanAndEvent(query, queryable, async () => {
|
|
976
|
+
return { value: await queryable.executeRaw(query) };
|
|
660
977
|
});
|
|
661
978
|
}
|
|
662
979
|
case "query": {
|
|
663
980
|
const query = renderQuery(node.args, scope, generators);
|
|
664
|
-
return this.#
|
|
665
|
-
|
|
981
|
+
return this.#withQuerySpanAndEvent(query, queryable, async () => {
|
|
982
|
+
const result = await queryable.queryRaw(query);
|
|
983
|
+
if (node.args.type === "rawSql") {
|
|
984
|
+
return { value: this.#rawSerializer(result), lastInsertId: result.lastInsertId };
|
|
985
|
+
} else {
|
|
986
|
+
return { value: this.#serializer(result), lastInsertId: result.lastInsertId };
|
|
987
|
+
}
|
|
666
988
|
});
|
|
667
989
|
}
|
|
668
990
|
case "reverse": {
|
|
669
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
670
|
-
return Array.isArray(value) ? value.reverse() : value;
|
|
991
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
992
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
671
993
|
}
|
|
672
994
|
case "unique": {
|
|
673
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
995
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
674
996
|
if (!Array.isArray(value)) {
|
|
675
|
-
return value;
|
|
997
|
+
return { value, lastInsertId };
|
|
676
998
|
}
|
|
677
999
|
if (value.length > 1) {
|
|
678
1000
|
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
679
1001
|
}
|
|
680
|
-
return value[0] ?? null;
|
|
1002
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
681
1003
|
}
|
|
682
1004
|
case "required": {
|
|
683
|
-
const value = await this.interpretNode(node.args, queryable, scope, generators);
|
|
1005
|
+
const { value, lastInsertId } = await this.interpretNode(node.args, queryable, scope, generators);
|
|
684
1006
|
if (isEmpty(value)) {
|
|
685
1007
|
throw new Error("Required value is empty");
|
|
686
1008
|
}
|
|
687
|
-
return value;
|
|
1009
|
+
return { value, lastInsertId };
|
|
688
1010
|
}
|
|
689
1011
|
case "mapField": {
|
|
690
|
-
const value = await this.interpretNode(node.args.records, queryable, scope, generators);
|
|
691
|
-
return mapField(value, node.args.field);
|
|
1012
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.records, queryable, scope, generators);
|
|
1013
|
+
return { value: mapField(value, node.args.field), lastInsertId };
|
|
692
1014
|
}
|
|
693
1015
|
case "join": {
|
|
694
|
-
const parent = await this.interpretNode(node.args.parent, queryable, scope, generators);
|
|
1016
|
+
const { value: parent, lastInsertId } = await this.interpretNode(node.args.parent, queryable, scope, generators);
|
|
1017
|
+
if (parent === null) {
|
|
1018
|
+
return { value: null, lastInsertId };
|
|
1019
|
+
}
|
|
695
1020
|
const children = await Promise.all(
|
|
696
1021
|
node.args.children.map(async (joinExpr) => ({
|
|
697
1022
|
joinExpr,
|
|
698
|
-
childRecords: await this.interpretNode(joinExpr.child, queryable, scope, generators)
|
|
1023
|
+
childRecords: (await this.interpretNode(joinExpr.child, queryable, scope, generators)).value
|
|
699
1024
|
}))
|
|
700
1025
|
);
|
|
701
1026
|
if (Array.isArray(parent)) {
|
|
702
1027
|
for (const record of parent) {
|
|
703
1028
|
attachChildrenToParent(asRecord(record), children);
|
|
704
1029
|
}
|
|
705
|
-
return parent;
|
|
1030
|
+
return { value: parent, lastInsertId };
|
|
706
1031
|
}
|
|
707
|
-
return attachChildrenToParent(asRecord(parent), children);
|
|
1032
|
+
return { value: attachChildrenToParent(asRecord(parent), children), lastInsertId };
|
|
708
1033
|
}
|
|
709
1034
|
case "transaction": {
|
|
710
1035
|
if (!this.#transactionManager.enabled) {
|
|
@@ -723,16 +1048,16 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
723
1048
|
}
|
|
724
1049
|
}
|
|
725
1050
|
case "dataMap": {
|
|
726
|
-
const
|
|
727
|
-
return applyDataMap(
|
|
1051
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1052
|
+
return { value: applyDataMap(value, node.args.structure, node.args.enums), lastInsertId };
|
|
728
1053
|
}
|
|
729
1054
|
case "validate": {
|
|
730
|
-
const
|
|
731
|
-
performValidation(
|
|
732
|
-
return
|
|
1055
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1056
|
+
performValidation(value, node.args.rules, node.args);
|
|
1057
|
+
return { value, lastInsertId };
|
|
733
1058
|
}
|
|
734
1059
|
case "if": {
|
|
735
|
-
const value = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
1060
|
+
const { value } = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
736
1061
|
if (doesSatisfyRule(value, node.args.rule)) {
|
|
737
1062
|
return await this.interpretNode(node.args.then, queryable, scope, generators);
|
|
738
1063
|
} else {
|
|
@@ -740,42 +1065,73 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
740
1065
|
}
|
|
741
1066
|
}
|
|
742
1067
|
case "unit": {
|
|
743
|
-
return void 0;
|
|
1068
|
+
return { value: void 0 };
|
|
744
1069
|
}
|
|
745
1070
|
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);
|
|
1071
|
+
const { value: from } = await this.interpretNode(node.args.from, queryable, scope, generators);
|
|
1072
|
+
const { value: to } = await this.interpretNode(node.args.to, queryable, scope, generators);
|
|
748
1073
|
const toSet = new Set(asList(to));
|
|
749
|
-
return asList(from).filter((item) => !toSet.has(item));
|
|
1074
|
+
return { value: asList(from).filter((item) => !toSet.has(item)) };
|
|
1075
|
+
}
|
|
1076
|
+
case "distinctBy": {
|
|
1077
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1078
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1079
|
+
const result = [];
|
|
1080
|
+
for (const item of asList(value)) {
|
|
1081
|
+
const key = getRecordKey(item, node.args.fields);
|
|
1082
|
+
if (!seen.has(key)) {
|
|
1083
|
+
seen.add(key);
|
|
1084
|
+
result.push(item);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return { value: result, lastInsertId };
|
|
1088
|
+
}
|
|
1089
|
+
case "paginate": {
|
|
1090
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1091
|
+
const list = asList(value);
|
|
1092
|
+
const linkingFields = node.args.pagination.linkingFields;
|
|
1093
|
+
if (linkingFields !== null) {
|
|
1094
|
+
const groupedByParent = /* @__PURE__ */ new Map();
|
|
1095
|
+
for (const item of list) {
|
|
1096
|
+
const parentKey = getRecordKey(item, linkingFields);
|
|
1097
|
+
if (!groupedByParent.has(parentKey)) {
|
|
1098
|
+
groupedByParent.set(parentKey, []);
|
|
1099
|
+
}
|
|
1100
|
+
groupedByParent.get(parentKey).push(item);
|
|
1101
|
+
}
|
|
1102
|
+
const groupList = Array.from(groupedByParent.entries());
|
|
1103
|
+
groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
|
|
1104
|
+
return {
|
|
1105
|
+
value: groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination)),
|
|
1106
|
+
lastInsertId
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
return { value: paginate(list, node.args.pagination), lastInsertId };
|
|
1110
|
+
}
|
|
1111
|
+
case "extendRecord": {
|
|
1112
|
+
const { value, lastInsertId } = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
1113
|
+
const record = value === null ? {} : asRecord(value);
|
|
1114
|
+
for (const [key, entry] of Object.entries(node.args.values)) {
|
|
1115
|
+
if (entry.type === "lastInsertId") {
|
|
1116
|
+
record[key] = lastInsertId;
|
|
1117
|
+
} else {
|
|
1118
|
+
record[key] = evaluateParam(entry.value, scope, generators);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return { value: record, lastInsertId };
|
|
750
1122
|
}
|
|
751
1123
|
default:
|
|
752
1124
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
753
1125
|
}
|
|
754
1126
|
}
|
|
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
|
-
);
|
|
1127
|
+
#withQuerySpanAndEvent(query, queryable, execute) {
|
|
1128
|
+
return withQuerySpanAndEvent({
|
|
1129
|
+
query,
|
|
1130
|
+
queryable,
|
|
1131
|
+
execute,
|
|
1132
|
+
tracingHelper: this.#tracingHelper,
|
|
1133
|
+
onQuery: this.#onQuery
|
|
1134
|
+
});
|
|
779
1135
|
}
|
|
780
1136
|
};
|
|
781
1137
|
function isEmpty(value) {
|
|
@@ -819,7 +1175,12 @@ function attachChildrenToParent(parentRecord, children) {
|
|
|
819
1175
|
}
|
|
820
1176
|
function filterChildRecords(records, parentRecord, joinExpr) {
|
|
821
1177
|
if (Array.isArray(records)) {
|
|
822
|
-
|
|
1178
|
+
const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
|
|
1179
|
+
if (joinExpr.isRelationUnique) {
|
|
1180
|
+
return filtered.length > 0 ? filtered[0] : null;
|
|
1181
|
+
} else {
|
|
1182
|
+
return filtered;
|
|
1183
|
+
}
|
|
823
1184
|
} else if (records === null) {
|
|
824
1185
|
return null;
|
|
825
1186
|
} else {
|
|
@@ -835,6 +1196,18 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
|
|
|
835
1196
|
}
|
|
836
1197
|
return true;
|
|
837
1198
|
}
|
|
1199
|
+
function paginate(list, { cursor, skip, take }) {
|
|
1200
|
+
const cursorIndex = cursor !== null ? list.findIndex((item) => doKeysMatch(item, cursor)) : 0;
|
|
1201
|
+
if (cursorIndex === -1) {
|
|
1202
|
+
return [];
|
|
1203
|
+
}
|
|
1204
|
+
const start = cursorIndex + (skip ?? 0);
|
|
1205
|
+
const end = take !== null ? start + take : list.length;
|
|
1206
|
+
return list.slice(start, end);
|
|
1207
|
+
}
|
|
1208
|
+
function getRecordKey(record, fields) {
|
|
1209
|
+
return JSON.stringify(fields.map((field) => record[field]));
|
|
1210
|
+
}
|
|
838
1211
|
|
|
839
1212
|
// src/transactionManager/TransactionManager.ts
|
|
840
1213
|
import { Debug } from "@prisma/debug";
|
|
@@ -849,12 +1222,11 @@ async function randomUUID() {
|
|
|
849
1222
|
}
|
|
850
1223
|
|
|
851
1224
|
// src/transactionManager/TransactionManagerErrors.ts
|
|
852
|
-
var TransactionManagerError = class extends
|
|
1225
|
+
var TransactionManagerError = class extends UserFacingError {
|
|
1226
|
+
name = "TransactionManagerError";
|
|
853
1227
|
constructor(message, meta) {
|
|
854
|
-
super("Transaction API error: " + message);
|
|
855
|
-
this.meta = meta;
|
|
1228
|
+
super("Transaction API error: " + message, "P2028", meta);
|
|
856
1229
|
}
|
|
857
|
-
code = "P2028";
|
|
858
1230
|
};
|
|
859
1231
|
var TransactionNotFoundError = class extends TransactionManagerError {
|
|
860
1232
|
constructor() {
|
|
@@ -865,12 +1237,12 @@ var TransactionNotFoundError = class extends TransactionManagerError {
|
|
|
865
1237
|
};
|
|
866
1238
|
var TransactionClosedError = class extends TransactionManagerError {
|
|
867
1239
|
constructor(operation) {
|
|
868
|
-
super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction
|
|
1240
|
+
super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction.`);
|
|
869
1241
|
}
|
|
870
1242
|
};
|
|
871
1243
|
var TransactionRolledBackError = class extends TransactionManagerError {
|
|
872
1244
|
constructor(operation) {
|
|
873
|
-
super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back
|
|
1245
|
+
super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back.`);
|
|
874
1246
|
}
|
|
875
1247
|
};
|
|
876
1248
|
var TransactionStartTimeoutError = class extends TransactionManagerError {
|
|
@@ -902,6 +1274,16 @@ var MAX_CLOSED_TRANSACTIONS = 100;
|
|
|
902
1274
|
var debug = Debug("prisma:client:transactionManager");
|
|
903
1275
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
904
1276
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
1277
|
+
var PHANTOM_COMMIT_QUERY = () => ({
|
|
1278
|
+
sql: '-- Implicit "COMMIT" query via underlying driver',
|
|
1279
|
+
args: [],
|
|
1280
|
+
argTypes: []
|
|
1281
|
+
});
|
|
1282
|
+
var PHANTOM_ROLLBACK_QUERY = () => ({
|
|
1283
|
+
sql: '-- Implicit "ROLLBACK" query via underlying driver',
|
|
1284
|
+
args: [],
|
|
1285
|
+
argTypes: []
|
|
1286
|
+
});
|
|
905
1287
|
var TransactionManager = class {
|
|
906
1288
|
// The map of active transactions.
|
|
907
1289
|
transactions = /* @__PURE__ */ new Map();
|
|
@@ -911,14 +1293,17 @@ var TransactionManager = class {
|
|
|
911
1293
|
driverAdapter;
|
|
912
1294
|
transactionOptions;
|
|
913
1295
|
tracingHelper;
|
|
1296
|
+
#onQuery;
|
|
914
1297
|
constructor({
|
|
915
1298
|
driverAdapter,
|
|
916
1299
|
transactionOptions,
|
|
917
|
-
tracingHelper
|
|
1300
|
+
tracingHelper,
|
|
1301
|
+
onQuery
|
|
918
1302
|
}) {
|
|
919
1303
|
this.driverAdapter = driverAdapter;
|
|
920
1304
|
this.transactionOptions = transactionOptions;
|
|
921
1305
|
this.tracingHelper = tracingHelper;
|
|
1306
|
+
this.#onQuery = onQuery;
|
|
922
1307
|
}
|
|
923
1308
|
async startTransaction(options) {
|
|
924
1309
|
return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
|
|
@@ -1022,14 +1407,20 @@ var TransactionManager = class {
|
|
|
1022
1407
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
1023
1408
|
tx.status = status;
|
|
1024
1409
|
if (tx.transaction && status === "committed") {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1410
|
+
if (tx.transaction.options.usePhantomQuery) {
|
|
1411
|
+
await this.#withQuerySpanAndEvent(PHANTOM_COMMIT_QUERY(), tx.transaction, () => tx.transaction.commit());
|
|
1412
|
+
} else {
|
|
1413
|
+
await tx.transaction.commit();
|
|
1414
|
+
const query = COMMIT_QUERY();
|
|
1415
|
+
await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
|
|
1028
1416
|
}
|
|
1029
1417
|
} else if (tx.transaction) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1418
|
+
if (tx.transaction.options.usePhantomQuery) {
|
|
1419
|
+
await this.#withQuerySpanAndEvent(PHANTOM_ROLLBACK_QUERY(), tx.transaction, () => tx.transaction.rollback());
|
|
1420
|
+
} else {
|
|
1421
|
+
await tx.transaction.rollback();
|
|
1422
|
+
const query = ROLLBACK_QUERY();
|
|
1423
|
+
await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
|
|
1033
1424
|
}
|
|
1034
1425
|
}
|
|
1035
1426
|
clearTimeout(tx.timer);
|
|
@@ -1050,14 +1441,28 @@ var TransactionManager = class {
|
|
|
1050
1441
|
maxWait: options.maxWait
|
|
1051
1442
|
};
|
|
1052
1443
|
}
|
|
1444
|
+
#withQuerySpanAndEvent(query, queryable, execute) {
|
|
1445
|
+
return withQuerySpanAndEvent({
|
|
1446
|
+
query,
|
|
1447
|
+
queryable,
|
|
1448
|
+
execute,
|
|
1449
|
+
tracingHelper: this.tracingHelper,
|
|
1450
|
+
onQuery: this.#onQuery
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1053
1453
|
};
|
|
1054
1454
|
export {
|
|
1455
|
+
DataMapperError,
|
|
1055
1456
|
QueryInterpreter,
|
|
1056
1457
|
TransactionManager,
|
|
1057
1458
|
TransactionManagerError,
|
|
1058
1459
|
UserFacingError,
|
|
1460
|
+
doKeysMatch,
|
|
1461
|
+
isDeepStrictEqual,
|
|
1462
|
+
isPrismaValueBigInt,
|
|
1059
1463
|
isPrismaValueBytes,
|
|
1060
1464
|
isPrismaValueGenerator,
|
|
1061
1465
|
isPrismaValuePlaceholder,
|
|
1062
|
-
noopTracingHelper
|
|
1466
|
+
noopTracingHelper,
|
|
1467
|
+
safeJsonStringify
|
|
1063
1468
|
};
|