prisma-sql 1.71.0 → 1.72.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/index.js CHANGED
@@ -55,6 +55,265 @@ var __async = (__this, __arguments, generator) => {
55
55
  });
56
56
  };
57
57
 
58
+ // src/utils/normalize-value.ts
59
+ var globalDateMode = "iso";
60
+ function setNormalizeDateMode(mode) {
61
+ globalDateMode = mode;
62
+ }
63
+ function detectSqliteDateMode(client) {
64
+ try {
65
+ const tables = client.prepare(
66
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_prisma_%' LIMIT 50"
67
+ ).all();
68
+ for (const { name } of tables) {
69
+ const row = client.prepare(`SELECT typeof("createdAt") as t FROM "${name}" LIMIT 1`).get();
70
+ if (row) {
71
+ return row.t === "integer" ? "ms" : "iso";
72
+ }
73
+ }
74
+ } catch (e) {
75
+ }
76
+ return "iso";
77
+ }
78
+ var MAX_DEPTH = 20;
79
+ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
80
+ if (depth > MAX_DEPTH) {
81
+ throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
82
+ }
83
+ if (value instanceof Date) {
84
+ return normalizeDateValue(value);
85
+ }
86
+ if (typeof value === "bigint") {
87
+ return value.toString();
88
+ }
89
+ if (Array.isArray(value)) {
90
+ return normalizeArrayValue(value, seen, depth);
91
+ }
92
+ if (value && typeof value === "object") {
93
+ return normalizeObjectValue(value, seen, depth);
94
+ }
95
+ return value;
96
+ }
97
+ function normalizeDateValue(date) {
98
+ const t = date.getTime();
99
+ if (!Number.isFinite(t)) {
100
+ throw new Error("Invalid Date value in SQL params");
101
+ }
102
+ if (globalDateMode === "ms") {
103
+ return t;
104
+ }
105
+ return date.toISOString();
106
+ }
107
+ function normalizeArrayValue(value, seen, depth) {
108
+ const arrRef = value;
109
+ if (seen.has(arrRef)) {
110
+ throw new Error("Circular reference in SQL params");
111
+ }
112
+ seen.add(arrRef);
113
+ const out = value.map((v) => normalizeValue(v, seen, depth + 1));
114
+ seen.delete(arrRef);
115
+ return out;
116
+ }
117
+ function normalizeObjectValue(value, seen, depth) {
118
+ if (value instanceof Uint8Array) return value;
119
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
120
+ const proto = Object.getPrototypeOf(value);
121
+ const isPlain = proto === Object.prototype || proto === null;
122
+ if (!isPlain) return value;
123
+ const obj = value;
124
+ if (seen.has(obj)) {
125
+ throw new Error("Circular reference in SQL params");
126
+ }
127
+ seen.add(obj);
128
+ const out = {};
129
+ for (const [k, v] of Object.entries(obj)) {
130
+ out[k] = normalizeValue(v, seen, depth + 1);
131
+ }
132
+ seen.delete(obj);
133
+ return out;
134
+ }
135
+
136
+ // src/sql-builder-dialect.ts
137
+ var globalDialect = "postgres";
138
+ function setGlobalDialect(dialect) {
139
+ if (dialect !== "postgres" && dialect !== "sqlite") {
140
+ throw new Error(
141
+ `Invalid dialect: ${dialect}. Must be 'postgres' or 'sqlite'`
142
+ );
143
+ }
144
+ globalDialect = dialect;
145
+ }
146
+ function getGlobalDialect() {
147
+ return globalDialect;
148
+ }
149
+ function assertNonEmpty(value, name) {
150
+ if (!value || value.trim().length === 0) {
151
+ throw new Error(`${name} is required and cannot be empty`);
152
+ }
153
+ }
154
+ function arrayContains(column, value, arrayType, dialect) {
155
+ assertNonEmpty(column, "arrayContains column");
156
+ assertNonEmpty(value, "arrayContains value");
157
+ if (dialect === "postgres") {
158
+ return `${column} @> ARRAY[${value}]::${arrayType}`;
159
+ }
160
+ return `EXISTS (SELECT 1 FROM json_each(${column}) WHERE value = ${value})`;
161
+ }
162
+ function arrayOverlaps(column, value, arrayType, dialect) {
163
+ assertNonEmpty(column, "arrayOverlaps column");
164
+ assertNonEmpty(value, "arrayOverlaps value");
165
+ if (dialect === "postgres") {
166
+ return `${column} && ${value}::${arrayType}`;
167
+ }
168
+ return `EXISTS (
169
+ SELECT 1 FROM json_each(${column}) AS col
170
+ JOIN json_each(${value}) AS val
171
+ WHERE col.value = val.value
172
+ )`;
173
+ }
174
+ function arrayContainsAll(column, value, arrayType, dialect) {
175
+ assertNonEmpty(column, "arrayContainsAll column");
176
+ assertNonEmpty(value, "arrayContainsAll value");
177
+ if (dialect === "postgres") {
178
+ return `${column} @> ${value}::${arrayType}`;
179
+ }
180
+ return `NOT EXISTS (
181
+ SELECT 1 FROM json_each(${value}) AS val
182
+ WHERE NOT EXISTS (
183
+ SELECT 1 FROM json_each(${column}) AS col
184
+ WHERE col.value = val.value
185
+ )
186
+ )`;
187
+ }
188
+ function arrayIsEmpty(column, dialect) {
189
+ assertNonEmpty(column, "arrayIsEmpty column");
190
+ if (dialect === "postgres") {
191
+ return `(${column} IS NULL OR array_length(${column}, 1) IS NULL)`;
192
+ }
193
+ return `(${column} IS NULL OR json_array_length(${column}) = 0)`;
194
+ }
195
+ function arrayIsNotEmpty(column, dialect) {
196
+ assertNonEmpty(column, "arrayIsNotEmpty column");
197
+ if (dialect === "postgres") {
198
+ return `(${column} IS NOT NULL AND array_length(${column}, 1) IS NOT NULL)`;
199
+ }
200
+ return `(${column} IS NOT NULL AND json_array_length(${column}) > 0)`;
201
+ }
202
+ function arrayEquals(column, value, arrayType, dialect) {
203
+ assertNonEmpty(column, "arrayEquals column");
204
+ assertNonEmpty(value, "arrayEquals value");
205
+ if (dialect === "postgres") {
206
+ return `${column} = ${value}::${arrayType}`;
207
+ }
208
+ return `json(${column}) = json(${value})`;
209
+ }
210
+ function caseInsensitiveLike(column, pattern, dialect) {
211
+ assertNonEmpty(column, "caseInsensitiveLike column");
212
+ assertNonEmpty(pattern, "caseInsensitiveLike pattern");
213
+ if (dialect === "postgres") {
214
+ return `${column} ILIKE ${pattern}`;
215
+ }
216
+ return `LOWER(${column}) LIKE LOWER(${pattern})`;
217
+ }
218
+ function caseInsensitiveEquals(column, value, dialect) {
219
+ assertNonEmpty(column, "caseInsensitiveEquals column");
220
+ assertNonEmpty(value, "caseInsensitiveEquals value");
221
+ return `LOWER(${column}) = LOWER(${value})`;
222
+ }
223
+ function jsonExtractText(column, path, dialect) {
224
+ assertNonEmpty(column, "jsonExtractText column");
225
+ assertNonEmpty(path, "jsonExtractText path");
226
+ if (dialect === "postgres") {
227
+ const p = String(path).trim();
228
+ const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
229
+ return `${column}#>>${pathExpr}`;
230
+ }
231
+ return `json_extract(${column}, ${path})`;
232
+ }
233
+ function jsonExtractNumeric(column, path, dialect) {
234
+ assertNonEmpty(column, "jsonExtractNumeric column");
235
+ assertNonEmpty(path, "jsonExtractNumeric path");
236
+ if (dialect === "postgres") {
237
+ const p = String(path).trim();
238
+ const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
239
+ return `(${column}#>>${pathExpr})::numeric`;
240
+ }
241
+ return `CAST(json_extract(${column}, ${path}) AS REAL)`;
242
+ }
243
+ function jsonToText(column, dialect) {
244
+ assertNonEmpty(column, "jsonToText column");
245
+ if (dialect === "postgres") {
246
+ return `${column}::text`;
247
+ }
248
+ return column;
249
+ }
250
+ function inArray(column, value, dialect) {
251
+ assertNonEmpty(column, "inArray column");
252
+ assertNonEmpty(value, "inArray value");
253
+ if (dialect === "postgres") {
254
+ return `${column} = ANY(${value})`;
255
+ }
256
+ return `${column} IN (SELECT value FROM json_each(${value}))`;
257
+ }
258
+ function notInArray(column, value, dialect) {
259
+ assertNonEmpty(column, "notInArray column");
260
+ assertNonEmpty(value, "notInArray value");
261
+ if (dialect === "postgres") {
262
+ return `${column} != ALL(${value})`;
263
+ }
264
+ return `${column} NOT IN (SELECT value FROM json_each(${value}))`;
265
+ }
266
+ function getArrayType(prismaType, dialect) {
267
+ if (!prismaType || prismaType.length === 0) {
268
+ return dialect === "sqlite" ? "TEXT" : "text[]";
269
+ }
270
+ if (dialect === "sqlite") {
271
+ return "TEXT";
272
+ }
273
+ const baseType = prismaType.replace(/\[\]|\?/g, "");
274
+ switch (baseType) {
275
+ case "String":
276
+ return "text[]";
277
+ case "Int":
278
+ return "integer[]";
279
+ case "Float":
280
+ return "double precision[]";
281
+ case "Decimal":
282
+ return "numeric[]";
283
+ case "Boolean":
284
+ return "boolean[]";
285
+ case "BigInt":
286
+ return "bigint[]";
287
+ case "DateTime":
288
+ return "timestamptz[]";
289
+ default:
290
+ return `"${baseType}"[]`;
291
+ }
292
+ }
293
+ function jsonAgg(content, dialect) {
294
+ assertNonEmpty(content, "jsonAgg content");
295
+ if (dialect === "postgres") {
296
+ return `json_agg(${content})`;
297
+ }
298
+ return `json_group_array(${content})`;
299
+ }
300
+ function jsonBuildObject(pairs, dialect) {
301
+ const safePairs = (pairs != null ? pairs : "").trim();
302
+ if (dialect === "postgres") {
303
+ return safePairs.length > 0 ? `json_build_object(${safePairs})` : `json_build_object()`;
304
+ }
305
+ return safePairs.length > 0 ? `json_object(${safePairs})` : `json_object()`;
306
+ }
307
+ function prepareArrayParam(value, dialect) {
308
+ if (!Array.isArray(value)) {
309
+ throw new Error("prepareArrayParam requires array value");
310
+ }
311
+ if (dialect === "postgres") {
312
+ return value.map((v) => normalizeValue(v));
313
+ }
314
+ return JSON.stringify(value.map((v) => normalizeValue(v)));
315
+ }
316
+
58
317
  // src/builder/shared/constants.ts
59
318
  var IS_PRODUCTION = process.env.NODE_ENV === "production";
60
319
  var SQL_SEPARATORS = Object.freeze({
@@ -751,414 +1010,163 @@ function normalizeKeyList(input) {
751
1010
  if (raw.length === 0) return [];
752
1011
  if (raw.includes(",")) {
753
1012
  return raw.split(",").map((s2) => s2.trim()).filter((s2) => s2.length > 0);
754
- }
755
- return [raw];
756
- }
757
- const s = String(input).trim();
758
- return s.length > 0 ? [s] : [];
759
- }
760
-
761
- // src/builder/shared/model-field-cache.ts
762
- var FIELD_INDICES_CACHE = /* @__PURE__ */ new WeakMap();
763
- function normalizeField(field) {
764
- return field;
765
- }
766
- function getFieldIndices(model) {
767
- var _a3;
768
- let cached = FIELD_INDICES_CACHE.get(model);
769
- if (cached) return cached;
770
- const scalarFields = /* @__PURE__ */ new Map();
771
- const relationFields = /* @__PURE__ */ new Map();
772
- const allFieldsByName = /* @__PURE__ */ new Map();
773
- const scalarNames = [];
774
- const relationNames = [];
775
- const jsonFields = /* @__PURE__ */ new Set();
776
- const pkFields = [];
777
- const columnMap = /* @__PURE__ */ new Map();
778
- const quotedColumns = /* @__PURE__ */ new Map();
779
- for (const rawField of model.fields) {
780
- const field = normalizeField(rawField);
781
- allFieldsByName.set(field.name, field);
782
- if (field.isRelation) {
783
- relationFields.set(field.name, field);
784
- relationNames.push(field.name);
785
- } else {
786
- scalarFields.set(field.name, field);
787
- scalarNames.push(field.name);
788
- const fieldType = String((_a3 = field.type) != null ? _a3 : "").toLowerCase();
789
- if (fieldType === "json") {
790
- jsonFields.add(field.name);
791
- }
792
- if (field.isId || field.isPrimaryKey || field.primaryKey) {
793
- pkFields.push(field.name);
794
- }
795
- if (field.dbName && field.dbName !== field.name) {
796
- columnMap.set(field.name, field.dbName);
797
- }
798
- const columnName = field.dbName || field.name;
799
- quotedColumns.set(field.name, quote(columnName));
800
- }
801
- }
802
- cached = Object.freeze({
803
- scalarFields,
804
- relationFields,
805
- allFieldsByName,
806
- scalarNames,
807
- relationNames,
808
- jsonFields,
809
- pkFields,
810
- columnMap,
811
- quotedColumns
812
- });
813
- FIELD_INDICES_CACHE.set(model, cached);
814
- return cached;
815
- }
816
- function getRelationFieldSet(model) {
817
- return new Set(getFieldIndices(model).relationNames);
818
- }
819
- function getScalarFieldSet(model) {
820
- return new Set(getFieldIndices(model).scalarNames);
821
- }
822
- function getColumnMap(model) {
823
- return getFieldIndices(model).columnMap;
824
- }
825
- function getScalarFieldNames(model) {
826
- return [...getFieldIndices(model).scalarNames];
827
- }
828
- function getQuotedColumn(model, fieldName) {
829
- return getFieldIndices(model).quotedColumns.get(fieldName);
830
- }
831
- function getJsonFieldSet(model) {
832
- return getFieldIndices(model).jsonFields;
833
- }
834
- function parseJsonIfNeeded(isJson, value) {
835
- if (!isJson) return value;
836
- if (value == null) return value;
837
- if (typeof value !== "string") return value;
838
- try {
839
- return JSON.parse(value);
840
- } catch (e) {
841
- return value;
842
- }
843
- }
844
- function maybeParseJson(value, jsonSet, fieldName) {
845
- if (!jsonSet.has(fieldName)) return value;
846
- if (value == null) return value;
847
- if (typeof value !== "string") return value;
848
- try {
849
- return JSON.parse(value);
850
- } catch (e) {
851
- return value;
852
- }
853
- }
854
-
855
- // src/builder/joins.ts
856
- function isRelationField(fieldName, model) {
857
- return getRelationFieldSet(model).has(fieldName);
858
- }
859
- function isValidRelationField(field) {
860
- if (!isNotNullish(field)) return false;
861
- if (!field.isRelation) return false;
862
- if (!isNotNullish(field.relatedModel) || field.relatedModel.trim().length === 0)
863
- return false;
864
- const fk = normalizeKeyList(field.foreignKey);
865
- if (fk.length === 0) return false;
866
- const refs = normalizeKeyList(field.references);
867
- if (refs.length === 0) {
868
- return fk.length === 1;
869
- }
870
- if (refs.length !== fk.length) return false;
871
- return true;
872
- }
873
- function getReferenceFieldNames(field, foreignKeyCount) {
874
- const refs = normalizeKeyList(field.references);
875
- if (refs.length === 0) {
876
- if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
877
- return [];
878
- }
879
- if (refs.length !== foreignKeyCount) return [];
880
- return refs;
881
- }
882
- function joinCondition(field, parentModel, childModel, parentAlias, childAlias) {
883
- assertSafeAlias(parentAlias);
884
- assertSafeAlias(childAlias);
885
- const fkFields = normalizeKeyList(field.foreignKey);
886
- if (fkFields.length === 0) {
887
- throw createError(
888
- `Relation '${field.name}' is missing foreignKey. This indicates a schema parsing error. Relations must specify fields/references.`,
889
- { field: field.name }
890
- );
891
- }
892
- const refFields = getReferenceFieldNames(field, fkFields.length);
893
- if (refFields.length !== fkFields.length) {
894
- throw createError(
895
- `Relation '${field.name}' is missing references (or references count does not match foreignKey count). This is required to support non-id and composite keys.`,
896
- { field: field.name }
897
- );
898
- }
899
- const parts = [];
900
- for (let i = 0; i < fkFields.length; i++) {
901
- const fk = fkFields[i];
902
- const ref = refFields[i];
903
- const left = field.isForeignKeyLocal ? `${childAlias}.${quoteColumn(childModel, ref)}` : `${childAlias}.${quoteColumn(childModel, fk)}`;
904
- const right = field.isForeignKeyLocal ? `${parentAlias}.${quoteColumn(parentModel, fk)}` : `${parentAlias}.${quoteColumn(parentModel, ref)}`;
905
- parts.push(`${left} = ${right}`);
906
- }
907
- return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
908
- }
909
- function getModelByName(schemas, name) {
910
- return schemas.find((m) => m.name === name);
911
- }
912
-
913
- // src/utils/normalize-value.ts
914
- var globalDateMode = "iso";
915
- function setNormalizeDateMode(mode) {
916
- globalDateMode = mode;
917
- }
918
- function detectSqliteDateMode(client) {
919
- try {
920
- const tables = client.prepare(
921
- "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_prisma_%' LIMIT 50"
922
- ).all();
923
- for (const { name } of tables) {
924
- const row = client.prepare(`SELECT typeof("createdAt") as t FROM "${name}" LIMIT 1`).get();
925
- if (row) {
926
- return row.t === "integer" ? "ms" : "iso";
927
- }
928
- }
929
- } catch (e) {
930
- }
931
- return "iso";
932
- }
933
- var MAX_DEPTH = 20;
934
- function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
935
- if (depth > MAX_DEPTH) {
936
- throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
937
- }
938
- if (value instanceof Date) {
939
- return normalizeDateValue(value);
940
- }
941
- if (typeof value === "bigint") {
942
- return value.toString();
943
- }
944
- if (Array.isArray(value)) {
945
- return normalizeArrayValue(value, seen, depth);
946
- }
947
- if (value && typeof value === "object") {
948
- return normalizeObjectValue(value, seen, depth);
949
- }
950
- return value;
951
- }
952
- function normalizeDateValue(date) {
953
- const t = date.getTime();
954
- if (!Number.isFinite(t)) {
955
- throw new Error("Invalid Date value in SQL params");
956
- }
957
- if (globalDateMode === "ms") {
958
- return t;
959
- }
960
- return date.toISOString();
961
- }
962
- function normalizeArrayValue(value, seen, depth) {
963
- const arrRef = value;
964
- if (seen.has(arrRef)) {
965
- throw new Error("Circular reference in SQL params");
966
- }
967
- seen.add(arrRef);
968
- const out = value.map((v) => normalizeValue(v, seen, depth + 1));
969
- seen.delete(arrRef);
970
- return out;
971
- }
972
- function normalizeObjectValue(value, seen, depth) {
973
- if (value instanceof Uint8Array) return value;
974
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
975
- const proto = Object.getPrototypeOf(value);
976
- const isPlain = proto === Object.prototype || proto === null;
977
- if (!isPlain) return value;
978
- const obj = value;
979
- if (seen.has(obj)) {
980
- throw new Error("Circular reference in SQL params");
981
- }
982
- seen.add(obj);
983
- const out = {};
984
- for (const [k, v] of Object.entries(obj)) {
985
- out[k] = normalizeValue(v, seen, depth + 1);
986
- }
987
- seen.delete(obj);
988
- return out;
989
- }
990
-
991
- // src/sql-builder-dialect.ts
992
- var globalDialect = "postgres";
993
- function getGlobalDialect() {
994
- return globalDialect;
995
- }
996
- function assertNonEmpty(value, name) {
997
- if (!value || value.trim().length === 0) {
998
- throw new Error(`${name} is required and cannot be empty`);
999
- }
1000
- }
1001
- function arrayContains(column, value, arrayType, dialect) {
1002
- assertNonEmpty(column, "arrayContains column");
1003
- assertNonEmpty(value, "arrayContains value");
1004
- if (dialect === "postgres") {
1005
- return `${column} @> ARRAY[${value}]::${arrayType}`;
1006
- }
1007
- return `EXISTS (SELECT 1 FROM json_each(${column}) WHERE value = ${value})`;
1008
- }
1009
- function arrayOverlaps(column, value, arrayType, dialect) {
1010
- assertNonEmpty(column, "arrayOverlaps column");
1011
- assertNonEmpty(value, "arrayOverlaps value");
1012
- if (dialect === "postgres") {
1013
- return `${column} && ${value}::${arrayType}`;
1014
- }
1015
- return `EXISTS (
1016
- SELECT 1 FROM json_each(${column}) AS col
1017
- JOIN json_each(${value}) AS val
1018
- WHERE col.value = val.value
1019
- )`;
1020
- }
1021
- function arrayContainsAll(column, value, arrayType, dialect) {
1022
- assertNonEmpty(column, "arrayContainsAll column");
1023
- assertNonEmpty(value, "arrayContainsAll value");
1024
- if (dialect === "postgres") {
1025
- return `${column} @> ${value}::${arrayType}`;
1026
- }
1027
- return `NOT EXISTS (
1028
- SELECT 1 FROM json_each(${value}) AS val
1029
- WHERE NOT EXISTS (
1030
- SELECT 1 FROM json_each(${column}) AS col
1031
- WHERE col.value = val.value
1032
- )
1033
- )`;
1034
- }
1035
- function arrayIsEmpty(column, dialect) {
1036
- assertNonEmpty(column, "arrayIsEmpty column");
1037
- if (dialect === "postgres") {
1038
- return `(${column} IS NULL OR array_length(${column}, 1) IS NULL)`;
1013
+ }
1014
+ return [raw];
1039
1015
  }
1040
- return `(${column} IS NULL OR json_array_length(${column}) = 0)`;
1016
+ const s = String(input).trim();
1017
+ return s.length > 0 ? [s] : [];
1041
1018
  }
1042
- function arrayIsNotEmpty(column, dialect) {
1043
- assertNonEmpty(column, "arrayIsNotEmpty column");
1044
- if (dialect === "postgres") {
1045
- return `(${column} IS NOT NULL AND array_length(${column}, 1) IS NOT NULL)`;
1046
- }
1047
- return `(${column} IS NOT NULL AND json_array_length(${column}) > 0)`;
1019
+
1020
+ // src/builder/shared/model-field-cache.ts
1021
+ var FIELD_INDICES_CACHE = /* @__PURE__ */ new WeakMap();
1022
+ function normalizeField(field) {
1023
+ return field;
1048
1024
  }
1049
- function arrayEquals(column, value, arrayType, dialect) {
1050
- assertNonEmpty(column, "arrayEquals column");
1051
- assertNonEmpty(value, "arrayEquals value");
1052
- if (dialect === "postgres") {
1053
- return `${column} = ${value}::${arrayType}`;
1025
+ function getFieldIndices(model) {
1026
+ var _a3;
1027
+ let cached = FIELD_INDICES_CACHE.get(model);
1028
+ if (cached) return cached;
1029
+ const scalarFields = /* @__PURE__ */ new Map();
1030
+ const relationFields = /* @__PURE__ */ new Map();
1031
+ const allFieldsByName = /* @__PURE__ */ new Map();
1032
+ const scalarNames = [];
1033
+ const relationNames = [];
1034
+ const jsonFields = /* @__PURE__ */ new Set();
1035
+ const pkFields = [];
1036
+ const columnMap = /* @__PURE__ */ new Map();
1037
+ const quotedColumns = /* @__PURE__ */ new Map();
1038
+ for (const rawField of model.fields) {
1039
+ const field = normalizeField(rawField);
1040
+ allFieldsByName.set(field.name, field);
1041
+ if (field.isRelation) {
1042
+ relationFields.set(field.name, field);
1043
+ relationNames.push(field.name);
1044
+ } else {
1045
+ scalarFields.set(field.name, field);
1046
+ scalarNames.push(field.name);
1047
+ const fieldType = String((_a3 = field.type) != null ? _a3 : "").toLowerCase();
1048
+ if (fieldType === "json") {
1049
+ jsonFields.add(field.name);
1050
+ }
1051
+ if (field.isId || field.isPrimaryKey || field.primaryKey) {
1052
+ pkFields.push(field.name);
1053
+ }
1054
+ if (field.dbName && field.dbName !== field.name) {
1055
+ columnMap.set(field.name, field.dbName);
1056
+ }
1057
+ const columnName = field.dbName || field.name;
1058
+ quotedColumns.set(field.name, quote(columnName));
1059
+ }
1054
1060
  }
1055
- return `json(${column}) = json(${value})`;
1061
+ cached = Object.freeze({
1062
+ scalarFields,
1063
+ relationFields,
1064
+ allFieldsByName,
1065
+ scalarNames,
1066
+ relationNames,
1067
+ jsonFields,
1068
+ pkFields,
1069
+ columnMap,
1070
+ quotedColumns
1071
+ });
1072
+ FIELD_INDICES_CACHE.set(model, cached);
1073
+ return cached;
1056
1074
  }
1057
- function caseInsensitiveLike(column, pattern, dialect) {
1058
- assertNonEmpty(column, "caseInsensitiveLike column");
1059
- assertNonEmpty(pattern, "caseInsensitiveLike pattern");
1060
- if (dialect === "postgres") {
1061
- return `${column} ILIKE ${pattern}`;
1062
- }
1063
- return `LOWER(${column}) LIKE LOWER(${pattern})`;
1075
+ function getRelationFieldSet(model) {
1076
+ return new Set(getFieldIndices(model).relationNames);
1064
1077
  }
1065
- function caseInsensitiveEquals(column, value, dialect) {
1066
- assertNonEmpty(column, "caseInsensitiveEquals column");
1067
- assertNonEmpty(value, "caseInsensitiveEquals value");
1068
- return `LOWER(${column}) = LOWER(${value})`;
1078
+ function getScalarFieldSet(model) {
1079
+ return new Set(getFieldIndices(model).scalarNames);
1069
1080
  }
1070
- function jsonExtractText(column, path, dialect) {
1071
- assertNonEmpty(column, "jsonExtractText column");
1072
- assertNonEmpty(path, "jsonExtractText path");
1073
- if (dialect === "postgres") {
1074
- const p = String(path).trim();
1075
- const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
1076
- return `${column}#>>${pathExpr}`;
1077
- }
1078
- return `json_extract(${column}, ${path})`;
1081
+ function getColumnMap(model) {
1082
+ return getFieldIndices(model).columnMap;
1079
1083
  }
1080
- function jsonExtractNumeric(column, path, dialect) {
1081
- assertNonEmpty(column, "jsonExtractNumeric column");
1082
- assertNonEmpty(path, "jsonExtractNumeric path");
1083
- if (dialect === "postgres") {
1084
- const p = String(path).trim();
1085
- const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
1086
- return `(${column}#>>${pathExpr})::numeric`;
1087
- }
1088
- return `CAST(json_extract(${column}, ${path}) AS REAL)`;
1084
+ function getScalarFieldNames(model) {
1085
+ return [...getFieldIndices(model).scalarNames];
1089
1086
  }
1090
- function jsonToText(column, dialect) {
1091
- assertNonEmpty(column, "jsonToText column");
1092
- if (dialect === "postgres") {
1093
- return `${column}::text`;
1094
- }
1095
- return column;
1087
+ function getQuotedColumn(model, fieldName) {
1088
+ return getFieldIndices(model).quotedColumns.get(fieldName);
1096
1089
  }
1097
- function inArray(column, value, dialect) {
1098
- assertNonEmpty(column, "inArray column");
1099
- assertNonEmpty(value, "inArray value");
1100
- if (dialect === "postgres") {
1101
- return `${column} = ANY(${value})`;
1102
- }
1103
- return `${column} IN (SELECT value FROM json_each(${value}))`;
1090
+ function getJsonFieldSet(model) {
1091
+ return getFieldIndices(model).jsonFields;
1104
1092
  }
1105
- function notInArray(column, value, dialect) {
1106
- assertNonEmpty(column, "notInArray column");
1107
- assertNonEmpty(value, "notInArray value");
1108
- if (dialect === "postgres") {
1109
- return `${column} != ALL(${value})`;
1093
+ function parseJsonIfNeeded(isJson, value) {
1094
+ if (!isJson) return value;
1095
+ if (value == null) return value;
1096
+ if (typeof value !== "string") return value;
1097
+ try {
1098
+ return JSON.parse(value);
1099
+ } catch (e) {
1100
+ return value;
1110
1101
  }
1111
- return `${column} NOT IN (SELECT value FROM json_each(${value}))`;
1112
1102
  }
1113
- function getArrayType(prismaType, dialect) {
1114
- if (!prismaType || prismaType.length === 0) {
1115
- return dialect === "sqlite" ? "TEXT" : "text[]";
1116
- }
1117
- if (dialect === "sqlite") {
1118
- return "TEXT";
1119
- }
1120
- const baseType = prismaType.replace(/\[\]|\?/g, "");
1121
- switch (baseType) {
1122
- case "String":
1123
- return "text[]";
1124
- case "Int":
1125
- return "integer[]";
1126
- case "Float":
1127
- return "double precision[]";
1128
- case "Decimal":
1129
- return "numeric[]";
1130
- case "Boolean":
1131
- return "boolean[]";
1132
- case "BigInt":
1133
- return "bigint[]";
1134
- case "DateTime":
1135
- return "timestamptz[]";
1136
- default:
1137
- return `"${baseType}"[]`;
1103
+ function maybeParseJson(value, jsonSet, fieldName) {
1104
+ if (!jsonSet.has(fieldName)) return value;
1105
+ if (value == null) return value;
1106
+ if (typeof value !== "string") return value;
1107
+ try {
1108
+ return JSON.parse(value);
1109
+ } catch (e) {
1110
+ return value;
1138
1111
  }
1139
1112
  }
1140
- function jsonAgg(content, dialect) {
1141
- assertNonEmpty(content, "jsonAgg content");
1142
- if (dialect === "postgres") {
1143
- return `json_agg(${content})`;
1113
+
1114
+ // src/builder/joins.ts
1115
+ function isRelationField(fieldName, model) {
1116
+ return getRelationFieldSet(model).has(fieldName);
1117
+ }
1118
+ function isValidRelationField(field) {
1119
+ if (!isNotNullish(field)) return false;
1120
+ if (!field.isRelation) return false;
1121
+ if (!isNotNullish(field.relatedModel) || field.relatedModel.trim().length === 0)
1122
+ return false;
1123
+ const fk = normalizeKeyList(field.foreignKey);
1124
+ if (fk.length === 0) return false;
1125
+ const refs = normalizeKeyList(field.references);
1126
+ if (refs.length === 0) {
1127
+ return fk.length === 1;
1144
1128
  }
1145
- return `json_group_array(${content})`;
1129
+ if (refs.length !== fk.length) return false;
1130
+ return true;
1146
1131
  }
1147
- function jsonBuildObject(pairs, dialect) {
1148
- const safePairs = (pairs != null ? pairs : "").trim();
1149
- if (dialect === "postgres") {
1150
- return safePairs.length > 0 ? `json_build_object(${safePairs})` : `json_build_object()`;
1132
+ function getReferenceFieldNames(field, foreignKeyCount) {
1133
+ const refs = normalizeKeyList(field.references);
1134
+ if (refs.length === 0) {
1135
+ if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
1136
+ return [];
1151
1137
  }
1152
- return safePairs.length > 0 ? `json_object(${safePairs})` : `json_object()`;
1138
+ if (refs.length !== foreignKeyCount) return [];
1139
+ return refs;
1153
1140
  }
1154
- function prepareArrayParam(value, dialect) {
1155
- if (!Array.isArray(value)) {
1156
- throw new Error("prepareArrayParam requires array value");
1141
+ function joinCondition(field, parentModel, childModel, parentAlias, childAlias) {
1142
+ assertSafeAlias(parentAlias);
1143
+ assertSafeAlias(childAlias);
1144
+ const fkFields = normalizeKeyList(field.foreignKey);
1145
+ if (fkFields.length === 0) {
1146
+ throw createError(
1147
+ `Relation '${field.name}' is missing foreignKey. This indicates a schema parsing error. Relations must specify fields/references.`,
1148
+ { field: field.name }
1149
+ );
1157
1150
  }
1158
- if (dialect === "postgres") {
1159
- return value.map((v) => normalizeValue(v));
1151
+ const refFields = getReferenceFieldNames(field, fkFields.length);
1152
+ if (refFields.length !== fkFields.length) {
1153
+ throw createError(
1154
+ `Relation '${field.name}' is missing references (or references count does not match foreignKey count). This is required to support non-id and composite keys.`,
1155
+ { field: field.name }
1156
+ );
1160
1157
  }
1161
- return JSON.stringify(value.map((v) => normalizeValue(v)));
1158
+ const parts = [];
1159
+ for (let i = 0; i < fkFields.length; i++) {
1160
+ const fk = fkFields[i];
1161
+ const ref = refFields[i];
1162
+ const left = field.isForeignKeyLocal ? `${childAlias}.${quoteColumn(childModel, ref)}` : `${childAlias}.${quoteColumn(childModel, fk)}`;
1163
+ const right = field.isForeignKeyLocal ? `${parentAlias}.${quoteColumn(parentModel, fk)}` : `${parentAlias}.${quoteColumn(parentModel, ref)}`;
1164
+ parts.push(`${left} = ${right}`);
1165
+ }
1166
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
1167
+ }
1168
+ function getModelByName(schemas, name) {
1169
+ return schemas.find((m) => m.name === name);
1162
1170
  }
1163
1171
  function normalizeIntLike(name, v, opts = {}) {
1164
1172
  var _a3, _b;
@@ -4866,7 +4874,6 @@ var DEFAULT_SELECT_CACHE = /* @__PURE__ */ new WeakMap();
4866
4874
  function toSelectEntries(select) {
4867
4875
  const out = [];
4868
4876
  for (const [k, v] of Object.entries(select)) {
4869
- if (k === "_count") continue;
4870
4877
  if (v !== false && v !== void 0) out.push([k, v]);
4871
4878
  }
4872
4879
  return out;
@@ -4959,7 +4966,9 @@ function validateFieldKeys(entries, scalarSet, relationSet, allowCount = false)
4959
4966
  if (!scalarSet.has(k) && !relationSet.has(k)) unknown.push(k);
4960
4967
  }
4961
4968
  if (unknown.length > 0) {
4962
- throw new Error(`Select contains unknown fields: ${unknown.join(", ")}`);
4969
+ throw new Error(
4970
+ `Unknown field '${unknown.join("', '")}' does not exist on this model`
4971
+ );
4963
4972
  }
4964
4973
  }
4965
4974
  function buildSelectedScalarParts(entries, scalarNames, alias, model) {
@@ -5151,12 +5160,41 @@ function buildNestedToOneSelects(relations, aliasMap) {
5151
5160
  }
5152
5161
  return selects;
5153
5162
  }
5163
+ function extractCountSelectFromRelArgs(relArgs) {
5164
+ if (!isPlainObject(relArgs)) return null;
5165
+ const obj = relArgs;
5166
+ if (!isPlainObject(obj.select)) return null;
5167
+ const sel = obj.select;
5168
+ const countRaw = sel["_count"];
5169
+ if (!countRaw) return null;
5170
+ if (isPlainObject(countRaw) && "select" in countRaw) {
5171
+ return countRaw.select;
5172
+ }
5173
+ return null;
5174
+ }
5154
5175
  function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
5155
5176
  const nestedToOnes = extractNestedToOneRelations(
5156
5177
  relArgs,
5157
5178
  relModel,
5158
5179
  ctx.schemaByName
5159
5180
  );
5181
+ const countSelect = extractCountSelectFromRelArgs(relArgs);
5182
+ const countJoins = [];
5183
+ function appendCountToSelect(baseSelect2) {
5184
+ if (!countSelect || Object.keys(countSelect).length === 0) return baseSelect2;
5185
+ const countBuild = buildRelationCountSql(
5186
+ countSelect,
5187
+ relModel,
5188
+ ctx.schemas,
5189
+ relAlias,
5190
+ ctx.params,
5191
+ ctx.dialect
5192
+ );
5193
+ if (!countBuild.jsonPairs) return baseSelect2;
5194
+ countJoins.push(...countBuild.joins);
5195
+ const countExpr = `'_count', ${jsonBuildObject(countBuild.jsonPairs, ctx.dialect)}`;
5196
+ return baseSelect2 ? `${baseSelect2}${SQL_SEPARATORS.FIELD_LIST}${countExpr}` : countExpr;
5197
+ }
5160
5198
  if (nestedToOnes.length === 0) {
5161
5199
  let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
5162
5200
  let nestedIncludes = [];
@@ -5185,7 +5223,8 @@ function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
5185
5223
  ).join(SQL_SEPARATORS.FIELD_LIST);
5186
5224
  relSelect = isNotNullish(relSelect) && relSelect.trim().length > 0 ? `${relSelect}${SQL_SEPARATORS.FIELD_LIST}${nestedSelects2}` : nestedSelects2;
5187
5225
  }
5188
- return { select: relSelect, nestedJoins: [] };
5226
+ relSelect = appendCountToSelect(relSelect);
5227
+ return { select: relSelect, nestedJoins: countJoins };
5189
5228
  }
5190
5229
  const { joins, aliasMap } = buildNestedToOneJoins(
5191
5230
  nestedToOnes,
@@ -5203,9 +5242,11 @@ function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
5203
5242
  for (const ns of nestedSelects) {
5204
5243
  allParts.push(ns);
5205
5244
  }
5245
+ let finalSelect = allParts.join(SQL_SEPARATORS.FIELD_LIST);
5246
+ finalSelect = appendCountToSelect(finalSelect);
5206
5247
  return {
5207
- select: allParts.join(SQL_SEPARATORS.FIELD_LIST),
5208
- nestedJoins: joins
5248
+ select: finalSelect,
5249
+ nestedJoins: [...joins, ...countJoins]
5209
5250
  };
5210
5251
  }
5211
5252
  function buildWhereParts(whereInput, relModel, relAlias, ctx) {
@@ -5587,7 +5628,7 @@ function validateDistinct(model, distinct) {
5587
5628
  const relationSet = getRelationFieldSet(model);
5588
5629
  if (relationSet.has(f)) {
5589
5630
  throw new Error(
5590
- `distinct field '${f}' is a relation. Only scalar fields are allowed.
5631
+ `distinct field '${f}' is a relation field. Only scalar fields are allowed.
5591
5632
  Available scalar fields: ${[...scalarSet].join(", ")}`
5592
5633
  );
5593
5634
  }
@@ -5625,7 +5666,7 @@ function validateOrderBy(model, orderBy) {
5625
5666
  if (!scalarSet.has(f)) {
5626
5667
  if (relationSet.has(f)) {
5627
5668
  throw new Error(
5628
- `orderBy field '${f}' is a relation. Only scalar fields are allowed.
5669
+ `orderBy field '${f}' is a relation field. Only scalar fields are allowed.
5629
5670
  Available scalar fields: ${[...scalarSet].join(", ")}`
5630
5671
  );
5631
5672
  }
@@ -9365,10 +9406,30 @@ function executeRaw(client, sql, params, dialect) {
9365
9406
  function buildSQL(model, models, method, args, dialect) {
9366
9407
  return buildSQLWithCache(model, models, method, args, dialect);
9367
9408
  }
9409
+ function createToSQLFunction(models, dialect) {
9410
+ if (!models || !Array.isArray(models) || models.length === 0) {
9411
+ throw new Error("createToSQL requires non-empty models array");
9412
+ }
9413
+ const modelMap = new Map(models.map((m) => [m.name, m]));
9414
+ setGlobalDialect(dialect);
9415
+ return function toSQL(model, method, args = {}) {
9416
+ const m = modelMap.get(model);
9417
+ if (!m) {
9418
+ throw new Error(
9419
+ `Model '${model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
9420
+ );
9421
+ }
9422
+ return buildSQL(m, models, method, args, dialect);
9423
+ };
9424
+ }
9425
+ function createToSQL(modelsOrDmmf, dialect) {
9426
+ const models = Array.isArray(modelsOrDmmf) ? modelsOrDmmf : convertDMMFToModels(modelsOrDmmf.datamodel);
9427
+ return createToSQLFunction(models, dialect);
9428
+ }
9368
9429
  function generateSQL2(directive) {
9369
9430
  return generateSQL(directive);
9370
9431
  }
9371
9432
 
9372
- export { buildBatchCountSql, buildBatchSql, buildLateralReducerConfig, buildReducerConfig, buildSQL, countIncludeDepth, createProgressiveReducer, createStreamingReducer, createTransactionExecutor, detectSqliteDateMode, executePostgresQuery, executeRaw, executeSqliteQuery, executeWhereInSegments, executeWhereInSegmentsStreaming, executeWithPreFetchedParents, extractCountValue, generateSQL2 as generateSQL, getOrPrepareStatement, getPrimaryKeyField, getRelationStats, getRowTransformer, normalizeParams, normalizeValue, parseBatchCountResults, parseBatchResults, planQueryStrategy, reduceFlatRows, reduceLateralRows, setJsonRowFactor, setNormalizeDateMode, setRelationStats, setRoundtripRowEquivalent, shouldSqliteUseGet, transformAggregateRow, transformQueryResults };
9433
+ export { buildBatchCountSql, buildBatchSql, buildLateralReducerConfig, buildReducerConfig, buildSQL, countIncludeDepth, createProgressiveReducer, createStreamingReducer, createToSQL, createTransactionExecutor, detectSqliteDateMode, executePostgresQuery, executeRaw, executeSqliteQuery, executeWhereInSegments, executeWhereInSegmentsStreaming, executeWithPreFetchedParents, extractCountValue, generateSQL2 as generateSQL, getOrPrepareStatement, getPrimaryKeyField, getRelationStats, getRowTransformer, normalizeParams, normalizeValue, parseBatchCountResults, parseBatchResults, planQueryStrategy, reduceFlatRows, reduceLateralRows, setJsonRowFactor, setNormalizeDateMode, setRelationStats, setRoundtripRowEquivalent, shouldSqliteUseGet, transformAggregateRow, transformQueryResults };
9373
9434
  //# sourceMappingURL=index.js.map
9374
9435
  //# sourceMappingURL=index.js.map