@prisma/client-engine-runtime 6.9.0-dev.4 → 6.9.0-dev.41

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,244 @@
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) {
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: { value: new NowGenerator() }
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 : 0}.`;
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
- constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
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
- return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot()).catch(
620
- (e) => rethrowAsUserFacing(e)
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
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
627
- return results[results.length - 1];
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
- nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
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(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
650
- return parts.reduce((acc, part) => acc.concat(asList(part)), []);
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(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
654
- return parts.reduce((acc, part) => asNumber(acc) + asNumber(part));
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.#withQueryEvent(query, queryable, async () => {
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.#withQueryEvent(query, queryable, async () => {
665
- return this.#serializer(await queryable.queryRaw(query));
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 data = await this.interpretNode(node.args.expr, queryable, scope, generators);
727
- return applyDataMap(data, node.args.structure);
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 data = await this.interpretNode(node.args.expr, queryable, scope, generators);
731
- performValidation(data, node.args.rules, node.args);
732
- return data;
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
- #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
- );
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
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
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 Error {
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
- await tx.transaction.commit();
1026
- if (!tx.transaction.options.usePhantomQuery) {
1027
- await tx.transaction.executeRaw(COMMIT_QUERY());
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
- await tx.transaction.rollback();
1031
- if (!tx.transaction.options.usePhantomQuery) {
1032
- await tx.transaction.executeRaw(ROLLBACK_QUERY());
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
  };