prisma-sql 1.54.0 → 1.56.0
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/generator.cjs +117 -124
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +117 -124
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +829 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +73 -61
- package/dist/index.d.ts +73 -61
- package/dist/index.js +824 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -704,6 +704,9 @@ function assertNoControlChars(label, s) {
|
|
|
704
704
|
);
|
|
705
705
|
}
|
|
706
706
|
}
|
|
707
|
+
function quoteRawIdent(id) {
|
|
708
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
709
|
+
}
|
|
707
710
|
function isIdentCharCode(c) {
|
|
708
711
|
return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
|
|
709
712
|
}
|
|
@@ -725,33 +728,33 @@ function parseQuotedPart(input, start) {
|
|
|
725
728
|
}
|
|
726
729
|
if (!sawAny) {
|
|
727
730
|
throw new Error(
|
|
728
|
-
`
|
|
731
|
+
`qualified name has empty quoted identifier part: ${JSON.stringify(input)}`
|
|
729
732
|
);
|
|
730
733
|
}
|
|
731
734
|
return i + 1;
|
|
732
735
|
}
|
|
733
736
|
if (c === 10 || c === 13 || c === 0) {
|
|
734
737
|
throw new Error(
|
|
735
|
-
`
|
|
738
|
+
`qualified name contains invalid characters: ${JSON.stringify(input)}`
|
|
736
739
|
);
|
|
737
740
|
}
|
|
738
741
|
sawAny = true;
|
|
739
742
|
i++;
|
|
740
743
|
}
|
|
741
744
|
throw new Error(
|
|
742
|
-
`
|
|
745
|
+
`qualified name has unterminated quoted identifier: ${JSON.stringify(input)}`
|
|
743
746
|
);
|
|
744
747
|
}
|
|
745
748
|
function parseUnquotedPart(input, start) {
|
|
746
749
|
const n = input.length;
|
|
747
750
|
let i = start;
|
|
748
751
|
if (i >= n) {
|
|
749
|
-
throw new Error(`
|
|
752
|
+
throw new Error(`qualified name is invalid: ${JSON.stringify(input)}`);
|
|
750
753
|
}
|
|
751
754
|
const c0 = input.charCodeAt(i);
|
|
752
755
|
if (!isIdentStartCharCode(c0)) {
|
|
753
756
|
throw new Error(
|
|
754
|
-
`
|
|
757
|
+
`qualified name must use identifiers (or quoted identifiers). Got: ${JSON.stringify(input)}`
|
|
755
758
|
);
|
|
756
759
|
}
|
|
757
760
|
i++;
|
|
@@ -760,15 +763,15 @@ function parseUnquotedPart(input, start) {
|
|
|
760
763
|
if (c === 46) break;
|
|
761
764
|
if (!isIdentCharCode(c)) {
|
|
762
765
|
throw new Error(
|
|
763
|
-
`
|
|
766
|
+
`qualified name contains invalid identifier characters: ${JSON.stringify(input)}`
|
|
764
767
|
);
|
|
765
768
|
}
|
|
766
769
|
i++;
|
|
767
770
|
}
|
|
768
771
|
return i;
|
|
769
772
|
}
|
|
770
|
-
function assertSafeQualifiedName(
|
|
771
|
-
const raw = String(
|
|
773
|
+
function assertSafeQualifiedName(input) {
|
|
774
|
+
const raw = String(input);
|
|
772
775
|
const trimmed = raw.trim();
|
|
773
776
|
if (trimmed.length === 0) {
|
|
774
777
|
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
@@ -842,7 +845,7 @@ function quote2(id) {
|
|
|
842
845
|
);
|
|
843
846
|
}
|
|
844
847
|
if (needsQuoting(id)) {
|
|
845
|
-
return
|
|
848
|
+
return quoteRawIdent(id);
|
|
846
849
|
}
|
|
847
850
|
return id;
|
|
848
851
|
}
|
|
@@ -2481,28 +2484,24 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2481
2484
|
function toSafeSqlIdentifier(input) {
|
|
2482
2485
|
const raw = String(input);
|
|
2483
2486
|
const n = raw.length;
|
|
2487
|
+
if (n === 0) return "_t";
|
|
2484
2488
|
let out = "";
|
|
2485
2489
|
for (let i = 0; i < n; i++) {
|
|
2486
2490
|
const c = raw.charCodeAt(i);
|
|
2487
2491
|
const isAZ = c >= 65 && c <= 90 || c >= 97 && c <= 122;
|
|
2488
2492
|
const is09 = c >= 48 && c <= 57;
|
|
2489
2493
|
const isUnderscore = c === 95;
|
|
2490
|
-
|
|
2491
|
-
out += raw[i];
|
|
2492
|
-
} else {
|
|
2493
|
-
out += "_";
|
|
2494
|
-
}
|
|
2494
|
+
out += isAZ || is09 || isUnderscore ? raw[i] : "_";
|
|
2495
2495
|
}
|
|
2496
|
-
if (out.length === 0) out = "_t";
|
|
2497
2496
|
const c0 = out.charCodeAt(0);
|
|
2498
2497
|
const startsOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95;
|
|
2499
|
-
|
|
2500
|
-
const lowered = out.toLowerCase();
|
|
2498
|
+
const lowered = (startsOk ? out : `_${out}`).toLowerCase();
|
|
2501
2499
|
return ALIAS_FORBIDDEN_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
2502
2500
|
}
|
|
2503
2501
|
function createAliasGenerator(maxAliases = 1e4) {
|
|
2504
2502
|
let counter = 0;
|
|
2505
2503
|
const usedAliases = /* @__PURE__ */ new Set();
|
|
2504
|
+
const maxLen = 63;
|
|
2506
2505
|
return {
|
|
2507
2506
|
next(baseName) {
|
|
2508
2507
|
if (usedAliases.size >= maxAliases) {
|
|
@@ -2512,14 +2511,13 @@ function createAliasGenerator(maxAliases = 1e4) {
|
|
|
2512
2511
|
}
|
|
2513
2512
|
const base = toSafeSqlIdentifier(baseName);
|
|
2514
2513
|
const suffix = `_${counter}`;
|
|
2515
|
-
const maxLen = 63;
|
|
2516
2514
|
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2517
2515
|
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2518
2516
|
const alias = `${trimmedBase}${suffix}`;
|
|
2519
2517
|
counter += 1;
|
|
2520
2518
|
if (usedAliases.has(alias)) {
|
|
2521
2519
|
throw new Error(
|
|
2522
|
-
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}
|
|
2520
|
+
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}.`
|
|
2523
2521
|
);
|
|
2524
2522
|
}
|
|
2525
2523
|
usedAliases.add(alias);
|
|
@@ -2571,24 +2569,19 @@ function normalizeDynamicNameOrThrow(dynamicName, index) {
|
|
|
2571
2569
|
}
|
|
2572
2570
|
return dn;
|
|
2573
2571
|
}
|
|
2574
|
-
function assertUniqueDynamicName(dn, seen) {
|
|
2575
|
-
if (seen.has(dn)) {
|
|
2576
|
-
throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
|
|
2577
|
-
}
|
|
2578
|
-
seen.add(dn);
|
|
2579
|
-
}
|
|
2580
|
-
function validateMappingEntry(m, expectedIndex, seenDynamic) {
|
|
2581
|
-
assertSequentialIndex(m.index, expectedIndex);
|
|
2582
|
-
assertExactlyOneOfDynamicOrValue(m);
|
|
2583
|
-
if (typeof m.dynamicName === "string") {
|
|
2584
|
-
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2585
|
-
assertUniqueDynamicName(dn, seenDynamic);
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
2588
2572
|
function validateMappings(mappings) {
|
|
2589
2573
|
const seenDynamic = /* @__PURE__ */ new Set();
|
|
2590
2574
|
for (let i = 0; i < mappings.length; i++) {
|
|
2591
|
-
|
|
2575
|
+
const m = mappings[i];
|
|
2576
|
+
assertSequentialIndex(m.index, i + 1);
|
|
2577
|
+
assertExactlyOneOfDynamicOrValue(m);
|
|
2578
|
+
if (typeof m.dynamicName === "string") {
|
|
2579
|
+
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2580
|
+
if (seenDynamic.has(dn)) {
|
|
2581
|
+
throw new Error(`CRITICAL: Duplicate dynamic param name: ${dn}`);
|
|
2582
|
+
}
|
|
2583
|
+
seenDynamic.add(dn);
|
|
2584
|
+
}
|
|
2592
2585
|
}
|
|
2593
2586
|
}
|
|
2594
2587
|
function validateState(params, mappings, index) {
|
|
@@ -2600,16 +2593,19 @@ function validateState(params, mappings, index) {
|
|
|
2600
2593
|
}
|
|
2601
2594
|
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2602
2595
|
let index = startIndex;
|
|
2603
|
-
const params = initialParams.length > 0 ?
|
|
2604
|
-
const mappings = initialMappings.length > 0 ?
|
|
2596
|
+
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2597
|
+
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2605
2598
|
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2606
|
-
for (
|
|
2599
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
2600
|
+
const m = mappings[i];
|
|
2607
2601
|
if (typeof m.dynamicName === "string") {
|
|
2608
2602
|
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2609
2603
|
}
|
|
2610
2604
|
}
|
|
2611
2605
|
let dirty = true;
|
|
2612
2606
|
let cachedSnapshot = null;
|
|
2607
|
+
let frozenParams = null;
|
|
2608
|
+
let frozenMappings = null;
|
|
2613
2609
|
function assertCanAdd() {
|
|
2614
2610
|
if (index > MAX_PARAM_INDEX) {
|
|
2615
2611
|
throw new Error(
|
|
@@ -2661,13 +2657,17 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2661
2657
|
}
|
|
2662
2658
|
function snapshot() {
|
|
2663
2659
|
if (!dirty && cachedSnapshot) return cachedSnapshot;
|
|
2660
|
+
if (!frozenParams) frozenParams = Object.freeze(params.slice());
|
|
2661
|
+
if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
|
|
2664
2662
|
const snap = {
|
|
2665
2663
|
index,
|
|
2666
|
-
params,
|
|
2667
|
-
mappings
|
|
2664
|
+
params: frozenParams,
|
|
2665
|
+
mappings: frozenMappings
|
|
2668
2666
|
};
|
|
2669
2667
|
cachedSnapshot = snap;
|
|
2670
2668
|
dirty = false;
|
|
2669
|
+
frozenParams = null;
|
|
2670
|
+
frozenMappings = null;
|
|
2671
2671
|
return snap;
|
|
2672
2672
|
}
|
|
2673
2673
|
return {
|
|
@@ -2691,11 +2691,11 @@ function createParamStore(startIndex = 1) {
|
|
|
2691
2691
|
return createStoreInternal(startIndex);
|
|
2692
2692
|
}
|
|
2693
2693
|
function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
|
|
2694
|
-
validateState(
|
|
2694
|
+
validateState(existingParams, existingMappings, nextIndex);
|
|
2695
2695
|
return createStoreInternal(
|
|
2696
2696
|
nextIndex,
|
|
2697
|
-
|
|
2698
|
-
|
|
2697
|
+
existingParams.slice(),
|
|
2698
|
+
existingMappings.slice()
|
|
2699
2699
|
);
|
|
2700
2700
|
}
|
|
2701
2701
|
|
|
@@ -2877,7 +2877,7 @@ function getRelationTableReference(relModel, dialect) {
|
|
|
2877
2877
|
dialect
|
|
2878
2878
|
);
|
|
2879
2879
|
}
|
|
2880
|
-
function resolveRelationOrThrow(model,
|
|
2880
|
+
function resolveRelationOrThrow(model, schemaByName, relName) {
|
|
2881
2881
|
const field = model.fields.find((f) => f.name === relName);
|
|
2882
2882
|
if (!isNotNullish(field)) {
|
|
2883
2883
|
throw new Error(
|
|
@@ -2931,8 +2931,9 @@ function validateOrderByForModel(model, orderBy) {
|
|
|
2931
2931
|
throw new Error("orderBy array entries must have exactly one field");
|
|
2932
2932
|
}
|
|
2933
2933
|
const fieldName = String(entries[0][0]).trim();
|
|
2934
|
-
if (fieldName.length === 0)
|
|
2934
|
+
if (fieldName.length === 0) {
|
|
2935
2935
|
throw new Error("orderBy field name cannot be empty");
|
|
2936
|
+
}
|
|
2936
2937
|
if (!scalarSet.has(fieldName)) {
|
|
2937
2938
|
throw new Error(
|
|
2938
2939
|
`orderBy references unknown or non-scalar field '${fieldName}' on model ${model.name}`
|
|
@@ -2991,8 +2992,9 @@ function extractRelationPaginationConfig(relArgs) {
|
|
|
2991
2992
|
function maybeReverseNegativeTake(takeVal, hasOrderBy, orderByInput) {
|
|
2992
2993
|
if (typeof takeVal !== "number") return { takeVal, orderByInput };
|
|
2993
2994
|
if (takeVal >= 0) return { takeVal, orderByInput };
|
|
2994
|
-
if (!hasOrderBy)
|
|
2995
|
+
if (!hasOrderBy) {
|
|
2995
2996
|
throw new Error("Negative take requires orderBy for deterministic results");
|
|
2997
|
+
}
|
|
2996
2998
|
return {
|
|
2997
2999
|
takeVal: Math.abs(takeVal),
|
|
2998
3000
|
orderByInput: reverseOrderByInput(orderByInput)
|
|
@@ -3002,9 +3004,7 @@ function finalizeOrderByForInclude(args) {
|
|
|
3002
3004
|
if (args.hasOrderBy && isNotNullish(args.orderByInput)) {
|
|
3003
3005
|
validateOrderByForModel(args.relModel, args.orderByInput);
|
|
3004
3006
|
}
|
|
3005
|
-
if (!args.hasPagination)
|
|
3006
|
-
return args.orderByInput;
|
|
3007
|
-
}
|
|
3007
|
+
if (!args.hasPagination) return args.orderByInput;
|
|
3008
3008
|
return ensureDeterministicOrderByInput({
|
|
3009
3009
|
orderBy: args.hasOrderBy ? args.orderByInput : void 0,
|
|
3010
3010
|
model: args.relModel,
|
|
@@ -3065,7 +3065,9 @@ function buildOrderBySql(finalOrderByInput, relAlias, dialect, relModel) {
|
|
|
3065
3065
|
return isNotNullish(finalOrderByInput) ? buildOrderBy(finalOrderByInput, relAlias, dialect, relModel) : "";
|
|
3066
3066
|
}
|
|
3067
3067
|
function buildBaseSql(args) {
|
|
3068
|
-
|
|
3068
|
+
const joins = args.joins ? ` ${args.joins}` : "";
|
|
3069
|
+
const where = `${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
|
|
3070
|
+
return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias}${joins} ` + where;
|
|
3069
3071
|
}
|
|
3070
3072
|
function buildOneToOneIncludeSql(args) {
|
|
3071
3073
|
const objExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
@@ -3077,9 +3079,7 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3077
3079
|
joinPredicate: args.joinPredicate,
|
|
3078
3080
|
whereClause: args.whereClause
|
|
3079
3081
|
});
|
|
3080
|
-
if (args.orderBySql) {
|
|
3081
|
-
sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3082
|
-
}
|
|
3082
|
+
if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3083
3083
|
if (isNotNullish(args.takeVal)) {
|
|
3084
3084
|
return appendLimitOffset(
|
|
3085
3085
|
sql,
|
|
@@ -3132,7 +3132,7 @@ function buildListIncludeSpec(args) {
|
|
|
3132
3132
|
`include.${args.relName}`
|
|
3133
3133
|
);
|
|
3134
3134
|
const selectExpr = jsonAgg("row", args.ctx.dialect);
|
|
3135
|
-
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${rowAlias}`;
|
|
3135
|
+
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
3136
3136
|
return Object.freeze({ name: args.relName, sql, isOneToOne: false });
|
|
3137
3137
|
}
|
|
3138
3138
|
function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
@@ -3230,12 +3230,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3230
3230
|
`Query complexity limit exceeded: ${stats.totalSubqueries} subqueries generated. Maximum allowed: ${MAX_TOTAL_SUBQUERIES}. This indicates exponential include nesting. Stats: depth=${stats.maxDepth}, includes=${stats.totalIncludes}. Path: ${visitPath.join(" -> ")}. Simplify your include structure or split into multiple queries.`
|
|
3231
3231
|
);
|
|
3232
3232
|
}
|
|
3233
|
-
const resolved = resolveRelationOrThrow(
|
|
3234
|
-
model,
|
|
3235
|
-
schemas,
|
|
3236
|
-
schemaByName,
|
|
3237
|
-
relName
|
|
3238
|
-
);
|
|
3233
|
+
const resolved = resolveRelationOrThrow(model, schemaByName, relName);
|
|
3239
3234
|
const relationPath = `${model.name}.${relName}`;
|
|
3240
3235
|
const currentPath = [...visitPath, relationPath];
|
|
3241
3236
|
if (visitPath.includes(relationPath)) {
|
|
@@ -3291,7 +3286,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3291
3286
|
stats
|
|
3292
3287
|
);
|
|
3293
3288
|
}
|
|
3294
|
-
function resolveCountRelationOrThrow(relName, model,
|
|
3289
|
+
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
3295
3290
|
const relationSet = getRelationFieldSet(model);
|
|
3296
3291
|
if (!relationSet.has(relName)) {
|
|
3297
3292
|
throw new Error(
|
|
@@ -3299,10 +3294,11 @@ function resolveCountRelationOrThrow(relName, model, schemas, schemaByName) {
|
|
|
3299
3294
|
);
|
|
3300
3295
|
}
|
|
3301
3296
|
const field = model.fields.find((f) => f.name === relName);
|
|
3302
|
-
if (!field)
|
|
3297
|
+
if (!field) {
|
|
3303
3298
|
throw new Error(
|
|
3304
3299
|
`_count.${relName} references unknown relation on model ${model.name}`
|
|
3305
3300
|
);
|
|
3301
|
+
}
|
|
3306
3302
|
if (!isValidRelationField(field)) {
|
|
3307
3303
|
throw new Error(
|
|
3308
3304
|
`_count.${relName} has invalid relation metadata on model ${model.name}`
|
|
@@ -3330,8 +3326,9 @@ function defaultReferencesForCount(fkCount) {
|
|
|
3330
3326
|
}
|
|
3331
3327
|
function resolveCountKeyPairs(field) {
|
|
3332
3328
|
const fkFields = normalizeKeyList(field.foreignKey);
|
|
3333
|
-
if (fkFields.length === 0)
|
|
3329
|
+
if (fkFields.length === 0) {
|
|
3334
3330
|
throw new Error("Relation count requires foreignKey");
|
|
3331
|
+
}
|
|
3335
3332
|
const refsRaw = field.references;
|
|
3336
3333
|
const refs = normalizeKeyList(refsRaw);
|
|
3337
3334
|
const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
|
|
@@ -3407,12 +3404,7 @@ function buildRelationCountSql(countSelect, model, schemas, parentAlias, _params
|
|
|
3407
3404
|
for (const m of schemas) schemaByName.set(m.name, m);
|
|
3408
3405
|
for (const [relName, shouldCount] of Object.entries(countSelect)) {
|
|
3409
3406
|
if (!shouldCount) continue;
|
|
3410
|
-
const resolved = resolveCountRelationOrThrow(
|
|
3411
|
-
relName,
|
|
3412
|
-
model,
|
|
3413
|
-
schemas,
|
|
3414
|
-
schemaByName
|
|
3415
|
-
);
|
|
3407
|
+
const resolved = resolveCountRelationOrThrow(relName, model, schemaByName);
|
|
3416
3408
|
const built = buildCountJoinAndPair({
|
|
3417
3409
|
relName,
|
|
3418
3410
|
field: resolved.field,
|
|
@@ -5140,6 +5132,618 @@ function buildSQLWithCache(model, models, method, args, dialect) {
|
|
|
5140
5132
|
queryCache.size;
|
|
5141
5133
|
return result;
|
|
5142
5134
|
}
|
|
5135
|
+
|
|
5136
|
+
// src/batch.ts
|
|
5137
|
+
function assertNoControlChars2(label, s) {
|
|
5138
|
+
for (let i = 0; i < s.length; i++) {
|
|
5139
|
+
const c = s.charCodeAt(i);
|
|
5140
|
+
if (c <= 31 || c === 127) {
|
|
5141
|
+
throw new Error(`${label} contains control characters`);
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
function assertSafeIdentifier(label, s) {
|
|
5146
|
+
const raw = String(s);
|
|
5147
|
+
if (raw.trim() !== raw) {
|
|
5148
|
+
throw new Error(`${label} must not contain leading/trailing whitespace`);
|
|
5149
|
+
}
|
|
5150
|
+
if (raw.length === 0) throw new Error(`${label} cannot be empty`);
|
|
5151
|
+
assertNoControlChars2(label, raw);
|
|
5152
|
+
if (/[ \t\r\n]/.test(raw)) {
|
|
5153
|
+
throw new Error(`${label} must not contain whitespace`);
|
|
5154
|
+
}
|
|
5155
|
+
if (raw.includes(";")) {
|
|
5156
|
+
throw new Error(`${label} must not contain semicolons`);
|
|
5157
|
+
}
|
|
5158
|
+
if (raw.includes("--") || raw.includes("/*") || raw.includes("*/")) {
|
|
5159
|
+
throw new Error(`${label} must not contain SQL comment tokens`);
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
function quoteIdent(id) {
|
|
5163
|
+
const raw = String(id);
|
|
5164
|
+
assertSafeIdentifier("Identifier", raw);
|
|
5165
|
+
return `"${raw.replace(/"/g, '""')}"`;
|
|
5166
|
+
}
|
|
5167
|
+
function makeBatchAlias(i) {
|
|
5168
|
+
return `k${i}`;
|
|
5169
|
+
}
|
|
5170
|
+
function replacePgPlaceholders(sql, replace) {
|
|
5171
|
+
const s = String(sql);
|
|
5172
|
+
const n = s.length;
|
|
5173
|
+
let i = 0;
|
|
5174
|
+
let mode = "normal";
|
|
5175
|
+
let dollarTag = null;
|
|
5176
|
+
let out = "";
|
|
5177
|
+
const startsWith = (pos, lit) => s.slice(pos, pos + lit.length) === lit;
|
|
5178
|
+
const readDollarTag = (pos) => {
|
|
5179
|
+
if (s.charCodeAt(pos) !== 36) return null;
|
|
5180
|
+
let j = pos + 1;
|
|
5181
|
+
while (j < n) {
|
|
5182
|
+
const c = s.charCodeAt(j);
|
|
5183
|
+
if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
|
|
5184
|
+
j++;
|
|
5185
|
+
continue;
|
|
5186
|
+
}
|
|
5187
|
+
break;
|
|
5188
|
+
}
|
|
5189
|
+
if (j < n && s.charCodeAt(j) === 36 && j > pos) {
|
|
5190
|
+
return s.slice(pos, j + 1);
|
|
5191
|
+
}
|
|
5192
|
+
if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
|
|
5193
|
+
return "$$";
|
|
5194
|
+
}
|
|
5195
|
+
return null;
|
|
5196
|
+
};
|
|
5197
|
+
while (i < n) {
|
|
5198
|
+
const ch = s.charCodeAt(i);
|
|
5199
|
+
if (mode === "normal") {
|
|
5200
|
+
if (ch === 39) {
|
|
5201
|
+
out += s[i];
|
|
5202
|
+
mode = "single";
|
|
5203
|
+
i++;
|
|
5204
|
+
continue;
|
|
5205
|
+
}
|
|
5206
|
+
if (ch === 34) {
|
|
5207
|
+
out += s[i];
|
|
5208
|
+
mode = "double";
|
|
5209
|
+
i++;
|
|
5210
|
+
continue;
|
|
5211
|
+
}
|
|
5212
|
+
if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
|
|
5213
|
+
out += "--";
|
|
5214
|
+
mode = "lineComment";
|
|
5215
|
+
i += 2;
|
|
5216
|
+
continue;
|
|
5217
|
+
}
|
|
5218
|
+
if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
|
|
5219
|
+
out += "/*";
|
|
5220
|
+
mode = "blockComment";
|
|
5221
|
+
i += 2;
|
|
5222
|
+
continue;
|
|
5223
|
+
}
|
|
5224
|
+
if (ch === 36) {
|
|
5225
|
+
const tag = readDollarTag(i);
|
|
5226
|
+
if (tag) {
|
|
5227
|
+
out += tag;
|
|
5228
|
+
mode = "dollar";
|
|
5229
|
+
dollarTag = tag;
|
|
5230
|
+
i += tag.length;
|
|
5231
|
+
continue;
|
|
5232
|
+
}
|
|
5233
|
+
let j = i + 1;
|
|
5234
|
+
if (j < n) {
|
|
5235
|
+
const c1 = s.charCodeAt(j);
|
|
5236
|
+
if (c1 >= 48 && c1 <= 57) {
|
|
5237
|
+
while (j < n) {
|
|
5238
|
+
const cj = s.charCodeAt(j);
|
|
5239
|
+
if (cj >= 48 && cj <= 57) {
|
|
5240
|
+
j++;
|
|
5241
|
+
continue;
|
|
5242
|
+
}
|
|
5243
|
+
break;
|
|
5244
|
+
}
|
|
5245
|
+
const numStr = s.slice(i + 1, j);
|
|
5246
|
+
const oldIndex = Number(numStr);
|
|
5247
|
+
if (!Number.isInteger(oldIndex) || oldIndex < 1) {
|
|
5248
|
+
throw new Error(`Invalid param placeholder: $${numStr}`);
|
|
5249
|
+
}
|
|
5250
|
+
out += replace(oldIndex);
|
|
5251
|
+
i = j;
|
|
5252
|
+
continue;
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
out += s[i];
|
|
5257
|
+
i++;
|
|
5258
|
+
continue;
|
|
5259
|
+
}
|
|
5260
|
+
if (mode === "single") {
|
|
5261
|
+
out += s[i];
|
|
5262
|
+
if (ch === 39) {
|
|
5263
|
+
if (i + 1 < n && s.charCodeAt(i + 1) === 39) {
|
|
5264
|
+
out += s[i + 1];
|
|
5265
|
+
i += 2;
|
|
5266
|
+
continue;
|
|
5267
|
+
}
|
|
5268
|
+
mode = "normal";
|
|
5269
|
+
i++;
|
|
5270
|
+
continue;
|
|
5271
|
+
}
|
|
5272
|
+
i++;
|
|
5273
|
+
continue;
|
|
5274
|
+
}
|
|
5275
|
+
if (mode === "double") {
|
|
5276
|
+
out += s[i];
|
|
5277
|
+
if (ch === 34) {
|
|
5278
|
+
if (i + 1 < n && s.charCodeAt(i + 1) === 34) {
|
|
5279
|
+
out += s[i + 1];
|
|
5280
|
+
i += 2;
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
mode = "normal";
|
|
5284
|
+
i++;
|
|
5285
|
+
continue;
|
|
5286
|
+
}
|
|
5287
|
+
i++;
|
|
5288
|
+
continue;
|
|
5289
|
+
}
|
|
5290
|
+
if (mode === "lineComment") {
|
|
5291
|
+
out += s[i];
|
|
5292
|
+
if (ch === 10) {
|
|
5293
|
+
mode = "normal";
|
|
5294
|
+
}
|
|
5295
|
+
i++;
|
|
5296
|
+
continue;
|
|
5297
|
+
}
|
|
5298
|
+
if (mode === "blockComment") {
|
|
5299
|
+
if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
|
|
5300
|
+
out += "*/";
|
|
5301
|
+
i += 2;
|
|
5302
|
+
mode = "normal";
|
|
5303
|
+
continue;
|
|
5304
|
+
}
|
|
5305
|
+
out += s[i];
|
|
5306
|
+
i++;
|
|
5307
|
+
continue;
|
|
5308
|
+
}
|
|
5309
|
+
if (mode === "dollar") {
|
|
5310
|
+
if (dollarTag && startsWith(i, dollarTag)) {
|
|
5311
|
+
out += dollarTag;
|
|
5312
|
+
i += dollarTag.length;
|
|
5313
|
+
mode = "normal";
|
|
5314
|
+
dollarTag = null;
|
|
5315
|
+
continue;
|
|
5316
|
+
}
|
|
5317
|
+
out += s[i];
|
|
5318
|
+
i++;
|
|
5319
|
+
continue;
|
|
5320
|
+
}
|
|
5321
|
+
out += s[i];
|
|
5322
|
+
i++;
|
|
5323
|
+
}
|
|
5324
|
+
return out;
|
|
5325
|
+
}
|
|
5326
|
+
function reindexParams(sql, params, offset) {
|
|
5327
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
5328
|
+
throw new Error(`Invalid param offset: ${offset}`);
|
|
5329
|
+
}
|
|
5330
|
+
const newParams = [];
|
|
5331
|
+
const paramMap = /* @__PURE__ */ new Map();
|
|
5332
|
+
const reindexed = replacePgPlaceholders(sql, (oldIndex) => {
|
|
5333
|
+
const existing = paramMap.get(oldIndex);
|
|
5334
|
+
if (existing !== void 0) return `$${existing}`;
|
|
5335
|
+
const pos = oldIndex - 1;
|
|
5336
|
+
if (pos >= params.length) {
|
|
5337
|
+
throw new Error(
|
|
5338
|
+
`Param placeholder $${oldIndex} exceeds params length (${params.length})`
|
|
5339
|
+
);
|
|
5340
|
+
}
|
|
5341
|
+
const newIndex = offset + newParams.length + 1;
|
|
5342
|
+
paramMap.set(oldIndex, newIndex);
|
|
5343
|
+
newParams.push(params[pos]);
|
|
5344
|
+
return `$${newIndex}`;
|
|
5345
|
+
});
|
|
5346
|
+
return { sql: reindexed, params: newParams };
|
|
5347
|
+
}
|
|
5348
|
+
function wrapQueryForMethod(method, cteName, resultAlias) {
|
|
5349
|
+
const outKey = quoteIdent(resultAlias);
|
|
5350
|
+
switch (method) {
|
|
5351
|
+
case "findMany":
|
|
5352
|
+
case "groupBy":
|
|
5353
|
+
return `(SELECT COALESCE(json_agg(row_to_json(t)), '[]'::json) FROM ${cteName} t) AS ${outKey}`;
|
|
5354
|
+
case "findFirst":
|
|
5355
|
+
case "findUnique":
|
|
5356
|
+
return `(SELECT row_to_json(t) FROM ${cteName} t LIMIT 1) AS ${outKey}`;
|
|
5357
|
+
case "count":
|
|
5358
|
+
return `(SELECT * FROM ${cteName}) AS ${outKey}`;
|
|
5359
|
+
case "aggregate":
|
|
5360
|
+
return `(SELECT row_to_json(t) FROM ${cteName} t) AS ${outKey}`;
|
|
5361
|
+
default:
|
|
5362
|
+
throw new Error(`Unsupported batch method: ${method}`);
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
function isAllCountQueries(queries, keys) {
|
|
5366
|
+
var _a;
|
|
5367
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5368
|
+
if (((_a = queries[keys[i]]) == null ? void 0 : _a.method) !== "count") return false;
|
|
5369
|
+
}
|
|
5370
|
+
return true;
|
|
5371
|
+
}
|
|
5372
|
+
function looksTooComplexForFilter(sql) {
|
|
5373
|
+
const s = sql.toLowerCase();
|
|
5374
|
+
if (s.includes(" group by ")) return true;
|
|
5375
|
+
if (s.includes(" having ")) return true;
|
|
5376
|
+
if (s.includes(" union ")) return true;
|
|
5377
|
+
if (s.includes(" intersect ")) return true;
|
|
5378
|
+
if (s.includes(" except ")) return true;
|
|
5379
|
+
if (s.includes(" window ")) return true;
|
|
5380
|
+
if (s.includes(" distinct ")) return true;
|
|
5381
|
+
return false;
|
|
5382
|
+
}
|
|
5383
|
+
function containsPgPlaceholder(sql) {
|
|
5384
|
+
return /\$[1-9][0-9]*/.test(sql);
|
|
5385
|
+
}
|
|
5386
|
+
function parseSimpleCountSql(sql) {
|
|
5387
|
+
const trimmed = sql.trim().replace(/;$/, "").trim();
|
|
5388
|
+
const lower = trimmed.toLowerCase();
|
|
5389
|
+
if (!lower.startsWith("select")) return null;
|
|
5390
|
+
if (!lower.includes("count(*)")) return null;
|
|
5391
|
+
if (looksTooComplexForFilter(trimmed)) return null;
|
|
5392
|
+
const match = trimmed.match(
|
|
5393
|
+
/^select\s+count\(\*\)(?:\s*::\s*[a-zA-Z0-9_\."]+)?\s+as\s+("[^"]+"|[a-zA-Z_][a-zA-Z0-9_\.]*)\s+from\s+([\s\S]+?)(?:\s+where\s+([\s\S]+))?$/i
|
|
5394
|
+
);
|
|
5395
|
+
if (!match) return null;
|
|
5396
|
+
const fromSql = match[2].trim();
|
|
5397
|
+
const whereSql = match[3] ? match[3].trim() : null;
|
|
5398
|
+
if (!fromSql) return null;
|
|
5399
|
+
return { fromSql, whereSql };
|
|
5400
|
+
}
|
|
5401
|
+
function buildMergedCountBatchSql(queries, keys, aliasesByKey, modelMap, models, dialect) {
|
|
5402
|
+
const modelGroups = /* @__PURE__ */ new Map();
|
|
5403
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5404
|
+
const key = keys[i];
|
|
5405
|
+
const q = queries[key];
|
|
5406
|
+
const alias = aliasesByKey.get(key);
|
|
5407
|
+
if (!alias) return null;
|
|
5408
|
+
if (!modelGroups.has(q.model)) modelGroups.set(q.model, []);
|
|
5409
|
+
modelGroups.get(q.model).push({ key, alias, args: q.args || {} });
|
|
5410
|
+
}
|
|
5411
|
+
const subqueries = [];
|
|
5412
|
+
let aliasIndex = 0;
|
|
5413
|
+
for (const [modelName, items] of modelGroups) {
|
|
5414
|
+
const model = modelMap.get(modelName);
|
|
5415
|
+
if (!model) return null;
|
|
5416
|
+
let sharedFrom = null;
|
|
5417
|
+
const expressions = [];
|
|
5418
|
+
const localParams = [];
|
|
5419
|
+
const localKeys = [];
|
|
5420
|
+
const localAliases = [];
|
|
5421
|
+
for (let i = 0; i < items.length; i++) {
|
|
5422
|
+
const { key, alias: alias2, args } = items[i];
|
|
5423
|
+
const built = buildSQLWithCache(model, models, "count", args, dialect);
|
|
5424
|
+
const parsed = parseSimpleCountSql(built.sql);
|
|
5425
|
+
if (!parsed) return null;
|
|
5426
|
+
if (containsPgPlaceholder(parsed.fromSql)) {
|
|
5427
|
+
return null;
|
|
5428
|
+
}
|
|
5429
|
+
if (!parsed.whereSql) {
|
|
5430
|
+
if (built.params.length > 0) return null;
|
|
5431
|
+
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5432
|
+
if (sharedFrom !== parsed.fromSql) return null;
|
|
5433
|
+
expressions.push(`count(*) AS ${quoteIdent(alias2)}`);
|
|
5434
|
+
localKeys.push(key);
|
|
5435
|
+
localAliases.push(alias2);
|
|
5436
|
+
continue;
|
|
5437
|
+
}
|
|
5438
|
+
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5439
|
+
if (sharedFrom !== parsed.fromSql) return null;
|
|
5440
|
+
const re = reindexParams(
|
|
5441
|
+
parsed.whereSql,
|
|
5442
|
+
built.params,
|
|
5443
|
+
localParams.length
|
|
5444
|
+
);
|
|
5445
|
+
for (let p = 0; p < re.params.length; p++) localParams.push(re.params[p]);
|
|
5446
|
+
expressions.push(
|
|
5447
|
+
`count(*) FILTER (WHERE ${re.sql}) AS ${quoteIdent(alias2)}`
|
|
5448
|
+
);
|
|
5449
|
+
localKeys.push(key);
|
|
5450
|
+
localAliases.push(alias2);
|
|
5451
|
+
}
|
|
5452
|
+
if (!sharedFrom) return null;
|
|
5453
|
+
const alias = `m_${aliasIndex++}`;
|
|
5454
|
+
const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
|
|
5455
|
+
subqueries.push({
|
|
5456
|
+
alias,
|
|
5457
|
+
sql: subSql,
|
|
5458
|
+
params: localParams,
|
|
5459
|
+
keys: localKeys,
|
|
5460
|
+
aliases: localAliases
|
|
5461
|
+
});
|
|
5462
|
+
}
|
|
5463
|
+
if (subqueries.length === 0) return null;
|
|
5464
|
+
let offset = 0;
|
|
5465
|
+
const rewrittenSubs = [];
|
|
5466
|
+
const finalParams = [];
|
|
5467
|
+
for (let i = 0; i < subqueries.length; i++) {
|
|
5468
|
+
const sq = subqueries[i];
|
|
5469
|
+
const re = reindexParams(sq.sql, sq.params, offset);
|
|
5470
|
+
offset += re.params.length;
|
|
5471
|
+
rewrittenSubs.push(re.sql);
|
|
5472
|
+
for (let p = 0; p < re.params.length; p++) finalParams.push(re.params[p]);
|
|
5473
|
+
}
|
|
5474
|
+
const selectParts = [];
|
|
5475
|
+
for (let i = 0; i < subqueries.length; i++) {
|
|
5476
|
+
const sq = subqueries[i];
|
|
5477
|
+
for (let k = 0; k < sq.aliases.length; k++) {
|
|
5478
|
+
const outAlias = sq.aliases[k];
|
|
5479
|
+
selectParts.push(
|
|
5480
|
+
`${sq.alias}.${quoteIdent(outAlias)} AS ${quoteIdent(outAlias)}`
|
|
5481
|
+
);
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
const fromSql = rewrittenSubs.join(" CROSS JOIN ");
|
|
5485
|
+
const sql = `SELECT ${selectParts.join(", ")} FROM ${fromSql}`;
|
|
5486
|
+
const aliases = keys.map((k) => aliasesByKey.get(k));
|
|
5487
|
+
return { sql, params: finalParams, keys, aliases };
|
|
5488
|
+
}
|
|
5489
|
+
function buildBatchSql(queries, modelMap, models, dialect) {
|
|
5490
|
+
const keys = Object.keys(queries);
|
|
5491
|
+
if (keys.length === 0) {
|
|
5492
|
+
throw new Error("buildBatchSql requires at least one query");
|
|
5493
|
+
}
|
|
5494
|
+
if (dialect !== "postgres") {
|
|
5495
|
+
throw new Error("Batch queries are only supported for postgres dialect");
|
|
5496
|
+
}
|
|
5497
|
+
const aliases = new Array(keys.length);
|
|
5498
|
+
const aliasesByKey = /* @__PURE__ */ new Map();
|
|
5499
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5500
|
+
const a = makeBatchAlias(i);
|
|
5501
|
+
aliases[i] = a;
|
|
5502
|
+
aliasesByKey.set(keys[i], a);
|
|
5503
|
+
}
|
|
5504
|
+
if (isAllCountQueries(queries, keys)) {
|
|
5505
|
+
const merged = buildMergedCountBatchSql(
|
|
5506
|
+
queries,
|
|
5507
|
+
keys,
|
|
5508
|
+
aliasesByKey,
|
|
5509
|
+
modelMap,
|
|
5510
|
+
models,
|
|
5511
|
+
dialect
|
|
5512
|
+
);
|
|
5513
|
+
if (merged) return merged;
|
|
5514
|
+
}
|
|
5515
|
+
const ctes = new Array(keys.length);
|
|
5516
|
+
const selects = new Array(keys.length);
|
|
5517
|
+
const allParams = [];
|
|
5518
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5519
|
+
const key = keys[i];
|
|
5520
|
+
const query = queries[key];
|
|
5521
|
+
const model = modelMap.get(query.model);
|
|
5522
|
+
if (!model) {
|
|
5523
|
+
throw new Error(
|
|
5524
|
+
`Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5525
|
+
);
|
|
5526
|
+
}
|
|
5527
|
+
const { sql: querySql, params: queryParams } = buildSQLWithCache(
|
|
5528
|
+
model,
|
|
5529
|
+
models,
|
|
5530
|
+
query.method,
|
|
5531
|
+
query.args || {},
|
|
5532
|
+
dialect
|
|
5533
|
+
);
|
|
5534
|
+
const { sql: reindexedSql, params: reindexedParams } = reindexParams(
|
|
5535
|
+
querySql,
|
|
5536
|
+
queryParams,
|
|
5537
|
+
allParams.length
|
|
5538
|
+
);
|
|
5539
|
+
for (let p = 0; p < reindexedParams.length; p++) {
|
|
5540
|
+
allParams.push(reindexedParams[p]);
|
|
5541
|
+
}
|
|
5542
|
+
const cteName = `batch_${i}`;
|
|
5543
|
+
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5544
|
+
selects[i] = wrapQueryForMethod(query.method, cteName, aliases[i]);
|
|
5545
|
+
}
|
|
5546
|
+
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5547
|
+
return { sql, params: allParams, keys, aliases };
|
|
5548
|
+
}
|
|
5549
|
+
function buildBatchCountSql(queries, modelMap, models, dialect) {
|
|
5550
|
+
if (queries.length === 0) {
|
|
5551
|
+
throw new Error("buildBatchCountSql requires at least one query");
|
|
5552
|
+
}
|
|
5553
|
+
if (dialect !== "postgres") {
|
|
5554
|
+
throw new Error(
|
|
5555
|
+
"Batch count queries are only supported for postgres dialect"
|
|
5556
|
+
);
|
|
5557
|
+
}
|
|
5558
|
+
const ctes = new Array(queries.length);
|
|
5559
|
+
const selects = new Array(queries.length);
|
|
5560
|
+
const allParams = [];
|
|
5561
|
+
for (let i = 0; i < queries.length; i++) {
|
|
5562
|
+
const query = queries[i];
|
|
5563
|
+
const model = modelMap.get(query.model);
|
|
5564
|
+
if (!model) {
|
|
5565
|
+
throw new Error(
|
|
5566
|
+
`Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5567
|
+
);
|
|
5568
|
+
}
|
|
5569
|
+
const { sql: querySql, params: queryParams } = buildSQLWithCache(
|
|
5570
|
+
model,
|
|
5571
|
+
models,
|
|
5572
|
+
"count",
|
|
5573
|
+
query.args || {},
|
|
5574
|
+
dialect
|
|
5575
|
+
);
|
|
5576
|
+
const { sql: reindexedSql, params: reindexedParams } = reindexParams(
|
|
5577
|
+
querySql,
|
|
5578
|
+
queryParams,
|
|
5579
|
+
allParams.length
|
|
5580
|
+
);
|
|
5581
|
+
for (let p = 0; p < reindexedParams.length; p++) {
|
|
5582
|
+
allParams.push(reindexedParams[p]);
|
|
5583
|
+
}
|
|
5584
|
+
const cteName = `count_${i}`;
|
|
5585
|
+
const resultKey = `count_${i}`;
|
|
5586
|
+
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5587
|
+
selects[i] = `(SELECT * FROM ${cteName}) AS ${quoteIdent(resultKey)}`;
|
|
5588
|
+
}
|
|
5589
|
+
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5590
|
+
return { sql, params: allParams };
|
|
5591
|
+
}
|
|
5592
|
+
function looksLikeJsonString(s) {
|
|
5593
|
+
const t = s.trim();
|
|
5594
|
+
if (t.length === 0) return false;
|
|
5595
|
+
const c0 = t.charCodeAt(0);
|
|
5596
|
+
const cN = t.charCodeAt(t.length - 1);
|
|
5597
|
+
if (c0 === 123 && cN === 125) return true;
|
|
5598
|
+
if (c0 === 91 && cN === 93) return true;
|
|
5599
|
+
if (t === "null" || t === "true" || t === "false") return true;
|
|
5600
|
+
return false;
|
|
5601
|
+
}
|
|
5602
|
+
function parseJsonValue(value) {
|
|
5603
|
+
if (typeof value !== "string") return value;
|
|
5604
|
+
if (!looksLikeJsonString(value)) return value;
|
|
5605
|
+
try {
|
|
5606
|
+
return JSON.parse(value);
|
|
5607
|
+
} catch (e) {
|
|
5608
|
+
return value;
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
function parseCountValue(value) {
|
|
5612
|
+
if (value === null || value === void 0) return 0;
|
|
5613
|
+
if (typeof value === "number") return value;
|
|
5614
|
+
if (typeof value === "string") {
|
|
5615
|
+
const n = Number.parseInt(value, 10);
|
|
5616
|
+
return Number.isFinite(n) ? n : 0;
|
|
5617
|
+
}
|
|
5618
|
+
if (typeof value === "object") {
|
|
5619
|
+
const obj = value;
|
|
5620
|
+
const countKey = Object.prototype.hasOwnProperty.call(obj, "count") ? "count" : Object.prototype.hasOwnProperty.call(obj, "_count") ? "_count" : Object.keys(obj).find((k) => k.endsWith("_count"));
|
|
5621
|
+
if (countKey !== void 0) {
|
|
5622
|
+
const v = obj[countKey];
|
|
5623
|
+
if (typeof v === "number") return v;
|
|
5624
|
+
if (typeof v === "string") {
|
|
5625
|
+
const n = Number.parseInt(v, 10);
|
|
5626
|
+
return Number.isFinite(n) ? n : 0;
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
return 0;
|
|
5631
|
+
}
|
|
5632
|
+
function parseBatchCountResults(row, count) {
|
|
5633
|
+
const results = [];
|
|
5634
|
+
for (let i = 0; i < count; i++) {
|
|
5635
|
+
const key = `count_${i}`;
|
|
5636
|
+
const value = row[key];
|
|
5637
|
+
results.push(parseCountValue(value));
|
|
5638
|
+
}
|
|
5639
|
+
return results;
|
|
5640
|
+
}
|
|
5641
|
+
function parseBatchResults(row, keys, queries, aliases) {
|
|
5642
|
+
const results = {};
|
|
5643
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5644
|
+
const key = keys[i];
|
|
5645
|
+
const columnKey = aliases && aliases[i] ? aliases[i] : key;
|
|
5646
|
+
const rawValue = row[columnKey];
|
|
5647
|
+
const query = queries[key];
|
|
5648
|
+
switch (query.method) {
|
|
5649
|
+
case "findMany": {
|
|
5650
|
+
const parsed = parseJsonValue(rawValue);
|
|
5651
|
+
results[key] = Array.isArray(parsed) ? parsed : [];
|
|
5652
|
+
break;
|
|
5653
|
+
}
|
|
5654
|
+
case "findFirst":
|
|
5655
|
+
case "findUnique": {
|
|
5656
|
+
const parsed = parseJsonValue(rawValue);
|
|
5657
|
+
results[key] = parsed != null ? parsed : null;
|
|
5658
|
+
break;
|
|
5659
|
+
}
|
|
5660
|
+
case "count": {
|
|
5661
|
+
results[key] = parseCountValue(rawValue);
|
|
5662
|
+
break;
|
|
5663
|
+
}
|
|
5664
|
+
case "aggregate": {
|
|
5665
|
+
const parsed = parseJsonValue(rawValue);
|
|
5666
|
+
const obj = parsed != null ? parsed : {};
|
|
5667
|
+
results[key] = transformQueryResults("aggregate", [obj]);
|
|
5668
|
+
break;
|
|
5669
|
+
}
|
|
5670
|
+
case "groupBy": {
|
|
5671
|
+
const parsed = parseJsonValue(rawValue);
|
|
5672
|
+
const arr = Array.isArray(parsed) ? parsed : [];
|
|
5673
|
+
results[key] = transformQueryResults("groupBy", arr);
|
|
5674
|
+
break;
|
|
5675
|
+
}
|
|
5676
|
+
default:
|
|
5677
|
+
results[key] = rawValue;
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
return results;
|
|
5681
|
+
}
|
|
5682
|
+
|
|
5683
|
+
// src/transaction.ts
|
|
5684
|
+
function isolationLevelToPostgresKeyword(level) {
|
|
5685
|
+
switch (level) {
|
|
5686
|
+
case "ReadCommitted":
|
|
5687
|
+
return "read committed";
|
|
5688
|
+
case "RepeatableRead":
|
|
5689
|
+
return "repeatable read";
|
|
5690
|
+
case "Serializable":
|
|
5691
|
+
return "serializable";
|
|
5692
|
+
default:
|
|
5693
|
+
return void 0;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
function createTransactionExecutor(deps) {
|
|
5697
|
+
const { modelMap, allModels, dialect, executeRaw, postgresClient } = deps;
|
|
5698
|
+
return {
|
|
5699
|
+
execute(queries, options) {
|
|
5700
|
+
return __async(this, null, function* () {
|
|
5701
|
+
if (queries.length === 0) return [];
|
|
5702
|
+
if (dialect !== "postgres") {
|
|
5703
|
+
throw new Error("$transaction is only supported for postgres dialect");
|
|
5704
|
+
}
|
|
5705
|
+
if (!postgresClient) {
|
|
5706
|
+
throw new Error("postgresClient is required for transactions");
|
|
5707
|
+
}
|
|
5708
|
+
const transactionCallback = (sql) => __async(null, null, function* () {
|
|
5709
|
+
const results = [];
|
|
5710
|
+
const isolationLevel = isolationLevelToPostgresKeyword(
|
|
5711
|
+
options == null ? void 0 : options.isolationLevel
|
|
5712
|
+
);
|
|
5713
|
+
if (isolationLevel) {
|
|
5714
|
+
yield sql.unsafe(
|
|
5715
|
+
`SET TRANSACTION ISOLATION LEVEL ${isolationLevel.toUpperCase()}`
|
|
5716
|
+
);
|
|
5717
|
+
}
|
|
5718
|
+
if (options == null ? void 0 : options.timeout) {
|
|
5719
|
+
yield sql.unsafe(
|
|
5720
|
+
`SET LOCAL statement_timeout = ${Math.floor(options.timeout)}`
|
|
5721
|
+
);
|
|
5722
|
+
}
|
|
5723
|
+
for (const q of queries) {
|
|
5724
|
+
const model = modelMap.get(q.model);
|
|
5725
|
+
if (!model) {
|
|
5726
|
+
throw new Error(
|
|
5727
|
+
`Model '${q.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5728
|
+
);
|
|
5729
|
+
}
|
|
5730
|
+
const { sql: sqlStr, params } = buildSQLWithCache(
|
|
5731
|
+
model,
|
|
5732
|
+
allModels,
|
|
5733
|
+
q.method,
|
|
5734
|
+
q.args || {},
|
|
5735
|
+
dialect
|
|
5736
|
+
);
|
|
5737
|
+
const rawResults = yield sql.unsafe(sqlStr, params);
|
|
5738
|
+
results.push(transformQueryResults(q.method, rawResults));
|
|
5739
|
+
}
|
|
5740
|
+
return results;
|
|
5741
|
+
});
|
|
5742
|
+
return yield postgresClient.begin(transactionCallback);
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5745
|
+
};
|
|
5746
|
+
}
|
|
5143
5747
|
var ACCELERATED_METHODS = /* @__PURE__ */ new Set([
|
|
5144
5748
|
"findMany",
|
|
5145
5749
|
"findFirst",
|
|
@@ -5156,10 +5760,15 @@ function executePostgres(client, sql, params) {
|
|
|
5156
5760
|
return yield client.unsafe(sql, params);
|
|
5157
5761
|
});
|
|
5158
5762
|
}
|
|
5159
|
-
function
|
|
5763
|
+
function shouldSqliteUseGet(method) {
|
|
5764
|
+
return method === "count" || method === "findFirst" || method === "findUnique" || method === "aggregate";
|
|
5765
|
+
}
|
|
5766
|
+
function executeSqlite(db, method, sql, params) {
|
|
5160
5767
|
const stmt = db.prepare(sql);
|
|
5161
|
-
if (
|
|
5162
|
-
|
|
5768
|
+
if (shouldSqliteUseGet(method)) {
|
|
5769
|
+
const row = stmt.get(...params);
|
|
5770
|
+
if (row === void 0) return [];
|
|
5771
|
+
return [row];
|
|
5163
5772
|
}
|
|
5164
5773
|
return stmt.all(...params);
|
|
5165
5774
|
}
|
|
@@ -5179,7 +5788,7 @@ function executeWithTiming(input) {
|
|
|
5179
5788
|
console.log("SQL:", sql);
|
|
5180
5789
|
console.log("Params:", params);
|
|
5181
5790
|
}
|
|
5182
|
-
const results = yield input.executeQuery(sql, params);
|
|
5791
|
+
const results = yield input.executeQuery(input.method, sql, params);
|
|
5183
5792
|
const duration = Date.now() - startTime;
|
|
5184
5793
|
(_a = input.onQuery) == null ? void 0 : _a.call(input, {
|
|
5185
5794
|
model: input.modelName,
|
|
@@ -5206,11 +5815,11 @@ function fallbackToPrisma(ctx, modelName, method, args) {
|
|
|
5206
5815
|
return ctx.$parent[modelName][method](args);
|
|
5207
5816
|
}
|
|
5208
5817
|
function createExecuteQuery(client, dialect) {
|
|
5209
|
-
return (sql, params) => __async(null, null, function* () {
|
|
5818
|
+
return (method, sql, params) => __async(null, null, function* () {
|
|
5210
5819
|
if (dialect === "postgres") {
|
|
5211
5820
|
return yield executePostgres(client, sql, params);
|
|
5212
5821
|
}
|
|
5213
|
-
return executeSqlite(client, sql, params);
|
|
5822
|
+
return executeSqlite(client, method, sql, params);
|
|
5214
5823
|
});
|
|
5215
5824
|
}
|
|
5216
5825
|
function logAcceleratedError(debug, dialect, modelName, method, error) {
|
|
@@ -5256,6 +5865,58 @@ function handleMethodCall(ctx, method, args, deps) {
|
|
|
5256
5865
|
}
|
|
5257
5866
|
});
|
|
5258
5867
|
}
|
|
5868
|
+
var DeferredQuery = class {
|
|
5869
|
+
constructor(model, method, args) {
|
|
5870
|
+
this.model = model;
|
|
5871
|
+
this.method = method;
|
|
5872
|
+
this.args = args;
|
|
5873
|
+
}
|
|
5874
|
+
then(onfulfilled, onrejected) {
|
|
5875
|
+
throw new Error(
|
|
5876
|
+
"Cannot await a batch query. Batch queries must not be awaited inside the $batch callback."
|
|
5877
|
+
);
|
|
5878
|
+
}
|
|
5879
|
+
};
|
|
5880
|
+
function createBatchProxy(modelMap, allowedModels) {
|
|
5881
|
+
return new Proxy(
|
|
5882
|
+
{},
|
|
5883
|
+
{
|
|
5884
|
+
get(_target, modelName) {
|
|
5885
|
+
if (typeof modelName === "symbol") return void 0;
|
|
5886
|
+
const model = modelMap.get(modelName);
|
|
5887
|
+
if (!model) {
|
|
5888
|
+
throw new Error(
|
|
5889
|
+
`Model '${modelName}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5890
|
+
);
|
|
5891
|
+
}
|
|
5892
|
+
if (allowedModels && !allowedModels.includes(modelName)) {
|
|
5893
|
+
throw new Error(
|
|
5894
|
+
`Model '${modelName}' not allowed. Allowed: ${allowedModels.join(", ")}`
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
return new Proxy(
|
|
5898
|
+
{},
|
|
5899
|
+
{
|
|
5900
|
+
get(_target2, method) {
|
|
5901
|
+
if (!ACCELERATED_METHODS.has(method)) {
|
|
5902
|
+
throw new Error(
|
|
5903
|
+
`Method '${method}' not supported in batch. Supported: ${[...ACCELERATED_METHODS].join(", ")}`
|
|
5904
|
+
);
|
|
5905
|
+
}
|
|
5906
|
+
return (args) => {
|
|
5907
|
+
return new DeferredQuery(
|
|
5908
|
+
modelName,
|
|
5909
|
+
method,
|
|
5910
|
+
args
|
|
5911
|
+
);
|
|
5912
|
+
};
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
);
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
);
|
|
5919
|
+
}
|
|
5259
5920
|
function speedExtension(config) {
|
|
5260
5921
|
const {
|
|
5261
5922
|
postgres,
|
|
@@ -5280,9 +5941,7 @@ function speedExtension(config) {
|
|
|
5280
5941
|
} else if (dmmf) {
|
|
5281
5942
|
models = convertDMMFToModels(dmmf.datamodel);
|
|
5282
5943
|
} else {
|
|
5283
|
-
throw new Error(
|
|
5284
|
-
'speedExtension requires either models or dmmf parameter.\n\n\u26A0\uFE0F RECOMMENDED APPROACH:\n Use the generated extension for zero runtime overhead:\n\n import { speedExtension } from "./generated/sql"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql })\n )\n\n 1. Add generator to schema.prisma:\n generator sql {\n provider = "prisma-sql-generator"\n }\n\n 2. Run: npx prisma generate\n\n 3. Import from generated file\n\n\u274C RUNTIME-ONLY MODE:\n If you cannot use the generator, provide models or dmmf:\n\n import { speedExtension } from "prisma-sql"\n import { MODELS } from "./generated/sql"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql, models: MODELS })\n )\n\n Or with DMMF (auto-converts on startup):\n\n import { Prisma } from "@prisma/client"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql, dmmf: Prisma.dmmf })\n )'
|
|
5285
|
-
);
|
|
5944
|
+
throw new Error("speedExtension requires either models or dmmf parameter.");
|
|
5286
5945
|
}
|
|
5287
5946
|
if (!Array.isArray(models) || models.length === 0) {
|
|
5288
5947
|
throw new Error("speedExtension: models array is empty or invalid");
|
|
@@ -5313,10 +5972,86 @@ function speedExtension(config) {
|
|
|
5313
5972
|
for (const method of ACCELERATED_METHODS) {
|
|
5314
5973
|
methodHandlers[method] = createMethodHandler(method);
|
|
5315
5974
|
}
|
|
5975
|
+
const executeRaw = (sql, params) => __async(null, null, function* () {
|
|
5976
|
+
if (dialect === "postgres") {
|
|
5977
|
+
return yield client.unsafe(sql, params);
|
|
5978
|
+
}
|
|
5979
|
+
throw new Error("Raw execution for sqlite not supported in transactions");
|
|
5980
|
+
});
|
|
5981
|
+
const txExecutor = createTransactionExecutor({
|
|
5982
|
+
modelMap,
|
|
5983
|
+
allModels: models,
|
|
5984
|
+
dialect,
|
|
5985
|
+
executeRaw,
|
|
5986
|
+
postgresClient: postgres
|
|
5987
|
+
});
|
|
5988
|
+
function batch(callback) {
|
|
5989
|
+
return __async(this, null, function* () {
|
|
5990
|
+
const batchProxy = createBatchProxy(modelMap, allowedModels);
|
|
5991
|
+
const queries = yield callback(batchProxy);
|
|
5992
|
+
const batchQueries = {};
|
|
5993
|
+
for (const [key, deferred] of Object.entries(queries)) {
|
|
5994
|
+
if (!(deferred instanceof DeferredQuery)) {
|
|
5995
|
+
throw new Error(
|
|
5996
|
+
`Batch query '${key}' must be a deferred query. Did you await it?`
|
|
5997
|
+
);
|
|
5998
|
+
}
|
|
5999
|
+
batchQueries[key] = {
|
|
6000
|
+
model: deferred.model,
|
|
6001
|
+
method: deferred.method,
|
|
6002
|
+
args: deferred.args || {}
|
|
6003
|
+
};
|
|
6004
|
+
}
|
|
6005
|
+
const startTime = Date.now();
|
|
6006
|
+
const { sql, params, keys, aliases } = buildBatchSql(
|
|
6007
|
+
batchQueries,
|
|
6008
|
+
modelMap,
|
|
6009
|
+
models,
|
|
6010
|
+
dialect
|
|
6011
|
+
);
|
|
6012
|
+
if (debug) {
|
|
6013
|
+
console.log(`[${dialect}] $batch (${keys.length} queries)`);
|
|
6014
|
+
console.log("SQL:", sql);
|
|
6015
|
+
console.log("Params:", params);
|
|
6016
|
+
}
|
|
6017
|
+
const rows = yield executeQuery("findMany", sql, params);
|
|
6018
|
+
const row = rows[0];
|
|
6019
|
+
const results = parseBatchResults(row, keys, batchQueries, aliases);
|
|
6020
|
+
const duration = Date.now() - startTime;
|
|
6021
|
+
onQuery == null ? void 0 : onQuery({
|
|
6022
|
+
model: "_batch",
|
|
6023
|
+
method: "batch",
|
|
6024
|
+
sql,
|
|
6025
|
+
params,
|
|
6026
|
+
duration
|
|
6027
|
+
});
|
|
6028
|
+
return results;
|
|
6029
|
+
});
|
|
6030
|
+
}
|
|
6031
|
+
function transaction(queries, options) {
|
|
6032
|
+
return __async(this, null, function* () {
|
|
6033
|
+
const startTime = Date.now();
|
|
6034
|
+
if (debug) {
|
|
6035
|
+
console.log(`[${dialect}] $transaction (${queries.length} queries)`);
|
|
6036
|
+
}
|
|
6037
|
+
const results = yield txExecutor.execute(queries, options);
|
|
6038
|
+
const duration = Date.now() - startTime;
|
|
6039
|
+
onQuery == null ? void 0 : onQuery({
|
|
6040
|
+
model: "_transaction",
|
|
6041
|
+
method: "transaction",
|
|
6042
|
+
sql: `TRANSACTION(${queries.length})`,
|
|
6043
|
+
params: [],
|
|
6044
|
+
duration
|
|
6045
|
+
});
|
|
6046
|
+
return results;
|
|
6047
|
+
});
|
|
6048
|
+
}
|
|
5316
6049
|
return prisma.$extends({
|
|
5317
6050
|
name: "prisma-sql-speed",
|
|
5318
6051
|
client: {
|
|
5319
|
-
$original: prisma
|
|
6052
|
+
$original: prisma,
|
|
6053
|
+
$batch: batch,
|
|
6054
|
+
$transaction: transaction
|
|
5320
6055
|
},
|
|
5321
6056
|
model: {
|
|
5322
6057
|
$allModels: methodHandlers
|
|
@@ -5324,6 +6059,10 @@ function speedExtension(config) {
|
|
|
5324
6059
|
});
|
|
5325
6060
|
};
|
|
5326
6061
|
}
|
|
6062
|
+
function extendPrisma(prisma, config) {
|
|
6063
|
+
const extension = speedExtension(config);
|
|
6064
|
+
return extension(prisma);
|
|
6065
|
+
}
|
|
5327
6066
|
function createToSQLFunction(models, dialect) {
|
|
5328
6067
|
if (!models || !Array.isArray(models) || models.length === 0) {
|
|
5329
6068
|
throw new Error("createToSQL requires non-empty models array");
|
|
@@ -5358,13 +6097,18 @@ function createPrismaSQL(config) {
|
|
|
5358
6097
|
throw new Error("createPrismaSQL: models array is empty or invalid");
|
|
5359
6098
|
}
|
|
5360
6099
|
const toSQL = createToSQLFunction(models, dialect);
|
|
6100
|
+
const modelMap = new Map(models.map((m) => [m.name, m]));
|
|
5361
6101
|
function query(_0, _1) {
|
|
5362
6102
|
return __async(this, arguments, function* (model, method, args = {}) {
|
|
5363
6103
|
const { sql, params } = toSQL(model, method, args);
|
|
5364
6104
|
return execute(client, sql, params);
|
|
5365
6105
|
});
|
|
5366
6106
|
}
|
|
5367
|
-
|
|
6107
|
+
function batchSql(queries) {
|
|
6108
|
+
const { sql, params } = buildBatchSql(queries, modelMap, models, dialect);
|
|
6109
|
+
return { sql, params };
|
|
6110
|
+
}
|
|
6111
|
+
return { toSQL, query, batchSql, client };
|
|
5368
6112
|
}
|
|
5369
6113
|
function generateSQL2(directive) {
|
|
5370
6114
|
return generateSQL(directive);
|
|
@@ -5406,6 +6150,6 @@ function generateSQLByModel(directives) {
|
|
|
5406
6150
|
return byModel;
|
|
5407
6151
|
}
|
|
5408
6152
|
|
|
5409
|
-
export { buildSQL, createPrismaSQL, createToSQL, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel, getGlobalDialect, setGlobalDialect, speedExtension, transformQueryResults };
|
|
6153
|
+
export { buildBatchCountSql, buildBatchSql, buildSQL, createPrismaSQL, createToSQL, createTransactionExecutor, extendPrisma, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel, getGlobalDialect, parseBatchCountResults, parseBatchResults, setGlobalDialect, speedExtension, transformQueryResults };
|
|
5410
6154
|
//# sourceMappingURL=index.js.map
|
|
5411
6155
|
//# sourceMappingURL=index.js.map
|