@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/index.mjs CHANGED
@@ -1,12 +1,255 @@
1
- // src/interpreter/QueryInterpreter.ts
2
- import { SpanKind } from "@opentelemetry/api";
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: { value: new NowGenerator() }
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 : 0}.`;
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
- constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
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
- return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot()).catch(
620
- (e) => rethrowAsUserFacing(e)
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
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
627
- return results[results.length - 1];
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
- nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
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(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
650
- return parts.reduce((acc, part) => acc.concat(asList(part)), []);
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(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
654
- return parts.reduce((acc, part) => asNumber(acc) + asNumber(part));
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.#withQueryEvent(query, queryable, async () => {
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.#withQueryEvent(query, queryable, async () => {
665
- return this.#serializer(await queryable.queryRaw(query));
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 data = await this.interpretNode(node.args.expr, queryable, scope, generators);
727
- return applyDataMap(data, node.args.structure);
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 data = await this.interpretNode(node.args.expr, queryable, scope, generators);
731
- performValidation(data, node.args.rules, node.args);
732
- return data;
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
- #withQueryEvent(query, queryable, execute) {
756
- return this.#tracingHelper.runInChildSpan(
757
- {
758
- name: "db_query",
759
- kind: SpanKind.CLIENT,
760
- attributes: {
761
- "db.query.text": query.sql,
762
- "db.system.name": providerToOtelSystem(queryable.provider)
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
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
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 Error {
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
- await tx.transaction.commit();
1026
- if (!tx.transaction.options.usePhantomQuery) {
1027
- await tx.transaction.executeRaw(COMMIT_QUERY());
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
- await tx.transaction.rollback();
1031
- if (!tx.transaction.options.usePhantomQuery) {
1032
- await tx.transaction.executeRaw(ROLLBACK_QUERY());
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
  };