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