@prisma/client-engine-runtime 6.7.0-dev.4 → 6.7.0-dev.40

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,3 +1,30 @@
1
+ // src/interpreter/QueryInterpreter.ts
2
+ import { SpanKind } from "@opentelemetry/api";
3
+
4
+ // src/utils.ts
5
+ function assertNever(_, message) {
6
+ throw new Error(message);
7
+ }
8
+
9
+ // src/tracing.ts
10
+ var noopTracingHelper = {
11
+ runInChildSpan(_, callback) {
12
+ return callback();
13
+ }
14
+ };
15
+ function providerToOtelSystem(provider) {
16
+ switch (provider) {
17
+ case "postgres":
18
+ return "postgresql";
19
+ case "mysql":
20
+ return "mysql";
21
+ case "sqlite":
22
+ return "sqlite";
23
+ default:
24
+ assertNever(provider, `Unknown provider: ${provider}`);
25
+ }
26
+ }
27
+
1
28
  // src/UserFacingError.ts
2
29
  import { isDriverAdapterError } from "@prisma/driver-adapter-utils";
3
30
  var UserFacingError = class extends Error {
@@ -70,10 +97,8 @@ function getErrorCode(err) {
70
97
  case "sqlite":
71
98
  case "mysql":
72
99
  return;
73
- default: {
74
- const cause = err.cause;
75
- throw new Error(`Unknown error: ${cause}`);
76
- }
100
+ default:
101
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
77
102
  }
78
103
  }
79
104
  function renderErrorMessage(err) {
@@ -128,10 +153,8 @@ function renderErrorMessage(err) {
128
153
  case "postgres":
129
154
  case "mysql":
130
155
  return;
131
- default: {
132
- const cause = err.cause;
133
- throw new Error(`Unknown error: ${cause}`);
134
- }
156
+ default:
157
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
135
158
  }
136
159
  }
137
160
  function renderConstraint(constraint) {
@@ -145,6 +168,100 @@ function renderConstraint(constraint) {
145
168
  return "(not available)";
146
169
  }
147
170
 
171
+ // src/interpreter/DataMapper.ts
172
+ function applyDataMap(data, structure) {
173
+ switch (structure.type) {
174
+ case "Object":
175
+ return mapArrayOrObject(data, structure.fields);
176
+ case "Value":
177
+ return mapValue(data, structure.resultType);
178
+ default:
179
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
180
+ }
181
+ }
182
+ function mapArrayOrObject(data, fields) {
183
+ if (data === null) return null;
184
+ if (Array.isArray(data)) {
185
+ const rows = data;
186
+ return rows.map((row) => mapObject(row, fields));
187
+ }
188
+ if (typeof data === "object") {
189
+ const row = data;
190
+ return mapObject(row, fields);
191
+ }
192
+ throw new Error(`DataMapper: Expected an array or an object, got: ${typeof data}`);
193
+ }
194
+ function mapObject(data, fields) {
195
+ if (typeof data !== "object") {
196
+ throw new Error(`DataMapper: Expected an object, but got '${typeof data}'`);
197
+ }
198
+ const result = {};
199
+ for (const [name, node] of Object.entries(fields)) {
200
+ switch (node.type) {
201
+ case "Object":
202
+ if (Object.hasOwn(data, name)) {
203
+ result[name] = mapArrayOrObject(data[name], node.fields);
204
+ } else {
205
+ throw new Error(
206
+ `DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
207
+ );
208
+ }
209
+ break;
210
+ case "Value":
211
+ {
212
+ const dbName = node.dbName;
213
+ if (Object.hasOwn(data, dbName)) {
214
+ result[name] = mapValue(data[dbName], node.resultType);
215
+ } else {
216
+ throw new Error(
217
+ `DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
218
+ );
219
+ }
220
+ }
221
+ break;
222
+ default:
223
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
224
+ }
225
+ }
226
+ return result;
227
+ }
228
+ function mapValue(value, resultType) {
229
+ if (value === null) return null;
230
+ switch (resultType.type) {
231
+ case "Any":
232
+ return value;
233
+ case "String":
234
+ return typeof value === "string" ? value : `${value}`;
235
+ case "Int":
236
+ return typeof value === "number" ? value : parseInt(`${value}`, 10);
237
+ case "BigInt":
238
+ return typeof value === "bigint" ? value : BigInt(`${value}`);
239
+ case "Float":
240
+ return typeof value === "number" ? value : parseFloat(`${value}`);
241
+ case "Boolean":
242
+ return typeof value === "boolean" ? value : value !== "0";
243
+ case "Decimal":
244
+ return typeof value === "number" ? value : parseFloat(`${value}`);
245
+ case "Date":
246
+ return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
247
+ case "Array": {
248
+ const values = value;
249
+ return values.map((v) => {
250
+ mapValue(v, resultType.inner);
251
+ });
252
+ }
253
+ case "Object":
254
+ return typeof value === "object" ? value : { value };
255
+ case "Bytes":
256
+ if (typeof value !== "string") {
257
+ throw new Error(`DataMapper: Bytes data is not a string, got: ${typeof value}`);
258
+ }
259
+ return value;
260
+ default:
261
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
262
+ }
263
+ }
264
+
148
265
  // src/interpreter/generators.ts
149
266
  import cuid1 from "@bugsnag/cuid";
150
267
  import { createId as cuid2 } from "@paralleldrive/cuid2";
@@ -159,6 +276,7 @@ var GeneratorRegistry = class {
159
276
  this.register("cuid", new CuidGenerator());
160
277
  this.register("ulid", new UlidGenerator());
161
278
  this.register("nanoid", new NanoIdGenerator());
279
+ this.register("product", new ProductGenerator());
162
280
  }
163
281
  /**
164
282
  * Returns a snapshot of the generator registry. It's 'frozen' in time at the moment of this
@@ -221,6 +339,22 @@ var NanoIdGenerator = class {
221
339
  }
222
340
  }
223
341
  };
342
+ var ProductGenerator = class {
343
+ generate(lhs, rhs) {
344
+ if (lhs === void 0 || rhs === void 0) {
345
+ throw new Error("Invalid Product generator arguments");
346
+ }
347
+ if (Array.isArray(lhs) && Array.isArray(rhs)) {
348
+ return lhs.flatMap((l) => rhs.map((r) => [l, r]));
349
+ } else if (Array.isArray(lhs)) {
350
+ return lhs.map((l) => [l, rhs]);
351
+ } else if (Array.isArray(rhs)) {
352
+ return rhs.map((r) => [lhs, r]);
353
+ } else {
354
+ return [[lhs, rhs]];
355
+ }
356
+ }
357
+ };
224
358
 
225
359
  // src/QueryPlan.ts
226
360
  function isPrismaValuePlaceholder(value) {
@@ -230,46 +364,49 @@ function isPrismaValueGenerator(value) {
230
364
  return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
231
365
  }
232
366
 
233
- // src/utils.ts
234
- function assertNever(_, message) {
235
- throw new Error(message);
236
- }
237
-
238
367
  // src/interpreter/renderQuery.ts
239
368
  function renderQuery(dbQuery, scope, generators) {
240
369
  const queryType = dbQuery.type;
241
370
  switch (queryType) {
242
371
  case "rawSql":
243
- return renderRawSql(dbQuery.sql, substituteParams(dbQuery.params, scope, generators));
372
+ return renderRawSql(dbQuery.sql, evaluateParams(dbQuery.params, scope, generators));
244
373
  case "templateSql":
245
374
  return renderTemplateSql(
246
375
  dbQuery.fragments,
247
376
  dbQuery.placeholderFormat,
248
- substituteParams(dbQuery.params, scope, generators)
377
+ evaluateParams(dbQuery.params, scope, generators)
249
378
  );
250
379
  default:
251
380
  assertNever(queryType, `Invalid query type`);
252
381
  }
253
382
  }
254
- function substituteParams(params, scope, generators) {
255
- return params.map((param) => {
256
- if (isPrismaValueGenerator(param)) {
257
- const { name, args } = param.prisma__value;
383
+ function evaluateParams(params, scope, generators) {
384
+ return params.map((param) => evaluateParam(param, scope, generators));
385
+ }
386
+ function evaluateParam(param, scope, generators) {
387
+ let value = param;
388
+ while (doesRequireEvaluation(value)) {
389
+ if (isPrismaValuePlaceholder(value)) {
390
+ const found = scope[value.prisma__value.name];
391
+ if (found === void 0) {
392
+ throw new Error(`Missing value for query variable ${value.prisma__value.name}`);
393
+ }
394
+ value = found;
395
+ } else if (isPrismaValueGenerator(value)) {
396
+ const { name, args } = value.prisma__value;
258
397
  const generator = generators[name];
259
398
  if (!generator) {
260
399
  throw new Error(`Encountered an unknown generator '${name}'`);
261
400
  }
262
- return generator.generate(...args);
263
- }
264
- if (!isPrismaValuePlaceholder(param)) {
265
- return param;
266
- }
267
- const value = scope[param.prisma__value.name];
268
- if (value === void 0) {
269
- throw new Error(`Missing value for query variable ${param.prisma__value.name}`);
401
+ value = generator.generate(...args.map((arg) => evaluateParam(arg, scope, generators)));
402
+ } else {
403
+ assertNever(value, `Unexpected unevaluated value type: ${value}`);
270
404
  }
271
- return value;
272
- });
405
+ }
406
+ if (Array.isArray(value)) {
407
+ value = value.map((el) => evaluateParam(el, scope, generators));
408
+ }
409
+ return value;
273
410
  }
274
411
  function renderTemplateSql(fragments, placeholderFormat, params) {
275
412
  let paramIndex = 0;
@@ -298,6 +435,29 @@ function renderTemplateSql(fragments, placeholderFormat, params) {
298
435
  }).join(",");
299
436
  return `(${placeholders})`;
300
437
  }
438
+ case "parameterTupleList": {
439
+ if (paramIndex >= params.length) {
440
+ throw new Error(`Malformed query template. Fragments attempt to read over ${params.length} parameters.`);
441
+ }
442
+ const paramValue = params[paramIndex++];
443
+ if (!Array.isArray(paramValue)) {
444
+ throw new Error(`Malformed query template. Tuple list expected.`);
445
+ }
446
+ if (paramValue.length === 0) {
447
+ throw new Error(`Malformed query template. Tuple list cannot be empty.`);
448
+ }
449
+ const tupleList = paramValue.map((tuple) => {
450
+ if (!Array.isArray(tuple)) {
451
+ throw new Error(`Malformed query template. Tuple expected.`);
452
+ }
453
+ const elements = tuple.map((value) => {
454
+ flattenedParams.push(value);
455
+ return formatPlaceholder(placeholderFormat, placeholderNumber++);
456
+ }).join(",");
457
+ return `(${elements})`;
458
+ }).join(",");
459
+ return tupleList;
460
+ }
301
461
  default:
302
462
  assertNever(fragmentType, "Invalid fragment type");
303
463
  }
@@ -356,9 +516,12 @@ function placeholderTypeToArgType(type) {
356
516
  }
357
517
  return mappedType;
358
518
  }
519
+ function doesRequireEvaluation(param) {
520
+ return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
521
+ }
359
522
 
360
- // src/interpreter/serialize.ts
361
- function serialize(resultSet) {
523
+ // src/interpreter/serializeSql.ts
524
+ function serializeSql(resultSet) {
362
525
  return resultSet.rows.map(
363
526
  (row) => row.reduce((acc, value, index) => {
364
527
  const splitByDot = resultSet.columnNames[index].split(".");
@@ -380,15 +543,28 @@ function serialize(resultSet) {
380
543
  }
381
544
 
382
545
  // src/interpreter/QueryInterpreter.ts
383
- var QueryInterpreter = class {
546
+ var QueryInterpreter = class _QueryInterpreter {
384
547
  #transactionManager;
385
548
  #placeholderValues;
386
549
  #onQuery;
387
550
  #generators = new GeneratorRegistry();
388
- constructor({ transactionManager, placeholderValues, onQuery }) {
551
+ #tracingHelper;
552
+ #serializer;
553
+ constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
389
554
  this.#transactionManager = transactionManager;
390
555
  this.#placeholderValues = placeholderValues;
391
556
  this.#onQuery = onQuery;
557
+ this.#tracingHelper = tracingHelper;
558
+ this.#serializer = serializer;
559
+ }
560
+ static forSql(options) {
561
+ return new _QueryInterpreter({
562
+ transactionManager: options.transactionManager,
563
+ placeholderValues: options.placeholderValues,
564
+ onQuery: options.onQuery,
565
+ tracingHelper: options.tracingHelper,
566
+ serializer: serializeSql
567
+ });
392
568
  }
393
569
  async run(queryPlan, queryable) {
394
570
  return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot()).catch(
@@ -432,14 +608,14 @@ var QueryInterpreter = class {
432
608
  }
433
609
  case "execute": {
434
610
  const query = renderQuery(node.args, scope, generators);
435
- return this.#withQueryEvent(query, async () => {
611
+ return this.#withQueryEvent(query, queryable, async () => {
436
612
  return await queryable.executeRaw(query);
437
613
  });
438
614
  }
439
615
  case "query": {
440
616
  const query = renderQuery(node.args, scope, generators);
441
- return this.#withQueryEvent(query, async () => {
442
- return serialize(await queryable.queryRaw(query));
617
+ return this.#withQueryEvent(query, queryable, async () => {
618
+ return this.#serializer(await queryable.queryRaw(query));
443
619
  });
444
620
  }
445
621
  case "reverse": {
@@ -499,24 +675,38 @@ var QueryInterpreter = class {
499
675
  throw e;
500
676
  }
501
677
  }
502
- default: {
503
- node;
504
- throw new Error(`Unexpected node type: ${node.type}`);
678
+ case "dataMap": {
679
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
680
+ return applyDataMap(data, node.args.structure);
505
681
  }
682
+ default:
683
+ assertNever(node, `Unexpected node type: ${node.type}`);
506
684
  }
507
685
  }
508
- async #withQueryEvent(query, execute) {
509
- const timestamp = /* @__PURE__ */ new Date();
510
- const startInstant = performance.now();
511
- const result = await execute();
512
- const endInstant = performance.now();
513
- this.#onQuery?.({
514
- timestamp,
515
- duration: endInstant - startInstant,
516
- query: query.sql,
517
- params: query.args
518
- });
519
- return result;
686
+ #withQueryEvent(query, queryable, execute) {
687
+ return this.#tracingHelper.runInChildSpan(
688
+ {
689
+ name: "db_query",
690
+ kind: SpanKind.CLIENT,
691
+ attributes: {
692
+ "db.query.text": query.sql,
693
+ "db.system.name": providerToOtelSystem(queryable.provider)
694
+ }
695
+ },
696
+ async () => {
697
+ const timestamp = /* @__PURE__ */ new Date();
698
+ const startInstant = performance.now();
699
+ const result = await execute();
700
+ const endInstant = performance.now();
701
+ this.#onQuery?.({
702
+ timestamp,
703
+ duration: endInstant - startInstant,
704
+ query: query.sql,
705
+ params: query.args
706
+ });
707
+ return result;
708
+ }
709
+ );
520
710
  }
521
711
  };
522
712
  function isEmpty(value) {
@@ -561,6 +751,8 @@ function attachChildrenToParent(parentRecord, children) {
561
751
  function filterChildRecords(records, parentRecord, joinExpr) {
562
752
  if (Array.isArray(records)) {
563
753
  return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
754
+ } else if (records === null) {
755
+ return null;
564
756
  } else {
565
757
  const record = asRecord(records);
566
758
  return childRecordMatchesParent(record, parentRecord, joinExpr) ? record : null;
@@ -649,11 +841,20 @@ var TransactionManager = class {
649
841
  closedTransactions = [];
650
842
  driverAdapter;
651
843
  transactionOptions;
652
- constructor({ driverAdapter, transactionOptions }) {
844
+ tracingHelper;
845
+ constructor({
846
+ driverAdapter,
847
+ transactionOptions,
848
+ tracingHelper
849
+ }) {
653
850
  this.driverAdapter = driverAdapter;
654
851
  this.transactionOptions = transactionOptions;
852
+ this.tracingHelper = tracingHelper;
655
853
  }
656
854
  async startTransaction(options) {
855
+ return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
856
+ }
857
+ async #startTransactionImpl(options) {
657
858
  const validatedOptions = options !== void 0 ? this.validateOptions(options) : this.transactionOptions;
658
859
  const transaction = {
659
860
  id: await randomUUID(),
@@ -687,12 +888,16 @@ var TransactionManager = class {
687
888
  }
688
889
  }
689
890
  async commitTransaction(transactionId) {
690
- const txw = this.getActiveTransaction(transactionId, "commit");
691
- await this.closeTransaction(txw, "committed");
891
+ return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
892
+ const txw = this.getActiveTransaction(transactionId, "commit");
893
+ await this.closeTransaction(txw, "committed");
894
+ });
692
895
  }
693
896
  async rollbackTransaction(transactionId) {
694
- const txw = this.getActiveTransaction(transactionId, "rollback");
695
- await this.closeTransaction(txw, "rolled_back");
897
+ return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
898
+ const txw = this.getActiveTransaction(transactionId, "rollback");
899
+ await this.closeTransaction(txw, "rolled_back");
900
+ });
696
901
  }
697
902
  getTransaction(txInfo, operation) {
698
903
  const tx = this.getActiveTransaction(txInfo.id, operation);
@@ -783,5 +988,6 @@ export {
783
988
  TransactionManagerError,
784
989
  UserFacingError,
785
990
  isPrismaValueGenerator,
786
- isPrismaValuePlaceholder
991
+ isPrismaValuePlaceholder,
992
+ noopTracingHelper
787
993
  };
@@ -0,0 +1,3 @@
1
+ import { ResultNode } from '../QueryPlan';
2
+ import { Value } from './scope';
3
+ export declare function applyDataMap(data: Value, structure: ResultNode): Value;
@@ -1,7 +1,9 @@
1
- import { SqlQueryable } from '@prisma/driver-adapter-utils';
1
+ import { SqlQueryable, SqlResultSet } from '@prisma/driver-adapter-utils';
2
2
  import { QueryEvent } from '../events';
3
3
  import { QueryPlanNode } from '../QueryPlan';
4
+ import { type TracingHelper } from '../tracing';
4
5
  import { type TransactionManager } from '../transactionManager/TransactionManager';
6
+ import { Value } from './scope';
5
7
  export type QueryInterpreterTransactionManager = {
6
8
  enabled: true;
7
9
  manager: TransactionManager;
@@ -12,10 +14,18 @@ export type QueryInterpreterOptions = {
12
14
  transactionManager: QueryInterpreterTransactionManager;
13
15
  placeholderValues: Record<string, unknown>;
14
16
  onQuery?: (event: QueryEvent) => void;
17
+ tracingHelper: TracingHelper;
18
+ serializer: (results: SqlResultSet) => Value;
15
19
  };
16
20
  export declare class QueryInterpreter {
17
21
  #private;
18
- constructor({ transactionManager, placeholderValues, onQuery }: QueryInterpreterOptions);
22
+ constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }: QueryInterpreterOptions);
23
+ static forSql(options: {
24
+ transactionManager: QueryInterpreterTransactionManager;
25
+ placeholderValues: Record<string, unknown>;
26
+ onQuery?: (event: QueryEvent) => void;
27
+ tracingHelper: TracingHelper;
28
+ }): QueryInterpreter;
19
29
  run(queryPlan: QueryPlanNode, queryable: SqlQueryable): Promise<unknown>;
20
30
  private interpretNode;
21
31
  }
@@ -0,0 +1,2 @@
1
+ import type { SqlResultSet } from '@prisma/driver-adapter-utils';
2
+ export declare function serializeSql(resultSet: SqlResultSet): Record<string, unknown>[];
@@ -0,0 +1,11 @@
1
+ import type { Context, Span, SpanOptions } from '@opentelemetry/api';
2
+ import type { Provider } from '@prisma/driver-adapter-utils';
3
+ export type SpanCallback<R> = (span?: Span, context?: Context) => R;
4
+ export type ExtendedSpanOptions = SpanOptions & {
5
+ name: string;
6
+ };
7
+ export interface TracingHelper {
8
+ runInChildSpan<R>(nameOrOptions: string | ExtendedSpanOptions, callback: SpanCallback<R>): R;
9
+ }
10
+ export declare const noopTracingHelper: TracingHelper;
11
+ export declare function providerToOtelSystem(provider: Provider): string;
@@ -1,13 +1,17 @@
1
1
  import { SqlDriverAdapter, Transaction } from '@prisma/driver-adapter-utils';
2
+ import { TracingHelper } from '../tracing';
2
3
  import { Options, TransactionInfo } from './Transaction';
3
4
  export declare class TransactionManager {
5
+ #private;
4
6
  private transactions;
5
7
  private closedTransactions;
6
8
  private readonly driverAdapter;
7
9
  private readonly transactionOptions;
8
- constructor({ driverAdapter, transactionOptions }: {
10
+ private readonly tracingHelper;
11
+ constructor({ driverAdapter, transactionOptions, tracingHelper, }: {
9
12
  driverAdapter: SqlDriverAdapter;
10
13
  transactionOptions: Options;
14
+ tracingHelper: TracingHelper;
11
15
  });
12
16
  startTransaction(options?: Options): Promise<TransactionInfo>;
13
17
  commitTransaction(transactionId: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/client-engine-runtime",
3
- "version": "6.7.0-dev.4",
3
+ "version": "6.7.0-dev.40",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -25,12 +25,13 @@
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
27
  "@bugsnag/cuid": "3.2.1",
28
+ "@opentelemetry/api": "1.9.0",
28
29
  "@paralleldrive/cuid2": "2.2.2",
29
30
  "nanoid": "5.1.5",
30
31
  "ulid": "3.0.0",
31
32
  "uuid": "11.1.0",
32
- "@prisma/debug": "6.7.0-dev.4",
33
- "@prisma/driver-adapter-utils": "6.7.0-dev.4"
33
+ "@prisma/debug": "6.7.0-dev.40",
34
+ "@prisma/driver-adapter-utils": "6.7.0-dev.40"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/jest": "29.5.14",
@@ -1,2 +0,0 @@
1
- import type { SqlResultSet } from '@prisma/driver-adapter-utils';
2
- export declare function serialize(resultSet: SqlResultSet): Record<string, unknown>[];