prisma-sql 1.37.0 → 1.38.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.
@@ -56,7 +56,7 @@ var require_package = __commonJS({
56
56
  "package.json"(exports$1, module) {
57
57
  module.exports = {
58
58
  name: "prisma-sql",
59
- version: "1.37.0",
59
+ version: "1.38.0",
60
60
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
61
61
  main: "dist/index.cjs",
62
62
  module: "dist/index.js",
@@ -97,6 +97,17 @@ var require_package = __commonJS({
97
97
  "sonar-cli": "sonar-scanner -Dsonar.projectKey=prisma-sql -Dsonar.sources=./src -Dsonar.host.url=http://localhost:9000 -Dsonar.login=sqp_9fe07460d0aa83f711d0edf4f317f05019d0613b",
98
98
  sonar: "yarn sonar-cli && npx tsx scripts/sonar.ts"
99
99
  },
100
+ workspaces: {
101
+ packages: [
102
+ "demo"
103
+ ],
104
+ nohoist: [
105
+ "**/prisma",
106
+ "**/prisma/**",
107
+ "**/@prisma/**",
108
+ "**/prisma-*"
109
+ ]
110
+ },
100
111
  keywords: [
101
112
  "prisma",
102
113
  "sql",
@@ -304,7 +315,7 @@ function getArrayType(prismaType, dialect) {
304
315
  case "DateTime":
305
316
  return "timestamptz[]";
306
317
  default:
307
- return "text[]";
318
+ return `"${baseType}"[]`;
308
319
  }
309
320
  }
310
321
  function jsonAgg(content, dialect) {
@@ -1949,7 +1960,7 @@ function buildLogical(operator, value, ctx, builder) {
1949
1960
  }
1950
1961
  function buildScalarField(fieldName, value, ctx) {
1951
1962
  const field = assertFieldExists(fieldName, ctx.model, ctx.path);
1952
- const expr = col(ctx.alias, fieldName);
1963
+ const expr = col(ctx.alias, fieldName, ctx.model);
1953
1964
  if (value === null) {
1954
1965
  return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
1955
1966
  }
@@ -3437,8 +3448,8 @@ function replaceOrderByAlias(orderBy, fromAlias, outerAlias) {
3437
3448
  const replacement = `${outerAlias}.`;
3438
3449
  return orderBy.split(needle).join(replacement);
3439
3450
  }
3440
- function buildDistinctColumns(distinct, fromAlias) {
3441
- return distinct.map((f) => col(fromAlias, f)).join(SQL_SEPARATORS.FIELD_LIST);
3451
+ function buildDistinctColumns(distinct, fromAlias, model) {
3452
+ return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
3442
3453
  }
3443
3454
  function buildOutputColumns(scalarNames, includeNames, hasCount) {
3444
3455
  const outputCols = [...scalarNames, ...includeNames];
@@ -3452,13 +3463,13 @@ function buildOutputColumns(scalarNames, includeNames, hasCount) {
3452
3463
  return formatted;
3453
3464
  }
3454
3465
  function buildWindowOrder(args) {
3455
- const { baseOrder, idField, fromAlias } = args;
3466
+ const { baseOrder, idField, fromAlias, model } = args;
3456
3467
  const orderFields = baseOrder.split(SQL_SEPARATORS.ORDER_BY).map((s) => s.trim().toLowerCase());
3457
3468
  const hasIdInOrder = orderFields.some(
3458
3469
  (f) => f.startsWith(`${fromAlias}.id `) || f.startsWith(`${fromAlias}."id" `)
3459
3470
  );
3460
3471
  if (hasIdInOrder) return baseOrder;
3461
- const idTiebreaker = idField ? `, ${col(fromAlias, "id")} ASC` : "";
3472
+ const idTiebreaker = idField ? `, ${col(fromAlias, "id", model)} ASC` : "";
3462
3473
  return `${baseOrder}${idTiebreaker}`;
3463
3474
  }
3464
3475
  function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
@@ -3475,14 +3486,17 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3475
3486
  includeNames,
3476
3487
  hasCount
3477
3488
  );
3478
- const distinctCols = buildDistinctColumns([...distinct], from.alias);
3479
- const fallbackOrder = [...distinct].map((f) => `${col(from.alias, f)} ASC`).join(SQL_SEPARATORS.FIELD_LIST);
3480
- const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
3489
+ const distinctCols = buildDistinctColumns([...distinct], from.alias, model);
3490
+ const fallbackOrder = [...distinct].map((f) => `${col(from.alias, f, model)} ASC`).join(SQL_SEPARATORS.FIELD_LIST);
3491
+ const idField = model.fields.find(
3492
+ (f) => f.name === "id" && !f.isRelation
3493
+ );
3481
3494
  const baseOrder = isNonEmptyString(orderBy) ? orderBy : fallbackOrder;
3482
3495
  const windowOrder = buildWindowOrder({
3483
3496
  baseOrder,
3484
3497
  idField,
3485
- fromAlias: from.alias
3498
+ fromAlias: from.alias,
3499
+ model
3486
3500
  });
3487
3501
  const outerOrder = isNonEmptyString(orderBy) ? replaceOrderByAlias(orderBy, from.alias, `"__tp_distinct"`) : replaceOrderByAlias(fallbackOrder, from.alias, `"__tp_distinct"`);
3488
3502
  const joins = buildJoinsSql(whereJoins, countJoins);
@@ -3593,9 +3607,9 @@ function withCountJoins(spec, countJoins, whereJoins) {
3593
3607
  whereJoins: [...whereJoins || [], ...countJoins || []]
3594
3608
  });
3595
3609
  }
3596
- function buildPostgresDistinctOnClause(fromAlias, distinct) {
3610
+ function buildPostgresDistinctOnClause(fromAlias, distinct, model) {
3597
3611
  if (!isNonEmptyArray(distinct)) return null;
3598
- const distinctCols = buildDistinctColumns([...distinct], fromAlias);
3612
+ const distinctCols = buildDistinctColumns([...distinct], fromAlias, model);
3599
3613
  return `${SQL_TEMPLATES.DISTINCT_ON} (${distinctCols})`;
3600
3614
  }
3601
3615
  function pushJoinGroups(parts, ...groups) {
@@ -3625,7 +3639,8 @@ function constructFinalSql(spec) {
3625
3639
  method,
3626
3640
  cursorClause,
3627
3641
  params,
3628
- dialect
3642
+ dialect,
3643
+ model
3629
3644
  } = spec;
3630
3645
  const useWindowDistinct = hasWindowDistinct(spec);
3631
3646
  assertDistinctAllowed(method, useWindowDistinct);
@@ -3639,7 +3654,7 @@ function constructFinalSql(spec) {
3639
3654
  return finalizeSql(sql2, params);
3640
3655
  }
3641
3656
  const parts = [SQL_TEMPLATES.SELECT];
3642
- const distinctOn = dialect === "postgres" ? buildPostgresDistinctOnClause(from.alias, distinct) : null;
3657
+ const distinctOn = dialect === "postgres" ? buildPostgresDistinctOnClause(from.alias, distinct, model) : null;
3643
3658
  if (distinctOn) parts.push(distinctOn);
3644
3659
  const baseSelect = (select != null ? select : "").trim();
3645
3660
  const fullSelectList = buildSelectList(baseSelect, includeCols);
@@ -3899,17 +3914,17 @@ function getModelFieldMap(model) {
3899
3914
  function isTruthySelection(v) {
3900
3915
  return v === true;
3901
3916
  }
3902
- function aggExprForField(aggKey, field, alias) {
3917
+ function aggExprForField(aggKey, field, alias, model) {
3903
3918
  if (aggKey === "_count") {
3904
- return field === "_all" ? `COUNT(*)` : `COUNT(${col(alias, field)})`;
3919
+ return field === "_all" ? `COUNT(*)` : `COUNT(${col(alias, field, model)})`;
3905
3920
  }
3906
3921
  if (field === "_all") {
3907
3922
  throw new Error(`'${aggKey}' does not support '_all'`);
3908
3923
  }
3909
- if (aggKey === "_sum") return `SUM(${col(alias, field)})`;
3910
- if (aggKey === "_avg") return `AVG(${col(alias, field)})`;
3911
- if (aggKey === "_min") return `MIN(${col(alias, field)})`;
3912
- return `MAX(${col(alias, field)})`;
3924
+ if (aggKey === "_sum") return `SUM(${col(alias, field, model)})`;
3925
+ if (aggKey === "_avg") return `AVG(${col(alias, field, model)})`;
3926
+ if (aggKey === "_min") return `MIN(${col(alias, field, model)})`;
3927
+ return `MAX(${col(alias, field, model)})`;
3913
3928
  }
3914
3929
  function buildComparisonOp(op) {
3915
3930
  const sqlOp = COMPARISON_OPS[op];
@@ -4090,7 +4105,7 @@ function buildHavingForAggregateFirstShape(aggKey, target, alias, params, dialec
4090
4105
  for (const [field, filter] of Object.entries(target)) {
4091
4106
  assertHavingAggTarget(aggKey, field, model);
4092
4107
  if (!isPlainObject(filter) || Object.keys(filter).length === 0) continue;
4093
- const expr = aggExprForField(aggKey, field, alias);
4108
+ const expr = aggExprForField(aggKey, field, alias, model);
4094
4109
  out.push(...buildHavingOpsForExpr(expr, filter, params, dialect));
4095
4110
  }
4096
4111
  return out;
@@ -4107,7 +4122,7 @@ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect
4107
4122
  assertAggregateFieldType(aggKey, field.type, field.name, model.name);
4108
4123
  const entries = Object.entries(aggFilter);
4109
4124
  if (entries.length === 0) continue;
4110
- const expr = aggExprForField(aggKey, fieldName, alias);
4125
+ const expr = aggExprForField(aggKey, fieldName, alias, model);
4111
4126
  for (const [op, val] of entries) {
4112
4127
  if (op === "mode") continue;
4113
4128
  const built = buildSimpleComparison(expr, op, val, params, dialect);
@@ -4146,10 +4161,10 @@ function assertCountableScalarField(fieldMap, model, fieldName) {
4146
4161
  );
4147
4162
  }
4148
4163
  }
4149
- function pushCountField(fields, alias, fieldName) {
4164
+ function pushCountField(fields, alias, fieldName, model) {
4150
4165
  const outAlias = `_count.${fieldName}`;
4151
4166
  fields.push(
4152
- `COUNT(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
4167
+ `COUNT(${col(alias, fieldName, model)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
4153
4168
  );
4154
4169
  }
4155
4170
  function addCountFields(fields, countArg, alias, model, fieldMap) {
@@ -4167,7 +4182,7 @@ function addCountFields(fields, countArg, alias, model, fieldMap) {
4167
4182
  );
4168
4183
  for (const [f] of selected) {
4169
4184
  assertCountableScalarField(fieldMap, model, f);
4170
- pushCountField(fields, alias, f);
4185
+ pushCountField(fields, alias, f, model);
4171
4186
  }
4172
4187
  }
4173
4188
  function getAggregateSelectionObject(args, agg) {
@@ -4188,10 +4203,10 @@ function assertAggregatableScalarField(fieldMap, model, agg, fieldName) {
4188
4203
  }
4189
4204
  return field;
4190
4205
  }
4191
- function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName) {
4206
+ function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model) {
4192
4207
  const outAlias = `${agg}.${fieldName}`;
4193
4208
  fields.push(
4194
- `${aggFn}(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
4209
+ `${aggFn}(${col(alias, fieldName, model)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
4195
4210
  );
4196
4211
  }
4197
4212
  function addAggregateFields(fields, args, alias, model, fieldMap) {
@@ -4209,7 +4224,7 @@ function addAggregateFields(fields, args, alias, model, fieldMap) {
4209
4224
  fieldName
4210
4225
  );
4211
4226
  assertAggregateFieldType(agg, field.type, fieldName, model.name);
4212
- pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName);
4227
+ pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model);
4213
4228
  }
4214
4229
  }
4215
4230
  }
@@ -4272,7 +4287,7 @@ function assertGroupByBy(args, model) {
4272
4287
  return byFields;
4273
4288
  }
4274
4289
  function buildGroupBySelectParts(args, alias, model, byFields) {
4275
- const groupCols = byFields.map((f) => col(alias, f));
4290
+ const groupCols = byFields.map((f) => col(alias, f, model));
4276
4291
  const groupFields = groupCols.join(SQL_SEPARATORS.FIELD_LIST);
4277
4292
  const aggFields = buildAggregateFields(args, alias, model);
4278
4293
  const selectFields = isNonEmptyArray(aggFields) ? groupCols.concat(aggFields).join(SQL_SEPARATORS.FIELD_LIST) : groupCols.join(SQL_SEPARATORS.FIELD_LIST);
@@ -4565,6 +4580,29 @@ function generateSQL2(directive) {
4565
4580
  }
4566
4581
 
4567
4582
  // src/code-emitter.ts
4583
+ function extractEnumMappings(datamodel) {
4584
+ const mappings = {};
4585
+ const fieldTypes = {};
4586
+ for (const enumDef of datamodel.enums) {
4587
+ const enumMapping = {};
4588
+ for (const value of enumDef.values) {
4589
+ enumMapping[value.name] = value.dbName || value.name;
4590
+ }
4591
+ if (Object.keys(enumMapping).length > 0) {
4592
+ mappings[enumDef.name] = enumMapping;
4593
+ }
4594
+ }
4595
+ for (const model of datamodel.models) {
4596
+ fieldTypes[model.name] = {};
4597
+ for (const field of model.fields) {
4598
+ const baseType = field.type.replace(/\[\]|\?/g, "");
4599
+ if (mappings[baseType]) {
4600
+ fieldTypes[model.name][field.name] = baseType;
4601
+ }
4602
+ }
4603
+ }
4604
+ return { mappings, fieldTypes };
4605
+ }
4568
4606
  function generateClient(options) {
4569
4607
  return __async(this, null, function* () {
4570
4608
  const { datamodel, outputDir, config } = options;
@@ -4606,7 +4644,7 @@ function generateClient(options) {
4606
4644
  }
4607
4645
  const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
4608
4646
  yield promises.mkdir(absoluteOutputDir, { recursive: true });
4609
- const code = generateCode(models, queries, config.dialect);
4647
+ const code = generateCode(models, queries, config.dialect, datamodel);
4610
4648
  const outputPath = path.join(absoluteOutputDir, "index.ts");
4611
4649
  yield promises.writeFile(outputPath, code);
4612
4650
  const totalQueries = Array.from(queries.values()).reduce(
@@ -4634,15 +4672,20 @@ function createQueryKey(processedQuery) {
4634
4672
  return value;
4635
4673
  });
4636
4674
  }
4637
- function generateCode(models, queries, dialect) {
4675
+ function generateCode(models, queries, dialect, datamodel) {
4638
4676
  const cleanModels = models.map((model) => __spreadProps(__spreadValues({}, model), {
4639
4677
  fields: model.fields.filter((f) => f !== void 0 && f !== null)
4640
4678
  }));
4679
+ const { mappings, fieldTypes } = extractEnumMappings(datamodel);
4641
4680
  return `// Generated by @prisma-sql/generator - DO NOT EDIT
4642
4681
  import { buildSQL, transformQueryResults, type PrismaMethod, type Model } from 'prisma-sql'
4643
4682
 
4644
4683
  export const MODELS: Model[] = ${JSON.stringify(cleanModels, null, 2)}
4645
4684
 
4685
+ const ENUM_MAPPINGS: Record<string, Record<string, string>> = ${JSON.stringify(mappings, null, 2)}
4686
+
4687
+ const ENUM_FIELDS: Record<string, Record<string, string>> = ${JSON.stringify(fieldTypes, null, 2)}
4688
+
4646
4689
  const QUERIES: Record<string, Record<string, Record<string, {
4647
4690
  sql: string
4648
4691
  params: unknown[]
@@ -4656,6 +4699,79 @@ function isDynamicParam(key: string): boolean {
4656
4699
  return key === 'skip' || key === 'take' || key === 'cursor'
4657
4700
  }
4658
4701
 
4702
+ function transformEnumInValue(value: unknown, enumType: string | undefined): unknown {
4703
+ if (!enumType || value === null || value === undefined) {
4704
+ return value
4705
+ }
4706
+
4707
+ const mapping = ENUM_MAPPINGS[enumType]
4708
+ if (!mapping) {
4709
+ return value
4710
+ }
4711
+
4712
+ // Handle array of enum values
4713
+ if (Array.isArray(value)) {
4714
+ return value.map(v => {
4715
+ if (typeof v === 'string' && mapping[v] !== undefined) {
4716
+ return mapping[v]
4717
+ }
4718
+ return v
4719
+ })
4720
+ }
4721
+
4722
+ // Handle single enum value
4723
+ if (typeof value === 'string' && mapping[value] !== undefined) {
4724
+ return mapping[value]
4725
+ }
4726
+
4727
+ return value
4728
+ }
4729
+
4730
+ function transformEnumValues(modelName: string, obj: any, currentPath: string[] = []): any {
4731
+ if (obj === null || obj === undefined) {
4732
+ return obj
4733
+ }
4734
+
4735
+ if (Array.isArray(obj)) {
4736
+ return obj.map(item => transformEnumValues(modelName, item, currentPath))
4737
+ }
4738
+
4739
+ if (typeof obj === 'object') {
4740
+ const transformed: any = {}
4741
+ const modelFields = ENUM_FIELDS[modelName] || {}
4742
+
4743
+ for (const [key, value] of Object.entries(obj)) {
4744
+ const newPath = [...currentPath, key]
4745
+
4746
+ // Check if current key is an enum field at root level
4747
+ const enumType = modelFields[key]
4748
+
4749
+ if (enumType) {
4750
+ // This is an enum field - check if value is direct or has operators
4751
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
4752
+ // Has operators like { equals: "ACTIVE" }, { in: ["ACTIVE"] }, etc.
4753
+ const transformedOperators: any = {}
4754
+ for (const [op, opValue] of Object.entries(value)) {
4755
+ transformedOperators[op] = transformEnumInValue(opValue, enumType)
4756
+ }
4757
+ transformed[key] = transformedOperators
4758
+ } else {
4759
+ // Direct value like { status: "ACTIVE" }
4760
+ transformed[key] = transformEnumInValue(value, enumType)
4761
+ }
4762
+ } else if (typeof value === 'object' && value !== null) {
4763
+ // Recursively transform nested objects (relations, logical operators, etc)
4764
+ transformed[key] = transformEnumValues(modelName, value, newPath)
4765
+ } else {
4766
+ transformed[key] = value
4767
+ }
4768
+ }
4769
+ return transformed
4770
+ }
4771
+
4772
+ return obj
4773
+ }
4774
+
4659
4775
  function normalizeQuery(args: any): string {
4660
4776
  if (!args) return '{}'
4661
4777
 
@@ -4771,7 +4887,10 @@ export function speedExtension(config: {
4771
4887
  const modelName = this?.name || this?.$name
4772
4888
  const startTime = Date.now()
4773
4889
 
4774
- const queryKey = normalizeQuery(args)
4890
+ // Transform enum values before processing
4891
+ const transformedArgs = transformEnumValues(modelName, args || {})
4892
+
4893
+ const queryKey = normalizeQuery(transformedArgs)
4775
4894
  const prebakedQuery = QUERIES[modelName]?.[method]?.[queryKey]
4776
4895
 
4777
4896
  let sql: string
@@ -4780,7 +4899,7 @@ export function speedExtension(config: {
4780
4899
 
4781
4900
  if (prebakedQuery) {
4782
4901
  sql = prebakedQuery.sql
4783
- params = [...prebakedQuery.params, ...extractDynamicParams(args, prebakedQuery.dynamicKeys)]
4902
+ params = [...prebakedQuery.params, ...extractDynamicParams(transformedArgs, prebakedQuery.dynamicKeys)]
4784
4903
  prebaked = true
4785
4904
  } else {
4786
4905
  const model = MODELS.find((m) => m.name === modelName)
@@ -4789,7 +4908,7 @@ export function speedExtension(config: {
4789
4908
  return this.$parent[modelName][method](args)
4790
4909
  }
4791
4910
 
4792
- const result = buildSQL(model, MODELS, method, args || {}, DIALECT)
4911
+ const result = buildSQL(model, MODELS, method, transformedArgs, DIALECT)
4793
4912
  sql = result.sql
4794
4913
  params = result.params
4795
4914
  }