prisma-sql 1.54.0 → 1.55.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
@@ -54,7 +54,7 @@ var require_package = __commonJS({
54
54
  "package.json"(exports$1, module) {
55
55
  module.exports = {
56
56
  name: "prisma-sql",
57
- version: "1.54.0",
57
+ version: "1.55.0",
58
58
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
59
59
  main: "dist/index.cjs",
60
60
  module: "dist/index.js",
@@ -813,6 +813,9 @@ function assertNoControlChars(label, s) {
813
813
  );
814
814
  }
815
815
  }
816
+ function quoteRawIdent(id) {
817
+ return `"${id.replace(/"/g, '""')}"`;
818
+ }
816
819
  function isIdentCharCode(c) {
817
820
  return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
818
821
  }
@@ -834,33 +837,33 @@ function parseQuotedPart(input, start) {
834
837
  }
835
838
  if (!sawAny) {
836
839
  throw new Error(
837
- `tableName/tableRef has empty quoted identifier part: ${JSON.stringify(input)}`
840
+ `qualified name has empty quoted identifier part: ${JSON.stringify(input)}`
838
841
  );
839
842
  }
840
843
  return i + 1;
841
844
  }
842
845
  if (c === 10 || c === 13 || c === 0) {
843
846
  throw new Error(
844
- `tableName/tableRef contains invalid characters: ${JSON.stringify(input)}`
847
+ `qualified name contains invalid characters: ${JSON.stringify(input)}`
845
848
  );
846
849
  }
847
850
  sawAny = true;
848
851
  i++;
849
852
  }
850
853
  throw new Error(
851
- `tableName/tableRef has unterminated quoted identifier: ${JSON.stringify(input)}`
854
+ `qualified name has unterminated quoted identifier: ${JSON.stringify(input)}`
852
855
  );
853
856
  }
854
857
  function parseUnquotedPart(input, start) {
855
858
  const n = input.length;
856
859
  let i = start;
857
860
  if (i >= n) {
858
- throw new Error(`tableName/tableRef is invalid: ${JSON.stringify(input)}`);
861
+ throw new Error(`qualified name is invalid: ${JSON.stringify(input)}`);
859
862
  }
860
863
  const c0 = input.charCodeAt(i);
861
864
  if (!isIdentStartCharCode(c0)) {
862
865
  throw new Error(
863
- `tableName/tableRef must use identifiers (or quoted identifiers). Got: ${JSON.stringify(input)}`
866
+ `qualified name must use identifiers (or quoted identifiers). Got: ${JSON.stringify(input)}`
864
867
  );
865
868
  }
866
869
  i++;
@@ -869,15 +872,15 @@ function parseUnquotedPart(input, start) {
869
872
  if (c === 46) break;
870
873
  if (!isIdentCharCode(c)) {
871
874
  throw new Error(
872
- `tableName/tableRef contains invalid identifier characters: ${JSON.stringify(input)}`
875
+ `qualified name contains invalid identifier characters: ${JSON.stringify(input)}`
873
876
  );
874
877
  }
875
878
  i++;
876
879
  }
877
880
  return i;
878
881
  }
879
- function assertSafeQualifiedName(tableRef) {
880
- const raw = String(tableRef);
882
+ function assertSafeQualifiedName(input) {
883
+ const raw = String(input);
881
884
  const trimmed = raw.trim();
882
885
  if (trimmed.length === 0) {
883
886
  throw new Error("tableName/tableRef is required and cannot be empty");
@@ -951,7 +954,7 @@ function quote2(id) {
951
954
  );
952
955
  }
953
956
  if (needsQuoting(id)) {
954
- return `"${id.replace(/"/g, '""')}"`;
957
+ return quoteRawIdent(id);
955
958
  }
956
959
  return id;
957
960
  }
@@ -2590,28 +2593,24 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
2590
2593
  function toSafeSqlIdentifier(input) {
2591
2594
  const raw = String(input);
2592
2595
  const n = raw.length;
2596
+ if (n === 0) return "_t";
2593
2597
  let out = "";
2594
2598
  for (let i = 0; i < n; i++) {
2595
2599
  const c = raw.charCodeAt(i);
2596
2600
  const isAZ = c >= 65 && c <= 90 || c >= 97 && c <= 122;
2597
2601
  const is09 = c >= 48 && c <= 57;
2598
2602
  const isUnderscore = c === 95;
2599
- if (isAZ || is09 || isUnderscore) {
2600
- out += raw[i];
2601
- } else {
2602
- out += "_";
2603
- }
2603
+ out += isAZ || is09 || isUnderscore ? raw[i] : "_";
2604
2604
  }
2605
- if (out.length === 0) out = "_t";
2606
2605
  const c0 = out.charCodeAt(0);
2607
2606
  const startsOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95;
2608
- if (!startsOk) out = `_${out}`;
2609
- const lowered = out.toLowerCase();
2607
+ const lowered = (startsOk ? out : `_${out}`).toLowerCase();
2610
2608
  return ALIAS_FORBIDDEN_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
2611
2609
  }
2612
2610
  function createAliasGenerator(maxAliases = 1e4) {
2613
2611
  let counter = 0;
2614
2612
  const usedAliases = /* @__PURE__ */ new Set();
2613
+ const maxLen = 63;
2615
2614
  return {
2616
2615
  next(baseName) {
2617
2616
  if (usedAliases.size >= maxAliases) {
@@ -2621,14 +2620,13 @@ function createAliasGenerator(maxAliases = 1e4) {
2621
2620
  }
2622
2621
  const base = toSafeSqlIdentifier(baseName);
2623
2622
  const suffix = `_${counter}`;
2624
- const maxLen = 63;
2625
2623
  const baseMax = Math.max(1, maxLen - suffix.length);
2626
2624
  const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
2627
2625
  const alias = `${trimmedBase}${suffix}`;
2628
2626
  counter += 1;
2629
2627
  if (usedAliases.has(alias)) {
2630
2628
  throw new Error(
2631
- `CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
2629
+ `CRITICAL: Duplicate alias '${alias}' at counter=${counter}.`
2632
2630
  );
2633
2631
  }
2634
2632
  usedAliases.add(alias);
@@ -2680,24 +2678,19 @@ function normalizeDynamicNameOrThrow(dynamicName, index) {
2680
2678
  }
2681
2679
  return dn;
2682
2680
  }
2683
- function assertUniqueDynamicName(dn, seen) {
2684
- if (seen.has(dn)) {
2685
- throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
2686
- }
2687
- seen.add(dn);
2688
- }
2689
- function validateMappingEntry(m, expectedIndex, seenDynamic) {
2690
- assertSequentialIndex(m.index, expectedIndex);
2691
- assertExactlyOneOfDynamicOrValue(m);
2692
- if (typeof m.dynamicName === "string") {
2693
- const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2694
- assertUniqueDynamicName(dn, seenDynamic);
2695
- }
2696
- }
2697
2681
  function validateMappings(mappings) {
2698
2682
  const seenDynamic = /* @__PURE__ */ new Set();
2699
2683
  for (let i = 0; i < mappings.length; i++) {
2700
- validateMappingEntry(mappings[i], i + 1, seenDynamic);
2684
+ const m = mappings[i];
2685
+ assertSequentialIndex(m.index, i + 1);
2686
+ assertExactlyOneOfDynamicOrValue(m);
2687
+ if (typeof m.dynamicName === "string") {
2688
+ const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2689
+ if (seenDynamic.has(dn)) {
2690
+ throw new Error(`CRITICAL: Duplicate dynamic param name: ${dn}`);
2691
+ }
2692
+ seenDynamic.add(dn);
2693
+ }
2701
2694
  }
2702
2695
  }
2703
2696
  function validateState(params, mappings, index) {
@@ -2709,16 +2702,19 @@ function validateState(params, mappings, index) {
2709
2702
  }
2710
2703
  function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2711
2704
  let index = startIndex;
2712
- const params = initialParams.length > 0 ? [...initialParams] : [];
2713
- const mappings = initialMappings.length > 0 ? [...initialMappings] : [];
2705
+ const params = initialParams.length > 0 ? initialParams.slice() : [];
2706
+ const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
2714
2707
  const dynamicNameToIndex = /* @__PURE__ */ new Map();
2715
- for (const m of mappings) {
2708
+ for (let i = 0; i < mappings.length; i++) {
2709
+ const m = mappings[i];
2716
2710
  if (typeof m.dynamicName === "string") {
2717
2711
  dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
2718
2712
  }
2719
2713
  }
2720
2714
  let dirty = true;
2721
2715
  let cachedSnapshot = null;
2716
+ let frozenParams = null;
2717
+ let frozenMappings = null;
2722
2718
  function assertCanAdd() {
2723
2719
  if (index > MAX_PARAM_INDEX) {
2724
2720
  throw new Error(
@@ -2770,13 +2766,17 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
2770
2766
  }
2771
2767
  function snapshot() {
2772
2768
  if (!dirty && cachedSnapshot) return cachedSnapshot;
2769
+ if (!frozenParams) frozenParams = Object.freeze(params.slice());
2770
+ if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
2773
2771
  const snap = {
2774
2772
  index,
2775
- params,
2776
- mappings
2773
+ params: frozenParams,
2774
+ mappings: frozenMappings
2777
2775
  };
2778
2776
  cachedSnapshot = snap;
2779
2777
  dirty = false;
2778
+ frozenParams = null;
2779
+ frozenMappings = null;
2780
2780
  return snap;
2781
2781
  }
2782
2782
  return {
@@ -2800,11 +2800,11 @@ function createParamStore(startIndex = 1) {
2800
2800
  return createStoreInternal(startIndex);
2801
2801
  }
2802
2802
  function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
2803
- validateState([...existingParams], [...existingMappings], nextIndex);
2803
+ validateState(existingParams, existingMappings, nextIndex);
2804
2804
  return createStoreInternal(
2805
2805
  nextIndex,
2806
- [...existingParams],
2807
- [...existingMappings]
2806
+ existingParams.slice(),
2807
+ existingMappings.slice()
2808
2808
  );
2809
2809
  }
2810
2810
 
@@ -2986,7 +2986,7 @@ function getRelationTableReference(relModel, dialect) {
2986
2986
  dialect
2987
2987
  );
2988
2988
  }
2989
- function resolveRelationOrThrow(model, schemas, schemaByName, relName) {
2989
+ function resolveRelationOrThrow(model, schemaByName, relName) {
2990
2990
  const field = model.fields.find((f) => f.name === relName);
2991
2991
  if (!isNotNullish(field)) {
2992
2992
  throw new Error(
@@ -3040,8 +3040,9 @@ function validateOrderByForModel(model, orderBy) {
3040
3040
  throw new Error("orderBy array entries must have exactly one field");
3041
3041
  }
3042
3042
  const fieldName = String(entries[0][0]).trim();
3043
- if (fieldName.length === 0)
3043
+ if (fieldName.length === 0) {
3044
3044
  throw new Error("orderBy field name cannot be empty");
3045
+ }
3045
3046
  if (!scalarSet.has(fieldName)) {
3046
3047
  throw new Error(
3047
3048
  `orderBy references unknown or non-scalar field '${fieldName}' on model ${model.name}`
@@ -3100,8 +3101,9 @@ function extractRelationPaginationConfig(relArgs) {
3100
3101
  function maybeReverseNegativeTake(takeVal, hasOrderBy, orderByInput) {
3101
3102
  if (typeof takeVal !== "number") return { takeVal, orderByInput };
3102
3103
  if (takeVal >= 0) return { takeVal, orderByInput };
3103
- if (!hasOrderBy)
3104
+ if (!hasOrderBy) {
3104
3105
  throw new Error("Negative take requires orderBy for deterministic results");
3106
+ }
3105
3107
  return {
3106
3108
  takeVal: Math.abs(takeVal),
3107
3109
  orderByInput: reverseOrderByInput(orderByInput)
@@ -3111,9 +3113,7 @@ function finalizeOrderByForInclude(args) {
3111
3113
  if (args.hasOrderBy && isNotNullish(args.orderByInput)) {
3112
3114
  validateOrderByForModel(args.relModel, args.orderByInput);
3113
3115
  }
3114
- if (!args.hasPagination) {
3115
- return args.orderByInput;
3116
- }
3116
+ if (!args.hasPagination) return args.orderByInput;
3117
3117
  return ensureDeterministicOrderByInput({
3118
3118
  orderBy: args.hasOrderBy ? args.orderByInput : void 0,
3119
3119
  model: args.relModel,
@@ -3174,7 +3174,9 @@ function buildOrderBySql(finalOrderByInput, relAlias, dialect, relModel) {
3174
3174
  return isNotNullish(finalOrderByInput) ? buildOrderBy(finalOrderByInput, relAlias, dialect, relModel) : "";
3175
3175
  }
3176
3176
  function buildBaseSql(args) {
3177
- return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias} ${args.joins} ${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
3177
+ const joins = args.joins ? ` ${args.joins}` : "";
3178
+ const where = `${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
3179
+ return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias}${joins} ` + where;
3178
3180
  }
3179
3181
  function buildOneToOneIncludeSql(args) {
3180
3182
  const objExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
@@ -3186,9 +3188,7 @@ function buildOneToOneIncludeSql(args) {
3186
3188
  joinPredicate: args.joinPredicate,
3187
3189
  whereClause: args.whereClause
3188
3190
  });
3189
- if (args.orderBySql) {
3190
- sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3191
- }
3191
+ if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3192
3192
  if (isNotNullish(args.takeVal)) {
3193
3193
  return appendLimitOffset(
3194
3194
  sql,
@@ -3241,7 +3241,7 @@ function buildListIncludeSpec(args) {
3241
3241
  `include.${args.relName}`
3242
3242
  );
3243
3243
  const selectExpr = jsonAgg("row", args.ctx.dialect);
3244
- const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${rowAlias}`;
3244
+ const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
3245
3245
  return Object.freeze({ name: args.relName, sql, isOneToOne: false });
3246
3246
  }
3247
3247
  function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
@@ -3339,12 +3339,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
3339
3339
  `Query complexity limit exceeded: ${stats.totalSubqueries} subqueries generated. Maximum allowed: ${MAX_TOTAL_SUBQUERIES}. This indicates exponential include nesting. Stats: depth=${stats.maxDepth}, includes=${stats.totalIncludes}. Path: ${visitPath.join(" -> ")}. Simplify your include structure or split into multiple queries.`
3340
3340
  );
3341
3341
  }
3342
- const resolved = resolveRelationOrThrow(
3343
- model,
3344
- schemas,
3345
- schemaByName,
3346
- relName
3347
- );
3342
+ const resolved = resolveRelationOrThrow(model, schemaByName, relName);
3348
3343
  const relationPath = `${model.name}.${relName}`;
3349
3344
  const currentPath = [...visitPath, relationPath];
3350
3345
  if (visitPath.includes(relationPath)) {
@@ -3400,7 +3395,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
3400
3395
  stats
3401
3396
  );
3402
3397
  }
3403
- function resolveCountRelationOrThrow(relName, model, schemas, schemaByName) {
3398
+ function resolveCountRelationOrThrow(relName, model, schemaByName) {
3404
3399
  const relationSet = getRelationFieldSet(model);
3405
3400
  if (!relationSet.has(relName)) {
3406
3401
  throw new Error(
@@ -3408,10 +3403,11 @@ function resolveCountRelationOrThrow(relName, model, schemas, schemaByName) {
3408
3403
  );
3409
3404
  }
3410
3405
  const field = model.fields.find((f) => f.name === relName);
3411
- if (!field)
3406
+ if (!field) {
3412
3407
  throw new Error(
3413
3408
  `_count.${relName} references unknown relation on model ${model.name}`
3414
3409
  );
3410
+ }
3415
3411
  if (!isValidRelationField(field)) {
3416
3412
  throw new Error(
3417
3413
  `_count.${relName} has invalid relation metadata on model ${model.name}`
@@ -3439,8 +3435,9 @@ function defaultReferencesForCount(fkCount) {
3439
3435
  }
3440
3436
  function resolveCountKeyPairs(field) {
3441
3437
  const fkFields = normalizeKeyList(field.foreignKey);
3442
- if (fkFields.length === 0)
3438
+ if (fkFields.length === 0) {
3443
3439
  throw new Error("Relation count requires foreignKey");
3440
+ }
3444
3441
  const refsRaw = field.references;
3445
3442
  const refs = normalizeKeyList(refsRaw);
3446
3443
  const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
@@ -3516,12 +3513,7 @@ function buildRelationCountSql(countSelect, model, schemas, parentAlias, _params
3516
3513
  for (const m of schemas) schemaByName.set(m.name, m);
3517
3514
  for (const [relName, shouldCount] of Object.entries(countSelect)) {
3518
3515
  if (!shouldCount) continue;
3519
- const resolved = resolveCountRelationOrThrow(
3520
- relName,
3521
- model,
3522
- schemas,
3523
- schemaByName
3524
- );
3516
+ const resolved = resolveCountRelationOrThrow(relName, model, schemaByName);
3525
3517
  const built = buildCountJoinAndPair({
3526
3518
  relName,
3527
3519
  field: resolved.field,
@@ -4951,24 +4943,7 @@ function generateCode(models, queries, dialect, datamodel) {
4951
4943
  }));
4952
4944
  const { mappings, fieldTypes } = extractEnumMappings(datamodel);
4953
4945
  return `// Generated by @prisma-sql/generator - DO NOT EDIT
4954
- import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
4955
-
4956
- export interface BatchCountQuery {
4957
- model: string
4958
- method: 'count'
4959
- args?: { where?: Record<string, unknown> }
4960
- }
4961
-
4962
- export interface TransactionQuery {
4963
- model: string
4964
- method: PrismaMethod
4965
- args?: Record<string, unknown>
4966
- }
4967
-
4968
- export interface TransactionOptions {
4969
- isolationLevel?: 'ReadCommitted' | 'RepeatableRead' | 'Serializable'
4970
- timeout?: number
4971
- }
4946
+ import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type BatchCountQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
4972
4947
 
4973
4948
  class DeferredQuery {
4974
4949
  constructor(
@@ -5544,6 +5519,8 @@ export type SpeedClient<T> = T & {
5544
5519
  $batchCount(queries: BatchCountQuery[]): Promise<number[]>
5545
5520
  $transaction(queries: TransactionQuery[], options?: TransactionOptions): Promise<unknown[]>
5546
5521
  }
5522
+
5523
+ export type { BatchCountQuery, TransactionQuery, TransactionOptions }
5547
5524
  `;
5548
5525
  }
5549
5526
  function formatQueries(queries) {