prisma-sql 1.58.0 → 1.59.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 +186 -229
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +186 -229
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +459 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.js +459 -233
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +0 -1
package/dist/generator.js
CHANGED
|
@@ -54,7 +54,7 @@ var require_package = __commonJS({
|
|
|
54
54
|
"package.json"(exports$1, module) {
|
|
55
55
|
module.exports = {
|
|
56
56
|
name: "prisma-sql",
|
|
57
|
-
version: "1.
|
|
57
|
+
version: "1.59.0",
|
|
58
58
|
description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
|
|
59
59
|
main: "dist/index.cjs",
|
|
60
60
|
module: "dist/index.js",
|
|
@@ -295,7 +295,8 @@ var REGEX_CACHE = {
|
|
|
295
295
|
var LIMITS = Object.freeze({
|
|
296
296
|
MAX_QUERY_DEPTH: 50,
|
|
297
297
|
MAX_ARRAY_SIZE: 1e4,
|
|
298
|
-
MAX_STRING_LENGTH: 1e4
|
|
298
|
+
MAX_STRING_LENGTH: 1e4,
|
|
299
|
+
MAX_HAVING_DEPTH: 50
|
|
299
300
|
});
|
|
300
301
|
|
|
301
302
|
// src/utils/normalize-value.ts
|
|
@@ -592,73 +593,6 @@ function createError(message, ctx, code = "VALIDATION_ERROR") {
|
|
|
592
593
|
return new SqlBuilderError(parts.join("\n"), code, ctx);
|
|
593
594
|
}
|
|
594
595
|
|
|
595
|
-
// src/builder/shared/model-field-cache.ts
|
|
596
|
-
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
597
|
-
function quote(id) {
|
|
598
|
-
const needsQuoting2 = !/^[a-z_][a-z0-9_]*$/.test(id) || /^(select|from|where|having|order|group|limit|offset|join|inner|left|right|outer|cross|full|and|or|not|by|as|on|union|intersect|except|case|when|then|else|end|user|users|table|column|index|values|in|like|between|is|exists|null|true|false|all|any|some|update|insert|delete|create|drop|alter|truncate|grant|revoke|exec|execute)$/i.test(
|
|
599
|
-
id
|
|
600
|
-
);
|
|
601
|
-
if (needsQuoting2) {
|
|
602
|
-
return `"${id.replace(/"/g, '""')}"`;
|
|
603
|
-
}
|
|
604
|
-
return id;
|
|
605
|
-
}
|
|
606
|
-
function ensureFullCache(model) {
|
|
607
|
-
let cache = MODEL_CACHE.get(model);
|
|
608
|
-
if (!cache) {
|
|
609
|
-
const fieldInfo = /* @__PURE__ */ new Map();
|
|
610
|
-
const scalarFields = /* @__PURE__ */ new Set();
|
|
611
|
-
const relationFields = /* @__PURE__ */ new Set();
|
|
612
|
-
const columnMap = /* @__PURE__ */ new Map();
|
|
613
|
-
const fieldByName = /* @__PURE__ */ new Map();
|
|
614
|
-
const quotedColumns = /* @__PURE__ */ new Map();
|
|
615
|
-
for (const f of model.fields) {
|
|
616
|
-
const info = {
|
|
617
|
-
name: f.name,
|
|
618
|
-
dbName: f.dbName || f.name,
|
|
619
|
-
type: f.type,
|
|
620
|
-
isRelation: !!f.isRelation,
|
|
621
|
-
isRequired: !!f.isRequired
|
|
622
|
-
};
|
|
623
|
-
fieldInfo.set(f.name, info);
|
|
624
|
-
fieldByName.set(f.name, f);
|
|
625
|
-
if (info.isRelation) {
|
|
626
|
-
relationFields.add(f.name);
|
|
627
|
-
} else {
|
|
628
|
-
scalarFields.add(f.name);
|
|
629
|
-
const dbName = info.dbName;
|
|
630
|
-
columnMap.set(f.name, dbName);
|
|
631
|
-
quotedColumns.set(f.name, quote(dbName));
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
cache = {
|
|
635
|
-
fieldInfo,
|
|
636
|
-
scalarFields,
|
|
637
|
-
relationFields,
|
|
638
|
-
columnMap,
|
|
639
|
-
fieldByName,
|
|
640
|
-
quotedColumns
|
|
641
|
-
};
|
|
642
|
-
MODEL_CACHE.set(model, cache);
|
|
643
|
-
}
|
|
644
|
-
return cache;
|
|
645
|
-
}
|
|
646
|
-
function getFieldInfo(model, fieldName) {
|
|
647
|
-
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
648
|
-
}
|
|
649
|
-
function getScalarFieldSet(model) {
|
|
650
|
-
return ensureFullCache(model).scalarFields;
|
|
651
|
-
}
|
|
652
|
-
function getRelationFieldSet(model) {
|
|
653
|
-
return ensureFullCache(model).relationFields;
|
|
654
|
-
}
|
|
655
|
-
function getColumnMap(model) {
|
|
656
|
-
return ensureFullCache(model).columnMap;
|
|
657
|
-
}
|
|
658
|
-
function getQuotedColumn(model, fieldName) {
|
|
659
|
-
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
596
|
// src/builder/shared/validators/sql-validators.ts
|
|
663
597
|
function isValidWhereClause(clause) {
|
|
664
598
|
return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
|
|
@@ -802,6 +736,78 @@ function needsQuoting(identifier) {
|
|
|
802
736
|
return false;
|
|
803
737
|
}
|
|
804
738
|
|
|
739
|
+
// src/builder/shared/model-field-cache.ts
|
|
740
|
+
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
741
|
+
function quoteIdent(id) {
|
|
742
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
743
|
+
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
744
|
+
}
|
|
745
|
+
if (/[\u0000-\u001F\u007F]/.test(id)) {
|
|
746
|
+
throw new Error(
|
|
747
|
+
`quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
if (needsQuoting(id)) {
|
|
751
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
752
|
+
}
|
|
753
|
+
return id;
|
|
754
|
+
}
|
|
755
|
+
function ensureFullCache(model) {
|
|
756
|
+
let cache = MODEL_CACHE.get(model);
|
|
757
|
+
if (!cache) {
|
|
758
|
+
const fieldInfo = /* @__PURE__ */ new Map();
|
|
759
|
+
const scalarFields = /* @__PURE__ */ new Set();
|
|
760
|
+
const relationFields = /* @__PURE__ */ new Set();
|
|
761
|
+
const columnMap = /* @__PURE__ */ new Map();
|
|
762
|
+
const fieldByName = /* @__PURE__ */ new Map();
|
|
763
|
+
const quotedColumns = /* @__PURE__ */ new Map();
|
|
764
|
+
for (const f of model.fields) {
|
|
765
|
+
const info = {
|
|
766
|
+
name: f.name,
|
|
767
|
+
dbName: f.dbName || f.name,
|
|
768
|
+
type: f.type,
|
|
769
|
+
isRelation: !!f.isRelation,
|
|
770
|
+
isRequired: !!f.isRequired
|
|
771
|
+
};
|
|
772
|
+
fieldInfo.set(f.name, info);
|
|
773
|
+
fieldByName.set(f.name, f);
|
|
774
|
+
if (info.isRelation) {
|
|
775
|
+
relationFields.add(f.name);
|
|
776
|
+
} else {
|
|
777
|
+
scalarFields.add(f.name);
|
|
778
|
+
const dbName = info.dbName;
|
|
779
|
+
columnMap.set(f.name, dbName);
|
|
780
|
+
quotedColumns.set(f.name, quoteIdent(dbName));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
cache = {
|
|
784
|
+
fieldInfo,
|
|
785
|
+
scalarFields,
|
|
786
|
+
relationFields,
|
|
787
|
+
columnMap,
|
|
788
|
+
fieldByName,
|
|
789
|
+
quotedColumns
|
|
790
|
+
};
|
|
791
|
+
MODEL_CACHE.set(model, cache);
|
|
792
|
+
}
|
|
793
|
+
return cache;
|
|
794
|
+
}
|
|
795
|
+
function getFieldInfo(model, fieldName) {
|
|
796
|
+
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
797
|
+
}
|
|
798
|
+
function getScalarFieldSet(model) {
|
|
799
|
+
return ensureFullCache(model).scalarFields;
|
|
800
|
+
}
|
|
801
|
+
function getRelationFieldSet(model) {
|
|
802
|
+
return ensureFullCache(model).relationFields;
|
|
803
|
+
}
|
|
804
|
+
function getColumnMap(model) {
|
|
805
|
+
return ensureFullCache(model).columnMap;
|
|
806
|
+
}
|
|
807
|
+
function getQuotedColumn(model, fieldName) {
|
|
808
|
+
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
809
|
+
}
|
|
810
|
+
|
|
805
811
|
// src/builder/shared/sql-utils.ts
|
|
806
812
|
function containsControlChars(s) {
|
|
807
813
|
return /[\u0000-\u001F\u007F]/.test(s);
|
|
@@ -944,7 +950,7 @@ function assertSafeQualifiedName(input) {
|
|
|
944
950
|
}
|
|
945
951
|
}
|
|
946
952
|
}
|
|
947
|
-
function
|
|
953
|
+
function quote(id) {
|
|
948
954
|
if (isEmptyString(id)) {
|
|
949
955
|
throw new Error("quote: identifier is required and cannot be empty");
|
|
950
956
|
}
|
|
@@ -964,13 +970,13 @@ function resolveColumnName(model, fieldName) {
|
|
|
964
970
|
return columnMap.get(fieldName) || fieldName;
|
|
965
971
|
}
|
|
966
972
|
function quoteColumn(model, fieldName) {
|
|
967
|
-
if (!model) return
|
|
973
|
+
if (!model) return quote(fieldName);
|
|
968
974
|
const cached = getQuotedColumn(model, fieldName);
|
|
969
|
-
return cached ||
|
|
975
|
+
return cached || quote(fieldName);
|
|
970
976
|
}
|
|
971
977
|
function col(alias, field, model) {
|
|
972
978
|
const columnName = model ? getColumnMap(model).get(field) || field : field;
|
|
973
|
-
const quoted = model ? getQuotedColumn(model, field) ||
|
|
979
|
+
const quoted = model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName);
|
|
974
980
|
return alias + "." + quoted;
|
|
975
981
|
}
|
|
976
982
|
function colWithAlias(alias, field, model) {
|
|
@@ -981,9 +987,9 @@ function colWithAlias(alias, field, model) {
|
|
|
981
987
|
throw new Error("colWithAlias: field is required and cannot be empty");
|
|
982
988
|
}
|
|
983
989
|
const columnName = resolveColumnName(model, field);
|
|
984
|
-
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) ||
|
|
990
|
+
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName));
|
|
985
991
|
if (columnName !== field) {
|
|
986
|
-
return columnRef + " AS " +
|
|
992
|
+
return columnRef + " AS " + quote(field);
|
|
987
993
|
}
|
|
988
994
|
return columnRef;
|
|
989
995
|
}
|
|
@@ -1006,7 +1012,7 @@ function buildTableReference(schemaName, tableName, dialect) {
|
|
|
1006
1012
|
}
|
|
1007
1013
|
const d = dialect != null ? dialect : "postgres";
|
|
1008
1014
|
if (d === "sqlite") {
|
|
1009
|
-
return
|
|
1015
|
+
return quote(tableName);
|
|
1010
1016
|
}
|
|
1011
1017
|
if (isEmptyString(schemaName)) {
|
|
1012
1018
|
throw new Error(
|
|
@@ -1671,6 +1677,7 @@ function buildOrderEntries(orderBy) {
|
|
|
1671
1677
|
}
|
|
1672
1678
|
return entries;
|
|
1673
1679
|
}
|
|
1680
|
+
var MAX_NOT_DEPTH = 50;
|
|
1674
1681
|
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1675
1682
|
const entries = Object.entries(val).filter(
|
|
1676
1683
|
([k, v]) => k !== "mode" && v !== void 0
|
|
@@ -1685,13 +1692,18 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
|
1685
1692
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1686
1693
|
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1687
1694
|
}
|
|
1688
|
-
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1695
|
+
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect, depth = 0) {
|
|
1689
1696
|
if (val === void 0) return "";
|
|
1697
|
+
if (depth > MAX_NOT_DEPTH) {
|
|
1698
|
+
throw new Error(
|
|
1699
|
+
`NOT operator nesting too deep (max ${MAX_NOT_DEPTH} levels). This usually indicates a circular reference or adversarial input.`
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1690
1702
|
if (val === null) {
|
|
1691
1703
|
return handleNullValue(expr, op);
|
|
1692
1704
|
}
|
|
1693
1705
|
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1694
|
-
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1706
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect, depth);
|
|
1695
1707
|
}
|
|
1696
1708
|
if (op === Ops.NOT) {
|
|
1697
1709
|
const placeholder = params.addAuto(val);
|
|
@@ -1737,7 +1749,7 @@ function normalizeMode(v) {
|
|
|
1737
1749
|
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1738
1750
|
return void 0;
|
|
1739
1751
|
}
|
|
1740
|
-
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1752
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, depth = 0) {
|
|
1741
1753
|
const innerMode = normalizeMode(val.mode);
|
|
1742
1754
|
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1743
1755
|
const entries = Object.entries(val).filter(
|
|
@@ -1754,7 +1766,8 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
|
1754
1766
|
params,
|
|
1755
1767
|
effectiveMode,
|
|
1756
1768
|
fieldType,
|
|
1757
|
-
void 0
|
|
1769
|
+
void 0,
|
|
1770
|
+
depth + 1
|
|
1758
1771
|
);
|
|
1759
1772
|
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1760
1773
|
}
|
|
@@ -1767,23 +1780,20 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
|
1767
1780
|
val,
|
|
1768
1781
|
params,
|
|
1769
1782
|
dialect,
|
|
1770
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(
|
|
1783
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(
|
|
1784
|
+
e,
|
|
1785
|
+
subOp,
|
|
1786
|
+
subVal,
|
|
1787
|
+
p,
|
|
1788
|
+
effectiveMode,
|
|
1789
|
+
fieldType,
|
|
1790
|
+
d,
|
|
1791
|
+
depth + 1
|
|
1792
|
+
),
|
|
1771
1793
|
` ${SQL_TEMPLATES.AND} `
|
|
1772
1794
|
);
|
|
1773
1795
|
}
|
|
1774
|
-
function buildDynamicLikePattern(op, placeholder
|
|
1775
|
-
if (dialect === "postgres") {
|
|
1776
|
-
switch (op) {
|
|
1777
|
-
case Ops.CONTAINS:
|
|
1778
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1779
|
-
case Ops.STARTS_WITH:
|
|
1780
|
-
return `(${placeholder} || '%')`;
|
|
1781
|
-
case Ops.ENDS_WITH:
|
|
1782
|
-
return `('%' || ${placeholder})`;
|
|
1783
|
-
default:
|
|
1784
|
-
return placeholder;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1796
|
+
function buildDynamicLikePattern(op, placeholder) {
|
|
1787
1797
|
switch (op) {
|
|
1788
1798
|
case Ops.CONTAINS:
|
|
1789
1799
|
return `('%' || ${placeholder} || '%')`;
|
|
@@ -1799,7 +1809,7 @@ function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
|
1799
1809
|
if (val === void 0) return "";
|
|
1800
1810
|
if (isDynamicParameter(val)) {
|
|
1801
1811
|
const placeholder2 = params.addAuto(val);
|
|
1802
|
-
const patternExpr = buildDynamicLikePattern(op, placeholder2
|
|
1812
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2);
|
|
1803
1813
|
if (mode === Modes.INSENSITIVE) {
|
|
1804
1814
|
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1805
1815
|
}
|
|
@@ -2085,7 +2095,7 @@ function freezeJoins(items) {
|
|
|
2085
2095
|
function isListRelation(fieldType) {
|
|
2086
2096
|
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
2087
2097
|
}
|
|
2088
|
-
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
2098
|
+
function buildToOneNullCheck(field, parentModel, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
2089
2099
|
const isLocal = field.isForeignKeyLocal === true;
|
|
2090
2100
|
const fkFields = normalizeKeyList(field.foreignKey);
|
|
2091
2101
|
if (isLocal) {
|
|
@@ -2095,8 +2105,7 @@ function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, want
|
|
|
2095
2105
|
});
|
|
2096
2106
|
}
|
|
2097
2107
|
const parts = fkFields.map((fk) => {
|
|
2098
|
-
const
|
|
2099
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
2108
|
+
const expr = `${parentAlias}.${quoteColumn(parentModel, fk)}`;
|
|
2100
2109
|
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
2101
2110
|
});
|
|
2102
2111
|
if (parts.length === 1) return parts[0];
|
|
@@ -2141,7 +2150,7 @@ function buildListRelationFilters(args) {
|
|
|
2141
2150
|
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
2142
2151
|
if (checkField) {
|
|
2143
2152
|
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
2144
|
-
const whereClause = `${relAlias}.${
|
|
2153
|
+
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
2145
2154
|
return Object.freeze({
|
|
2146
2155
|
clause: whereClause,
|
|
2147
2156
|
joins: freezeJoins([leftJoinSql])
|
|
@@ -2234,6 +2243,7 @@ function buildToOneRelationFilters(args) {
|
|
|
2234
2243
|
const wantNull = filterKey === "is";
|
|
2235
2244
|
const clause2 = buildToOneNullCheck(
|
|
2236
2245
|
field,
|
|
2246
|
+
ctx.model,
|
|
2237
2247
|
ctx.alias,
|
|
2238
2248
|
relTable,
|
|
2239
2249
|
relAlias,
|
|
@@ -2463,7 +2473,8 @@ function buildWhereInternal(where, ctx, builder) {
|
|
|
2463
2473
|
}
|
|
2464
2474
|
const allJoins = [];
|
|
2465
2475
|
const clauses = [];
|
|
2466
|
-
for (const
|
|
2476
|
+
for (const key in where) {
|
|
2477
|
+
const value = where[key];
|
|
2467
2478
|
if (value === void 0) continue;
|
|
2468
2479
|
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2469
2480
|
appendResult(result, clauses, allJoins);
|
|
@@ -2713,8 +2724,6 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2713
2724
|
}
|
|
2714
2725
|
let dirty = true;
|
|
2715
2726
|
let cachedSnapshot = null;
|
|
2716
|
-
let frozenParams = null;
|
|
2717
|
-
let frozenMappings = null;
|
|
2718
2727
|
function assertCanAdd() {
|
|
2719
2728
|
if (index > MAX_PARAM_INDEX) {
|
|
2720
2729
|
throw new Error(
|
|
@@ -2766,17 +2775,13 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2766
2775
|
}
|
|
2767
2776
|
function snapshot() {
|
|
2768
2777
|
if (!dirty && cachedSnapshot) return cachedSnapshot;
|
|
2769
|
-
if (!frozenParams) frozenParams = Object.freeze(params.slice());
|
|
2770
|
-
if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
|
|
2771
2778
|
const snap = {
|
|
2772
2779
|
index,
|
|
2773
|
-
params:
|
|
2774
|
-
mappings:
|
|
2780
|
+
params: params.slice(),
|
|
2781
|
+
mappings: mappings.slice()
|
|
2775
2782
|
};
|
|
2776
2783
|
cachedSnapshot = snap;
|
|
2777
2784
|
dirty = false;
|
|
2778
|
-
frozenParams = null;
|
|
2779
|
-
frozenMappings = null;
|
|
2780
2785
|
return snap;
|
|
2781
2786
|
}
|
|
2782
2787
|
return {
|
|
@@ -3706,7 +3711,7 @@ function buildDistinctColumns(distinct, fromAlias, model) {
|
|
|
3706
3711
|
}
|
|
3707
3712
|
function buildOutputColumns(scalarNames, includeNames, hasCount) {
|
|
3708
3713
|
const outputCols = hasCount ? [...scalarNames, ...includeNames, "_count"] : [...scalarNames, ...includeNames];
|
|
3709
|
-
const formatted = outputCols.map((n) =>
|
|
3714
|
+
const formatted = outputCols.map((n) => quote(n)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3710
3715
|
if (!isNonEmptyString(formatted)) {
|
|
3711
3716
|
throw new Error("distinct emulation requires at least one output column");
|
|
3712
3717
|
}
|
|
@@ -3802,7 +3807,7 @@ function buildIncludeColumns(spec) {
|
|
|
3802
3807
|
dialect
|
|
3803
3808
|
);
|
|
3804
3809
|
if (countBuild.jsonPairs) {
|
|
3805
|
-
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " +
|
|
3810
|
+
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " + quote("_count");
|
|
3806
3811
|
}
|
|
3807
3812
|
countJoins = countBuild.joins;
|
|
3808
3813
|
}
|
|
@@ -3815,7 +3820,7 @@ function buildIncludeColumns(spec) {
|
|
|
3815
3820
|
const emptyJson = dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3816
3821
|
const includeCols = hasIncludes ? includes.map((inc) => {
|
|
3817
3822
|
const expr = inc.isOneToOne ? "(" + inc.sql + ")" : "COALESCE((" + inc.sql + "), " + emptyJson + ")";
|
|
3818
|
-
return expr + " " + SQL_TEMPLATES.AS + " " +
|
|
3823
|
+
return expr + " " + SQL_TEMPLATES.AS + " " + quote(inc.name);
|
|
3819
3824
|
}).join(SQL_SEPARATORS.FIELD_LIST) : "";
|
|
3820
3825
|
const allCols = joinNonEmpty(
|
|
3821
3826
|
[includeCols, countCols],
|
|
@@ -4180,6 +4185,7 @@ function buildComparisons(expr, filter, params, dialect, builder, excludeKeys =
|
|
|
4180
4185
|
}
|
|
4181
4186
|
|
|
4182
4187
|
// src/builder/aggregates.ts
|
|
4188
|
+
var MAX_NOT_DEPTH2 = 50;
|
|
4183
4189
|
var AGGREGATES = [
|
|
4184
4190
|
["_sum", "SUM"],
|
|
4185
4191
|
["_avg", "AVG"],
|
|
@@ -4294,8 +4300,13 @@ function buildBinaryComparison(expr, op, val, params) {
|
|
|
4294
4300
|
const placeholder = addHavingParam(params, op, val);
|
|
4295
4301
|
return expr + " " + sqlOp + " " + placeholder;
|
|
4296
4302
|
}
|
|
4297
|
-
function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
4303
|
+
function buildSimpleComparison(expr, op, val, params, dialect, depth = 0) {
|
|
4298
4304
|
assertHavingOp(op);
|
|
4305
|
+
if (depth > MAX_NOT_DEPTH2) {
|
|
4306
|
+
throw new Error(
|
|
4307
|
+
`NOT operator nesting too deep in HAVING (max ${MAX_NOT_DEPTH2} levels).`
|
|
4308
|
+
);
|
|
4309
|
+
}
|
|
4299
4310
|
if (val === null) return buildNullComparison(expr, op);
|
|
4300
4311
|
if (op === Ops.NOT && isPlainObject(val)) {
|
|
4301
4312
|
return buildNotComposite(
|
|
@@ -4303,7 +4314,7 @@ function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
|
4303
4314
|
val,
|
|
4304
4315
|
params,
|
|
4305
4316
|
dialect,
|
|
4306
|
-
buildSimpleComparison,
|
|
4317
|
+
(e, subOp, subVal, p, d) => buildSimpleComparison(e, subOp, subVal, p, d, depth + 1),
|
|
4307
4318
|
SQL_SEPARATORS.CONDITION_AND
|
|
4308
4319
|
);
|
|
4309
4320
|
}
|
|
@@ -4320,23 +4331,36 @@ function combineLogical(key, subClauses) {
|
|
|
4320
4331
|
if (key === LogicalOps.NOT) return negateClauses(subClauses);
|
|
4321
4332
|
return subClauses.join(" " + key + " ");
|
|
4322
4333
|
}
|
|
4323
|
-
function buildHavingNode(node, alias, params, dialect, model) {
|
|
4334
|
+
function buildHavingNode(node, alias, params, dialect, model, depth = 0) {
|
|
4335
|
+
if (depth > LIMITS.MAX_HAVING_DEPTH) {
|
|
4336
|
+
throw new Error(
|
|
4337
|
+
`HAVING clause nesting too deep (max ${LIMITS.MAX_HAVING_DEPTH} levels). This usually indicates a circular reference.`
|
|
4338
|
+
);
|
|
4339
|
+
}
|
|
4324
4340
|
const clauses = [];
|
|
4325
4341
|
for (const key in node) {
|
|
4326
4342
|
if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
|
|
4327
4343
|
const value = node[key];
|
|
4328
|
-
const built = buildHavingEntry(
|
|
4344
|
+
const built = buildHavingEntry(
|
|
4345
|
+
key,
|
|
4346
|
+
value,
|
|
4347
|
+
alias,
|
|
4348
|
+
params,
|
|
4349
|
+
dialect,
|
|
4350
|
+
model,
|
|
4351
|
+
depth
|
|
4352
|
+
);
|
|
4329
4353
|
for (const c of built) {
|
|
4330
4354
|
if (c && c.length > 0) clauses.push(c);
|
|
4331
4355
|
}
|
|
4332
4356
|
}
|
|
4333
4357
|
return clauses.join(SQL_SEPARATORS.CONDITION_AND);
|
|
4334
4358
|
}
|
|
4335
|
-
function buildLogicalClause2(key, value, alias, params, dialect, model) {
|
|
4359
|
+
function buildLogicalClause2(key, value, alias, params, dialect, model, depth = 0) {
|
|
4336
4360
|
const items = normalizeLogicalValue2(key, value);
|
|
4337
4361
|
const subClauses = [];
|
|
4338
4362
|
for (const it of items) {
|
|
4339
|
-
const c = buildHavingNode(it, alias, params, dialect, model);
|
|
4363
|
+
const c = buildHavingNode(it, alias, params, dialect, model, depth + 1);
|
|
4340
4364
|
if (c && c.length > 0) subClauses.push("(" + c + ")");
|
|
4341
4365
|
}
|
|
4342
4366
|
if (subClauses.length === 0) return "";
|
|
@@ -4395,7 +4419,7 @@ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect
|
|
|
4395
4419
|
}
|
|
4396
4420
|
return out;
|
|
4397
4421
|
}
|
|
4398
|
-
function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
4422
|
+
function buildHavingEntry(key, value, alias, params, dialect, model, depth = 0) {
|
|
4399
4423
|
if (isLogicalKey(key)) {
|
|
4400
4424
|
const logical = buildLogicalClause2(
|
|
4401
4425
|
key,
|
|
@@ -4403,7 +4427,8 @@ function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
|
4403
4427
|
alias,
|
|
4404
4428
|
params,
|
|
4405
4429
|
dialect,
|
|
4406
|
-
model
|
|
4430
|
+
model,
|
|
4431
|
+
depth
|
|
4407
4432
|
);
|
|
4408
4433
|
return logical ? [logical] : [];
|
|
4409
4434
|
}
|
|
@@ -4430,7 +4455,7 @@ function buildHavingClause(having, alias, params, model, dialect) {
|
|
|
4430
4455
|
if (!isNotNullish(having)) return "";
|
|
4431
4456
|
const d = dialect != null ? dialect : getGlobalDialect();
|
|
4432
4457
|
if (!isPlainObject(having)) throw new Error("having must be an object");
|
|
4433
|
-
return buildHavingNode(having, alias, params, d, model);
|
|
4458
|
+
return buildHavingNode(having, alias, params, d, model, 0);
|
|
4434
4459
|
}
|
|
4435
4460
|
function normalizeCountArg(v) {
|
|
4436
4461
|
if (!isNotNullish(v)) return void 0;
|
|
@@ -4440,13 +4465,13 @@ function normalizeCountArg(v) {
|
|
|
4440
4465
|
}
|
|
4441
4466
|
function pushCountAllField(fields) {
|
|
4442
4467
|
fields.push(
|
|
4443
|
-
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " +
|
|
4468
|
+
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " + quote("_count._all")
|
|
4444
4469
|
);
|
|
4445
4470
|
}
|
|
4446
4471
|
function pushCountField(fields, alias, fieldName, model) {
|
|
4447
4472
|
const outAlias = "_count." + fieldName;
|
|
4448
4473
|
fields.push(
|
|
4449
|
-
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4474
|
+
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4450
4475
|
);
|
|
4451
4476
|
}
|
|
4452
4477
|
function addCountFields(fields, countArg, alias, model) {
|
|
@@ -4483,7 +4508,7 @@ function assertAggregatableScalarField(model, agg, fieldName) {
|
|
|
4483
4508
|
function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model) {
|
|
4484
4509
|
const outAlias = agg + "." + fieldName;
|
|
4485
4510
|
fields.push(
|
|
4486
|
-
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4511
|
+
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4487
4512
|
);
|
|
4488
4513
|
}
|
|
4489
4514
|
function addAggregateFields(fields, args, alias, model) {
|
|
@@ -4567,7 +4592,7 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4567
4592
|
assertSafeAlias(alias);
|
|
4568
4593
|
assertSafeTableRef(tableName);
|
|
4569
4594
|
const byFields = assertGroupByBy(args, model);
|
|
4570
|
-
const d = getGlobalDialect();
|
|
4595
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
4571
4596
|
const params = createParamStore(whereResult.nextParamIndex);
|
|
4572
4597
|
const { groupFields, selectFields } = buildGroupBySelectParts(
|
|
4573
4598
|
args,
|
|
@@ -4630,7 +4655,7 @@ function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
|
4630
4655
|
SQL_TEMPLATES.SELECT,
|
|
4631
4656
|
SQL_TEMPLATES.COUNT_ALL,
|
|
4632
4657
|
SQL_TEMPLATES.AS,
|
|
4633
|
-
|
|
4658
|
+
quote("_count._all"),
|
|
4634
4659
|
SQL_TEMPLATES.FROM,
|
|
4635
4660
|
tableName,
|
|
4636
4661
|
alias
|
|
@@ -4671,15 +4696,21 @@ function buildSqlResult(args) {
|
|
|
4671
4696
|
return buildAggregateSql(processed, whereResult, tableName, alias, modelDef);
|
|
4672
4697
|
}
|
|
4673
4698
|
if (method === "groupBy") {
|
|
4674
|
-
return buildGroupBySql(
|
|
4699
|
+
return buildGroupBySql(
|
|
4700
|
+
processed,
|
|
4701
|
+
whereResult,
|
|
4702
|
+
tableName,
|
|
4703
|
+
alias,
|
|
4704
|
+
modelDef,
|
|
4705
|
+
dialect
|
|
4706
|
+
);
|
|
4675
4707
|
}
|
|
4676
4708
|
if (method === "count") {
|
|
4677
4709
|
return buildCountSql(
|
|
4678
4710
|
whereResult,
|
|
4679
4711
|
tableName,
|
|
4680
4712
|
alias,
|
|
4681
|
-
processed.skip
|
|
4682
|
-
);
|
|
4713
|
+
processed.skip);
|
|
4683
4714
|
}
|
|
4684
4715
|
return buildSelectSql({
|
|
4685
4716
|
method,
|
|
@@ -4719,22 +4750,23 @@ function normalizeSqlAndMappingsForDialect(sql, paramMappings, dialect) {
|
|
|
4719
4750
|
}
|
|
4720
4751
|
function buildParamsFromMappings(mappings) {
|
|
4721
4752
|
const sorted = [...mappings].sort((a, b) => a.index - b.index);
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4753
|
+
const staticParams = [];
|
|
4754
|
+
const dynamicKeys = [];
|
|
4755
|
+
let paramOrder = "";
|
|
4756
|
+
for (const m of sorted) {
|
|
4757
|
+
if (m.dynamicName !== void 0) {
|
|
4758
|
+
dynamicKeys.push(m.dynamicName);
|
|
4759
|
+
paramOrder += "d";
|
|
4760
|
+
} else if (m.value !== void 0) {
|
|
4761
|
+
staticParams.push(m.value);
|
|
4762
|
+
paramOrder += "s";
|
|
4763
|
+
} else {
|
|
4732
4764
|
throw new Error(
|
|
4733
4765
|
`CRITICAL: ParamMap ${m.index} has neither dynamicName nor value`
|
|
4734
4766
|
);
|
|
4735
|
-
}
|
|
4736
|
-
|
|
4737
|
-
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
return { staticParams, dynamicKeys, paramOrder };
|
|
4738
4770
|
}
|
|
4739
4771
|
function resolveModelContext(directive) {
|
|
4740
4772
|
const { model, datamodel } = directive.context;
|
|
@@ -4800,12 +4832,13 @@ function finalizeDirective(args) {
|
|
|
4800
4832
|
return (_a = m.value) != null ? _a : void 0;
|
|
4801
4833
|
});
|
|
4802
4834
|
validateParamConsistencyByDialect(normalizedSql, params, dialect);
|
|
4803
|
-
const { staticParams, dynamicKeys } = buildParamsFromMappings(normalizedMappings);
|
|
4835
|
+
const { staticParams, dynamicKeys, paramOrder } = buildParamsFromMappings(normalizedMappings);
|
|
4804
4836
|
return {
|
|
4805
4837
|
method: directive.method,
|
|
4806
4838
|
sql: normalizedSql,
|
|
4807
4839
|
staticParams,
|
|
4808
4840
|
dynamicKeys,
|
|
4841
|
+
paramOrder,
|
|
4809
4842
|
paramMappings: normalizedMappings,
|
|
4810
4843
|
originalDirective: directive
|
|
4811
4844
|
};
|
|
@@ -4944,6 +4977,7 @@ function generateCode(models, queries, dialect, datamodel) {
|
|
|
4944
4977
|
const { mappings, fieldTypes } = extractEnumMappings(datamodel);
|
|
4945
4978
|
return `// Generated by @prisma-sql/generator - DO NOT EDIT
|
|
4946
4979
|
import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type BatchCountQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
|
|
4980
|
+
import { normalizeValue } from 'prisma-sql/utils/normalize-value'
|
|
4947
4981
|
|
|
4948
4982
|
class DeferredQuery {
|
|
4949
4983
|
constructor(
|
|
@@ -4985,14 +5019,12 @@ function createBatchProxy(): BatchProxy {
|
|
|
4985
5019
|
{
|
|
4986
5020
|
get(_target, modelName: string): any {
|
|
4987
5021
|
if (typeof modelName === 'symbol') return undefined
|
|
4988
|
-
|
|
4989
5022
|
const model = MODEL_MAP.get(modelName)
|
|
4990
5023
|
if (!model) {
|
|
4991
5024
|
throw new Error(
|
|
4992
5025
|
\`Model '\${modelName}' not found. Available: \${[...MODEL_MAP.keys()].join(', ')}\`,
|
|
4993
5026
|
)
|
|
4994
5027
|
}
|
|
4995
|
-
|
|
4996
5028
|
return new Proxy(
|
|
4997
5029
|
{},
|
|
4998
5030
|
{
|
|
@@ -5002,7 +5034,6 @@ function createBatchProxy(): BatchProxy {
|
|
|
5002
5034
|
\`Method '\${method}' not supported in batch. Supported: \${[...ACCELERATED_METHODS].join(', ')}\`,
|
|
5003
5035
|
)
|
|
5004
5036
|
}
|
|
5005
|
-
|
|
5006
5037
|
return (args?: any): DeferredQuery => {
|
|
5007
5038
|
return new DeferredQuery(
|
|
5008
5039
|
modelName,
|
|
@@ -5018,52 +5049,6 @@ function createBatchProxy(): BatchProxy {
|
|
|
5018
5049
|
) as BatchProxy
|
|
5019
5050
|
}
|
|
5020
5051
|
|
|
5021
|
-
function normalizeValue(value: unknown, seen = new WeakSet<object>(), depth = 0): unknown {
|
|
5022
|
-
const MAX_DEPTH = 20
|
|
5023
|
-
if (depth > MAX_DEPTH) {
|
|
5024
|
-
throw new Error(\`Max normalization depth exceeded (\${MAX_DEPTH} levels)\`)
|
|
5025
|
-
}
|
|
5026
|
-
if (value instanceof Date) {
|
|
5027
|
-
const t = value.getTime()
|
|
5028
|
-
if (!Number.isFinite(t)) {
|
|
5029
|
-
throw new Error('Invalid Date value in SQL params')
|
|
5030
|
-
}
|
|
5031
|
-
return value.toISOString()
|
|
5032
|
-
}
|
|
5033
|
-
if (typeof value === 'bigint') {
|
|
5034
|
-
return value.toString()
|
|
5035
|
-
}
|
|
5036
|
-
if (Array.isArray(value)) {
|
|
5037
|
-
const arrRef = value as unknown as object
|
|
5038
|
-
if (seen.has(arrRef)) {
|
|
5039
|
-
throw new Error('Circular reference in SQL params')
|
|
5040
|
-
}
|
|
5041
|
-
seen.add(arrRef)
|
|
5042
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1))
|
|
5043
|
-
seen.delete(arrRef)
|
|
5044
|
-
return out
|
|
5045
|
-
}
|
|
5046
|
-
if (value && typeof value === 'object') {
|
|
5047
|
-
if (value instanceof Uint8Array) return value
|
|
5048
|
-
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) return value
|
|
5049
|
-
const proto = Object.getPrototypeOf(value)
|
|
5050
|
-
const isPlain = proto === Object.prototype || proto === null
|
|
5051
|
-
if (!isPlain) return value
|
|
5052
|
-
const obj = value as Record<string, unknown>
|
|
5053
|
-
if (seen.has(obj)) {
|
|
5054
|
-
throw new Error('Circular reference in SQL params')
|
|
5055
|
-
}
|
|
5056
|
-
seen.add(obj)
|
|
5057
|
-
const out: Record<string, unknown> = {}
|
|
5058
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
5059
|
-
out[k] = normalizeValue(v, seen, depth + 1)
|
|
5060
|
-
}
|
|
5061
|
-
seen.delete(obj)
|
|
5062
|
-
return out
|
|
5063
|
-
}
|
|
5064
|
-
return value
|
|
5065
|
-
}
|
|
5066
|
-
|
|
5067
5052
|
function normalizeParams(params: unknown[]): unknown[] {
|
|
5068
5053
|
return params.map(p => normalizeValue(p))
|
|
5069
5054
|
}
|
|
@@ -5153,19 +5138,16 @@ function transformEnumValuesByModel(modelName: string, obj: any, path: string[]
|
|
|
5153
5138
|
if (typeof obj === 'object') {
|
|
5154
5139
|
const transformed: any = {}
|
|
5155
5140
|
const modelFields = ENUM_FIELDS[modelName] || {}
|
|
5156
|
-
|
|
5157
5141
|
for (const [key, value] of Object.entries(obj)) {
|
|
5158
5142
|
const nextPath = [...path, key]
|
|
5159
5143
|
|
|
5160
5144
|
const relModel = getRelatedModelName(modelName, key)
|
|
5161
|
-
|
|
5162
5145
|
if (relModel && value && typeof value === 'object') {
|
|
5163
5146
|
transformed[key] = transformEnumValuesByModel(relModel, value, nextPath)
|
|
5164
5147
|
continue
|
|
5165
5148
|
}
|
|
5166
5149
|
|
|
5167
5150
|
const enumType = modelFields[key]
|
|
5168
|
-
|
|
5169
5151
|
if (enumType) {
|
|
5170
5152
|
if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
|
5171
5153
|
const transformedOperators: any = {}
|
|
@@ -5184,25 +5166,27 @@ function transformEnumValuesByModel(modelName: string, obj: any, path: string[]
|
|
|
5184
5166
|
transformed[key] = value
|
|
5185
5167
|
}
|
|
5186
5168
|
}
|
|
5187
|
-
|
|
5188
5169
|
return transformed
|
|
5189
5170
|
}
|
|
5190
5171
|
|
|
5191
5172
|
return obj
|
|
5192
5173
|
}
|
|
5193
5174
|
|
|
5175
|
+
function bigIntSafeReplacer(_key: string, value: unknown): unknown {
|
|
5176
|
+
if (typeof value === 'bigint') return '__bigint__' + value.toString()
|
|
5177
|
+
return value
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5194
5180
|
function normalizeQuery(args: any): string {
|
|
5195
5181
|
if (!args) return '{}'
|
|
5196
|
-
|
|
5197
|
-
const normalized = JSON.parse(
|
|
5182
|
+
const jsonStr = JSON.stringify(args, bigIntSafeReplacer)
|
|
5183
|
+
const normalized = JSON.parse(jsonStr)
|
|
5198
5184
|
|
|
5199
5185
|
function replaceDynamicParams(obj: any, path: string[] = []): any {
|
|
5200
5186
|
if (!obj || typeof obj !== 'object') return obj
|
|
5201
|
-
|
|
5202
5187
|
if (Array.isArray(obj)) {
|
|
5203
5188
|
return obj.map((v) => replaceDynamicParams(v, path))
|
|
5204
5189
|
}
|
|
5205
|
-
|
|
5206
5190
|
const result: any = {}
|
|
5207
5191
|
for (const [key, value] of Object.entries(obj)) {
|
|
5208
5192
|
if (isDynamicKeyForQueryKey(path, key)) {
|
|
@@ -5218,7 +5202,6 @@ function normalizeQuery(args: any): string {
|
|
|
5218
5202
|
|
|
5219
5203
|
function removeEmptyObjects(obj: any): any {
|
|
5220
5204
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj
|
|
5221
|
-
|
|
5222
5205
|
const result: any = {}
|
|
5223
5206
|
for (const [key, value] of Object.entries(obj)) {
|
|
5224
5207
|
if (value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
@@ -5232,6 +5215,7 @@ function normalizeQuery(args: any): string {
|
|
|
5232
5215
|
const cleaned = removeEmptyObjects(withMarkers)
|
|
5233
5216
|
|
|
5234
5217
|
return JSON.stringify(cleaned, (key, value) => {
|
|
5218
|
+
if (typeof value === 'bigint') return '__bigint__' + value.toString()
|
|
5235
5219
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
5236
5220
|
const sorted: Record<string, unknown> = {}
|
|
5237
5221
|
for (const k of Object.keys(value).sort()) {
|
|
@@ -5245,37 +5229,28 @@ function normalizeQuery(args: any): string {
|
|
|
5245
5229
|
|
|
5246
5230
|
function extractDynamicParams(args: any, dynamicKeys: string[]): unknown[] {
|
|
5247
5231
|
const params: unknown[] = []
|
|
5248
|
-
|
|
5249
5232
|
for (const key of dynamicKeys) {
|
|
5250
5233
|
const parts = key.split(':')
|
|
5251
5234
|
const lookupKey = parts.length === 2 ? parts[1] : key
|
|
5252
|
-
|
|
5253
5235
|
const value =
|
|
5254
5236
|
lookupKey.includes('.') ? getByPath(args, lookupKey) : args?.[lookupKey]
|
|
5255
|
-
|
|
5256
5237
|
if (value === undefined) {
|
|
5257
5238
|
throw new Error(\`Missing required parameter: \${key}\`)
|
|
5258
5239
|
}
|
|
5259
|
-
|
|
5260
5240
|
params.push(normalizeValue(value))
|
|
5261
5241
|
}
|
|
5262
|
-
|
|
5263
5242
|
return params
|
|
5264
5243
|
}
|
|
5265
5244
|
|
|
5266
5245
|
async function executeQuery(client: any, sql: string, params: unknown[]): Promise<unknown[]> {
|
|
5267
5246
|
const normalizedParams = normalizeParams(params)
|
|
5268
|
-
|
|
5269
5247
|
if (DIALECT === 'postgres') {
|
|
5270
5248
|
return await client.unsafe(sql, normalizedParams)
|
|
5271
5249
|
}
|
|
5272
|
-
|
|
5273
5250
|
const stmt = client.prepare(sql)
|
|
5274
|
-
|
|
5275
5251
|
if (sql.toUpperCase().includes('COUNT(*) AS')) {
|
|
5276
5252
|
return [stmt.get(...normalizedParams)]
|
|
5277
5253
|
}
|
|
5278
|
-
|
|
5279
5254
|
return stmt.all(...normalizedParams)
|
|
5280
5255
|
}
|
|
5281
5256
|
|
|
@@ -5300,14 +5275,11 @@ export function speedExtension(config: {
|
|
|
5300
5275
|
}) => void
|
|
5301
5276
|
}) {
|
|
5302
5277
|
const { postgres, sqlite, debug, onQuery } = config
|
|
5303
|
-
|
|
5304
5278
|
if (!postgres && !sqlite) {
|
|
5305
5279
|
throw new Error('speedExtension requires postgres or sqlite client')
|
|
5306
5280
|
}
|
|
5307
|
-
|
|
5308
5281
|
const client = postgres || sqlite
|
|
5309
5282
|
const actualDialect = postgres ? 'postgres' : 'sqlite'
|
|
5310
|
-
|
|
5311
5283
|
if (actualDialect !== DIALECT) {
|
|
5312
5284
|
throw new Error(\`Generated code is for \${DIALECT}, but you provided \${actualDialect}\`)
|
|
5313
5285
|
}
|
|
@@ -5324,12 +5296,9 @@ export function speedExtension(config: {
|
|
|
5324
5296
|
const handleMethod = async function(this: any, method: PrismaMethod, args: any) {
|
|
5325
5297
|
const modelName = this?.name || this?.$name
|
|
5326
5298
|
const startTime = Date.now()
|
|
5327
|
-
|
|
5328
5299
|
const transformedArgs = transformEnumValuesByModel(modelName, args || {})
|
|
5329
|
-
|
|
5330
5300
|
const queryKey = normalizeQuery(transformedArgs)
|
|
5331
5301
|
const prebakedQuery = QUERIES[modelName]?.[method]?.[queryKey]
|
|
5332
|
-
|
|
5333
5302
|
let sql: string
|
|
5334
5303
|
let params: unknown[]
|
|
5335
5304
|
let prebaked = false
|
|
@@ -5343,11 +5312,9 @@ export function speedExtension(config: {
|
|
|
5343
5312
|
prebaked = true
|
|
5344
5313
|
} else {
|
|
5345
5314
|
const model = MODELS.find((m) => m.name === modelName)
|
|
5346
|
-
|
|
5347
5315
|
if (!model) {
|
|
5348
5316
|
return this.$parent[modelName][method](args)
|
|
5349
5317
|
}
|
|
5350
|
-
|
|
5351
5318
|
const result = buildSQL(model, MODELS, method, transformedArgs, DIALECT)
|
|
5352
5319
|
sql = result.sql
|
|
5353
5320
|
params = normalizeParams(result.params as unknown[])
|
|
@@ -5379,16 +5346,13 @@ export function speedExtension(config: {
|
|
|
5379
5346
|
): Promise<{ [K in keyof T]: any }> {
|
|
5380
5347
|
const batchProxy = createBatchProxy()
|
|
5381
5348
|
const queries = await callback(batchProxy)
|
|
5382
|
-
|
|
5383
5349
|
const batchQueries: Record<string, BatchQuery> = {}
|
|
5384
|
-
|
|
5385
5350
|
for (const [key, deferred] of Object.entries(queries)) {
|
|
5386
5351
|
if (!(deferred instanceof DeferredQuery)) {
|
|
5387
5352
|
throw new Error(
|
|
5388
5353
|
\`Batch query '\${key}' must be a deferred query. Did you await it?\`,
|
|
5389
5354
|
)
|
|
5390
5355
|
}
|
|
5391
|
-
|
|
5392
5356
|
batchQueries[key] = {
|
|
5393
5357
|
model: deferred.model,
|
|
5394
5358
|
method: deferred.method,
|
|
@@ -5414,8 +5378,8 @@ export function speedExtension(config: {
|
|
|
5414
5378
|
const rows = await client.unsafe(sql, normalizedParams as any[])
|
|
5415
5379
|
const row = rows[0] as Record<string, unknown>
|
|
5416
5380
|
const results = parseBatchResults(row, keys, batchQueries, aliases)
|
|
5417
|
-
const duration = Date.now() - startTime
|
|
5418
5381
|
|
|
5382
|
+
const duration = Date.now() - startTime
|
|
5419
5383
|
onQuery?.({
|
|
5420
5384
|
model: '_batch',
|
|
5421
5385
|
method: 'batch',
|
|
@@ -5430,7 +5394,6 @@ export function speedExtension(config: {
|
|
|
5430
5394
|
|
|
5431
5395
|
async function batchCount(queries: BatchCountQuery[]): Promise<number[]> {
|
|
5432
5396
|
if (queries.length === 0) return []
|
|
5433
|
-
|
|
5434
5397
|
const startTime = Date.now()
|
|
5435
5398
|
const { sql, params } = buildBatchCountSql(
|
|
5436
5399
|
queries,
|
|
@@ -5449,8 +5412,8 @@ export function speedExtension(config: {
|
|
|
5449
5412
|
const rows = await client.unsafe(sql, normalizedParams as any[])
|
|
5450
5413
|
const row = rows[0] as Record<string, unknown>
|
|
5451
5414
|
const results = parseBatchCountResults(row, queries.length)
|
|
5452
|
-
const duration = Date.now() - startTime
|
|
5453
5415
|
|
|
5416
|
+
const duration = Date.now() - startTime
|
|
5454
5417
|
onQuery?.({
|
|
5455
5418
|
model: '_batch',
|
|
5456
5419
|
method: 'count',
|
|
@@ -5468,14 +5431,11 @@ export function speedExtension(config: {
|
|
|
5468
5431
|
options?: TransactionOptions,
|
|
5469
5432
|
): Promise<unknown[]> {
|
|
5470
5433
|
const startTime = Date.now()
|
|
5471
|
-
|
|
5472
5434
|
if (debug) {
|
|
5473
5435
|
console.log(\`[\${DIALECT}] $transaction (\${queries.length} queries)\`)
|
|
5474
5436
|
}
|
|
5475
|
-
|
|
5476
5437
|
const results = await txExecutor.execute(queries, options)
|
|
5477
5438
|
const duration = Date.now() - startTime
|
|
5478
|
-
|
|
5479
5439
|
onQuery?.({
|
|
5480
5440
|
model: '_transaction',
|
|
5481
5441
|
method: 'transaction',
|
|
@@ -5484,13 +5444,11 @@ export function speedExtension(config: {
|
|
|
5484
5444
|
duration,
|
|
5485
5445
|
prebaked: false,
|
|
5486
5446
|
})
|
|
5487
|
-
|
|
5488
5447
|
return results
|
|
5489
5448
|
}
|
|
5490
5449
|
|
|
5491
5450
|
return prisma.$extends({
|
|
5492
5451
|
name: 'prisma-sql-generated',
|
|
5493
|
-
|
|
5494
5452
|
client: {
|
|
5495
5453
|
$original: prisma,
|
|
5496
5454
|
$batch: batch as <T extends Record<string, DeferredQuery>>(
|
|
@@ -5499,7 +5457,6 @@ export function speedExtension(config: {
|
|
|
5499
5457
|
$batchCount: batchCount as (...args: any[]) => Promise<number[]>,
|
|
5500
5458
|
$transaction: transaction as (...args: any[]) => Promise<unknown[]>,
|
|
5501
5459
|
},
|
|
5502
|
-
|
|
5503
5460
|
model: {
|
|
5504
5461
|
$allModels: {
|
|
5505
5462
|
async findMany(args: any) {
|