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

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