@prisma/client-engine-runtime 6.9.0-dev.3 → 6.9.0-dev.30

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,234 @@
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 "Object":
49
+ return mapArrayOrObject(data, structure.fields);
50
+ case "Value":
51
+ return mapValue(data, "<result>", structure.resultType);
52
+ default:
53
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
54
+ }
55
+ }
56
+ function mapArrayOrObject(data, fields) {
57
+ if (data === null) return null;
58
+ if (Array.isArray(data)) {
59
+ const rows = data;
60
+ return rows.map((row) => mapObject(row, fields));
61
+ }
62
+ if (typeof data === "object") {
63
+ const row = data;
64
+ return mapObject(row, fields);
65
+ }
66
+ if (typeof data === "string") {
67
+ let decodedData;
68
+ try {
69
+ decodedData = JSON.parse(data);
70
+ } catch (error) {
71
+ throw new DataMapperError(`Expected an array or object, got a string that is not valid JSON`, {
72
+ cause: error
73
+ });
74
+ }
75
+ return mapArrayOrObject(decodedData, fields);
76
+ }
77
+ throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
78
+ }
79
+ function mapObject(data, fields) {
80
+ if (typeof data !== "object") {
81
+ throw new DataMapperError(`Expected an object, but got '${typeof data}'`);
82
+ }
83
+ const result = {};
84
+ for (const [name, node] of Object.entries(fields)) {
85
+ switch (node.type) {
86
+ case "Object": {
87
+ if (!node.flattened && !Object.hasOwn(data, name)) {
88
+ throw new DataMapperError(
89
+ `Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
90
+ );
91
+ }
92
+ const target = node.flattened ? data : data[name];
93
+ result[name] = mapArrayOrObject(target, node.fields);
94
+ break;
95
+ }
96
+ case "Value":
97
+ {
98
+ const dbName = node.dbName;
99
+ if (Object.hasOwn(data, dbName)) {
100
+ result[name] = mapValue(data[dbName], dbName, node.resultType);
101
+ } else {
102
+ throw new DataMapperError(
103
+ `Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
104
+ );
105
+ }
106
+ }
107
+ break;
108
+ default:
109
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
110
+ }
111
+ }
112
+ return result;
113
+ }
114
+ function mapValue(value, columnName, resultType) {
115
+ if (value === null) return null;
116
+ switch (resultType.type) {
117
+ case "Any":
118
+ return value;
119
+ case "String": {
120
+ if (typeof value !== "string") {
121
+ throw new DataMapperError(`Expected a string in column '${columnName}', got ${typeof value}: ${value}`);
122
+ }
123
+ return value;
124
+ }
125
+ case "Int": {
126
+ switch (typeof value) {
127
+ case "number": {
128
+ return Math.trunc(value);
129
+ }
130
+ case "string": {
131
+ const numberValue = Math.trunc(Number(value));
132
+ if (Number.isNaN(numberValue) || !Number.isFinite(numberValue)) {
133
+ throw new DataMapperError(`Expected an integer in column '${columnName}', got string: ${value}`);
134
+ }
135
+ if (!Number.isSafeInteger(numberValue)) {
136
+ throw new DataMapperError(
137
+ `Integer value in column '${columnName}' is too large to represent as a JavaScript number without loss of precision, got: ${value}. Consider using BigInt type.`
138
+ );
139
+ }
140
+ return numberValue;
141
+ }
142
+ default:
143
+ throw new DataMapperError(`Expected an integer in column '${columnName}', got ${typeof value}: ${value}`);
144
+ }
145
+ }
146
+ case "BigInt": {
147
+ if (typeof value !== "number" && typeof value !== "string") {
148
+ throw new DataMapperError(`Expected a bigint in column '${columnName}', got ${typeof value}: ${value}`);
149
+ }
150
+ return { $type: "BigInt", value };
151
+ }
152
+ case "Float": {
153
+ if (typeof value === "number") return value;
154
+ if (typeof value === "string") {
155
+ const parsedValue = Number(value);
156
+ if (Number.isNaN(parsedValue) && !/^[-+]?nan$/.test(value.toLowerCase())) {
157
+ throw new DataMapperError(`Expected a float in column '${columnName}', got string: ${value}`);
158
+ }
159
+ return parsedValue;
160
+ }
161
+ throw new DataMapperError(`Expected a float in column '${columnName}', got ${typeof value}: ${value}`);
162
+ }
163
+ case "Boolean": {
164
+ if (typeof value === "boolean") return value;
165
+ if (typeof value === "number") return value === 1;
166
+ if (typeof value === "string") {
167
+ if (value === "true" || value === "TRUE" || value === "1") {
168
+ return true;
169
+ } else if (value === "false" || value === "FALSE" || value === "0") {
170
+ return false;
171
+ } else {
172
+ throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
173
+ }
174
+ }
175
+ throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
176
+ }
177
+ case "Decimal":
178
+ if (typeof value !== "number" && typeof value !== "string" && !Decimal2.isDecimal(value)) {
179
+ throw new DataMapperError(`Expected a decimal in column '${columnName}', got ${typeof value}: ${value}`);
180
+ }
181
+ return { $type: "Decimal", value };
182
+ case "Date": {
183
+ if (typeof value === "string") {
184
+ return { $type: "DateTime", value: ensureTimezoneInIsoString(value) };
185
+ }
186
+ if (typeof value === "number" || value instanceof Date) {
187
+ return { $type: "DateTime", value };
188
+ }
189
+ throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
190
+ }
191
+ case "Time": {
192
+ if (typeof value === "string") {
193
+ return { $type: "DateTime", value: `1970-01-01T${ensureTimezoneInIsoString(value)}` };
194
+ }
195
+ throw new DataMapperError(`Expected a time in column '${columnName}', got ${typeof value}: ${value}`);
196
+ }
197
+ case "Array": {
198
+ const values = value;
199
+ return values.map((v, i) => mapValue(v, `${columnName}[${i}]`, resultType.inner));
200
+ }
201
+ case "Object": {
202
+ const jsonValue = typeof value === "string" ? value : safeJsonStringify(value);
203
+ return { $type: "Json", value: jsonValue };
204
+ }
205
+ case "Bytes": {
206
+ if (typeof value === "string" && value.startsWith("\\x")) {
207
+ return { $type: "Bytes", value: Buffer.from(value.slice(2), "hex").toString("base64") };
208
+ }
209
+ if (Array.isArray(value)) {
210
+ return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
211
+ }
212
+ throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
213
+ }
214
+ default:
215
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
216
+ }
217
+ }
218
+ var TIMEZONE_PATTERN = /Z$|(?<!\d{4}-\d{2})[+-]\d{2}(:?\d{2})?$/;
219
+ function ensureTimezoneInIsoString(dt) {
220
+ const results = TIMEZONE_PATTERN.exec(dt);
221
+ if (results === null) {
222
+ return `${dt}Z`;
223
+ } else if (results[0] !== "Z" && results[1] === void 0) {
224
+ return `${dt}:00`;
225
+ } else {
226
+ return dt;
227
+ }
228
+ }
8
229
 
9
230
  // src/tracing.ts
231
+ import { SpanKind } from "@opentelemetry/api";
10
232
  var noopTracingHelper = {
11
233
  runInChildSpan(_, callback) {
12
234
  return callback();
@@ -24,6 +246,37 @@ function providerToOtelSystem(provider) {
24
246
  assertNever(provider, `Unknown provider: ${provider}`);
25
247
  }
26
248
  }
249
+ async function withQuerySpanAndEvent({
250
+ query,
251
+ queryable,
252
+ tracingHelper,
253
+ onQuery,
254
+ execute
255
+ }) {
256
+ return await tracingHelper.runInChildSpan(
257
+ {
258
+ name: "db_query",
259
+ kind: SpanKind.CLIENT,
260
+ attributes: {
261
+ "db.query.text": query.sql,
262
+ "db.system.name": providerToOtelSystem(queryable.provider)
263
+ }
264
+ },
265
+ async () => {
266
+ const timestamp = /* @__PURE__ */ new Date();
267
+ const startInstant = performance.now();
268
+ const result = await execute();
269
+ const endInstant = performance.now();
270
+ onQuery?.({
271
+ timestamp,
272
+ duration: endInstant - startInstant,
273
+ query: query.sql,
274
+ params: query.args
275
+ });
276
+ return result;
277
+ }
278
+ );
279
+ }
27
280
 
28
281
  // src/UserFacingError.ts
29
282
  import { isDriverAdapterError } from "@prisma/driver-adapter-utils";
@@ -34,7 +287,7 @@ var UserFacingError = class extends Error {
34
287
  constructor(message, code, meta) {
35
288
  super(message);
36
289
  this.code = code;
37
- this.meta = meta;
290
+ this.meta = meta ?? {};
38
291
  }
39
292
  toQueryResponseErrorObject() {
40
293
  return {
@@ -57,7 +310,7 @@ function rethrowAsUserFacing(error) {
57
310
  if (!code || !message) {
58
311
  throw error;
59
312
  }
60
- throw new UserFacingError(message, code, error);
313
+ throw new UserFacingError(message, code, { driverAdapterError: error });
61
314
  }
62
315
  function getErrorCode(err) {
63
316
  switch (err.cause.kind) {
@@ -168,101 +421,6 @@ function renderConstraint(constraint) {
168
421
  return "(not available)";
169
422
  }
170
423
 
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
424
  // src/interpreter/generators.ts
267
425
  import cuid1 from "@bugsnag/cuid";
268
426
  import { createId as cuid2 } from "@paralleldrive/cuid2";
@@ -367,6 +525,9 @@ function isPrismaValueGenerator(value) {
367
525
  function isPrismaValueBytes(value) {
368
526
  return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
369
527
  }
528
+ function isPrismaValueBigInt(value) {
529
+ return typeof value === "object" && value !== null && value["prisma__type"] === "bigint";
530
+ }
370
531
 
371
532
  // src/interpreter/renderQuery.ts
372
533
  function renderQuery(dbQuery, scope, generators) {
@@ -409,9 +570,10 @@ function evaluateParam(param, scope, generators) {
409
570
  }
410
571
  if (Array.isArray(value)) {
411
572
  value = value.map((el) => evaluateParam(el, scope, generators));
412
- }
413
- if (isPrismaValueBytes(value)) {
573
+ } else if (isPrismaValueBytes(value)) {
414
574
  value = Buffer.from(value.prisma__value, "base64");
575
+ } else if (isPrismaValueBigInt(value)) {
576
+ value = BigInt(value.prisma__value);
415
577
  }
416
578
  return value;
417
579
  }
@@ -505,6 +667,7 @@ function doesRequireEvaluation(param) {
505
667
  }
506
668
 
507
669
  // src/interpreter/serializeSql.ts
670
+ import { ColumnTypeEnum } from "@prisma/driver-adapter-utils";
508
671
  function serializeSql(resultSet) {
509
672
  return resultSet.rows.map(
510
673
  (row) => row.reduce((acc, value, index) => {
@@ -525,6 +688,96 @@ function serializeSql(resultSet) {
525
688
  }, {})
526
689
  );
527
690
  }
691
+ function serializeRawSql(resultSet) {
692
+ const types = resultSet.columnTypes.map((type) => serializeColumnType(type));
693
+ const mappers = types.map((type) => {
694
+ switch (type) {
695
+ case "int":
696
+ return (value) => value === null ? null : typeof value === "number" ? value : parseInt(`${value}`, 10);
697
+ case "bigint":
698
+ return (value) => value === null ? null : typeof value === "bigint" ? value : BigInt(`${value}`);
699
+ default:
700
+ return (value) => value;
701
+ }
702
+ });
703
+ return {
704
+ columns: resultSet.columnNames,
705
+ types: resultSet.columnTypes.map((type) => serializeColumnType(type)),
706
+ rows: resultSet.rows.map((row) => row.map((value, index) => mappers[index](value)))
707
+ };
708
+ }
709
+ function serializeColumnType(columnType) {
710
+ switch (columnType) {
711
+ case ColumnTypeEnum.Int32:
712
+ return "int";
713
+ case ColumnTypeEnum.Int64:
714
+ return "bigint";
715
+ case ColumnTypeEnum.Float:
716
+ return "float";
717
+ case ColumnTypeEnum.Double:
718
+ return "double";
719
+ case ColumnTypeEnum.Text:
720
+ return "string";
721
+ case ColumnTypeEnum.Enum:
722
+ return "enum";
723
+ case ColumnTypeEnum.Bytes:
724
+ return "bytes";
725
+ case ColumnTypeEnum.Boolean:
726
+ return "bool";
727
+ case ColumnTypeEnum.Character:
728
+ return "char";
729
+ case ColumnTypeEnum.Numeric:
730
+ return "decimal";
731
+ case ColumnTypeEnum.Json:
732
+ return "json";
733
+ case ColumnTypeEnum.Uuid:
734
+ return "uuid";
735
+ case ColumnTypeEnum.DateTime:
736
+ return "datetime";
737
+ case ColumnTypeEnum.Date:
738
+ return "date";
739
+ case ColumnTypeEnum.Time:
740
+ return "time";
741
+ case ColumnTypeEnum.Int32Array:
742
+ return "int-array";
743
+ case ColumnTypeEnum.Int64Array:
744
+ return "bigint-array";
745
+ case ColumnTypeEnum.FloatArray:
746
+ return "float-array";
747
+ case ColumnTypeEnum.DoubleArray:
748
+ return "double-array";
749
+ case ColumnTypeEnum.TextArray:
750
+ return "string-array";
751
+ case ColumnTypeEnum.EnumArray:
752
+ return "string-array";
753
+ case ColumnTypeEnum.BytesArray:
754
+ return "bytes-array";
755
+ case ColumnTypeEnum.BooleanArray:
756
+ return "bool-array";
757
+ case ColumnTypeEnum.CharacterArray:
758
+ return "char-array";
759
+ case ColumnTypeEnum.NumericArray:
760
+ return "decimal-array";
761
+ case ColumnTypeEnum.JsonArray:
762
+ return "json-array";
763
+ case ColumnTypeEnum.UuidArray:
764
+ return "uuid-array";
765
+ case ColumnTypeEnum.DateTimeArray:
766
+ return "datetime-array";
767
+ case ColumnTypeEnum.DateArray:
768
+ return "date-array";
769
+ case ColumnTypeEnum.TimeArray:
770
+ return "time-array";
771
+ case ColumnTypeEnum.UnknownNumber:
772
+ return "unknown";
773
+ /// The following PlanetScale type IDs are mapped into Set:
774
+ /// - SET (SET) -> e.g. `"foo,bar"` (String-encoded, comma-separated)
775
+ case ColumnTypeEnum.Set:
776
+ return "string";
777
+ default:
778
+ assertNever(columnType, `Unexpected column type: ${columnType}`);
779
+ }
780
+ }
528
781
 
529
782
  // src/interpreter/validation.ts
530
783
  function performValidation(data, rules, error) {
@@ -552,6 +805,8 @@ function doesSatisfyRule(data, rule) {
552
805
  return rule.args !== 0;
553
806
  }
554
807
  return rule.args !== 1;
808
+ case "affectedRowCountEq":
809
+ return data === rule.args;
555
810
  case "never":
556
811
  return false;
557
812
  default:
@@ -569,7 +824,9 @@ function renderMessage(data, error) {
569
824
  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
825
  }
571
826
  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}.`;
827
+ 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}.`;
828
+ case "INCOMPLETE_CONNECT_OUTPUT":
829
+ 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
830
  case "RECORDS_NOT_CONNECTED":
574
831
  return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
575
832
  default:
@@ -582,6 +839,8 @@ function getErrorCode2(error) {
582
839
  return "P2014";
583
840
  case "RECORDS_NOT_CONNECTED":
584
841
  return "P2017";
842
+ case "INCOMPLETE_CONNECT_OUTPUT":
843
+ return "P2018";
585
844
  case "MISSING_RECORD":
586
845
  case "MISSING_RELATED_RECORD":
587
846
  case "INCOMPLETE_CONNECT_INPUT":
@@ -599,12 +858,21 @@ var QueryInterpreter = class _QueryInterpreter {
599
858
  #generators = new GeneratorRegistry();
600
859
  #tracingHelper;
601
860
  #serializer;
602
- constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
861
+ #rawSerializer;
862
+ constructor({
863
+ transactionManager,
864
+ placeholderValues,
865
+ onQuery,
866
+ tracingHelper,
867
+ serializer,
868
+ rawSerializer
869
+ }) {
603
870
  this.#transactionManager = transactionManager;
604
871
  this.#placeholderValues = placeholderValues;
605
872
  this.#onQuery = onQuery;
606
873
  this.#tracingHelper = tracingHelper;
607
874
  this.#serializer = serializer;
875
+ this.#rawSerializer = rawSerializer ?? serializer;
608
876
  }
609
877
  static forSql(options) {
610
878
  return new _QueryInterpreter({
@@ -612,7 +880,8 @@ var QueryInterpreter = class _QueryInterpreter {
612
880
  placeholderValues: options.placeholderValues,
613
881
  onQuery: options.onQuery,
614
882
  tracingHelper: options.tracingHelper,
615
- serializer: serializeSql
883
+ serializer: serializeSql,
884
+ rawSerializer: serializeRawSql
616
885
  });
617
886
  }
618
887
  async run(queryPlan, queryable) {
@@ -623,8 +892,11 @@ var QueryInterpreter = class _QueryInterpreter {
623
892
  async interpretNode(node, queryable, scope, generators) {
624
893
  switch (node.type) {
625
894
  case "seq": {
626
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
627
- return results[results.length - 1];
895
+ let result;
896
+ for (const arg of node.args) {
897
+ result = await this.interpretNode(arg, queryable, scope, generators);
898
+ }
899
+ return result;
628
900
  }
629
901
  case "get": {
630
902
  return scope[node.args.name];
@@ -647,22 +919,26 @@ var QueryInterpreter = class _QueryInterpreter {
647
919
  }
648
920
  case "concat": {
649
921
  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)), []);
922
+ return parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : [];
651
923
  }
652
924
  case "sum": {
653
925
  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));
926
+ return parts.length > 0 ? parts.reduce((acc, part) => asNumber(acc) + asNumber(part)) : 0;
655
927
  }
656
928
  case "execute": {
657
929
  const query = renderQuery(node.args, scope, generators);
658
- return this.#withQueryEvent(query, queryable, async () => {
930
+ return this.#withQuerySpanAndEvent(query, queryable, async () => {
659
931
  return await queryable.executeRaw(query);
660
932
  });
661
933
  }
662
934
  case "query": {
663
935
  const query = renderQuery(node.args, scope, generators);
664
- return this.#withQueryEvent(query, queryable, async () => {
665
- return this.#serializer(await queryable.queryRaw(query));
936
+ return this.#withQuerySpanAndEvent(query, queryable, async () => {
937
+ if (node.args.type === "rawSql") {
938
+ return this.#rawSerializer(await queryable.queryRaw(query));
939
+ } else {
940
+ return this.#serializer(await queryable.queryRaw(query));
941
+ }
666
942
  });
667
943
  }
668
944
  case "reverse": {
@@ -692,6 +968,9 @@ var QueryInterpreter = class _QueryInterpreter {
692
968
  }
693
969
  case "join": {
694
970
  const parent = await this.interpretNode(node.args.parent, queryable, scope, generators);
971
+ if (parent === null) {
972
+ return null;
973
+ }
695
974
  const children = await Promise.all(
696
975
  node.args.children.map(async (joinExpr) => ({
697
976
  joinExpr,
@@ -748,34 +1027,50 @@ var QueryInterpreter = class _QueryInterpreter {
748
1027
  const toSet = new Set(asList(to));
749
1028
  return asList(from).filter((item) => !toSet.has(item));
750
1029
  }
1030
+ case "distinctBy": {
1031
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
1032
+ const seen = /* @__PURE__ */ new Set();
1033
+ const result = [];
1034
+ for (const item of asList(value)) {
1035
+ const key = getRecordKey(item, node.args.fields);
1036
+ if (!seen.has(key)) {
1037
+ seen.add(key);
1038
+ result.push(item);
1039
+ }
1040
+ }
1041
+ return result;
1042
+ }
1043
+ case "paginate": {
1044
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
1045
+ const list = asList(value);
1046
+ const linkingFields = node.args.pagination.linkingFields;
1047
+ if (linkingFields !== null) {
1048
+ const groupedByParent = /* @__PURE__ */ new Map();
1049
+ for (const item of list) {
1050
+ const parentKey = getRecordKey(item, linkingFields);
1051
+ if (!groupedByParent.has(parentKey)) {
1052
+ groupedByParent.set(parentKey, []);
1053
+ }
1054
+ groupedByParent.get(parentKey).push(item);
1055
+ }
1056
+ const groupList = Array.from(groupedByParent.entries());
1057
+ groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
1058
+ return groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination));
1059
+ }
1060
+ return paginate(list, node.args.pagination);
1061
+ }
751
1062
  default:
752
1063
  assertNever(node, `Unexpected node type: ${node.type}`);
753
1064
  }
754
1065
  }
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
- );
1066
+ #withQuerySpanAndEvent(query, queryable, execute) {
1067
+ return withQuerySpanAndEvent({
1068
+ query,
1069
+ queryable,
1070
+ execute,
1071
+ tracingHelper: this.#tracingHelper,
1072
+ onQuery: this.#onQuery
1073
+ });
779
1074
  }
780
1075
  };
781
1076
  function isEmpty(value) {
@@ -819,7 +1114,12 @@ function attachChildrenToParent(parentRecord, children) {
819
1114
  }
820
1115
  function filterChildRecords(records, parentRecord, joinExpr) {
821
1116
  if (Array.isArray(records)) {
822
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
1117
+ const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
1118
+ if (joinExpr.isRelationUnique) {
1119
+ return filtered.length > 0 ? filtered[0] : null;
1120
+ } else {
1121
+ return filtered;
1122
+ }
823
1123
  } else if (records === null) {
824
1124
  return null;
825
1125
  } else {
@@ -835,6 +1135,18 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
835
1135
  }
836
1136
  return true;
837
1137
  }
1138
+ function paginate(list, { cursor, skip, take }) {
1139
+ const cursorIndex = cursor !== null ? list.findIndex((item) => doKeysMatch(item, cursor)) : 0;
1140
+ if (cursorIndex === -1) {
1141
+ return [];
1142
+ }
1143
+ const start = cursorIndex + (skip ?? 0);
1144
+ const end = take !== null ? start + take : list.length;
1145
+ return list.slice(start, end);
1146
+ }
1147
+ function getRecordKey(record, fields) {
1148
+ return JSON.stringify(fields.map((field) => record[field]));
1149
+ }
838
1150
 
839
1151
  // src/transactionManager/TransactionManager.ts
840
1152
  import { Debug } from "@prisma/debug";
@@ -849,12 +1161,11 @@ async function randomUUID() {
849
1161
  }
850
1162
 
851
1163
  // src/transactionManager/TransactionManagerErrors.ts
852
- var TransactionManagerError = class extends Error {
1164
+ var TransactionManagerError = class extends UserFacingError {
1165
+ name = "TransactionManagerError";
853
1166
  constructor(message, meta) {
854
- super("Transaction API error: " + message);
855
- this.meta = meta;
1167
+ super("Transaction API error: " + message, "P2028", meta);
856
1168
  }
857
- code = "P2028";
858
1169
  };
859
1170
  var TransactionNotFoundError = class extends TransactionManagerError {
860
1171
  constructor() {
@@ -865,12 +1176,12 @@ var TransactionNotFoundError = class extends TransactionManagerError {
865
1176
  };
866
1177
  var TransactionClosedError = class extends TransactionManagerError {
867
1178
  constructor(operation) {
868
- super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction`);
1179
+ super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction.`);
869
1180
  }
870
1181
  };
871
1182
  var TransactionRolledBackError = class extends TransactionManagerError {
872
1183
  constructor(operation) {
873
- super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back`);
1184
+ super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back.`);
874
1185
  }
875
1186
  };
876
1187
  var TransactionStartTimeoutError = class extends TransactionManagerError {
@@ -902,6 +1213,16 @@ var MAX_CLOSED_TRANSACTIONS = 100;
902
1213
  var debug = Debug("prisma:client:transactionManager");
903
1214
  var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
904
1215
  var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
1216
+ var PHANTOM_COMMIT_QUERY = () => ({
1217
+ sql: '-- Implicit "COMMIT" query via underlying driver',
1218
+ args: [],
1219
+ argTypes: []
1220
+ });
1221
+ var PHANTOM_ROLLBACK_QUERY = () => ({
1222
+ sql: '-- Implicit "ROLLBACK" query via underlying driver',
1223
+ args: [],
1224
+ argTypes: []
1225
+ });
905
1226
  var TransactionManager = class {
906
1227
  // The map of active transactions.
907
1228
  transactions = /* @__PURE__ */ new Map();
@@ -911,14 +1232,17 @@ var TransactionManager = class {
911
1232
  driverAdapter;
912
1233
  transactionOptions;
913
1234
  tracingHelper;
1235
+ #onQuery;
914
1236
  constructor({
915
1237
  driverAdapter,
916
1238
  transactionOptions,
917
- tracingHelper
1239
+ tracingHelper,
1240
+ onQuery
918
1241
  }) {
919
1242
  this.driverAdapter = driverAdapter;
920
1243
  this.transactionOptions = transactionOptions;
921
1244
  this.tracingHelper = tracingHelper;
1245
+ this.#onQuery = onQuery;
922
1246
  }
923
1247
  async startTransaction(options) {
924
1248
  return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
@@ -1022,14 +1346,20 @@ var TransactionManager = class {
1022
1346
  debug("Closing transaction.", { transactionId: tx.id, status });
1023
1347
  tx.status = status;
1024
1348
  if (tx.transaction && status === "committed") {
1025
- await tx.transaction.commit();
1026
- if (!tx.transaction.options.usePhantomQuery) {
1027
- await tx.transaction.executeRaw(COMMIT_QUERY());
1349
+ if (tx.transaction.options.usePhantomQuery) {
1350
+ await this.#withQuerySpanAndEvent(PHANTOM_COMMIT_QUERY(), tx.transaction, () => tx.transaction.commit());
1351
+ } else {
1352
+ await tx.transaction.commit();
1353
+ const query = COMMIT_QUERY();
1354
+ await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
1028
1355
  }
1029
1356
  } else if (tx.transaction) {
1030
- await tx.transaction.rollback();
1031
- if (!tx.transaction.options.usePhantomQuery) {
1032
- await tx.transaction.executeRaw(ROLLBACK_QUERY());
1357
+ if (tx.transaction.options.usePhantomQuery) {
1358
+ await this.#withQuerySpanAndEvent(PHANTOM_ROLLBACK_QUERY(), tx.transaction, () => tx.transaction.rollback());
1359
+ } else {
1360
+ await tx.transaction.rollback();
1361
+ const query = ROLLBACK_QUERY();
1362
+ await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
1033
1363
  }
1034
1364
  }
1035
1365
  clearTimeout(tx.timer);
@@ -1050,14 +1380,28 @@ var TransactionManager = class {
1050
1380
  maxWait: options.maxWait
1051
1381
  };
1052
1382
  }
1383
+ #withQuerySpanAndEvent(query, queryable, execute) {
1384
+ return withQuerySpanAndEvent({
1385
+ query,
1386
+ queryable,
1387
+ execute,
1388
+ tracingHelper: this.tracingHelper,
1389
+ onQuery: this.#onQuery
1390
+ });
1391
+ }
1053
1392
  };
1054
1393
  export {
1394
+ DataMapperError,
1055
1395
  QueryInterpreter,
1056
1396
  TransactionManager,
1057
1397
  TransactionManagerError,
1058
1398
  UserFacingError,
1399
+ doKeysMatch,
1400
+ isDeepStrictEqual,
1401
+ isPrismaValueBigInt,
1059
1402
  isPrismaValueBytes,
1060
1403
  isPrismaValueGenerator,
1061
1404
  isPrismaValuePlaceholder,
1062
- noopTracingHelper
1405
+ noopTracingHelper,
1406
+ safeJsonStringify
1063
1407
  };