prisma-sql 1.74.0 → 1.75.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.js CHANGED
@@ -2312,7 +2312,7 @@ var require_package = __commonJS({
2312
2312
  "package.json"(exports$1, module) {
2313
2313
  module.exports = {
2314
2314
  name: "prisma-sql",
2315
- version: "1.74.0",
2315
+ version: "1.75.0",
2316
2316
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
2317
2317
  main: "dist/index.cjs",
2318
2318
  module: "dist/index.js",
@@ -3525,7 +3525,7 @@ var getNextSort = (sortRaw) => {
3525
3525
  if (s === "desc") return "asc";
3526
3526
  return sortRaw;
3527
3527
  };
3528
- var flipObjectSort = (obj) => {
3528
+ var flipScalarSortObject = (obj) => {
3529
3529
  const out = __spreadValues({}, obj);
3530
3530
  const hasSort = Object.prototype.hasOwnProperty.call(obj, "sort");
3531
3531
  const hasDirection = Object.prototype.hasOwnProperty.call(obj, "direction");
@@ -3541,26 +3541,64 @@ var flipObjectSort = (obj) => {
3541
3541
  }
3542
3542
  return out;
3543
3543
  };
3544
+ function isScalarSortConfig(obj) {
3545
+ return Object.prototype.hasOwnProperty.call(obj, "sort") || Object.prototype.hasOwnProperty.call(obj, "direction");
3546
+ }
3547
+ function flipRelationOrderByValue(obj) {
3548
+ const out = {};
3549
+ for (const [k, v] of Object.entries(obj)) {
3550
+ out[k] = flipValue(v);
3551
+ }
3552
+ return out;
3553
+ }
3544
3554
  var flipValue = (v) => {
3545
3555
  if (typeof v === "string") return flipSortString(v);
3546
- if (isPlainObject(v)) return flipObjectSort(v);
3556
+ if (isPlainObject(v)) {
3557
+ if (isScalarSortConfig(v)) return flipScalarSortObject(v);
3558
+ return flipRelationOrderByValue(v);
3559
+ }
3547
3560
  return v;
3548
3561
  };
3549
- var assertSingleFieldObject = (item) => {
3562
+ var expandToSingleFieldEntries = (item) => {
3550
3563
  if (!isPlainObject(item)) {
3551
3564
  throw new Error("orderBy array entries must be objects");
3552
3565
  }
3553
3566
  const entries = Object.entries(item);
3554
- if (entries.length !== 1) {
3555
- throw new Error("orderBy array entries must have exactly one field");
3567
+ if (entries.length === 0) {
3568
+ throw new Error("orderBy array entries must have at least one field");
3556
3569
  }
3557
- return entries[0];
3570
+ return entries;
3558
3571
  };
3572
+ function expandOrderByInput(orderBy) {
3573
+ if (!isNotNullish(orderBy)) return [];
3574
+ if (Array.isArray(orderBy)) {
3575
+ const result = [];
3576
+ for (const item of orderBy) {
3577
+ result.push(...expandToSingleFieldEntries(item));
3578
+ }
3579
+ return result;
3580
+ }
3581
+ if (isPlainObject(orderBy)) {
3582
+ return Object.entries(orderBy);
3583
+ }
3584
+ throw new Error("orderBy must be an object or array of objects");
3585
+ }
3586
+ function isScalarOrderByValue(v) {
3587
+ if (typeof v === "string") {
3588
+ const lower = v.toLowerCase();
3589
+ return lower === "asc" || lower === "desc";
3590
+ }
3591
+ if (isPlainObject(v) && isScalarSortConfig(v)) return true;
3592
+ return false;
3593
+ }
3559
3594
  var flipOrderByArray = (orderBy) => {
3560
- return orderBy.map((item) => {
3561
- const [k, v] = assertSingleFieldObject(item);
3562
- return { [k]: flipValue(v) };
3563
- });
3595
+ const result = [];
3596
+ for (const item of orderBy) {
3597
+ for (const [k, v] of expandToSingleFieldEntries(item)) {
3598
+ result.push({ [k]: flipValue(v) });
3599
+ }
3600
+ }
3601
+ return result;
3564
3602
  };
3565
3603
  var flipOrderByObject = (orderBy) => {
3566
3604
  const out = {};
@@ -3580,18 +3618,24 @@ function reverseOrderByInput(orderBy) {
3580
3618
  throw new Error("orderBy must be an object or array of objects");
3581
3619
  }
3582
3620
  var normalizePairs = (pairs, parseValue) => {
3583
- return pairs.map(([field, rawValue]) => {
3621
+ const result = [];
3622
+ for (const [field, rawValue] of pairs) {
3623
+ if (!isScalarOrderByValue(rawValue)) continue;
3584
3624
  const parsed = parseValue(rawValue, field);
3585
- return {
3625
+ result.push({
3586
3626
  [field]: parsed.nulls !== void 0 ? { direction: parsed.direction, nulls: parsed.nulls } : parsed.direction
3587
- };
3588
- });
3627
+ });
3628
+ }
3629
+ return result;
3589
3630
  };
3590
3631
  function normalizeOrderByInput(orderBy, parseValue) {
3591
3632
  if (!isNotNullish(orderBy)) return [];
3592
3633
  if (Array.isArray(orderBy)) {
3593
- const pairs = orderBy.map(assertSingleFieldObject);
3594
- return normalizePairs(pairs, parseValue);
3634
+ const allPairs = [];
3635
+ for (const item of orderBy) {
3636
+ allPairs.push(...expandToSingleFieldEntries(item));
3637
+ }
3638
+ return normalizePairs(allPairs, parseValue);
3595
3639
  }
3596
3640
  if (isPlainObject(orderBy)) {
3597
3641
  return normalizePairs(Object.entries(orderBy), parseValue);
@@ -3603,9 +3647,11 @@ function normalizeAndValidateOrderBy(orderBy, model, parseValue) {
3603
3647
  const normalized = normalizeOrderByInput(orderBy, parseValue);
3604
3648
  const entries = [];
3605
3649
  const scalarSet = getScalarFieldSet(model);
3650
+ const relationSet = getRelationFieldSet(model);
3606
3651
  for (const item of normalized) {
3607
3652
  const [[field, value]] = Object.entries(item);
3608
3653
  if (!scalarSet.has(field)) {
3654
+ if (relationSet.has(field)) continue;
3609
3655
  throw new Error(
3610
3656
  `orderBy field '${field}' not found on model ${model.name}`
3611
3657
  );
@@ -4064,16 +4110,6 @@ function buildOrderBy(orderBy, alias, dialect, model) {
4064
4110
  const d = dialect != null ? dialect : getGlobalDialect();
4065
4111
  return buildOrderByFragment(entries, alias, d, model);
4066
4112
  }
4067
- function buildOrderByClause(args, alias, dialect, model) {
4068
- if (!isNotNullish(args.orderBy)) return "";
4069
- const result = buildOrderBy(args.orderBy, alias, dialect, model);
4070
- if (!isNonEmptyString(result)) {
4071
- throw new Error(
4072
- "buildOrderByClause: orderBy specified but produced empty result"
4073
- );
4074
- }
4075
- return result;
4076
- }
4077
4113
  function normalizeTakeLike(v) {
4078
4114
  const n = normalizeIntLike("take", v, {
4079
4115
  min: Number.MIN_SAFE_INTEGER,
@@ -7848,6 +7884,114 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect, out
7848
7884
  });
7849
7885
  }
7850
7886
 
7887
+ // src/builder/shared/order-by-relation.ts
7888
+ function resolveTableRef(model, dialect) {
7889
+ const tableName = model.tableName || model.dbName || model.name;
7890
+ if (dialect === "sqlite") {
7891
+ return quote(tableName);
7892
+ }
7893
+ const schema = model.schema || model.schemaName || "public";
7894
+ return buildTableReference(schema, tableName, dialect);
7895
+ }
7896
+ function findRelationField(model, fieldName) {
7897
+ return model.fields.find((f) => f.name === fieldName && f.isRelation);
7898
+ }
7899
+ function buildOrderByWithRelations(orderBy, alias, dialect, model, schemas) {
7900
+ if (!isNotNullish(orderBy)) return { sql: "", joins: [] };
7901
+ const expanded = expandOrderByInput(orderBy);
7902
+ if (expanded.length === 0) return { sql: "", joins: [] };
7903
+ const relationSet = getRelationFieldSet(model);
7904
+ const scalarSet = getScalarFieldSet(model);
7905
+ const orderFragments = [];
7906
+ const joins = [];
7907
+ const usedAliases = /* @__PURE__ */ new Set();
7908
+ let relAliasCounter = 0;
7909
+ for (const [fieldName, value] of expanded) {
7910
+ if (scalarSet.has(fieldName)) {
7911
+ const entries = normalizeAndValidateOrderBy(
7912
+ [{ [fieldName]: value }],
7913
+ model,
7914
+ parseOrderByValue
7915
+ );
7916
+ const sql = buildOrderByFragment(entries, alias, dialect, model);
7917
+ if (sql) orderFragments.push(sql);
7918
+ continue;
7919
+ }
7920
+ if (relationSet.has(fieldName)) {
7921
+ if (!isPlainObject(value)) {
7922
+ throw new Error(`Relation orderBy for '${fieldName}' must be an object`);
7923
+ }
7924
+ const nestedEntries = Object.entries(value);
7925
+ if (nestedEntries.length === 0) continue;
7926
+ if ("_count" in value) {
7927
+ throw new Error(
7928
+ `Relation orderBy with _count on '${fieldName}' is not yet supported by prisma-sql`
7929
+ );
7930
+ }
7931
+ const field = findRelationField(model, fieldName);
7932
+ if (!field) {
7933
+ throw new Error(
7934
+ `Relation field '${fieldName}' not found on model ${model.name}`
7935
+ );
7936
+ }
7937
+ const relatedModel = getModelByName(schemas, field.relatedModel);
7938
+ if (!relatedModel) {
7939
+ throw new Error(
7940
+ `Related model '${field.relatedModel}' not found for relation '${fieldName}'`
7941
+ );
7942
+ }
7943
+ const relScalarSet = getScalarFieldSet(relatedModel);
7944
+ const relRelationSet = getRelationFieldSet(relatedModel);
7945
+ for (const [nestedField] of nestedEntries) {
7946
+ if (relRelationSet.has(nestedField)) {
7947
+ throw new Error(
7948
+ `Nested relation orderBy (${fieldName}.${nestedField}) is not yet supported by prisma-sql`
7949
+ );
7950
+ }
7951
+ if (!relScalarSet.has(nestedField)) {
7952
+ throw new Error(
7953
+ `orderBy field '${nestedField}' does not exist on related model '${relatedModel.name}'`
7954
+ );
7955
+ }
7956
+ }
7957
+ let joinAlias;
7958
+ do {
7959
+ joinAlias = `ob_${relAliasCounter++}`;
7960
+ } while (usedAliases.has(joinAlias));
7961
+ usedAliases.add(joinAlias);
7962
+ const tableRef = resolveTableRef(relatedModel, dialect);
7963
+ const cond = joinCondition(
7964
+ field,
7965
+ model,
7966
+ relatedModel,
7967
+ alias,
7968
+ joinAlias
7969
+ );
7970
+ joins.push(`LEFT JOIN ${tableRef} ${joinAlias} ON ${cond}`);
7971
+ const entries = normalizeAndValidateOrderBy(
7972
+ [value],
7973
+ relatedModel,
7974
+ parseOrderByValue
7975
+ );
7976
+ const sql = buildOrderByFragment(
7977
+ entries,
7978
+ joinAlias,
7979
+ dialect,
7980
+ relatedModel
7981
+ );
7982
+ if (sql) orderFragments.push(sql);
7983
+ continue;
7984
+ }
7985
+ throw new Error(
7986
+ `orderBy field '${fieldName}' does not exist on model ${model.name}`
7987
+ );
7988
+ }
7989
+ return {
7990
+ sql: orderFragments.join(SQL_SEPARATORS.ORDER_BY),
7991
+ joins
7992
+ };
7993
+ }
7994
+
7851
7995
  // src/builder/select.ts
7852
7996
  function normalizeOrderByInput3(orderBy) {
7853
7997
  return normalizeOrderByInput(orderBy, parseOrderByValue);
@@ -7927,20 +8071,13 @@ Available fields: ${[...scalarSet].join(", ")}`
7927
8071
  assertScalarField(model, f, "distinct");
7928
8072
  }
7929
8073
  }
7930
- function validateOrderBy(model, orderBy) {
8074
+ function validateOrderBy(model, orderBy, schemas) {
7931
8075
  if (!isNotNullish(orderBy)) return;
7932
- const items = normalizeOrderByInput3(orderBy);
7933
- if (items.length === 0) return;
8076
+ const expanded = expandOrderByInput(orderBy);
8077
+ if (expanded.length === 0) return;
7934
8078
  const scalarSet = getScalarFieldSet(model);
7935
8079
  const relationSet = getRelationFieldSet(model);
7936
- for (const it of items) {
7937
- const entries = Object.entries(it);
7938
- if (entries.length !== 1) {
7939
- throw new Error(
7940
- `orderBy array entries must have exactly one field. Got ${entries.length} fields: ${Object.keys(it).join(", ")}`
7941
- );
7942
- }
7943
- const [fieldName, value] = entries[0];
8080
+ for (const [fieldName, value] of expanded) {
7944
8081
  const f = String(fieldName).trim();
7945
8082
  if (f.length === 0) {
7946
8083
  throw new Error("orderBy field name cannot be empty");
@@ -7950,20 +8087,21 @@ function validateOrderBy(model, orderBy) {
7950
8087
  `orderBy field name too long (${f.length} chars, max 255): ${f.slice(0, 50)}...`
7951
8088
  );
7952
8089
  }
7953
- if (!scalarSet.has(f)) {
7954
- if (relationSet.has(f)) {
7955
- throw new Error(
7956
- `orderBy field '${f}' is a relation field. Only scalar fields are allowed.
7957
- Available scalar fields: ${[...scalarSet].join(", ")}`
7958
- );
8090
+ if (scalarSet.has(f)) {
8091
+ assertScalarField(model, f, "orderBy");
8092
+ parseOrderByValue(value, f);
8093
+ continue;
8094
+ }
8095
+ if (relationSet.has(f)) {
8096
+ if (!isPlainObject(value)) {
8097
+ throw new Error(`Relation orderBy for '${f}' must be an object`);
7959
8098
  }
7960
- throw new Error(
7961
- `orderBy field '${f}' does not exist on model ${model.name}.
7962
- Available fields: ${[...scalarSet].join(", ")}`
7963
- );
8099
+ continue;
7964
8100
  }
7965
- assertScalarField(model, f, "orderBy");
7966
- parseOrderByValue(value, f);
8101
+ throw new Error(
8102
+ `orderBy field '${f}' does not exist on model ${model.name}.
8103
+ Available fields: ${[...scalarSet].join(", ")}`
8104
+ );
7967
8105
  }
7968
8106
  }
7969
8107
  function validateCursor(model, cursor, distinct) {
@@ -8046,11 +8184,12 @@ function buildSelectSpec(input) {
8046
8184
  model,
8047
8185
  alias
8048
8186
  );
8049
- const orderByClause = buildOrderByClause(
8050
- normalizedArgs,
8187
+ const orderByResult = buildOrderByWithRelations(
8188
+ normalizedArgs.orderBy,
8051
8189
  alias,
8052
8190
  dialect,
8053
- model
8191
+ model,
8192
+ schemas
8054
8193
  );
8055
8194
  const { take, skip, cursor } = getPaginationParams(method, normalizedArgs);
8056
8195
  const params = createParamStoreFrom(
@@ -8083,13 +8222,15 @@ function buildSelectSpec(input) {
8083
8222
  "Cursor pagination with distinct is not supported in SQLite due to window function limitations. Use findMany with skip/take instead, or remove distinct."
8084
8223
  );
8085
8224
  }
8225
+ const orderByJoins = orderByResult.joins;
8226
+ const combinedWhereJoins = whereResult.joins ? [...whereResult.joins, ...orderByJoins] : orderByJoins.length > 0 ? orderByJoins : [];
8086
8227
  return {
8087
8228
  select: selectFields,
8088
8229
  includes,
8089
8230
  from: { table: tableName, alias },
8090
8231
  whereClause: whereResult.clause,
8091
- whereJoins: whereResult.joins,
8092
- orderBy: orderByClause,
8232
+ whereJoins: combinedWhereJoins,
8233
+ orderBy: orderByResult.sql,
8093
8234
  pagination: { take, skip },
8094
8235
  distinct: normalizedArgs.distinct,
8095
8236
  method,