prisma-sql 1.54.0 → 1.56.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
@@ -704,6 +704,9 @@ function assertNoControlChars(label, s) {
704
704
  );
705
705
  }
706
706
  }
707
+ function quoteRawIdent(id) {
708
+ return `"${id.replace(/"/g, '""')}"`;
709
+ }
707
710
  function isIdentCharCode(c) {
708
711
  return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
709
712
  }
@@ -725,33 +728,33 @@ function parseQuotedPart(input, start) {
725
728
  }
726
729
  if (!sawAny) {
727
730
  throw new Error(
728
- `tableName/tableRef has empty quoted identifier part: ${JSON.stringify(input)}`
731
+ `qualified name has empty quoted identifier part: ${JSON.stringify(input)}`
729
732
  );
730
733
  }
731
734
  return i + 1;
732
735
  }
733
736
  if (c === 10 || c === 13 || c === 0) {
734
737
  throw new Error(
735
- `tableName/tableRef contains invalid characters: ${JSON.stringify(input)}`
738
+ `qualified name contains invalid characters: ${JSON.stringify(input)}`
736
739
  );
737
740
  }
738
741
  sawAny = true;
739
742
  i++;
740
743
  }
741
744
  throw new Error(
742
- `tableName/tableRef has unterminated quoted identifier: ${JSON.stringify(input)}`
745
+ `qualified name has unterminated quoted identifier: ${JSON.stringify(input)}`
743
746
  );
744
747
  }
745
748
  function parseUnquotedPart(input, start) {
746
749
  const n = input.length;
747
750
  let i = start;
748
751
  if (i >= n) {
749
- throw new Error(`tableName/tableRef is invalid: ${JSON.stringify(input)}`);
752
+ throw new Error(`qualified name is invalid: ${JSON.stringify(input)}`);
750
753
  }
751
754
  const c0 = input.charCodeAt(i);
752
755
  if (!isIdentStartCharCode(c0)) {
753
756
  throw new Error(
754
- `tableName/tableRef must use identifiers (or quoted identifiers). Got: ${JSON.stringify(input)}`
757
+ `qualified name must use identifiers (or quoted identifiers). Got: ${JSON.stringify(input)}`
755
758
  );
756
759
  }
757
760
  i++;
@@ -760,15 +763,15 @@ function parseUnquotedPart(input, start) {
760
763
  if (c === 46) break;
761
764
  if (!isIdentCharCode(c)) {
762
765
  throw new Error(
763
- `tableName/tableRef contains invalid identifier characters: ${JSON.stringify(input)}`
766
+ `qualified name contains invalid identifier characters: ${JSON.stringify(input)}`
764
767
  );
765
768
  }
766
769
  i++;
767
770
  }
768
771
  return i;
769
772
  }
770
- function assertSafeQualifiedName(tableRef) {
771
- const raw = String(tableRef);
773
+ function assertSafeQualifiedName(input) {
774
+ const raw = String(input);
772
775
  const trimmed = raw.trim();
773
776
  if (trimmed.length === 0) {
774
777
  throw new Error("tableName/tableRef is required and cannot be empty");
@@ -842,7 +845,7 @@ function quote2(id) {
842
845
  );
843
846
  }
844
847
  if (needsQuoting(id)) {
845
- return `"${id.replace(/"/g, '""')}"`;
848
+ return quoteRawIdent(id);
846
849
  }
847
850
  return id;
848
851
  }
@@ -2481,28 +2484,24 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
2481
2484
  function toSafeSqlIdentifier(input) {
2482
2485
  const raw = String(input);
2483
2486
  const n = raw.length;
2487
+ if (n === 0) return "_t";
2484
2488
  let out = "";
2485
2489
  for (let i = 0; i < n; i++) {
2486
2490
  const c = raw.charCodeAt(i);
2487
2491
  const isAZ = c >= 65 && c <= 90 || c >= 97 && c <= 122;
2488
2492
  const is09 = c >= 48 && c <= 57;
2489
2493
  const isUnderscore = c === 95;
2490
- if (isAZ || is09 || isUnderscore) {
2491
- out += raw[i];
2492
- } else {
2493
- out += "_";
2494
- }
2494
+ out += isAZ || is09 || isUnderscore ? raw[i] : "_";
2495
2495
  }
2496
- if (out.length === 0) out = "_t";
2497
2496
  const c0 = out.charCodeAt(0);
2498
2497
  const startsOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95;
2499
- if (!startsOk) out = `_${out}`;
2500
- const lowered = out.toLowerCase();
2498
+ const lowered = (startsOk ? out : `_${out}`).toLowerCase();
2501
2499
  return ALIAS_FORBIDDEN_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
2502
2500
  }
2503
2501
  function createAliasGenerator(maxAliases = 1e4) {
2504
2502
  let counter = 0;
2505
2503
  const usedAliases = /* @__PURE__ */ new Set();
2504
+ const maxLen = 63;
2506
2505
  return {
2507
2506
  next(baseName) {
2508
2507
  if (usedAliases.size >= maxAliases) {
@@ -2512,14 +2511,13 @@ function createAliasGenerator(maxAliases = 1e4) {
2512
2511
  }
2513
2512
  const base = toSafeSqlIdentifier(baseName);
2514
2513
  const suffix = `_${counter}`;
2515
- const maxLen = 63;
2516
2514
  const baseMax = Math.max(1, maxLen - suffix.length);
2517
2515
  const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
2518
2516
  const alias = `${trimmedBase}${suffix}`;
2519
2517
  counter += 1;
2520
2518
  if (usedAliases.has(alias)) {
2521
2519
  throw new Error(
2522
- `CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
2520
+ `CRITICAL: Duplicate alias '${alias}' at counter=${counter}.`
2523
2521
  );
2524
2522
  }
2525
2523
  usedAliases.add(alias);
@@ -2571,24 +2569,19 @@ function normalizeDynamicNameOrThrow(dynamicName, index) {
2571
2569
  }
2572
2570
  return dn;
2573
2571
  }
2574
- function assertUniqueDynamicName(dn, seen) {
2575
- if (seen.has(dn)) {
2576
- throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
2577
- }
2578
- seen.add(dn);
2579
- }
2580
- function validateMappingEntry(m, expectedIndex, seenDynamic) {
2581
- assertSequentialIndex(m.index, expectedIndex);
2582
- assertExactlyOneOfDynamicOrValue(m);
2583
- if (typeof m.dynamicName === "string") {
2584
- const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2585
- assertUniqueDynamicName(dn, seenDynamic);
2586
- }
2587
- }
2588
2572
  function validateMappings(mappings) {
2589
2573
  const seenDynamic = /* @__PURE__ */ new Set();
2590
2574
  for (let i = 0; i < mappings.length; i++) {
2591
- validateMappingEntry(mappings[i], i + 1, seenDynamic);
2575
+ const m = mappings[i];
2576
+ assertSequentialIndex(m.index, i + 1);
2577
+ assertExactlyOneOfDynamicOrValue(m);
2578
+ if (typeof m.dynamicName === "string") {
2579
+ const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2580
+ if (seenDynamic.has(dn)) {
2581
+ throw new Error(`CRITICAL: Duplicate dynamic param name: ${dn}`);
2582
+ }
2583
+ seenDynamic.add(dn);
2584
+ }
2592
2585
  }
2593
2586
  }
2594
2587
  function validateState(params, mappings, index) {
@@ -2600,16 +2593,19 @@ function validateState(params, mappings, index) {
2600
2593
  }
2601
2594
  function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2602
2595
  let index = startIndex;
2603
- const params = initialParams.length > 0 ? [...initialParams] : [];
2604
- const mappings = initialMappings.length > 0 ? [...initialMappings] : [];
2596
+ const params = initialParams.length > 0 ? initialParams.slice() : [];
2597
+ const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
2605
2598
  const dynamicNameToIndex = /* @__PURE__ */ new Map();
2606
- for (const m of mappings) {
2599
+ for (let i = 0; i < mappings.length; i++) {
2600
+ const m = mappings[i];
2607
2601
  if (typeof m.dynamicName === "string") {
2608
2602
  dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
2609
2603
  }
2610
2604
  }
2611
2605
  let dirty = true;
2612
2606
  let cachedSnapshot = null;
2607
+ let frozenParams = null;
2608
+ let frozenMappings = null;
2613
2609
  function assertCanAdd() {
2614
2610
  if (index > MAX_PARAM_INDEX) {
2615
2611
  throw new Error(
@@ -2661,13 +2657,17 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
2661
2657
  }
2662
2658
  function snapshot() {
2663
2659
  if (!dirty && cachedSnapshot) return cachedSnapshot;
2660
+ if (!frozenParams) frozenParams = Object.freeze(params.slice());
2661
+ if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
2664
2662
  const snap = {
2665
2663
  index,
2666
- params,
2667
- mappings
2664
+ params: frozenParams,
2665
+ mappings: frozenMappings
2668
2666
  };
2669
2667
  cachedSnapshot = snap;
2670
2668
  dirty = false;
2669
+ frozenParams = null;
2670
+ frozenMappings = null;
2671
2671
  return snap;
2672
2672
  }
2673
2673
  return {
@@ -2691,11 +2691,11 @@ function createParamStore(startIndex = 1) {
2691
2691
  return createStoreInternal(startIndex);
2692
2692
  }
2693
2693
  function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
2694
- validateState([...existingParams], [...existingMappings], nextIndex);
2694
+ validateState(existingParams, existingMappings, nextIndex);
2695
2695
  return createStoreInternal(
2696
2696
  nextIndex,
2697
- [...existingParams],
2698
- [...existingMappings]
2697
+ existingParams.slice(),
2698
+ existingMappings.slice()
2699
2699
  );
2700
2700
  }
2701
2701
 
@@ -2877,7 +2877,7 @@ function getRelationTableReference(relModel, dialect) {
2877
2877
  dialect
2878
2878
  );
2879
2879
  }
2880
- function resolveRelationOrThrow(model, schemas, schemaByName, relName) {
2880
+ function resolveRelationOrThrow(model, schemaByName, relName) {
2881
2881
  const field = model.fields.find((f) => f.name === relName);
2882
2882
  if (!isNotNullish(field)) {
2883
2883
  throw new Error(
@@ -2931,8 +2931,9 @@ function validateOrderByForModel(model, orderBy) {
2931
2931
  throw new Error("orderBy array entries must have exactly one field");
2932
2932
  }
2933
2933
  const fieldName = String(entries[0][0]).trim();
2934
- if (fieldName.length === 0)
2934
+ if (fieldName.length === 0) {
2935
2935
  throw new Error("orderBy field name cannot be empty");
2936
+ }
2936
2937
  if (!scalarSet.has(fieldName)) {
2937
2938
  throw new Error(
2938
2939
  `orderBy references unknown or non-scalar field '${fieldName}' on model ${model.name}`
@@ -2991,8 +2992,9 @@ function extractRelationPaginationConfig(relArgs) {
2991
2992
  function maybeReverseNegativeTake(takeVal, hasOrderBy, orderByInput) {
2992
2993
  if (typeof takeVal !== "number") return { takeVal, orderByInput };
2993
2994
  if (takeVal >= 0) return { takeVal, orderByInput };
2994
- if (!hasOrderBy)
2995
+ if (!hasOrderBy) {
2995
2996
  throw new Error("Negative take requires orderBy for deterministic results");
2997
+ }
2996
2998
  return {
2997
2999
  takeVal: Math.abs(takeVal),
2998
3000
  orderByInput: reverseOrderByInput(orderByInput)
@@ -3002,9 +3004,7 @@ function finalizeOrderByForInclude(args) {
3002
3004
  if (args.hasOrderBy && isNotNullish(args.orderByInput)) {
3003
3005
  validateOrderByForModel(args.relModel, args.orderByInput);
3004
3006
  }
3005
- if (!args.hasPagination) {
3006
- return args.orderByInput;
3007
- }
3007
+ if (!args.hasPagination) return args.orderByInput;
3008
3008
  return ensureDeterministicOrderByInput({
3009
3009
  orderBy: args.hasOrderBy ? args.orderByInput : void 0,
3010
3010
  model: args.relModel,
@@ -3065,7 +3065,9 @@ function buildOrderBySql(finalOrderByInput, relAlias, dialect, relModel) {
3065
3065
  return isNotNullish(finalOrderByInput) ? buildOrderBy(finalOrderByInput, relAlias, dialect, relModel) : "";
3066
3066
  }
3067
3067
  function buildBaseSql(args) {
3068
- return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias} ${args.joins} ${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
3068
+ const joins = args.joins ? ` ${args.joins}` : "";
3069
+ const where = `${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
3070
+ return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias}${joins} ` + where;
3069
3071
  }
3070
3072
  function buildOneToOneIncludeSql(args) {
3071
3073
  const objExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
@@ -3077,9 +3079,7 @@ function buildOneToOneIncludeSql(args) {
3077
3079
  joinPredicate: args.joinPredicate,
3078
3080
  whereClause: args.whereClause
3079
3081
  });
3080
- if (args.orderBySql) {
3081
- sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3082
- }
3082
+ if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3083
3083
  if (isNotNullish(args.takeVal)) {
3084
3084
  return appendLimitOffset(
3085
3085
  sql,
@@ -3132,7 +3132,7 @@ function buildListIncludeSpec(args) {
3132
3132
  `include.${args.relName}`
3133
3133
  );
3134
3134
  const selectExpr = jsonAgg("row", args.ctx.dialect);
3135
- const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${rowAlias}`;
3135
+ const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
3136
3136
  return Object.freeze({ name: args.relName, sql, isOneToOne: false });
3137
3137
  }
3138
3138
  function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
@@ -3230,12 +3230,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
3230
3230
  `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.`
3231
3231
  );
3232
3232
  }
3233
- const resolved = resolveRelationOrThrow(
3234
- model,
3235
- schemas,
3236
- schemaByName,
3237
- relName
3238
- );
3233
+ const resolved = resolveRelationOrThrow(model, schemaByName, relName);
3239
3234
  const relationPath = `${model.name}.${relName}`;
3240
3235
  const currentPath = [...visitPath, relationPath];
3241
3236
  if (visitPath.includes(relationPath)) {
@@ -3291,7 +3286,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
3291
3286
  stats
3292
3287
  );
3293
3288
  }
3294
- function resolveCountRelationOrThrow(relName, model, schemas, schemaByName) {
3289
+ function resolveCountRelationOrThrow(relName, model, schemaByName) {
3295
3290
  const relationSet = getRelationFieldSet(model);
3296
3291
  if (!relationSet.has(relName)) {
3297
3292
  throw new Error(
@@ -3299,10 +3294,11 @@ function resolveCountRelationOrThrow(relName, model, schemas, schemaByName) {
3299
3294
  );
3300
3295
  }
3301
3296
  const field = model.fields.find((f) => f.name === relName);
3302
- if (!field)
3297
+ if (!field) {
3303
3298
  throw new Error(
3304
3299
  `_count.${relName} references unknown relation on model ${model.name}`
3305
3300
  );
3301
+ }
3306
3302
  if (!isValidRelationField(field)) {
3307
3303
  throw new Error(
3308
3304
  `_count.${relName} has invalid relation metadata on model ${model.name}`
@@ -3330,8 +3326,9 @@ function defaultReferencesForCount(fkCount) {
3330
3326
  }
3331
3327
  function resolveCountKeyPairs(field) {
3332
3328
  const fkFields = normalizeKeyList(field.foreignKey);
3333
- if (fkFields.length === 0)
3329
+ if (fkFields.length === 0) {
3334
3330
  throw new Error("Relation count requires foreignKey");
3331
+ }
3335
3332
  const refsRaw = field.references;
3336
3333
  const refs = normalizeKeyList(refsRaw);
3337
3334
  const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
@@ -3407,12 +3404,7 @@ function buildRelationCountSql(countSelect, model, schemas, parentAlias, _params
3407
3404
  for (const m of schemas) schemaByName.set(m.name, m);
3408
3405
  for (const [relName, shouldCount] of Object.entries(countSelect)) {
3409
3406
  if (!shouldCount) continue;
3410
- const resolved = resolveCountRelationOrThrow(
3411
- relName,
3412
- model,
3413
- schemas,
3414
- schemaByName
3415
- );
3407
+ const resolved = resolveCountRelationOrThrow(relName, model, schemaByName);
3416
3408
  const built = buildCountJoinAndPair({
3417
3409
  relName,
3418
3410
  field: resolved.field,
@@ -5140,6 +5132,618 @@ function buildSQLWithCache(model, models, method, args, dialect) {
5140
5132
  queryCache.size;
5141
5133
  return result;
5142
5134
  }
5135
+
5136
+ // src/batch.ts
5137
+ function assertNoControlChars2(label, s) {
5138
+ for (let i = 0; i < s.length; i++) {
5139
+ const c = s.charCodeAt(i);
5140
+ if (c <= 31 || c === 127) {
5141
+ throw new Error(`${label} contains control characters`);
5142
+ }
5143
+ }
5144
+ }
5145
+ function assertSafeIdentifier(label, s) {
5146
+ const raw = String(s);
5147
+ if (raw.trim() !== raw) {
5148
+ throw new Error(`${label} must not contain leading/trailing whitespace`);
5149
+ }
5150
+ if (raw.length === 0) throw new Error(`${label} cannot be empty`);
5151
+ assertNoControlChars2(label, raw);
5152
+ if (/[ \t\r\n]/.test(raw)) {
5153
+ throw new Error(`${label} must not contain whitespace`);
5154
+ }
5155
+ if (raw.includes(";")) {
5156
+ throw new Error(`${label} must not contain semicolons`);
5157
+ }
5158
+ if (raw.includes("--") || raw.includes("/*") || raw.includes("*/")) {
5159
+ throw new Error(`${label} must not contain SQL comment tokens`);
5160
+ }
5161
+ }
5162
+ function quoteIdent(id) {
5163
+ const raw = String(id);
5164
+ assertSafeIdentifier("Identifier", raw);
5165
+ return `"${raw.replace(/"/g, '""')}"`;
5166
+ }
5167
+ function makeBatchAlias(i) {
5168
+ return `k${i}`;
5169
+ }
5170
+ function replacePgPlaceholders(sql, replace) {
5171
+ const s = String(sql);
5172
+ const n = s.length;
5173
+ let i = 0;
5174
+ let mode = "normal";
5175
+ let dollarTag = null;
5176
+ let out = "";
5177
+ const startsWith = (pos, lit) => s.slice(pos, pos + lit.length) === lit;
5178
+ const readDollarTag = (pos) => {
5179
+ if (s.charCodeAt(pos) !== 36) return null;
5180
+ let j = pos + 1;
5181
+ while (j < n) {
5182
+ const c = s.charCodeAt(j);
5183
+ if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
5184
+ j++;
5185
+ continue;
5186
+ }
5187
+ break;
5188
+ }
5189
+ if (j < n && s.charCodeAt(j) === 36 && j > pos) {
5190
+ return s.slice(pos, j + 1);
5191
+ }
5192
+ if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
5193
+ return "$$";
5194
+ }
5195
+ return null;
5196
+ };
5197
+ while (i < n) {
5198
+ const ch = s.charCodeAt(i);
5199
+ if (mode === "normal") {
5200
+ if (ch === 39) {
5201
+ out += s[i];
5202
+ mode = "single";
5203
+ i++;
5204
+ continue;
5205
+ }
5206
+ if (ch === 34) {
5207
+ out += s[i];
5208
+ mode = "double";
5209
+ i++;
5210
+ continue;
5211
+ }
5212
+ if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
5213
+ out += "--";
5214
+ mode = "lineComment";
5215
+ i += 2;
5216
+ continue;
5217
+ }
5218
+ if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
5219
+ out += "/*";
5220
+ mode = "blockComment";
5221
+ i += 2;
5222
+ continue;
5223
+ }
5224
+ if (ch === 36) {
5225
+ const tag = readDollarTag(i);
5226
+ if (tag) {
5227
+ out += tag;
5228
+ mode = "dollar";
5229
+ dollarTag = tag;
5230
+ i += tag.length;
5231
+ continue;
5232
+ }
5233
+ let j = i + 1;
5234
+ if (j < n) {
5235
+ const c1 = s.charCodeAt(j);
5236
+ if (c1 >= 48 && c1 <= 57) {
5237
+ while (j < n) {
5238
+ const cj = s.charCodeAt(j);
5239
+ if (cj >= 48 && cj <= 57) {
5240
+ j++;
5241
+ continue;
5242
+ }
5243
+ break;
5244
+ }
5245
+ const numStr = s.slice(i + 1, j);
5246
+ const oldIndex = Number(numStr);
5247
+ if (!Number.isInteger(oldIndex) || oldIndex < 1) {
5248
+ throw new Error(`Invalid param placeholder: $${numStr}`);
5249
+ }
5250
+ out += replace(oldIndex);
5251
+ i = j;
5252
+ continue;
5253
+ }
5254
+ }
5255
+ }
5256
+ out += s[i];
5257
+ i++;
5258
+ continue;
5259
+ }
5260
+ if (mode === "single") {
5261
+ out += s[i];
5262
+ if (ch === 39) {
5263
+ if (i + 1 < n && s.charCodeAt(i + 1) === 39) {
5264
+ out += s[i + 1];
5265
+ i += 2;
5266
+ continue;
5267
+ }
5268
+ mode = "normal";
5269
+ i++;
5270
+ continue;
5271
+ }
5272
+ i++;
5273
+ continue;
5274
+ }
5275
+ if (mode === "double") {
5276
+ out += s[i];
5277
+ if (ch === 34) {
5278
+ if (i + 1 < n && s.charCodeAt(i + 1) === 34) {
5279
+ out += s[i + 1];
5280
+ i += 2;
5281
+ continue;
5282
+ }
5283
+ mode = "normal";
5284
+ i++;
5285
+ continue;
5286
+ }
5287
+ i++;
5288
+ continue;
5289
+ }
5290
+ if (mode === "lineComment") {
5291
+ out += s[i];
5292
+ if (ch === 10) {
5293
+ mode = "normal";
5294
+ }
5295
+ i++;
5296
+ continue;
5297
+ }
5298
+ if (mode === "blockComment") {
5299
+ if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
5300
+ out += "*/";
5301
+ i += 2;
5302
+ mode = "normal";
5303
+ continue;
5304
+ }
5305
+ out += s[i];
5306
+ i++;
5307
+ continue;
5308
+ }
5309
+ if (mode === "dollar") {
5310
+ if (dollarTag && startsWith(i, dollarTag)) {
5311
+ out += dollarTag;
5312
+ i += dollarTag.length;
5313
+ mode = "normal";
5314
+ dollarTag = null;
5315
+ continue;
5316
+ }
5317
+ out += s[i];
5318
+ i++;
5319
+ continue;
5320
+ }
5321
+ out += s[i];
5322
+ i++;
5323
+ }
5324
+ return out;
5325
+ }
5326
+ function reindexParams(sql, params, offset) {
5327
+ if (!Number.isInteger(offset) || offset < 0) {
5328
+ throw new Error(`Invalid param offset: ${offset}`);
5329
+ }
5330
+ const newParams = [];
5331
+ const paramMap = /* @__PURE__ */ new Map();
5332
+ const reindexed = replacePgPlaceholders(sql, (oldIndex) => {
5333
+ const existing = paramMap.get(oldIndex);
5334
+ if (existing !== void 0) return `$${existing}`;
5335
+ const pos = oldIndex - 1;
5336
+ if (pos >= params.length) {
5337
+ throw new Error(
5338
+ `Param placeholder $${oldIndex} exceeds params length (${params.length})`
5339
+ );
5340
+ }
5341
+ const newIndex = offset + newParams.length + 1;
5342
+ paramMap.set(oldIndex, newIndex);
5343
+ newParams.push(params[pos]);
5344
+ return `$${newIndex}`;
5345
+ });
5346
+ return { sql: reindexed, params: newParams };
5347
+ }
5348
+ function wrapQueryForMethod(method, cteName, resultAlias) {
5349
+ const outKey = quoteIdent(resultAlias);
5350
+ switch (method) {
5351
+ case "findMany":
5352
+ case "groupBy":
5353
+ return `(SELECT COALESCE(json_agg(row_to_json(t)), '[]'::json) FROM ${cteName} t) AS ${outKey}`;
5354
+ case "findFirst":
5355
+ case "findUnique":
5356
+ return `(SELECT row_to_json(t) FROM ${cteName} t LIMIT 1) AS ${outKey}`;
5357
+ case "count":
5358
+ return `(SELECT * FROM ${cteName}) AS ${outKey}`;
5359
+ case "aggregate":
5360
+ return `(SELECT row_to_json(t) FROM ${cteName} t) AS ${outKey}`;
5361
+ default:
5362
+ throw new Error(`Unsupported batch method: ${method}`);
5363
+ }
5364
+ }
5365
+ function isAllCountQueries(queries, keys) {
5366
+ var _a;
5367
+ for (let i = 0; i < keys.length; i++) {
5368
+ if (((_a = queries[keys[i]]) == null ? void 0 : _a.method) !== "count") return false;
5369
+ }
5370
+ return true;
5371
+ }
5372
+ function looksTooComplexForFilter(sql) {
5373
+ const s = sql.toLowerCase();
5374
+ if (s.includes(" group by ")) return true;
5375
+ if (s.includes(" having ")) return true;
5376
+ if (s.includes(" union ")) return true;
5377
+ if (s.includes(" intersect ")) return true;
5378
+ if (s.includes(" except ")) return true;
5379
+ if (s.includes(" window ")) return true;
5380
+ if (s.includes(" distinct ")) return true;
5381
+ return false;
5382
+ }
5383
+ function containsPgPlaceholder(sql) {
5384
+ return /\$[1-9][0-9]*/.test(sql);
5385
+ }
5386
+ function parseSimpleCountSql(sql) {
5387
+ const trimmed = sql.trim().replace(/;$/, "").trim();
5388
+ const lower = trimmed.toLowerCase();
5389
+ if (!lower.startsWith("select")) return null;
5390
+ if (!lower.includes("count(*)")) return null;
5391
+ if (looksTooComplexForFilter(trimmed)) return null;
5392
+ const match = trimmed.match(
5393
+ /^select\s+count\(\*\)(?:\s*::\s*[a-zA-Z0-9_\."]+)?\s+as\s+("[^"]+"|[a-zA-Z_][a-zA-Z0-9_\.]*)\s+from\s+([\s\S]+?)(?:\s+where\s+([\s\S]+))?$/i
5394
+ );
5395
+ if (!match) return null;
5396
+ const fromSql = match[2].trim();
5397
+ const whereSql = match[3] ? match[3].trim() : null;
5398
+ if (!fromSql) return null;
5399
+ return { fromSql, whereSql };
5400
+ }
5401
+ function buildMergedCountBatchSql(queries, keys, aliasesByKey, modelMap, models, dialect) {
5402
+ const modelGroups = /* @__PURE__ */ new Map();
5403
+ for (let i = 0; i < keys.length; i++) {
5404
+ const key = keys[i];
5405
+ const q = queries[key];
5406
+ const alias = aliasesByKey.get(key);
5407
+ if (!alias) return null;
5408
+ if (!modelGroups.has(q.model)) modelGroups.set(q.model, []);
5409
+ modelGroups.get(q.model).push({ key, alias, args: q.args || {} });
5410
+ }
5411
+ const subqueries = [];
5412
+ let aliasIndex = 0;
5413
+ for (const [modelName, items] of modelGroups) {
5414
+ const model = modelMap.get(modelName);
5415
+ if (!model) return null;
5416
+ let sharedFrom = null;
5417
+ const expressions = [];
5418
+ const localParams = [];
5419
+ const localKeys = [];
5420
+ const localAliases = [];
5421
+ for (let i = 0; i < items.length; i++) {
5422
+ const { key, alias: alias2, args } = items[i];
5423
+ const built = buildSQLWithCache(model, models, "count", args, dialect);
5424
+ const parsed = parseSimpleCountSql(built.sql);
5425
+ if (!parsed) return null;
5426
+ if (containsPgPlaceholder(parsed.fromSql)) {
5427
+ return null;
5428
+ }
5429
+ if (!parsed.whereSql) {
5430
+ if (built.params.length > 0) return null;
5431
+ if (sharedFrom === null) sharedFrom = parsed.fromSql;
5432
+ if (sharedFrom !== parsed.fromSql) return null;
5433
+ expressions.push(`count(*) AS ${quoteIdent(alias2)}`);
5434
+ localKeys.push(key);
5435
+ localAliases.push(alias2);
5436
+ continue;
5437
+ }
5438
+ if (sharedFrom === null) sharedFrom = parsed.fromSql;
5439
+ if (sharedFrom !== parsed.fromSql) return null;
5440
+ const re = reindexParams(
5441
+ parsed.whereSql,
5442
+ built.params,
5443
+ localParams.length
5444
+ );
5445
+ for (let p = 0; p < re.params.length; p++) localParams.push(re.params[p]);
5446
+ expressions.push(
5447
+ `count(*) FILTER (WHERE ${re.sql}) AS ${quoteIdent(alias2)}`
5448
+ );
5449
+ localKeys.push(key);
5450
+ localAliases.push(alias2);
5451
+ }
5452
+ if (!sharedFrom) return null;
5453
+ const alias = `m_${aliasIndex++}`;
5454
+ const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
5455
+ subqueries.push({
5456
+ alias,
5457
+ sql: subSql,
5458
+ params: localParams,
5459
+ keys: localKeys,
5460
+ aliases: localAliases
5461
+ });
5462
+ }
5463
+ if (subqueries.length === 0) return null;
5464
+ let offset = 0;
5465
+ const rewrittenSubs = [];
5466
+ const finalParams = [];
5467
+ for (let i = 0; i < subqueries.length; i++) {
5468
+ const sq = subqueries[i];
5469
+ const re = reindexParams(sq.sql, sq.params, offset);
5470
+ offset += re.params.length;
5471
+ rewrittenSubs.push(re.sql);
5472
+ for (let p = 0; p < re.params.length; p++) finalParams.push(re.params[p]);
5473
+ }
5474
+ const selectParts = [];
5475
+ for (let i = 0; i < subqueries.length; i++) {
5476
+ const sq = subqueries[i];
5477
+ for (let k = 0; k < sq.aliases.length; k++) {
5478
+ const outAlias = sq.aliases[k];
5479
+ selectParts.push(
5480
+ `${sq.alias}.${quoteIdent(outAlias)} AS ${quoteIdent(outAlias)}`
5481
+ );
5482
+ }
5483
+ }
5484
+ const fromSql = rewrittenSubs.join(" CROSS JOIN ");
5485
+ const sql = `SELECT ${selectParts.join(", ")} FROM ${fromSql}`;
5486
+ const aliases = keys.map((k) => aliasesByKey.get(k));
5487
+ return { sql, params: finalParams, keys, aliases };
5488
+ }
5489
+ function buildBatchSql(queries, modelMap, models, dialect) {
5490
+ const keys = Object.keys(queries);
5491
+ if (keys.length === 0) {
5492
+ throw new Error("buildBatchSql requires at least one query");
5493
+ }
5494
+ if (dialect !== "postgres") {
5495
+ throw new Error("Batch queries are only supported for postgres dialect");
5496
+ }
5497
+ const aliases = new Array(keys.length);
5498
+ const aliasesByKey = /* @__PURE__ */ new Map();
5499
+ for (let i = 0; i < keys.length; i++) {
5500
+ const a = makeBatchAlias(i);
5501
+ aliases[i] = a;
5502
+ aliasesByKey.set(keys[i], a);
5503
+ }
5504
+ if (isAllCountQueries(queries, keys)) {
5505
+ const merged = buildMergedCountBatchSql(
5506
+ queries,
5507
+ keys,
5508
+ aliasesByKey,
5509
+ modelMap,
5510
+ models,
5511
+ dialect
5512
+ );
5513
+ if (merged) return merged;
5514
+ }
5515
+ const ctes = new Array(keys.length);
5516
+ const selects = new Array(keys.length);
5517
+ const allParams = [];
5518
+ for (let i = 0; i < keys.length; i++) {
5519
+ const key = keys[i];
5520
+ const query = queries[key];
5521
+ const model = modelMap.get(query.model);
5522
+ if (!model) {
5523
+ throw new Error(
5524
+ `Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5525
+ );
5526
+ }
5527
+ const { sql: querySql, params: queryParams } = buildSQLWithCache(
5528
+ model,
5529
+ models,
5530
+ query.method,
5531
+ query.args || {},
5532
+ dialect
5533
+ );
5534
+ const { sql: reindexedSql, params: reindexedParams } = reindexParams(
5535
+ querySql,
5536
+ queryParams,
5537
+ allParams.length
5538
+ );
5539
+ for (let p = 0; p < reindexedParams.length; p++) {
5540
+ allParams.push(reindexedParams[p]);
5541
+ }
5542
+ const cteName = `batch_${i}`;
5543
+ ctes[i] = `${cteName} AS (${reindexedSql})`;
5544
+ selects[i] = wrapQueryForMethod(query.method, cteName, aliases[i]);
5545
+ }
5546
+ const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
5547
+ return { sql, params: allParams, keys, aliases };
5548
+ }
5549
+ function buildBatchCountSql(queries, modelMap, models, dialect) {
5550
+ if (queries.length === 0) {
5551
+ throw new Error("buildBatchCountSql requires at least one query");
5552
+ }
5553
+ if (dialect !== "postgres") {
5554
+ throw new Error(
5555
+ "Batch count queries are only supported for postgres dialect"
5556
+ );
5557
+ }
5558
+ const ctes = new Array(queries.length);
5559
+ const selects = new Array(queries.length);
5560
+ const allParams = [];
5561
+ for (let i = 0; i < queries.length; i++) {
5562
+ const query = queries[i];
5563
+ const model = modelMap.get(query.model);
5564
+ if (!model) {
5565
+ throw new Error(
5566
+ `Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5567
+ );
5568
+ }
5569
+ const { sql: querySql, params: queryParams } = buildSQLWithCache(
5570
+ model,
5571
+ models,
5572
+ "count",
5573
+ query.args || {},
5574
+ dialect
5575
+ );
5576
+ const { sql: reindexedSql, params: reindexedParams } = reindexParams(
5577
+ querySql,
5578
+ queryParams,
5579
+ allParams.length
5580
+ );
5581
+ for (let p = 0; p < reindexedParams.length; p++) {
5582
+ allParams.push(reindexedParams[p]);
5583
+ }
5584
+ const cteName = `count_${i}`;
5585
+ const resultKey = `count_${i}`;
5586
+ ctes[i] = `${cteName} AS (${reindexedSql})`;
5587
+ selects[i] = `(SELECT * FROM ${cteName}) AS ${quoteIdent(resultKey)}`;
5588
+ }
5589
+ const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
5590
+ return { sql, params: allParams };
5591
+ }
5592
+ function looksLikeJsonString(s) {
5593
+ const t = s.trim();
5594
+ if (t.length === 0) return false;
5595
+ const c0 = t.charCodeAt(0);
5596
+ const cN = t.charCodeAt(t.length - 1);
5597
+ if (c0 === 123 && cN === 125) return true;
5598
+ if (c0 === 91 && cN === 93) return true;
5599
+ if (t === "null" || t === "true" || t === "false") return true;
5600
+ return false;
5601
+ }
5602
+ function parseJsonValue(value) {
5603
+ if (typeof value !== "string") return value;
5604
+ if (!looksLikeJsonString(value)) return value;
5605
+ try {
5606
+ return JSON.parse(value);
5607
+ } catch (e) {
5608
+ return value;
5609
+ }
5610
+ }
5611
+ function parseCountValue(value) {
5612
+ if (value === null || value === void 0) return 0;
5613
+ if (typeof value === "number") return value;
5614
+ if (typeof value === "string") {
5615
+ const n = Number.parseInt(value, 10);
5616
+ return Number.isFinite(n) ? n : 0;
5617
+ }
5618
+ if (typeof value === "object") {
5619
+ const obj = value;
5620
+ const countKey = Object.prototype.hasOwnProperty.call(obj, "count") ? "count" : Object.prototype.hasOwnProperty.call(obj, "_count") ? "_count" : Object.keys(obj).find((k) => k.endsWith("_count"));
5621
+ if (countKey !== void 0) {
5622
+ const v = obj[countKey];
5623
+ if (typeof v === "number") return v;
5624
+ if (typeof v === "string") {
5625
+ const n = Number.parseInt(v, 10);
5626
+ return Number.isFinite(n) ? n : 0;
5627
+ }
5628
+ }
5629
+ }
5630
+ return 0;
5631
+ }
5632
+ function parseBatchCountResults(row, count) {
5633
+ const results = [];
5634
+ for (let i = 0; i < count; i++) {
5635
+ const key = `count_${i}`;
5636
+ const value = row[key];
5637
+ results.push(parseCountValue(value));
5638
+ }
5639
+ return results;
5640
+ }
5641
+ function parseBatchResults(row, keys, queries, aliases) {
5642
+ const results = {};
5643
+ for (let i = 0; i < keys.length; i++) {
5644
+ const key = keys[i];
5645
+ const columnKey = aliases && aliases[i] ? aliases[i] : key;
5646
+ const rawValue = row[columnKey];
5647
+ const query = queries[key];
5648
+ switch (query.method) {
5649
+ case "findMany": {
5650
+ const parsed = parseJsonValue(rawValue);
5651
+ results[key] = Array.isArray(parsed) ? parsed : [];
5652
+ break;
5653
+ }
5654
+ case "findFirst":
5655
+ case "findUnique": {
5656
+ const parsed = parseJsonValue(rawValue);
5657
+ results[key] = parsed != null ? parsed : null;
5658
+ break;
5659
+ }
5660
+ case "count": {
5661
+ results[key] = parseCountValue(rawValue);
5662
+ break;
5663
+ }
5664
+ case "aggregate": {
5665
+ const parsed = parseJsonValue(rawValue);
5666
+ const obj = parsed != null ? parsed : {};
5667
+ results[key] = transformQueryResults("aggregate", [obj]);
5668
+ break;
5669
+ }
5670
+ case "groupBy": {
5671
+ const parsed = parseJsonValue(rawValue);
5672
+ const arr = Array.isArray(parsed) ? parsed : [];
5673
+ results[key] = transformQueryResults("groupBy", arr);
5674
+ break;
5675
+ }
5676
+ default:
5677
+ results[key] = rawValue;
5678
+ }
5679
+ }
5680
+ return results;
5681
+ }
5682
+
5683
+ // src/transaction.ts
5684
+ function isolationLevelToPostgresKeyword(level) {
5685
+ switch (level) {
5686
+ case "ReadCommitted":
5687
+ return "read committed";
5688
+ case "RepeatableRead":
5689
+ return "repeatable read";
5690
+ case "Serializable":
5691
+ return "serializable";
5692
+ default:
5693
+ return void 0;
5694
+ }
5695
+ }
5696
+ function createTransactionExecutor(deps) {
5697
+ const { modelMap, allModels, dialect, executeRaw, postgresClient } = deps;
5698
+ return {
5699
+ execute(queries, options) {
5700
+ return __async(this, null, function* () {
5701
+ if (queries.length === 0) return [];
5702
+ if (dialect !== "postgres") {
5703
+ throw new Error("$transaction is only supported for postgres dialect");
5704
+ }
5705
+ if (!postgresClient) {
5706
+ throw new Error("postgresClient is required for transactions");
5707
+ }
5708
+ const transactionCallback = (sql) => __async(null, null, function* () {
5709
+ const results = [];
5710
+ const isolationLevel = isolationLevelToPostgresKeyword(
5711
+ options == null ? void 0 : options.isolationLevel
5712
+ );
5713
+ if (isolationLevel) {
5714
+ yield sql.unsafe(
5715
+ `SET TRANSACTION ISOLATION LEVEL ${isolationLevel.toUpperCase()}`
5716
+ );
5717
+ }
5718
+ if (options == null ? void 0 : options.timeout) {
5719
+ yield sql.unsafe(
5720
+ `SET LOCAL statement_timeout = ${Math.floor(options.timeout)}`
5721
+ );
5722
+ }
5723
+ for (const q of queries) {
5724
+ const model = modelMap.get(q.model);
5725
+ if (!model) {
5726
+ throw new Error(
5727
+ `Model '${q.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5728
+ );
5729
+ }
5730
+ const { sql: sqlStr, params } = buildSQLWithCache(
5731
+ model,
5732
+ allModels,
5733
+ q.method,
5734
+ q.args || {},
5735
+ dialect
5736
+ );
5737
+ const rawResults = yield sql.unsafe(sqlStr, params);
5738
+ results.push(transformQueryResults(q.method, rawResults));
5739
+ }
5740
+ return results;
5741
+ });
5742
+ return yield postgresClient.begin(transactionCallback);
5743
+ });
5744
+ }
5745
+ };
5746
+ }
5143
5747
  var ACCELERATED_METHODS = /* @__PURE__ */ new Set([
5144
5748
  "findMany",
5145
5749
  "findFirst",
@@ -5156,10 +5760,15 @@ function executePostgres(client, sql, params) {
5156
5760
  return yield client.unsafe(sql, params);
5157
5761
  });
5158
5762
  }
5159
- function executeSqlite(db, sql, params) {
5763
+ function shouldSqliteUseGet(method) {
5764
+ return method === "count" || method === "findFirst" || method === "findUnique" || method === "aggregate";
5765
+ }
5766
+ function executeSqlite(db, method, sql, params) {
5160
5767
  const stmt = db.prepare(sql);
5161
- if (sql.toUpperCase().includes("COUNT(*) AS")) {
5162
- return [stmt.get(...params)];
5768
+ if (shouldSqliteUseGet(method)) {
5769
+ const row = stmt.get(...params);
5770
+ if (row === void 0) return [];
5771
+ return [row];
5163
5772
  }
5164
5773
  return stmt.all(...params);
5165
5774
  }
@@ -5179,7 +5788,7 @@ function executeWithTiming(input) {
5179
5788
  console.log("SQL:", sql);
5180
5789
  console.log("Params:", params);
5181
5790
  }
5182
- const results = yield input.executeQuery(sql, params);
5791
+ const results = yield input.executeQuery(input.method, sql, params);
5183
5792
  const duration = Date.now() - startTime;
5184
5793
  (_a = input.onQuery) == null ? void 0 : _a.call(input, {
5185
5794
  model: input.modelName,
@@ -5206,11 +5815,11 @@ function fallbackToPrisma(ctx, modelName, method, args) {
5206
5815
  return ctx.$parent[modelName][method](args);
5207
5816
  }
5208
5817
  function createExecuteQuery(client, dialect) {
5209
- return (sql, params) => __async(null, null, function* () {
5818
+ return (method, sql, params) => __async(null, null, function* () {
5210
5819
  if (dialect === "postgres") {
5211
5820
  return yield executePostgres(client, sql, params);
5212
5821
  }
5213
- return executeSqlite(client, sql, params);
5822
+ return executeSqlite(client, method, sql, params);
5214
5823
  });
5215
5824
  }
5216
5825
  function logAcceleratedError(debug, dialect, modelName, method, error) {
@@ -5256,6 +5865,58 @@ function handleMethodCall(ctx, method, args, deps) {
5256
5865
  }
5257
5866
  });
5258
5867
  }
5868
+ var DeferredQuery = class {
5869
+ constructor(model, method, args) {
5870
+ this.model = model;
5871
+ this.method = method;
5872
+ this.args = args;
5873
+ }
5874
+ then(onfulfilled, onrejected) {
5875
+ throw new Error(
5876
+ "Cannot await a batch query. Batch queries must not be awaited inside the $batch callback."
5877
+ );
5878
+ }
5879
+ };
5880
+ function createBatchProxy(modelMap, allowedModels) {
5881
+ return new Proxy(
5882
+ {},
5883
+ {
5884
+ get(_target, modelName) {
5885
+ if (typeof modelName === "symbol") return void 0;
5886
+ const model = modelMap.get(modelName);
5887
+ if (!model) {
5888
+ throw new Error(
5889
+ `Model '${modelName}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5890
+ );
5891
+ }
5892
+ if (allowedModels && !allowedModels.includes(modelName)) {
5893
+ throw new Error(
5894
+ `Model '${modelName}' not allowed. Allowed: ${allowedModels.join(", ")}`
5895
+ );
5896
+ }
5897
+ return new Proxy(
5898
+ {},
5899
+ {
5900
+ get(_target2, method) {
5901
+ if (!ACCELERATED_METHODS.has(method)) {
5902
+ throw new Error(
5903
+ `Method '${method}' not supported in batch. Supported: ${[...ACCELERATED_METHODS].join(", ")}`
5904
+ );
5905
+ }
5906
+ return (args) => {
5907
+ return new DeferredQuery(
5908
+ modelName,
5909
+ method,
5910
+ args
5911
+ );
5912
+ };
5913
+ }
5914
+ }
5915
+ );
5916
+ }
5917
+ }
5918
+ );
5919
+ }
5259
5920
  function speedExtension(config) {
5260
5921
  const {
5261
5922
  postgres,
@@ -5280,9 +5941,7 @@ function speedExtension(config) {
5280
5941
  } else if (dmmf) {
5281
5942
  models = convertDMMFToModels(dmmf.datamodel);
5282
5943
  } else {
5283
- throw new Error(
5284
- 'speedExtension requires either models or dmmf parameter.\n\n\u26A0\uFE0F RECOMMENDED APPROACH:\n Use the generated extension for zero runtime overhead:\n\n import { speedExtension } from "./generated/sql"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql })\n )\n\n 1. Add generator to schema.prisma:\n generator sql {\n provider = "prisma-sql-generator"\n }\n\n 2. Run: npx prisma generate\n\n 3. Import from generated file\n\n\u274C RUNTIME-ONLY MODE:\n If you cannot use the generator, provide models or dmmf:\n\n import { speedExtension } from "prisma-sql"\n import { MODELS } from "./generated/sql"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql, models: MODELS })\n )\n\n Or with DMMF (auto-converts on startup):\n\n import { Prisma } from "@prisma/client"\n const prisma = new PrismaClient().$extends(\n speedExtension({ postgres: sql, dmmf: Prisma.dmmf })\n )'
5285
- );
5944
+ throw new Error("speedExtension requires either models or dmmf parameter.");
5286
5945
  }
5287
5946
  if (!Array.isArray(models) || models.length === 0) {
5288
5947
  throw new Error("speedExtension: models array is empty or invalid");
@@ -5313,10 +5972,86 @@ function speedExtension(config) {
5313
5972
  for (const method of ACCELERATED_METHODS) {
5314
5973
  methodHandlers[method] = createMethodHandler(method);
5315
5974
  }
5975
+ const executeRaw = (sql, params) => __async(null, null, function* () {
5976
+ if (dialect === "postgres") {
5977
+ return yield client.unsafe(sql, params);
5978
+ }
5979
+ throw new Error("Raw execution for sqlite not supported in transactions");
5980
+ });
5981
+ const txExecutor = createTransactionExecutor({
5982
+ modelMap,
5983
+ allModels: models,
5984
+ dialect,
5985
+ executeRaw,
5986
+ postgresClient: postgres
5987
+ });
5988
+ function batch(callback) {
5989
+ return __async(this, null, function* () {
5990
+ const batchProxy = createBatchProxy(modelMap, allowedModels);
5991
+ const queries = yield callback(batchProxy);
5992
+ const batchQueries = {};
5993
+ for (const [key, deferred] of Object.entries(queries)) {
5994
+ if (!(deferred instanceof DeferredQuery)) {
5995
+ throw new Error(
5996
+ `Batch query '${key}' must be a deferred query. Did you await it?`
5997
+ );
5998
+ }
5999
+ batchQueries[key] = {
6000
+ model: deferred.model,
6001
+ method: deferred.method,
6002
+ args: deferred.args || {}
6003
+ };
6004
+ }
6005
+ const startTime = Date.now();
6006
+ const { sql, params, keys, aliases } = buildBatchSql(
6007
+ batchQueries,
6008
+ modelMap,
6009
+ models,
6010
+ dialect
6011
+ );
6012
+ if (debug) {
6013
+ console.log(`[${dialect}] $batch (${keys.length} queries)`);
6014
+ console.log("SQL:", sql);
6015
+ console.log("Params:", params);
6016
+ }
6017
+ const rows = yield executeQuery("findMany", sql, params);
6018
+ const row = rows[0];
6019
+ const results = parseBatchResults(row, keys, batchQueries, aliases);
6020
+ const duration = Date.now() - startTime;
6021
+ onQuery == null ? void 0 : onQuery({
6022
+ model: "_batch",
6023
+ method: "batch",
6024
+ sql,
6025
+ params,
6026
+ duration
6027
+ });
6028
+ return results;
6029
+ });
6030
+ }
6031
+ function transaction(queries, options) {
6032
+ return __async(this, null, function* () {
6033
+ const startTime = Date.now();
6034
+ if (debug) {
6035
+ console.log(`[${dialect}] $transaction (${queries.length} queries)`);
6036
+ }
6037
+ const results = yield txExecutor.execute(queries, options);
6038
+ const duration = Date.now() - startTime;
6039
+ onQuery == null ? void 0 : onQuery({
6040
+ model: "_transaction",
6041
+ method: "transaction",
6042
+ sql: `TRANSACTION(${queries.length})`,
6043
+ params: [],
6044
+ duration
6045
+ });
6046
+ return results;
6047
+ });
6048
+ }
5316
6049
  return prisma.$extends({
5317
6050
  name: "prisma-sql-speed",
5318
6051
  client: {
5319
- $original: prisma
6052
+ $original: prisma,
6053
+ $batch: batch,
6054
+ $transaction: transaction
5320
6055
  },
5321
6056
  model: {
5322
6057
  $allModels: methodHandlers
@@ -5324,6 +6059,10 @@ function speedExtension(config) {
5324
6059
  });
5325
6060
  };
5326
6061
  }
6062
+ function extendPrisma(prisma, config) {
6063
+ const extension = speedExtension(config);
6064
+ return extension(prisma);
6065
+ }
5327
6066
  function createToSQLFunction(models, dialect) {
5328
6067
  if (!models || !Array.isArray(models) || models.length === 0) {
5329
6068
  throw new Error("createToSQL requires non-empty models array");
@@ -5358,13 +6097,18 @@ function createPrismaSQL(config) {
5358
6097
  throw new Error("createPrismaSQL: models array is empty or invalid");
5359
6098
  }
5360
6099
  const toSQL = createToSQLFunction(models, dialect);
6100
+ const modelMap = new Map(models.map((m) => [m.name, m]));
5361
6101
  function query(_0, _1) {
5362
6102
  return __async(this, arguments, function* (model, method, args = {}) {
5363
6103
  const { sql, params } = toSQL(model, method, args);
5364
6104
  return execute(client, sql, params);
5365
6105
  });
5366
6106
  }
5367
- return { toSQL, query, client };
6107
+ function batchSql(queries) {
6108
+ const { sql, params } = buildBatchSql(queries, modelMap, models, dialect);
6109
+ return { sql, params };
6110
+ }
6111
+ return { toSQL, query, batchSql, client };
5368
6112
  }
5369
6113
  function generateSQL2(directive) {
5370
6114
  return generateSQL(directive);
@@ -5406,6 +6150,6 @@ function generateSQLByModel(directives) {
5406
6150
  return byModel;
5407
6151
  }
5408
6152
 
5409
- export { buildSQL, createPrismaSQL, createToSQL, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel, getGlobalDialect, setGlobalDialect, speedExtension, transformQueryResults };
6153
+ export { buildBatchCountSql, buildBatchSql, buildSQL, createPrismaSQL, createToSQL, createTransactionExecutor, extendPrisma, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel, getGlobalDialect, parseBatchCountResults, parseBatchResults, setGlobalDialect, speedExtension, transformQueryResults };
5410
6154
  //# sourceMappingURL=index.js.map
5411
6155
  //# sourceMappingURL=index.js.map