@prisma/client-engine-runtime 6.7.0-dev.2 → 6.7.0-dev.20

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.js CHANGED
@@ -33,11 +33,183 @@ __export(index_exports, {
33
33
  QueryInterpreter: () => QueryInterpreter,
34
34
  TransactionManager: () => TransactionManager,
35
35
  TransactionManagerError: () => TransactionManagerError,
36
+ UserFacingError: () => UserFacingError,
36
37
  isPrismaValueGenerator: () => isPrismaValueGenerator,
37
- isPrismaValuePlaceholder: () => isPrismaValuePlaceholder
38
+ isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
39
+ noopTracingHelper: () => noopTracingHelper
38
40
  });
39
41
  module.exports = __toCommonJS(index_exports);
40
42
 
43
+ // src/interpreter/QueryInterpreter.ts
44
+ var import_api = require("@opentelemetry/api");
45
+
46
+ // src/utils.ts
47
+ function assertNever(_, message) {
48
+ throw new Error(message);
49
+ }
50
+
51
+ // src/tracing.ts
52
+ var noopTracingHelper = {
53
+ runInChildSpan(_, callback) {
54
+ return callback();
55
+ }
56
+ };
57
+ function providerToOtelSystem(provider) {
58
+ switch (provider) {
59
+ case "postgres":
60
+ return "postgresql";
61
+ case "mysql":
62
+ return "mysql";
63
+ case "sqlite":
64
+ return "sqlite";
65
+ default:
66
+ assertNever(provider, `Unknown provider: ${provider}`);
67
+ }
68
+ }
69
+
70
+ // src/UserFacingError.ts
71
+ var import_driver_adapter_utils = require("@prisma/driver-adapter-utils");
72
+ var UserFacingError = class extends Error {
73
+ name = "UserFacingError";
74
+ code;
75
+ meta;
76
+ constructor(message, code, meta) {
77
+ super(message);
78
+ this.code = code;
79
+ this.meta = meta;
80
+ }
81
+ toQueryResponseErrorObject() {
82
+ return {
83
+ error: this.message,
84
+ user_facing_error: {
85
+ is_panic: false,
86
+ message: this.message,
87
+ meta: this.meta,
88
+ error_code: this.code
89
+ }
90
+ };
91
+ }
92
+ };
93
+ function rethrowAsUserFacing(error) {
94
+ if (!(0, import_driver_adapter_utils.isDriverAdapterError)(error)) {
95
+ throw error;
96
+ }
97
+ const code = getErrorCode(error);
98
+ const message = renderErrorMessage(error);
99
+ if (!code || !message) {
100
+ throw error;
101
+ }
102
+ throw new UserFacingError(message, code, error);
103
+ }
104
+ function getErrorCode(err) {
105
+ switch (err.cause.kind) {
106
+ case "AuthenticationFailed":
107
+ return "P1000";
108
+ case "DatabaseDoesNotExist":
109
+ return "P1003";
110
+ case "SocketTimeout":
111
+ return "P1008";
112
+ case "DatabaseAlreadyExists":
113
+ return "P1009";
114
+ case "DatabaseAccessDenied":
115
+ return "P1010";
116
+ case "LengthMismatch":
117
+ return "P2000";
118
+ case "UniqueConstraintViolation":
119
+ return "P2002";
120
+ case "ForeignKeyConstraintViolation":
121
+ return "P2003";
122
+ case "UnsupportedNativeDataType":
123
+ return "P2010";
124
+ case "NullConstraintViolation":
125
+ return "P2011";
126
+ case "TableDoesNotExist":
127
+ return "P2021";
128
+ case "ColumnNotFound":
129
+ return "P2022";
130
+ case "InvalidIsolationLevel":
131
+ return "P2023";
132
+ case "TransactionWriteConflict":
133
+ return "P2034";
134
+ case "GenericJs":
135
+ return "P2036";
136
+ case "TooManyConnections":
137
+ return "P2037";
138
+ case "postgres":
139
+ case "sqlite":
140
+ case "mysql":
141
+ return;
142
+ default:
143
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
144
+ }
145
+ }
146
+ function renderErrorMessage(err) {
147
+ switch (err.cause.kind) {
148
+ case "AuthenticationFailed": {
149
+ const user = err.cause.user ?? "(not available)";
150
+ return `Authentication failed against the database server, the provided database credentials for \`${user}\` are not valid`;
151
+ }
152
+ case "DatabaseDoesNotExist": {
153
+ const db = err.cause.db ?? "(not available)";
154
+ return `Database \`${db}\` does not exist on the database server`;
155
+ }
156
+ case "SocketTimeout":
157
+ return `Operation has timed out`;
158
+ case "DatabaseAlreadyExists": {
159
+ const db = err.cause.db ?? "(not available)";
160
+ return `Database \`${db}\` already exists on the database server`;
161
+ }
162
+ case "DatabaseAccessDenied": {
163
+ const db = err.cause.db ?? "(not available)";
164
+ return `User was denied access on the database \`${db}\``;
165
+ }
166
+ case "LengthMismatch": {
167
+ const column = err.cause.column ?? "(not available)";
168
+ return `The provided value for the column is too long for the column's type. Column: ${column}`;
169
+ }
170
+ case "UniqueConstraintViolation":
171
+ return `Unique constraint failed on the ${renderConstraint({ fields: err.cause.fields })}`;
172
+ case "ForeignKeyConstraintViolation":
173
+ return `Foreign key constraint violated on the ${renderConstraint(err.cause.constraint)}`;
174
+ case "UnsupportedNativeDataType":
175
+ return `Failed to deserialize column of type '${err.cause.type}'. If you're using $queryRaw and this column is explicitly marked as \`Unsupported\` in your Prisma schema, try casting this column to any supported Prisma type such as \`String\`.`;
176
+ case "NullConstraintViolation":
177
+ return `Null constraint violation on the ${renderConstraint({ fields: err.cause.fields })}`;
178
+ case "TableDoesNotExist": {
179
+ const table = err.cause.table ?? "(not available)";
180
+ return `The table \`${table}\` does not exist in the current database.`;
181
+ }
182
+ case "ColumnNotFound": {
183
+ const column = err.cause.column ?? "(not available)";
184
+ return `The column \`${column}\` does not exist in the current database.`;
185
+ }
186
+ case "InvalidIsolationLevel":
187
+ return `Invalid isolation level \`${err.cause.level}\``;
188
+ case "TransactionWriteConflict":
189
+ return `Transaction failed due to a write conflict or a deadlock. Please retry your transaction`;
190
+ case "GenericJs":
191
+ return `Error in external connector (id ${err.cause.id})`;
192
+ case "TooManyConnections":
193
+ return `Too many database connections opened: ${err.cause.cause}`;
194
+ case "sqlite":
195
+ case "postgres":
196
+ case "mysql":
197
+ return;
198
+ default:
199
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
200
+ }
201
+ }
202
+ function renderConstraint(constraint) {
203
+ if (constraint && "fields" in constraint) {
204
+ return `fields: (${constraint.fields.map((field) => `\`${field}\``).join(", ")})`;
205
+ } else if (constraint && "index" in constraint) {
206
+ return `constraint: \`${constraint.index}\``;
207
+ } else if (constraint && "foreignKey" in constraint) {
208
+ return `foreign key`;
209
+ }
210
+ return "(not available)";
211
+ }
212
+
41
213
  // src/interpreter/generators.ts
42
214
  var import_cuid = __toESM(require("@bugsnag/cuid"));
43
215
  var import_cuid2 = require("@paralleldrive/cuid2");
@@ -52,6 +224,7 @@ var GeneratorRegistry = class {
52
224
  this.register("cuid", new CuidGenerator());
53
225
  this.register("ulid", new UlidGenerator());
54
226
  this.register("nanoid", new NanoIdGenerator());
227
+ this.register("product", new ProductGenerator());
55
228
  }
56
229
  /**
57
230
  * Returns a snapshot of the generator registry. It's 'frozen' in time at the moment of this
@@ -114,6 +287,22 @@ var NanoIdGenerator = class {
114
287
  }
115
288
  }
116
289
  };
290
+ var ProductGenerator = class {
291
+ generate(lhs, rhs) {
292
+ if (lhs === void 0 || rhs === void 0) {
293
+ throw new Error("Invalid Product generator arguments");
294
+ }
295
+ if (Array.isArray(lhs) && Array.isArray(rhs)) {
296
+ return lhs.flatMap((l) => rhs.map((r) => [l, r]));
297
+ } else if (Array.isArray(lhs)) {
298
+ return lhs.map((l) => [l, rhs]);
299
+ } else if (Array.isArray(rhs)) {
300
+ return rhs.map((r) => [lhs, r]);
301
+ } else {
302
+ return [[lhs, rhs]];
303
+ }
304
+ }
305
+ };
117
306
 
118
307
  // src/QueryPlan.ts
119
308
  function isPrismaValuePlaceholder(value) {
@@ -123,46 +312,49 @@ function isPrismaValueGenerator(value) {
123
312
  return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
124
313
  }
125
314
 
126
- // src/utils.ts
127
- function assertNever(_, message) {
128
- throw new Error(message);
129
- }
130
-
131
315
  // src/interpreter/renderQuery.ts
132
316
  function renderQuery(dbQuery, scope, generators) {
133
317
  const queryType = dbQuery.type;
134
318
  switch (queryType) {
135
319
  case "rawSql":
136
- return renderRawSql(dbQuery.sql, substituteParams(dbQuery.params, scope, generators));
320
+ return renderRawSql(dbQuery.sql, evaluateParams(dbQuery.params, scope, generators));
137
321
  case "templateSql":
138
322
  return renderTemplateSql(
139
323
  dbQuery.fragments,
140
324
  dbQuery.placeholderFormat,
141
- substituteParams(dbQuery.params, scope, generators)
325
+ evaluateParams(dbQuery.params, scope, generators)
142
326
  );
143
327
  default:
144
328
  assertNever(queryType, `Invalid query type`);
145
329
  }
146
330
  }
147
- function substituteParams(params, scope, generators) {
148
- return params.map((param) => {
149
- if (isPrismaValueGenerator(param)) {
150
- const { name, args } = param.prisma__value;
331
+ function evaluateParams(params, scope, generators) {
332
+ return params.map((param) => evaluateParam(param, scope, generators));
333
+ }
334
+ function evaluateParam(param, scope, generators) {
335
+ let value = param;
336
+ while (doesRequireEvaluation(value)) {
337
+ if (isPrismaValuePlaceholder(value)) {
338
+ const found = scope[value.prisma__value.name];
339
+ if (found === void 0) {
340
+ throw new Error(`Missing value for query variable ${value.prisma__value.name}`);
341
+ }
342
+ value = found;
343
+ } else if (isPrismaValueGenerator(value)) {
344
+ const { name, args } = value.prisma__value;
151
345
  const generator = generators[name];
152
346
  if (!generator) {
153
347
  throw new Error(`Encountered an unknown generator '${name}'`);
154
348
  }
155
- return generator.generate(...args);
156
- }
157
- if (!isPrismaValuePlaceholder(param)) {
158
- return param;
159
- }
160
- const value = scope[param.prisma__value.name];
161
- if (value === void 0) {
162
- throw new Error(`Missing value for query variable ${param.prisma__value.name}`);
349
+ value = generator.generate(...args.map((arg) => evaluateParam(arg, scope, generators)));
350
+ } else {
351
+ assertNever(value, `Unexpected unevaluated value type: ${value}`);
163
352
  }
164
- return value;
165
- });
353
+ }
354
+ if (Array.isArray(value)) {
355
+ value = value.map((el) => evaluateParam(el, scope, generators));
356
+ }
357
+ return value;
166
358
  }
167
359
  function renderTemplateSql(fragments, placeholderFormat, params) {
168
360
  let paramIndex = 0;
@@ -191,6 +383,29 @@ function renderTemplateSql(fragments, placeholderFormat, params) {
191
383
  }).join(",");
192
384
  return `(${placeholders})`;
193
385
  }
386
+ case "parameterTupleList": {
387
+ if (paramIndex >= params.length) {
388
+ throw new Error(`Malformed query template. Fragments attempt to read over ${params.length} parameters.`);
389
+ }
390
+ const paramValue = params[paramIndex++];
391
+ if (!Array.isArray(paramValue)) {
392
+ throw new Error(`Malformed query template. Tuple list expected.`);
393
+ }
394
+ if (paramValue.length === 0) {
395
+ throw new Error(`Malformed query template. Tuple list cannot be empty.`);
396
+ }
397
+ const tupleList = paramValue.map((tuple) => {
398
+ if (!Array.isArray(tuple)) {
399
+ throw new Error(`Malformed query template. Tuple expected.`);
400
+ }
401
+ const elements = tuple.map((value) => {
402
+ flattenedParams.push(value);
403
+ return formatPlaceholder(placeholderFormat, placeholderNumber++);
404
+ }).join(",");
405
+ return `(${elements})`;
406
+ }).join(",");
407
+ return tupleList;
408
+ }
194
409
  default:
195
410
  assertNever(fragmentType, "Invalid fragment type");
196
411
  }
@@ -249,6 +464,9 @@ function placeholderTypeToArgType(type) {
249
464
  }
250
465
  return mappedType;
251
466
  }
467
+ function doesRequireEvaluation(param) {
468
+ return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
469
+ }
252
470
 
253
471
  // src/interpreter/serialize.ts
254
472
  function serialize(resultSet) {
@@ -278,13 +496,17 @@ var QueryInterpreter = class {
278
496
  #placeholderValues;
279
497
  #onQuery;
280
498
  #generators = new GeneratorRegistry();
281
- constructor({ transactionManager, placeholderValues, onQuery }) {
499
+ #tracingHelper;
500
+ constructor({ transactionManager, placeholderValues, onQuery, tracingHelper }) {
282
501
  this.#transactionManager = transactionManager;
283
502
  this.#placeholderValues = placeholderValues;
284
503
  this.#onQuery = onQuery;
504
+ this.#tracingHelper = tracingHelper;
285
505
  }
286
506
  async run(queryPlan, queryable) {
287
- return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot());
507
+ return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot()).catch(
508
+ (e) => rethrowAsUserFacing(e)
509
+ );
288
510
  }
289
511
  async interpretNode(node, queryable, scope, generators) {
290
512
  switch (node.type) {
@@ -323,13 +545,13 @@ var QueryInterpreter = class {
323
545
  }
324
546
  case "execute": {
325
547
  const query = renderQuery(node.args, scope, generators);
326
- return this.#withQueryEvent(query, async () => {
548
+ return this.#withQueryEvent(query, queryable, async () => {
327
549
  return await queryable.executeRaw(query);
328
550
  });
329
551
  }
330
552
  case "query": {
331
553
  const query = renderQuery(node.args, scope, generators);
332
- return this.#withQueryEvent(query, async () => {
554
+ return this.#withQueryEvent(query, queryable, async () => {
333
555
  return serialize(await queryable.queryRaw(query));
334
556
  });
335
557
  }
@@ -390,24 +612,34 @@ var QueryInterpreter = class {
390
612
  throw e;
391
613
  }
392
614
  }
393
- default: {
394
- node;
395
- throw new Error(`Unexpected node type: ${node.type}`);
396
- }
615
+ default:
616
+ assertNever(node, `Unexpected node type: ${node.type}`);
397
617
  }
398
618
  }
399
- async #withQueryEvent(query, execute) {
400
- const timestamp = /* @__PURE__ */ new Date();
401
- const startInstant = performance.now();
402
- const result = await execute();
403
- const endInstant = performance.now();
404
- this.#onQuery?.({
405
- timestamp,
406
- duration: endInstant - startInstant,
407
- query: query.sql,
408
- params: query.args
409
- });
410
- return result;
619
+ #withQueryEvent(query, queryable, execute) {
620
+ return this.#tracingHelper.runInChildSpan(
621
+ {
622
+ name: "db_query",
623
+ kind: import_api.SpanKind.CLIENT,
624
+ attributes: {
625
+ "db.query.text": query.sql,
626
+ "db.system.name": providerToOtelSystem(queryable.provider)
627
+ }
628
+ },
629
+ async () => {
630
+ const timestamp = /* @__PURE__ */ new Date();
631
+ const startInstant = performance.now();
632
+ const result = await execute();
633
+ const endInstant = performance.now();
634
+ this.#onQuery?.({
635
+ timestamp,
636
+ duration: endInstant - startInstant,
637
+ query: query.sql,
638
+ params: query.args
639
+ });
640
+ return result;
641
+ }
642
+ );
411
643
  }
412
644
  };
413
645
  function isEmpty(value) {
@@ -452,6 +684,8 @@ function attachChildrenToParent(parentRecord, children) {
452
684
  function filterChildRecords(records, parentRecord, joinExpr) {
453
685
  if (Array.isArray(records)) {
454
686
  return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
687
+ } else if (records === null) {
688
+ return null;
455
689
  } else {
456
690
  const record = asRecord(records);
457
691
  return childRecordMatchesParent(record, parentRecord, joinExpr) ? record : null;
@@ -486,11 +720,6 @@ var TransactionManagerError = class extends Error {
486
720
  }
487
721
  code = "P2028";
488
722
  };
489
- var TransactionDriverAdapterError = class extends TransactionManagerError {
490
- constructor(message, errorParams) {
491
- super(`Error from Driver Adapter: ${message}`, { ...errorParams.driverAdapterError });
492
- }
493
- };
494
723
  var TransactionNotFoundError = class extends TransactionManagerError {
495
724
  constructor() {
496
725
  super(
@@ -508,7 +737,7 @@ var TransactionRolledBackError = class extends TransactionManagerError {
508
737
  super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction`);
509
738
  }
510
739
  };
511
- var TransactionStartTimoutError = class extends TransactionManagerError {
740
+ var TransactionStartTimeoutError = class extends TransactionManagerError {
512
741
  constructor() {
513
742
  super("Unable to start a transaction in the given time.");
514
743
  }
@@ -545,11 +774,20 @@ var TransactionManager = class {
545
774
  closedTransactions = [];
546
775
  driverAdapter;
547
776
  transactionOptions;
548
- constructor({ driverAdapter, transactionOptions }) {
777
+ tracingHelper;
778
+ constructor({
779
+ driverAdapter,
780
+ transactionOptions,
781
+ tracingHelper
782
+ }) {
549
783
  this.driverAdapter = driverAdapter;
550
784
  this.transactionOptions = transactionOptions;
785
+ this.tracingHelper = tracingHelper;
551
786
  }
552
787
  async startTransaction(options) {
788
+ return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
789
+ }
790
+ async #startTransactionImpl(options) {
553
791
  const validatedOptions = options !== void 0 ? this.validateOptions(options) : this.transactionOptions;
554
792
  const transaction = {
555
793
  id: await randomUUID(),
@@ -561,14 +799,7 @@ var TransactionManager = class {
561
799
  };
562
800
  this.transactions.set(transaction.id, transaction);
563
801
  transaction.timer = this.startTransactionTimeout(transaction.id, validatedOptions.maxWait);
564
- let startedTransaction;
565
- try {
566
- startedTransaction = await this.driverAdapter.startTransaction(validatedOptions.isolationLevel);
567
- } catch (error) {
568
- throw new TransactionDriverAdapterError("Failed to start transaction.", {
569
- driverAdapterError: error
570
- });
571
- }
802
+ const startedTransaction = await this.driverAdapter.startTransaction(validatedOptions.isolationLevel);
572
803
  switch (transaction.status) {
573
804
  case "waiting":
574
805
  transaction.transaction = startedTransaction;
@@ -578,7 +809,7 @@ var TransactionManager = class {
578
809
  transaction.timer = this.startTransactionTimeout(transaction.id, validatedOptions.timeout);
579
810
  return { id: transaction.id };
580
811
  case "timed_out":
581
- throw new TransactionStartTimoutError();
812
+ throw new TransactionStartTimeoutError();
582
813
  case "running":
583
814
  case "committed":
584
815
  case "rolled_back":
@@ -590,12 +821,16 @@ var TransactionManager = class {
590
821
  }
591
822
  }
592
823
  async commitTransaction(transactionId) {
593
- const txw = this.getActiveTransaction(transactionId, "commit");
594
- await this.closeTransaction(txw, "committed");
824
+ return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
825
+ const txw = this.getActiveTransaction(transactionId, "commit");
826
+ await this.closeTransaction(txw, "committed");
827
+ });
595
828
  }
596
829
  async rollbackTransaction(transactionId) {
597
- const txw = this.getActiveTransaction(transactionId, "rollback");
598
- await this.closeTransaction(txw, "rolled_back");
830
+ return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
831
+ const txw = this.getActiveTransaction(transactionId, "rollback");
832
+ await this.closeTransaction(txw, "rolled_back");
833
+ });
599
834
  }
600
835
  getTransaction(txInfo, operation) {
601
836
  const tx = this.getActiveTransaction(txInfo.id, operation);
@@ -651,24 +886,12 @@ var TransactionManager = class {
651
886
  debug("Closing transaction.", { transactionId: tx.id, status });
652
887
  tx.status = status;
653
888
  if (tx.transaction && status === "committed") {
654
- try {
655
- await tx.transaction.commit();
656
- } catch (error) {
657
- throw new TransactionDriverAdapterError("Failed to commit transaction.", {
658
- driverAdapterError: error
659
- });
660
- }
889
+ await tx.transaction.commit();
661
890
  if (!tx.transaction.options.usePhantomQuery) {
662
891
  await tx.transaction.executeRaw(COMMIT_QUERY());
663
892
  }
664
893
  } else if (tx.transaction) {
665
- try {
666
- await tx.transaction.rollback();
667
- } catch (error) {
668
- throw new TransactionDriverAdapterError("Failed to rollback transaction.", {
669
- driverAdapterError: error
670
- });
671
- }
894
+ await tx.transaction.rollback();
672
895
  if (!tx.transaction.options.usePhantomQuery) {
673
896
  await tx.transaction.executeRaw(ROLLBACK_QUERY());
674
897
  }
@@ -697,6 +920,8 @@ var TransactionManager = class {
697
920
  QueryInterpreter,
698
921
  TransactionManager,
699
922
  TransactionManagerError,
923
+ UserFacingError,
700
924
  isPrismaValueGenerator,
701
- isPrismaValuePlaceholder
925
+ isPrismaValuePlaceholder,
926
+ noopTracingHelper
702
927
  });