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.
@@ -2323,7 +2323,7 @@ var require_package = __commonJS({
2323
2323
  "package.json"(exports$1, module) {
2324
2324
  module.exports = {
2325
2325
  name: "prisma-sql",
2326
- version: "1.74.0",
2326
+ version: "1.75.0",
2327
2327
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
2328
2328
  main: "dist/index.cjs",
2329
2329
  module: "dist/index.js",
@@ -3536,7 +3536,7 @@ var getNextSort = (sortRaw) => {
3536
3536
  if (s === "desc") return "asc";
3537
3537
  return sortRaw;
3538
3538
  };
3539
- var flipObjectSort = (obj) => {
3539
+ var flipScalarSortObject = (obj) => {
3540
3540
  const out = __spreadValues({}, obj);
3541
3541
  const hasSort = Object.prototype.hasOwnProperty.call(obj, "sort");
3542
3542
  const hasDirection = Object.prototype.hasOwnProperty.call(obj, "direction");
@@ -3552,26 +3552,64 @@ var flipObjectSort = (obj) => {
3552
3552
  }
3553
3553
  return out;
3554
3554
  };
3555
+ function isScalarSortConfig(obj) {
3556
+ return Object.prototype.hasOwnProperty.call(obj, "sort") || Object.prototype.hasOwnProperty.call(obj, "direction");
3557
+ }
3558
+ function flipRelationOrderByValue(obj) {
3559
+ const out = {};
3560
+ for (const [k, v] of Object.entries(obj)) {
3561
+ out[k] = flipValue(v);
3562
+ }
3563
+ return out;
3564
+ }
3555
3565
  var flipValue = (v) => {
3556
3566
  if (typeof v === "string") return flipSortString(v);
3557
- if (isPlainObject(v)) return flipObjectSort(v);
3567
+ if (isPlainObject(v)) {
3568
+ if (isScalarSortConfig(v)) return flipScalarSortObject(v);
3569
+ return flipRelationOrderByValue(v);
3570
+ }
3558
3571
  return v;
3559
3572
  };
3560
- var assertSingleFieldObject = (item) => {
3573
+ var expandToSingleFieldEntries = (item) => {
3561
3574
  if (!isPlainObject(item)) {
3562
3575
  throw new Error("orderBy array entries must be objects");
3563
3576
  }
3564
3577
  const entries = Object.entries(item);
3565
- if (entries.length !== 1) {
3566
- throw new Error("orderBy array entries must have exactly one field");
3578
+ if (entries.length === 0) {
3579
+ throw new Error("orderBy array entries must have at least one field");
3567
3580
  }
3568
- return entries[0];
3581
+ return entries;
3569
3582
  };
3583
+ function expandOrderByInput(orderBy) {
3584
+ if (!isNotNullish(orderBy)) return [];
3585
+ if (Array.isArray(orderBy)) {
3586
+ const result = [];
3587
+ for (const item of orderBy) {
3588
+ result.push(...expandToSingleFieldEntries(item));
3589
+ }
3590
+ return result;
3591
+ }
3592
+ if (isPlainObject(orderBy)) {
3593
+ return Object.entries(orderBy);
3594
+ }
3595
+ throw new Error("orderBy must be an object or array of objects");
3596
+ }
3597
+ function isScalarOrderByValue(v) {
3598
+ if (typeof v === "string") {
3599
+ const lower = v.toLowerCase();
3600
+ return lower === "asc" || lower === "desc";
3601
+ }
3602
+ if (isPlainObject(v) && isScalarSortConfig(v)) return true;
3603
+ return false;
3604
+ }
3570
3605
  var flipOrderByArray = (orderBy) => {
3571
- return orderBy.map((item) => {
3572
- const [k, v] = assertSingleFieldObject(item);
3573
- return { [k]: flipValue(v) };
3574
- });
3606
+ const result = [];
3607
+ for (const item of orderBy) {
3608
+ for (const [k, v] of expandToSingleFieldEntries(item)) {
3609
+ result.push({ [k]: flipValue(v) });
3610
+ }
3611
+ }
3612
+ return result;
3575
3613
  };
3576
3614
  var flipOrderByObject = (orderBy) => {
3577
3615
  const out = {};
@@ -3591,18 +3629,24 @@ function reverseOrderByInput(orderBy) {
3591
3629
  throw new Error("orderBy must be an object or array of objects");
3592
3630
  }
3593
3631
  var normalizePairs = (pairs, parseValue) => {
3594
- return pairs.map(([field, rawValue]) => {
3632
+ const result = [];
3633
+ for (const [field, rawValue] of pairs) {
3634
+ if (!isScalarOrderByValue(rawValue)) continue;
3595
3635
  const parsed = parseValue(rawValue, field);
3596
- return {
3636
+ result.push({
3597
3637
  [field]: parsed.nulls !== void 0 ? { direction: parsed.direction, nulls: parsed.nulls } : parsed.direction
3598
- };
3599
- });
3638
+ });
3639
+ }
3640
+ return result;
3600
3641
  };
3601
3642
  function normalizeOrderByInput(orderBy, parseValue) {
3602
3643
  if (!isNotNullish(orderBy)) return [];
3603
3644
  if (Array.isArray(orderBy)) {
3604
- const pairs = orderBy.map(assertSingleFieldObject);
3605
- return normalizePairs(pairs, parseValue);
3645
+ const allPairs = [];
3646
+ for (const item of orderBy) {
3647
+ allPairs.push(...expandToSingleFieldEntries(item));
3648
+ }
3649
+ return normalizePairs(allPairs, parseValue);
3606
3650
  }
3607
3651
  if (isPlainObject(orderBy)) {
3608
3652
  return normalizePairs(Object.entries(orderBy), parseValue);
@@ -3614,9 +3658,11 @@ function normalizeAndValidateOrderBy(orderBy, model, parseValue) {
3614
3658
  const normalized = normalizeOrderByInput(orderBy, parseValue);
3615
3659
  const entries = [];
3616
3660
  const scalarSet = getScalarFieldSet(model);
3661
+ const relationSet = getRelationFieldSet(model);
3617
3662
  for (const item of normalized) {
3618
3663
  const [[field, value]] = Object.entries(item);
3619
3664
  if (!scalarSet.has(field)) {
3665
+ if (relationSet.has(field)) continue;
3620
3666
  throw new Error(
3621
3667
  `orderBy field '${field}' not found on model ${model.name}`
3622
3668
  );
@@ -4075,16 +4121,6 @@ function buildOrderBy(orderBy, alias, dialect, model) {
4075
4121
  const d = dialect != null ? dialect : getGlobalDialect();
4076
4122
  return buildOrderByFragment(entries, alias, d, model);
4077
4123
  }
4078
- function buildOrderByClause(args, alias, dialect, model) {
4079
- if (!isNotNullish(args.orderBy)) return "";
4080
- const result = buildOrderBy(args.orderBy, alias, dialect, model);
4081
- if (!isNonEmptyString(result)) {
4082
- throw new Error(
4083
- "buildOrderByClause: orderBy specified but produced empty result"
4084
- );
4085
- }
4086
- return result;
4087
- }
4088
4124
  function normalizeTakeLike(v) {
4089
4125
  const n = normalizeIntLike("take", v, {
4090
4126
  min: Number.MIN_SAFE_INTEGER,
@@ -7859,6 +7895,114 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect, out
7859
7895
  });
7860
7896
  }
7861
7897
 
7898
+ // src/builder/shared/order-by-relation.ts
7899
+ function resolveTableRef(model, dialect) {
7900
+ const tableName = model.tableName || model.dbName || model.name;
7901
+ if (dialect === "sqlite") {
7902
+ return quote(tableName);
7903
+ }
7904
+ const schema = model.schema || model.schemaName || "public";
7905
+ return buildTableReference(schema, tableName, dialect);
7906
+ }
7907
+ function findRelationField(model, fieldName) {
7908
+ return model.fields.find((f) => f.name === fieldName && f.isRelation);
7909
+ }
7910
+ function buildOrderByWithRelations(orderBy, alias, dialect, model, schemas) {
7911
+ if (!isNotNullish(orderBy)) return { sql: "", joins: [] };
7912
+ const expanded = expandOrderByInput(orderBy);
7913
+ if (expanded.length === 0) return { sql: "", joins: [] };
7914
+ const relationSet = getRelationFieldSet(model);
7915
+ const scalarSet = getScalarFieldSet(model);
7916
+ const orderFragments = [];
7917
+ const joins = [];
7918
+ const usedAliases = /* @__PURE__ */ new Set();
7919
+ let relAliasCounter = 0;
7920
+ for (const [fieldName, value] of expanded) {
7921
+ if (scalarSet.has(fieldName)) {
7922
+ const entries = normalizeAndValidateOrderBy(
7923
+ [{ [fieldName]: value }],
7924
+ model,
7925
+ parseOrderByValue
7926
+ );
7927
+ const sql = buildOrderByFragment(entries, alias, dialect, model);
7928
+ if (sql) orderFragments.push(sql);
7929
+ continue;
7930
+ }
7931
+ if (relationSet.has(fieldName)) {
7932
+ if (!isPlainObject(value)) {
7933
+ throw new Error(`Relation orderBy for '${fieldName}' must be an object`);
7934
+ }
7935
+ const nestedEntries = Object.entries(value);
7936
+ if (nestedEntries.length === 0) continue;
7937
+ if ("_count" in value) {
7938
+ throw new Error(
7939
+ `Relation orderBy with _count on '${fieldName}' is not yet supported by prisma-sql`
7940
+ );
7941
+ }
7942
+ const field = findRelationField(model, fieldName);
7943
+ if (!field) {
7944
+ throw new Error(
7945
+ `Relation field '${fieldName}' not found on model ${model.name}`
7946
+ );
7947
+ }
7948
+ const relatedModel = getModelByName(schemas, field.relatedModel);
7949
+ if (!relatedModel) {
7950
+ throw new Error(
7951
+ `Related model '${field.relatedModel}' not found for relation '${fieldName}'`
7952
+ );
7953
+ }
7954
+ const relScalarSet = getScalarFieldSet(relatedModel);
7955
+ const relRelationSet = getRelationFieldSet(relatedModel);
7956
+ for (const [nestedField] of nestedEntries) {
7957
+ if (relRelationSet.has(nestedField)) {
7958
+ throw new Error(
7959
+ `Nested relation orderBy (${fieldName}.${nestedField}) is not yet supported by prisma-sql`
7960
+ );
7961
+ }
7962
+ if (!relScalarSet.has(nestedField)) {
7963
+ throw new Error(
7964
+ `orderBy field '${nestedField}' does not exist on related model '${relatedModel.name}'`
7965
+ );
7966
+ }
7967
+ }
7968
+ let joinAlias;
7969
+ do {
7970
+ joinAlias = `ob_${relAliasCounter++}`;
7971
+ } while (usedAliases.has(joinAlias));
7972
+ usedAliases.add(joinAlias);
7973
+ const tableRef = resolveTableRef(relatedModel, dialect);
7974
+ const cond = joinCondition(
7975
+ field,
7976
+ model,
7977
+ relatedModel,
7978
+ alias,
7979
+ joinAlias
7980
+ );
7981
+ joins.push(`LEFT JOIN ${tableRef} ${joinAlias} ON ${cond}`);
7982
+ const entries = normalizeAndValidateOrderBy(
7983
+ [value],
7984
+ relatedModel,
7985
+ parseOrderByValue
7986
+ );
7987
+ const sql = buildOrderByFragment(
7988
+ entries,
7989
+ joinAlias,
7990
+ dialect,
7991
+ relatedModel
7992
+ );
7993
+ if (sql) orderFragments.push(sql);
7994
+ continue;
7995
+ }
7996
+ throw new Error(
7997
+ `orderBy field '${fieldName}' does not exist on model ${model.name}`
7998
+ );
7999
+ }
8000
+ return {
8001
+ sql: orderFragments.join(SQL_SEPARATORS.ORDER_BY),
8002
+ joins
8003
+ };
8004
+ }
8005
+
7862
8006
  // src/builder/select.ts
7863
8007
  function normalizeOrderByInput3(orderBy) {
7864
8008
  return normalizeOrderByInput(orderBy, parseOrderByValue);
@@ -7938,20 +8082,13 @@ Available fields: ${[...scalarSet].join(", ")}`
7938
8082
  assertScalarField(model, f, "distinct");
7939
8083
  }
7940
8084
  }
7941
- function validateOrderBy(model, orderBy) {
8085
+ function validateOrderBy(model, orderBy, schemas) {
7942
8086
  if (!isNotNullish(orderBy)) return;
7943
- const items = normalizeOrderByInput3(orderBy);
7944
- if (items.length === 0) return;
8087
+ const expanded = expandOrderByInput(orderBy);
8088
+ if (expanded.length === 0) return;
7945
8089
  const scalarSet = getScalarFieldSet(model);
7946
8090
  const relationSet = getRelationFieldSet(model);
7947
- for (const it of items) {
7948
- const entries = Object.entries(it);
7949
- if (entries.length !== 1) {
7950
- throw new Error(
7951
- `orderBy array entries must have exactly one field. Got ${entries.length} fields: ${Object.keys(it).join(", ")}`
7952
- );
7953
- }
7954
- const [fieldName, value] = entries[0];
8091
+ for (const [fieldName, value] of expanded) {
7955
8092
  const f = String(fieldName).trim();
7956
8093
  if (f.length === 0) {
7957
8094
  throw new Error("orderBy field name cannot be empty");
@@ -7961,20 +8098,21 @@ function validateOrderBy(model, orderBy) {
7961
8098
  `orderBy field name too long (${f.length} chars, max 255): ${f.slice(0, 50)}...`
7962
8099
  );
7963
8100
  }
7964
- if (!scalarSet.has(f)) {
7965
- if (relationSet.has(f)) {
7966
- throw new Error(
7967
- `orderBy field '${f}' is a relation field. Only scalar fields are allowed.
7968
- Available scalar fields: ${[...scalarSet].join(", ")}`
7969
- );
8101
+ if (scalarSet.has(f)) {
8102
+ assertScalarField(model, f, "orderBy");
8103
+ parseOrderByValue(value, f);
8104
+ continue;
8105
+ }
8106
+ if (relationSet.has(f)) {
8107
+ if (!isPlainObject(value)) {
8108
+ throw new Error(`Relation orderBy for '${f}' must be an object`);
7970
8109
  }
7971
- throw new Error(
7972
- `orderBy field '${f}' does not exist on model ${model.name}.
7973
- Available fields: ${[...scalarSet].join(", ")}`
7974
- );
8110
+ continue;
7975
8111
  }
7976
- assertScalarField(model, f, "orderBy");
7977
- parseOrderByValue(value, f);
8112
+ throw new Error(
8113
+ `orderBy field '${f}' does not exist on model ${model.name}.
8114
+ Available fields: ${[...scalarSet].join(", ")}`
8115
+ );
7978
8116
  }
7979
8117
  }
7980
8118
  function validateCursor(model, cursor, distinct) {
@@ -8057,11 +8195,12 @@ function buildSelectSpec(input) {
8057
8195
  model,
8058
8196
  alias
8059
8197
  );
8060
- const orderByClause = buildOrderByClause(
8061
- normalizedArgs,
8198
+ const orderByResult = buildOrderByWithRelations(
8199
+ normalizedArgs.orderBy,
8062
8200
  alias,
8063
8201
  dialect,
8064
- model
8202
+ model,
8203
+ schemas
8065
8204
  );
8066
8205
  const { take, skip, cursor } = getPaginationParams(method, normalizedArgs);
8067
8206
  const params = createParamStoreFrom(
@@ -8094,13 +8233,15 @@ function buildSelectSpec(input) {
8094
8233
  "Cursor pagination with distinct is not supported in SQLite due to window function limitations. Use findMany with skip/take instead, or remove distinct."
8095
8234
  );
8096
8235
  }
8236
+ const orderByJoins = orderByResult.joins;
8237
+ const combinedWhereJoins = whereResult.joins ? [...whereResult.joins, ...orderByJoins] : orderByJoins.length > 0 ? orderByJoins : [];
8097
8238
  return {
8098
8239
  select: selectFields,
8099
8240
  includes,
8100
8241
  from: { table: tableName, alias },
8101
8242
  whereClause: whereResult.clause,
8102
- whereJoins: whereResult.joins,
8103
- orderBy: orderByClause,
8243
+ whereJoins: combinedWhereJoins,
8244
+ orderBy: orderByResult.sql,
8104
8245
  pagination: { take, skip },
8105
8246
  distinct: normalizedArgs.distinct,
8106
8247
  method,