@prisma/client-engine-runtime 6.8.0-dev.4 → 6.8.0-dev.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/QueryPlan.d.ts +65 -1
- package/dist/index.d.mts +69 -1
- package/dist/index.d.ts +69 -1
- package/dist/index.js +104 -40
- package/dist/index.mjs +103 -40
- package/dist/interpreter/generators.d.ts +1 -2
- package/dist/interpreter/validation.d.ts +3 -0
- package/package.json +4 -3
package/dist/QueryPlan.d.ts
CHANGED
|
@@ -14,7 +14,12 @@ export type PrismaValueGenerator = {
|
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
|
|
17
|
-
export type
|
|
17
|
+
export type PrismaValueBytes = {
|
|
18
|
+
prisma__type: 'bytes';
|
|
19
|
+
prisma__value: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
|
|
22
|
+
export type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
|
|
18
23
|
export type PrismaValueType = {
|
|
19
24
|
type: 'Any';
|
|
20
25
|
} | {
|
|
@@ -141,4 +146,63 @@ export type QueryPlanNode = {
|
|
|
141
146
|
expr: QueryPlanNode;
|
|
142
147
|
structure: ResultNode;
|
|
143
148
|
};
|
|
149
|
+
} | {
|
|
150
|
+
type: 'validate';
|
|
151
|
+
args: {
|
|
152
|
+
expr: QueryPlanNode;
|
|
153
|
+
rules: DataRule[];
|
|
154
|
+
} & ValidationError;
|
|
155
|
+
} | {
|
|
156
|
+
type: 'if';
|
|
157
|
+
args: {
|
|
158
|
+
value: QueryPlanNode;
|
|
159
|
+
rule: DataRule;
|
|
160
|
+
then: QueryPlanNode;
|
|
161
|
+
else: QueryPlanNode;
|
|
162
|
+
};
|
|
163
|
+
} | {
|
|
164
|
+
type: 'unit';
|
|
165
|
+
};
|
|
166
|
+
export type DataRule = {
|
|
167
|
+
type: 'rowCountEq';
|
|
168
|
+
args: number;
|
|
169
|
+
} | {
|
|
170
|
+
type: 'rowCountNeq';
|
|
171
|
+
args: number;
|
|
172
|
+
} | {
|
|
173
|
+
type: 'never';
|
|
174
|
+
};
|
|
175
|
+
export type ValidationError = {
|
|
176
|
+
error_identifier: 'RELATION_VIOLATION';
|
|
177
|
+
context: {
|
|
178
|
+
relation: string;
|
|
179
|
+
modelA: string;
|
|
180
|
+
modelB: string;
|
|
181
|
+
};
|
|
182
|
+
} | {
|
|
183
|
+
error_identifier: 'MISSING_RELATED_RECORD';
|
|
184
|
+
context: {
|
|
185
|
+
model: string;
|
|
186
|
+
relation: string;
|
|
187
|
+
relationType: string;
|
|
188
|
+
operation: string;
|
|
189
|
+
neededFor?: string;
|
|
190
|
+
};
|
|
191
|
+
} | {
|
|
192
|
+
error_identifier: 'MISSING_RECORD';
|
|
193
|
+
context: {
|
|
194
|
+
operation: string;
|
|
195
|
+
};
|
|
196
|
+
} | {
|
|
197
|
+
error_identifier: 'INCOMPLETE_CONNECT_INPUT';
|
|
198
|
+
context: {
|
|
199
|
+
expectedRows: number;
|
|
200
|
+
};
|
|
201
|
+
} | {
|
|
202
|
+
error_identifier: 'RECORDS_NOT_CONNECTED';
|
|
203
|
+
context: {
|
|
204
|
+
relation: string;
|
|
205
|
+
parent: string;
|
|
206
|
+
child: string;
|
|
207
|
+
};
|
|
144
208
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -7,6 +7,16 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
|
|
|
7
7
|
import { SqlResultSet } from '@prisma/driver-adapter-utils';
|
|
8
8
|
import { Transaction } from '@prisma/driver-adapter-utils';
|
|
9
9
|
|
|
10
|
+
export declare type DataRule = {
|
|
11
|
+
type: 'rowCountEq';
|
|
12
|
+
args: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'rowCountNeq';
|
|
15
|
+
args: number;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'never';
|
|
18
|
+
};
|
|
19
|
+
|
|
10
20
|
declare type ExtendedSpanOptions = SpanOptions & {
|
|
11
21
|
name: string;
|
|
12
22
|
};
|
|
@@ -22,6 +32,8 @@ export declare type Fragment = {
|
|
|
22
32
|
type: 'parameterTupleList';
|
|
23
33
|
};
|
|
24
34
|
|
|
35
|
+
export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
|
|
36
|
+
|
|
25
37
|
export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
|
|
26
38
|
|
|
27
39
|
export declare function isPrismaValuePlaceholder(value: unknown): value is PrismaValuePlaceholder;
|
|
@@ -39,7 +51,12 @@ export declare interface PlaceholderFormat {
|
|
|
39
51
|
hasNumbering: boolean;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator;
|
|
54
|
+
export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
|
|
55
|
+
|
|
56
|
+
export declare type PrismaValueBytes = {
|
|
57
|
+
prisma__type: 'bytes';
|
|
58
|
+
prisma__value: string;
|
|
59
|
+
};
|
|
43
60
|
|
|
44
61
|
export declare type PrismaValueGenerator = {
|
|
45
62
|
prisma__type: 'generatorCall';
|
|
@@ -194,6 +211,22 @@ export declare type QueryPlanNode = {
|
|
|
194
211
|
expr: QueryPlanNode;
|
|
195
212
|
structure: ResultNode;
|
|
196
213
|
};
|
|
214
|
+
} | {
|
|
215
|
+
type: 'validate';
|
|
216
|
+
args: {
|
|
217
|
+
expr: QueryPlanNode;
|
|
218
|
+
rules: DataRule[];
|
|
219
|
+
} & ValidationError;
|
|
220
|
+
} | {
|
|
221
|
+
type: 'if';
|
|
222
|
+
args: {
|
|
223
|
+
value: QueryPlanNode;
|
|
224
|
+
rule: DataRule;
|
|
225
|
+
then: QueryPlanNode;
|
|
226
|
+
else: QueryPlanNode;
|
|
227
|
+
};
|
|
228
|
+
} | {
|
|
229
|
+
type: 'unit';
|
|
197
230
|
};
|
|
198
231
|
|
|
199
232
|
export declare type ResultNode = {
|
|
@@ -266,6 +299,41 @@ export declare class UserFacingError extends Error {
|
|
|
266
299
|
};
|
|
267
300
|
}
|
|
268
301
|
|
|
302
|
+
export declare type ValidationError = {
|
|
303
|
+
error_identifier: 'RELATION_VIOLATION';
|
|
304
|
+
context: {
|
|
305
|
+
relation: string;
|
|
306
|
+
modelA: string;
|
|
307
|
+
modelB: string;
|
|
308
|
+
};
|
|
309
|
+
} | {
|
|
310
|
+
error_identifier: 'MISSING_RELATED_RECORD';
|
|
311
|
+
context: {
|
|
312
|
+
model: string;
|
|
313
|
+
relation: string;
|
|
314
|
+
relationType: string;
|
|
315
|
+
operation: string;
|
|
316
|
+
neededFor?: string;
|
|
317
|
+
};
|
|
318
|
+
} | {
|
|
319
|
+
error_identifier: 'MISSING_RECORD';
|
|
320
|
+
context: {
|
|
321
|
+
operation: string;
|
|
322
|
+
};
|
|
323
|
+
} | {
|
|
324
|
+
error_identifier: 'INCOMPLETE_CONNECT_INPUT';
|
|
325
|
+
context: {
|
|
326
|
+
expectedRows: number;
|
|
327
|
+
};
|
|
328
|
+
} | {
|
|
329
|
+
error_identifier: 'RECORDS_NOT_CONNECTED';
|
|
330
|
+
context: {
|
|
331
|
+
relation: string;
|
|
332
|
+
parent: string;
|
|
333
|
+
child: string;
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
|
|
269
337
|
/**
|
|
270
338
|
* The general type of values each node can evaluate to.
|
|
271
339
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,16 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
|
|
|
7
7
|
import { SqlResultSet } from '@prisma/driver-adapter-utils';
|
|
8
8
|
import { Transaction } from '@prisma/driver-adapter-utils';
|
|
9
9
|
|
|
10
|
+
export declare type DataRule = {
|
|
11
|
+
type: 'rowCountEq';
|
|
12
|
+
args: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'rowCountNeq';
|
|
15
|
+
args: number;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'never';
|
|
18
|
+
};
|
|
19
|
+
|
|
10
20
|
declare type ExtendedSpanOptions = SpanOptions & {
|
|
11
21
|
name: string;
|
|
12
22
|
};
|
|
@@ -22,6 +32,8 @@ export declare type Fragment = {
|
|
|
22
32
|
type: 'parameterTupleList';
|
|
23
33
|
};
|
|
24
34
|
|
|
35
|
+
export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
|
|
36
|
+
|
|
25
37
|
export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
|
|
26
38
|
|
|
27
39
|
export declare function isPrismaValuePlaceholder(value: unknown): value is PrismaValuePlaceholder;
|
|
@@ -39,7 +51,12 @@ export declare interface PlaceholderFormat {
|
|
|
39
51
|
hasNumbering: boolean;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator;
|
|
54
|
+
export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
|
|
55
|
+
|
|
56
|
+
export declare type PrismaValueBytes = {
|
|
57
|
+
prisma__type: 'bytes';
|
|
58
|
+
prisma__value: string;
|
|
59
|
+
};
|
|
43
60
|
|
|
44
61
|
export declare type PrismaValueGenerator = {
|
|
45
62
|
prisma__type: 'generatorCall';
|
|
@@ -194,6 +211,22 @@ export declare type QueryPlanNode = {
|
|
|
194
211
|
expr: QueryPlanNode;
|
|
195
212
|
structure: ResultNode;
|
|
196
213
|
};
|
|
214
|
+
} | {
|
|
215
|
+
type: 'validate';
|
|
216
|
+
args: {
|
|
217
|
+
expr: QueryPlanNode;
|
|
218
|
+
rules: DataRule[];
|
|
219
|
+
} & ValidationError;
|
|
220
|
+
} | {
|
|
221
|
+
type: 'if';
|
|
222
|
+
args: {
|
|
223
|
+
value: QueryPlanNode;
|
|
224
|
+
rule: DataRule;
|
|
225
|
+
then: QueryPlanNode;
|
|
226
|
+
else: QueryPlanNode;
|
|
227
|
+
};
|
|
228
|
+
} | {
|
|
229
|
+
type: 'unit';
|
|
197
230
|
};
|
|
198
231
|
|
|
199
232
|
export declare type ResultNode = {
|
|
@@ -266,6 +299,41 @@ export declare class UserFacingError extends Error {
|
|
|
266
299
|
};
|
|
267
300
|
}
|
|
268
301
|
|
|
302
|
+
export declare type ValidationError = {
|
|
303
|
+
error_identifier: 'RELATION_VIOLATION';
|
|
304
|
+
context: {
|
|
305
|
+
relation: string;
|
|
306
|
+
modelA: string;
|
|
307
|
+
modelB: string;
|
|
308
|
+
};
|
|
309
|
+
} | {
|
|
310
|
+
error_identifier: 'MISSING_RELATED_RECORD';
|
|
311
|
+
context: {
|
|
312
|
+
model: string;
|
|
313
|
+
relation: string;
|
|
314
|
+
relationType: string;
|
|
315
|
+
operation: string;
|
|
316
|
+
neededFor?: string;
|
|
317
|
+
};
|
|
318
|
+
} | {
|
|
319
|
+
error_identifier: 'MISSING_RECORD';
|
|
320
|
+
context: {
|
|
321
|
+
operation: string;
|
|
322
|
+
};
|
|
323
|
+
} | {
|
|
324
|
+
error_identifier: 'INCOMPLETE_CONNECT_INPUT';
|
|
325
|
+
context: {
|
|
326
|
+
expectedRows: number;
|
|
327
|
+
};
|
|
328
|
+
} | {
|
|
329
|
+
error_identifier: 'RECORDS_NOT_CONNECTED';
|
|
330
|
+
context: {
|
|
331
|
+
relation: string;
|
|
332
|
+
parent: string;
|
|
333
|
+
child: string;
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
|
|
269
337
|
/**
|
|
270
338
|
* The general type of values each node can evaluate to.
|
|
271
339
|
*/
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
TransactionManager: () => TransactionManager,
|
|
35
35
|
TransactionManagerError: () => TransactionManagerError,
|
|
36
36
|
UserFacingError: () => UserFacingError,
|
|
37
|
+
isPrismaValueBytes: () => isPrismaValueBytes,
|
|
37
38
|
isPrismaValueGenerator: () => isPrismaValueGenerator,
|
|
38
39
|
isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
|
|
39
40
|
noopTracingHelper: () => noopTracingHelper
|
|
@@ -211,6 +212,7 @@ function renderConstraint(constraint) {
|
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
// src/interpreter/DataMapper.ts
|
|
215
|
+
var import_decimal = __toESM(require("decimal.js"));
|
|
214
216
|
function applyDataMap(data, structure) {
|
|
215
217
|
switch (structure.type) {
|
|
216
218
|
case "Object":
|
|
@@ -283,22 +285,21 @@ function mapValue(value, resultType) {
|
|
|
283
285
|
case "Boolean":
|
|
284
286
|
return typeof value === "boolean" ? value : value !== "0";
|
|
285
287
|
case "Decimal":
|
|
286
|
-
return typeof value === "number" ? value :
|
|
288
|
+
return typeof value === "number" ? new import_decimal.default(value) : new import_decimal.default(`${value}`);
|
|
287
289
|
case "Date":
|
|
288
290
|
return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
|
|
289
291
|
case "Array": {
|
|
290
292
|
const values = value;
|
|
291
|
-
return values.map((v) =>
|
|
292
|
-
mapValue(v, resultType.inner);
|
|
293
|
-
});
|
|
293
|
+
return values.map((v) => mapValue(v, resultType.inner));
|
|
294
294
|
}
|
|
295
295
|
case "Object":
|
|
296
|
-
return typeof value === "
|
|
297
|
-
case "Bytes":
|
|
298
|
-
if (
|
|
299
|
-
throw new Error(`DataMapper: Bytes data is
|
|
296
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
297
|
+
case "Bytes": {
|
|
298
|
+
if (!Array.isArray(value)) {
|
|
299
|
+
throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
|
|
300
300
|
}
|
|
301
|
-
return value;
|
|
301
|
+
return new Uint8Array(value);
|
|
302
|
+
}
|
|
302
303
|
default:
|
|
303
304
|
assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
|
|
304
305
|
}
|
|
@@ -405,6 +406,9 @@ function isPrismaValuePlaceholder(value) {
|
|
|
405
406
|
function isPrismaValueGenerator(value) {
|
|
406
407
|
return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
|
|
407
408
|
}
|
|
409
|
+
function isPrismaValueBytes(value) {
|
|
410
|
+
return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
|
|
411
|
+
}
|
|
408
412
|
|
|
409
413
|
// src/interpreter/renderQuery.ts
|
|
410
414
|
function renderQuery(dbQuery, scope, generators) {
|
|
@@ -448,6 +452,9 @@ function evaluateParam(param, scope, generators) {
|
|
|
448
452
|
if (Array.isArray(value)) {
|
|
449
453
|
value = value.map((el) => evaluateParam(el, scope, generators));
|
|
450
454
|
}
|
|
455
|
+
if (isPrismaValueBytes(value)) {
|
|
456
|
+
value = Buffer.from(value.prisma__value, "base64");
|
|
457
|
+
}
|
|
451
458
|
return value;
|
|
452
459
|
}
|
|
453
460
|
function renderTemplateSql(fragments, placeholderFormat, params) {
|
|
@@ -518,9 +525,6 @@ function renderRawSql(sql, params) {
|
|
|
518
525
|
};
|
|
519
526
|
}
|
|
520
527
|
function toArgType(value) {
|
|
521
|
-
if (value === null) {
|
|
522
|
-
return "Int32";
|
|
523
|
-
}
|
|
524
528
|
if (typeof value === "string") {
|
|
525
529
|
return "Text";
|
|
526
530
|
}
|
|
@@ -533,30 +537,10 @@ function toArgType(value) {
|
|
|
533
537
|
if (Array.isArray(value)) {
|
|
534
538
|
return "Array";
|
|
535
539
|
}
|
|
536
|
-
if (
|
|
537
|
-
return
|
|
538
|
-
}
|
|
539
|
-
return "Json";
|
|
540
|
-
}
|
|
541
|
-
function placeholderTypeToArgType(type) {
|
|
542
|
-
const typeMap = {
|
|
543
|
-
Any: "Json",
|
|
544
|
-
String: "Text",
|
|
545
|
-
Int: "Int32",
|
|
546
|
-
BigInt: "Int64",
|
|
547
|
-
Float: "Double",
|
|
548
|
-
Boolean: "Boolean",
|
|
549
|
-
Decimal: "Numeric",
|
|
550
|
-
Date: "DateTime",
|
|
551
|
-
Object: "Json",
|
|
552
|
-
Bytes: "Bytes",
|
|
553
|
-
Array: "Array"
|
|
554
|
-
};
|
|
555
|
-
const mappedType = typeMap[type];
|
|
556
|
-
if (!mappedType) {
|
|
557
|
-
throw new Error(`Unknown placeholder type: ${type}`);
|
|
540
|
+
if (Buffer.isBuffer(value)) {
|
|
541
|
+
return "Bytes";
|
|
558
542
|
}
|
|
559
|
-
return
|
|
543
|
+
return "Unknown";
|
|
560
544
|
}
|
|
561
545
|
function doesRequireEvaluation(param) {
|
|
562
546
|
return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
|
|
@@ -584,6 +568,71 @@ function serializeSql(resultSet) {
|
|
|
584
568
|
);
|
|
585
569
|
}
|
|
586
570
|
|
|
571
|
+
// src/interpreter/validation.ts
|
|
572
|
+
function performValidation(data, rules, error) {
|
|
573
|
+
if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
|
|
574
|
+
const message = renderMessage(data, error);
|
|
575
|
+
const code = getErrorCode2(error);
|
|
576
|
+
throw new UserFacingError(message, code, error.context);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function doesSatisfyRule(data, rule) {
|
|
580
|
+
switch (rule.type) {
|
|
581
|
+
case "rowCountEq":
|
|
582
|
+
if (Array.isArray(data)) {
|
|
583
|
+
return data.length === rule.args;
|
|
584
|
+
}
|
|
585
|
+
if (data === null) {
|
|
586
|
+
return rule.args === 0;
|
|
587
|
+
}
|
|
588
|
+
return rule.args === 1;
|
|
589
|
+
case "rowCountNeq":
|
|
590
|
+
if (Array.isArray(data)) {
|
|
591
|
+
return data.length !== rule.args;
|
|
592
|
+
}
|
|
593
|
+
if (data === null) {
|
|
594
|
+
return rule.args !== 0;
|
|
595
|
+
}
|
|
596
|
+
return rule.args !== 1;
|
|
597
|
+
case "never":
|
|
598
|
+
return false;
|
|
599
|
+
default:
|
|
600
|
+
assertNever(rule, `Unknown rule type: ${rule.type}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function renderMessage(data, error) {
|
|
604
|
+
switch (error.error_identifier) {
|
|
605
|
+
case "RELATION_VIOLATION":
|
|
606
|
+
return `The change you are trying to make would violate the required relation '${error.context.relation}' between the \`${error.context.modelA}\` and \`${error.context.modelB}\` models.`;
|
|
607
|
+
case "MISSING_RECORD":
|
|
608
|
+
return `An operation failed because it depends on one or more records that were required but not found. No record was found for ${error.context.operation}.`;
|
|
609
|
+
case "MISSING_RELATED_RECORD": {
|
|
610
|
+
const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
|
|
611
|
+
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}'.`;
|
|
612
|
+
}
|
|
613
|
+
case "INCOMPLETE_CONNECT_INPUT":
|
|
614
|
+
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}.`;
|
|
615
|
+
case "RECORDS_NOT_CONNECTED":
|
|
616
|
+
return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
|
|
617
|
+
default:
|
|
618
|
+
assertNever(error, `Unknown error identifier: ${error}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function getErrorCode2(error) {
|
|
622
|
+
switch (error.error_identifier) {
|
|
623
|
+
case "RELATION_VIOLATION":
|
|
624
|
+
return "P2014";
|
|
625
|
+
case "RECORDS_NOT_CONNECTED":
|
|
626
|
+
return "P2017";
|
|
627
|
+
case "MISSING_RECORD":
|
|
628
|
+
case "MISSING_RELATED_RECORD":
|
|
629
|
+
case "INCOMPLETE_CONNECT_INPUT":
|
|
630
|
+
return "P2025";
|
|
631
|
+
default:
|
|
632
|
+
assertNever(error, `Unknown error identifier: ${error}`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
587
636
|
// src/interpreter/QueryInterpreter.ts
|
|
588
637
|
var QueryInterpreter = class _QueryInterpreter {
|
|
589
638
|
#transactionManager;
|
|
@@ -624,11 +673,9 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
624
673
|
}
|
|
625
674
|
case "let": {
|
|
626
675
|
const nestedScope = Object.create(scope);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
})
|
|
631
|
-
);
|
|
676
|
+
for (const binding of node.args.bindings) {
|
|
677
|
+
nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
|
|
678
|
+
}
|
|
632
679
|
return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
|
|
633
680
|
}
|
|
634
681
|
case "getFirstNonEmpty": {
|
|
@@ -721,6 +768,22 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
721
768
|
const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
722
769
|
return applyDataMap(data, node.args.structure);
|
|
723
770
|
}
|
|
771
|
+
case "validate": {
|
|
772
|
+
const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
773
|
+
performValidation(data, node.args.rules, node.args);
|
|
774
|
+
return data;
|
|
775
|
+
}
|
|
776
|
+
case "if": {
|
|
777
|
+
const value = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
778
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
779
|
+
return await this.interpretNode(node.args.then, queryable, scope, generators);
|
|
780
|
+
} else {
|
|
781
|
+
return await this.interpretNode(node.args.else, queryable, scope, generators);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
case "unit": {
|
|
785
|
+
return void 0;
|
|
786
|
+
}
|
|
724
787
|
default:
|
|
725
788
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
726
789
|
}
|
|
@@ -1030,6 +1093,7 @@ var TransactionManager = class {
|
|
|
1030
1093
|
TransactionManager,
|
|
1031
1094
|
TransactionManagerError,
|
|
1032
1095
|
UserFacingError,
|
|
1096
|
+
isPrismaValueBytes,
|
|
1033
1097
|
isPrismaValueGenerator,
|
|
1034
1098
|
isPrismaValuePlaceholder,
|
|
1035
1099
|
noopTracingHelper
|
package/dist/index.mjs
CHANGED
|
@@ -169,6 +169,7 @@ function renderConstraint(constraint) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// src/interpreter/DataMapper.ts
|
|
172
|
+
import Decimal from "decimal.js";
|
|
172
173
|
function applyDataMap(data, structure) {
|
|
173
174
|
switch (structure.type) {
|
|
174
175
|
case "Object":
|
|
@@ -241,22 +242,21 @@ function mapValue(value, resultType) {
|
|
|
241
242
|
case "Boolean":
|
|
242
243
|
return typeof value === "boolean" ? value : value !== "0";
|
|
243
244
|
case "Decimal":
|
|
244
|
-
return typeof value === "number" ? value :
|
|
245
|
+
return typeof value === "number" ? new Decimal(value) : new Decimal(`${value}`);
|
|
245
246
|
case "Date":
|
|
246
247
|
return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
|
|
247
248
|
case "Array": {
|
|
248
249
|
const values = value;
|
|
249
|
-
return values.map((v) =>
|
|
250
|
-
mapValue(v, resultType.inner);
|
|
251
|
-
});
|
|
250
|
+
return values.map((v) => mapValue(v, resultType.inner));
|
|
252
251
|
}
|
|
253
252
|
case "Object":
|
|
254
|
-
return typeof value === "
|
|
255
|
-
case "Bytes":
|
|
256
|
-
if (
|
|
257
|
-
throw new Error(`DataMapper: Bytes data is
|
|
253
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
254
|
+
case "Bytes": {
|
|
255
|
+
if (!Array.isArray(value)) {
|
|
256
|
+
throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
|
|
258
257
|
}
|
|
259
|
-
return value;
|
|
258
|
+
return new Uint8Array(value);
|
|
259
|
+
}
|
|
260
260
|
default:
|
|
261
261
|
assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
|
|
262
262
|
}
|
|
@@ -363,6 +363,9 @@ function isPrismaValuePlaceholder(value) {
|
|
|
363
363
|
function isPrismaValueGenerator(value) {
|
|
364
364
|
return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
|
|
365
365
|
}
|
|
366
|
+
function isPrismaValueBytes(value) {
|
|
367
|
+
return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
|
|
368
|
+
}
|
|
366
369
|
|
|
367
370
|
// src/interpreter/renderQuery.ts
|
|
368
371
|
function renderQuery(dbQuery, scope, generators) {
|
|
@@ -406,6 +409,9 @@ function evaluateParam(param, scope, generators) {
|
|
|
406
409
|
if (Array.isArray(value)) {
|
|
407
410
|
value = value.map((el) => evaluateParam(el, scope, generators));
|
|
408
411
|
}
|
|
412
|
+
if (isPrismaValueBytes(value)) {
|
|
413
|
+
value = Buffer.from(value.prisma__value, "base64");
|
|
414
|
+
}
|
|
409
415
|
return value;
|
|
410
416
|
}
|
|
411
417
|
function renderTemplateSql(fragments, placeholderFormat, params) {
|
|
@@ -476,9 +482,6 @@ function renderRawSql(sql, params) {
|
|
|
476
482
|
};
|
|
477
483
|
}
|
|
478
484
|
function toArgType(value) {
|
|
479
|
-
if (value === null) {
|
|
480
|
-
return "Int32";
|
|
481
|
-
}
|
|
482
485
|
if (typeof value === "string") {
|
|
483
486
|
return "Text";
|
|
484
487
|
}
|
|
@@ -491,30 +494,10 @@ function toArgType(value) {
|
|
|
491
494
|
if (Array.isArray(value)) {
|
|
492
495
|
return "Array";
|
|
493
496
|
}
|
|
494
|
-
if (
|
|
495
|
-
return
|
|
496
|
-
}
|
|
497
|
-
return "Json";
|
|
498
|
-
}
|
|
499
|
-
function placeholderTypeToArgType(type) {
|
|
500
|
-
const typeMap = {
|
|
501
|
-
Any: "Json",
|
|
502
|
-
String: "Text",
|
|
503
|
-
Int: "Int32",
|
|
504
|
-
BigInt: "Int64",
|
|
505
|
-
Float: "Double",
|
|
506
|
-
Boolean: "Boolean",
|
|
507
|
-
Decimal: "Numeric",
|
|
508
|
-
Date: "DateTime",
|
|
509
|
-
Object: "Json",
|
|
510
|
-
Bytes: "Bytes",
|
|
511
|
-
Array: "Array"
|
|
512
|
-
};
|
|
513
|
-
const mappedType = typeMap[type];
|
|
514
|
-
if (!mappedType) {
|
|
515
|
-
throw new Error(`Unknown placeholder type: ${type}`);
|
|
497
|
+
if (Buffer.isBuffer(value)) {
|
|
498
|
+
return "Bytes";
|
|
516
499
|
}
|
|
517
|
-
return
|
|
500
|
+
return "Unknown";
|
|
518
501
|
}
|
|
519
502
|
function doesRequireEvaluation(param) {
|
|
520
503
|
return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
|
|
@@ -542,6 +525,71 @@ function serializeSql(resultSet) {
|
|
|
542
525
|
);
|
|
543
526
|
}
|
|
544
527
|
|
|
528
|
+
// src/interpreter/validation.ts
|
|
529
|
+
function performValidation(data, rules, error) {
|
|
530
|
+
if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
|
|
531
|
+
const message = renderMessage(data, error);
|
|
532
|
+
const code = getErrorCode2(error);
|
|
533
|
+
throw new UserFacingError(message, code, error.context);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function doesSatisfyRule(data, rule) {
|
|
537
|
+
switch (rule.type) {
|
|
538
|
+
case "rowCountEq":
|
|
539
|
+
if (Array.isArray(data)) {
|
|
540
|
+
return data.length === rule.args;
|
|
541
|
+
}
|
|
542
|
+
if (data === null) {
|
|
543
|
+
return rule.args === 0;
|
|
544
|
+
}
|
|
545
|
+
return rule.args === 1;
|
|
546
|
+
case "rowCountNeq":
|
|
547
|
+
if (Array.isArray(data)) {
|
|
548
|
+
return data.length !== rule.args;
|
|
549
|
+
}
|
|
550
|
+
if (data === null) {
|
|
551
|
+
return rule.args !== 0;
|
|
552
|
+
}
|
|
553
|
+
return rule.args !== 1;
|
|
554
|
+
case "never":
|
|
555
|
+
return false;
|
|
556
|
+
default:
|
|
557
|
+
assertNever(rule, `Unknown rule type: ${rule.type}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function renderMessage(data, error) {
|
|
561
|
+
switch (error.error_identifier) {
|
|
562
|
+
case "RELATION_VIOLATION":
|
|
563
|
+
return `The change you are trying to make would violate the required relation '${error.context.relation}' between the \`${error.context.modelA}\` and \`${error.context.modelB}\` models.`;
|
|
564
|
+
case "MISSING_RECORD":
|
|
565
|
+
return `An operation failed because it depends on one or more records that were required but not found. No record was found for ${error.context.operation}.`;
|
|
566
|
+
case "MISSING_RELATED_RECORD": {
|
|
567
|
+
const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
|
|
568
|
+
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}'.`;
|
|
569
|
+
}
|
|
570
|
+
case "INCOMPLETE_CONNECT_INPUT":
|
|
571
|
+
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}.`;
|
|
572
|
+
case "RECORDS_NOT_CONNECTED":
|
|
573
|
+
return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
|
|
574
|
+
default:
|
|
575
|
+
assertNever(error, `Unknown error identifier: ${error}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function getErrorCode2(error) {
|
|
579
|
+
switch (error.error_identifier) {
|
|
580
|
+
case "RELATION_VIOLATION":
|
|
581
|
+
return "P2014";
|
|
582
|
+
case "RECORDS_NOT_CONNECTED":
|
|
583
|
+
return "P2017";
|
|
584
|
+
case "MISSING_RECORD":
|
|
585
|
+
case "MISSING_RELATED_RECORD":
|
|
586
|
+
case "INCOMPLETE_CONNECT_INPUT":
|
|
587
|
+
return "P2025";
|
|
588
|
+
default:
|
|
589
|
+
assertNever(error, `Unknown error identifier: ${error}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
545
593
|
// src/interpreter/QueryInterpreter.ts
|
|
546
594
|
var QueryInterpreter = class _QueryInterpreter {
|
|
547
595
|
#transactionManager;
|
|
@@ -582,11 +630,9 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
582
630
|
}
|
|
583
631
|
case "let": {
|
|
584
632
|
const nestedScope = Object.create(scope);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
})
|
|
589
|
-
);
|
|
633
|
+
for (const binding of node.args.bindings) {
|
|
634
|
+
nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
|
|
635
|
+
}
|
|
590
636
|
return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
|
|
591
637
|
}
|
|
592
638
|
case "getFirstNonEmpty": {
|
|
@@ -679,6 +725,22 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
679
725
|
const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
680
726
|
return applyDataMap(data, node.args.structure);
|
|
681
727
|
}
|
|
728
|
+
case "validate": {
|
|
729
|
+
const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
|
|
730
|
+
performValidation(data, node.args.rules, node.args);
|
|
731
|
+
return data;
|
|
732
|
+
}
|
|
733
|
+
case "if": {
|
|
734
|
+
const value = await this.interpretNode(node.args.value, queryable, scope, generators);
|
|
735
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
736
|
+
return await this.interpretNode(node.args.then, queryable, scope, generators);
|
|
737
|
+
} else {
|
|
738
|
+
return await this.interpretNode(node.args.else, queryable, scope, generators);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
case "unit": {
|
|
742
|
+
return void 0;
|
|
743
|
+
}
|
|
682
744
|
default:
|
|
683
745
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
684
746
|
}
|
|
@@ -987,6 +1049,7 @@ export {
|
|
|
987
1049
|
TransactionManager,
|
|
988
1050
|
TransactionManagerError,
|
|
989
1051
|
UserFacingError,
|
|
1052
|
+
isPrismaValueBytes,
|
|
990
1053
|
isPrismaValueGenerator,
|
|
991
1054
|
isPrismaValuePlaceholder,
|
|
992
1055
|
noopTracingHelper
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PrismaValue } from '../QueryPlan';
|
|
2
1
|
export declare class GeneratorRegistry {
|
|
3
2
|
#private;
|
|
4
3
|
constructor();
|
|
@@ -17,5 +16,5 @@ export interface GeneratorRegistrySnapshot {
|
|
|
17
16
|
[key: string]: ValueGenerator;
|
|
18
17
|
}
|
|
19
18
|
export interface ValueGenerator {
|
|
20
|
-
generate(...args:
|
|
19
|
+
generate(...args: unknown[]): unknown;
|
|
21
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/client-engine-runtime",
|
|
3
|
-
"version": "6.8.0-dev.
|
|
3
|
+
"version": "6.8.0-dev.41",
|
|
4
4
|
"description": "This package is intended for Prisma's internal use",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
"@bugsnag/cuid": "3.2.1",
|
|
28
28
|
"@opentelemetry/api": "1.9.0",
|
|
29
29
|
"@paralleldrive/cuid2": "2.2.2",
|
|
30
|
+
"decimal.js": "10.5.0",
|
|
30
31
|
"nanoid": "5.1.5",
|
|
31
32
|
"ulid": "3.0.0",
|
|
32
33
|
"uuid": "11.1.0",
|
|
33
|
-
"@prisma/debug": "6.8.0-dev.
|
|
34
|
-
"@prisma/driver-adapter-utils": "6.8.0-dev.
|
|
34
|
+
"@prisma/debug": "6.8.0-dev.41",
|
|
35
|
+
"@prisma/driver-adapter-utils": "6.8.0-dev.41"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/jest": "29.5.14",
|