@prisma-next/sql-orm-lane 0.3.0-dev.3 → 0.3.0-dev.30

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.
Files changed (88) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-3DNKIXXB.js → chunk-C4EECZ4E.js} +106 -119
  3. package/dist/chunk-C4EECZ4E.js.map +1 -0
  4. package/dist/exports/orm.d.ts +3 -5
  5. package/dist/exports/orm.d.ts.map +1 -0
  6. package/dist/exports/orm.js +1 -1
  7. package/dist/index.d.ts +4 -42
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +1 -1
  10. package/dist/mutations/delete-builder.d.ts +9 -0
  11. package/dist/mutations/delete-builder.d.ts.map +1 -0
  12. package/dist/mutations/insert-builder.d.ts +7 -0
  13. package/dist/mutations/insert-builder.d.ts.map +1 -0
  14. package/dist/mutations/update-builder.d.ts +9 -0
  15. package/dist/mutations/update-builder.d.ts.map +1 -0
  16. package/dist/orm/builder.d.ts +38 -0
  17. package/dist/orm/builder.d.ts.map +1 -0
  18. package/dist/orm/capabilities.d.ts +3 -0
  19. package/dist/orm/capabilities.d.ts.map +1 -0
  20. package/dist/orm/context.d.ts +5 -0
  21. package/dist/orm/context.d.ts.map +1 -0
  22. package/dist/orm/state.d.ts +45 -0
  23. package/dist/orm/state.d.ts.map +1 -0
  24. package/dist/orm-include-child.d.ts +35 -0
  25. package/dist/orm-include-child.d.ts.map +1 -0
  26. package/dist/orm-relation-filter.d.ts +19 -0
  27. package/dist/orm-relation-filter.d.ts.map +1 -0
  28. package/dist/{orm-DAnGd7z2.d.ts → orm-types.d.ts} +16 -27
  29. package/dist/orm-types.d.ts.map +1 -0
  30. package/dist/orm.d.ts +5 -0
  31. package/dist/orm.d.ts.map +1 -0
  32. package/dist/plan/lowering.d.ts +2 -0
  33. package/dist/plan/lowering.d.ts.map +1 -0
  34. package/dist/plan/plan-assembly.d.ts +24 -0
  35. package/dist/plan/plan-assembly.d.ts.map +1 -0
  36. package/dist/plan/result-typing.d.ts +2 -0
  37. package/dist/plan/result-typing.d.ts.map +1 -0
  38. package/dist/relations/include-plan.d.ts +38 -0
  39. package/dist/relations/include-plan.d.ts.map +1 -0
  40. package/dist/selection/join.d.ts +3 -0
  41. package/dist/selection/join.d.ts.map +1 -0
  42. package/dist/selection/ordering.d.ts +11 -0
  43. package/dist/selection/ordering.d.ts.map +1 -0
  44. package/dist/selection/pagination.d.ts +6 -0
  45. package/dist/selection/pagination.d.ts.map +1 -0
  46. package/dist/selection/predicates.d.ts +10 -0
  47. package/dist/selection/predicates.d.ts.map +1 -0
  48. package/dist/selection/projection.d.ts +28 -0
  49. package/dist/selection/projection.d.ts.map +1 -0
  50. package/dist/selection/select-builder.d.ts +22 -0
  51. package/dist/selection/select-builder.d.ts.map +1 -0
  52. package/dist/types/internal.d.ts +4 -0
  53. package/dist/types/internal.d.ts.map +1 -0
  54. package/dist/utils/ast.d.ts +2 -0
  55. package/dist/utils/ast.d.ts.map +1 -0
  56. package/dist/utils/errors.d.ts +29 -0
  57. package/dist/utils/errors.d.ts.map +1 -0
  58. package/dist/utils/param-descriptor.d.ts +10 -0
  59. package/dist/utils/param-descriptor.d.ts.map +1 -0
  60. package/package.json +21 -21
  61. package/src/exports/orm.ts +12 -0
  62. package/src/index.ts +11 -0
  63. package/src/mutations/delete-builder.ts +87 -0
  64. package/src/mutations/insert-builder.ts +148 -0
  65. package/src/mutations/update-builder.ts +141 -0
  66. package/src/orm/builder.ts +744 -0
  67. package/src/orm/capabilities.ts +14 -0
  68. package/src/orm/context.ts +10 -0
  69. package/src/orm/state.ts +52 -0
  70. package/src/orm-include-child.ts +169 -0
  71. package/src/orm-relation-filter.ts +93 -0
  72. package/src/orm-types.ts +271 -0
  73. package/src/orm.ts +51 -0
  74. package/src/plan/lowering.ts +1 -0
  75. package/src/plan/plan-assembly.ts +312 -0
  76. package/src/plan/result-typing.ts +1 -0
  77. package/src/relations/include-plan.ts +324 -0
  78. package/src/selection/join.ts +13 -0
  79. package/src/selection/ordering.ts +52 -0
  80. package/src/selection/pagination.ts +11 -0
  81. package/src/selection/predicates.ts +104 -0
  82. package/src/selection/projection.ts +142 -0
  83. package/src/selection/select-builder.ts +80 -0
  84. package/src/types/internal.ts +3 -0
  85. package/src/utils/ast.ts +12 -0
  86. package/src/utils/errors.ts +130 -0
  87. package/src/utils/param-descriptor.ts +19 -0
  88. package/dist/chunk-3DNKIXXB.js.map +0 -1
@@ -0,0 +1,312 @@
1
+ import type { ExecutionPlan, ParamDescriptor, PlanMeta } from '@prisma-next/contract/types';
2
+ import { planInvalid } from '@prisma-next/plan';
3
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
4
+ import type {
5
+ BinaryExpr,
6
+ ExistsExpr,
7
+ Expression,
8
+ LoweredStatement,
9
+ SelectAst,
10
+ TableRef,
11
+ } from '@prisma-next/sql-relational-core/ast';
12
+ import { compact } from '@prisma-next/sql-relational-core/ast';
13
+ import type {
14
+ AnyExpressionSource,
15
+ AnyOrderBuilder,
16
+ BinaryBuilder,
17
+ } from '@prisma-next/sql-relational-core/types';
18
+ import {
19
+ collectColumnRefs,
20
+ getColumnMeta,
21
+ isColumnBuilder,
22
+ isExpressionBuilder,
23
+ isOperationExpr,
24
+ } from '@prisma-next/sql-relational-core/utils/guards';
25
+ import type { IncludeState } from '../relations/include-plan';
26
+ import type { ProjectionState } from '../selection/projection';
27
+
28
+ export interface MetaBuildArgs {
29
+ readonly contract: SqlContract<SqlStorage>;
30
+ readonly table: TableRef;
31
+ readonly projection: ProjectionState;
32
+ readonly includes?: ReadonlyArray<IncludeState>;
33
+ readonly where?: BinaryBuilder;
34
+ readonly orderBy?: AnyOrderBuilder;
35
+ readonly paramDescriptors: ParamDescriptor[];
36
+ readonly paramCodecs?: Record<string, string>;
37
+ }
38
+
39
+ /**
40
+ * Extracts column references from an ExpressionSource (ColumnBuilder or ExpressionBuilder).
41
+ * Skips entries with empty table or column names (e.g., placeholder columns for includes).
42
+ */
43
+ function collectRefsFromExpressionSource(
44
+ source: AnyExpressionSource,
45
+ refsColumns: Map<string, { table: string; column: string }>,
46
+ ): void {
47
+ if (isExpressionBuilder(source)) {
48
+ const allRefs = collectColumnRefs(source.expr);
49
+ for (const ref of allRefs) {
50
+ // Skip empty table/column (placeholders for includes)
51
+ if (ref.table && ref.column) {
52
+ refsColumns.set(`${ref.table}.${ref.column}`, {
53
+ table: ref.table,
54
+ column: ref.column,
55
+ });
56
+ }
57
+ }
58
+ } else if (isColumnBuilder(source)) {
59
+ const col = source as unknown as { table: string; column: string };
60
+ // Skip empty table/column (placeholders for includes)
61
+ if (col.table && col.column) {
62
+ refsColumns.set(`${col.table}.${col.column}`, {
63
+ table: col.table,
64
+ column: col.column,
65
+ });
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Extracts column references from an Expression (AST node).
72
+ */
73
+ function collectRefsFromExpression(
74
+ expr: Expression,
75
+ refsColumns: Map<string, { table: string; column: string }>,
76
+ ): void {
77
+ if (isOperationExpr(expr)) {
78
+ const allRefs = collectColumnRefs(expr);
79
+ for (const ref of allRefs) {
80
+ refsColumns.set(`${ref.table}.${ref.column}`, {
81
+ table: ref.table,
82
+ column: ref.column,
83
+ });
84
+ }
85
+ } else if (expr.kind === 'col') {
86
+ refsColumns.set(`${expr.table}.${expr.column}`, {
87
+ table: expr.table,
88
+ column: expr.column,
89
+ });
90
+ }
91
+ }
92
+
93
+ export function buildMeta(args: MetaBuildArgs): PlanMeta {
94
+ const refsColumns = new Map<string, { table: string; column: string }>();
95
+ const refsTables = new Set<string>([args.table.name]);
96
+
97
+ for (const column of args.projection.columns) {
98
+ collectRefsFromExpressionSource(column, refsColumns);
99
+ }
100
+
101
+ if (args.includes) {
102
+ for (const include of args.includes) {
103
+ refsTables.add(include.table.name);
104
+ const onLeft = include.on.left as unknown as { table: string; column: string };
105
+ const onRight = include.on.right as unknown as { table: string; column: string };
106
+ if (onLeft.table && onLeft.column && onRight.table && onRight.column) {
107
+ refsColumns.set(`${onLeft.table}.${onLeft.column}`, {
108
+ table: onLeft.table,
109
+ column: onLeft.column,
110
+ });
111
+ refsColumns.set(`${onRight.table}.${onRight.column}`, {
112
+ table: onRight.table,
113
+ column: onRight.column,
114
+ });
115
+ }
116
+ for (const column of include.childProjection.columns) {
117
+ const col = column as unknown as { table?: string; column?: string };
118
+ if (col.table && col.column) {
119
+ refsColumns.set(`${col.table}.${col.column}`, {
120
+ table: col.table,
121
+ column: col.column,
122
+ });
123
+ }
124
+ }
125
+ if (include.childWhere) {
126
+ // childWhere.left is Expression (already converted at builder creation time)
127
+ collectRefsFromExpression(include.childWhere.left, refsColumns);
128
+ }
129
+ if (include.childOrderBy) {
130
+ // childOrderBy.expr is Expression (already converted at builder creation time)
131
+ collectRefsFromExpression(include.childOrderBy.expr, refsColumns);
132
+ }
133
+ }
134
+ }
135
+
136
+ if (args.where) {
137
+ // args.where.left is Expression (already converted at builder creation time)
138
+ const leftExpr: Expression = args.where.left;
139
+ if (isOperationExpr(leftExpr)) {
140
+ const allRefs = collectColumnRefs(leftExpr);
141
+ for (const ref of allRefs) {
142
+ refsColumns.set(`${ref.table}.${ref.column}`, {
143
+ table: ref.table,
144
+ column: ref.column,
145
+ });
146
+ }
147
+ } else {
148
+ // leftExpr is ColumnRef
149
+ refsColumns.set(`${leftExpr.table}.${leftExpr.column}`, {
150
+ table: leftExpr.table,
151
+ column: leftExpr.column,
152
+ });
153
+ }
154
+ }
155
+
156
+ if (args.orderBy) {
157
+ // args.orderBy.expr is Expression (already converted at builder creation time)
158
+ const orderByExpr: Expression = args.orderBy.expr;
159
+ if (isOperationExpr(orderByExpr)) {
160
+ const allRefs = collectColumnRefs(orderByExpr);
161
+ for (const ref of allRefs) {
162
+ refsColumns.set(`${ref.table}.${ref.column}`, {
163
+ table: ref.table,
164
+ column: ref.column,
165
+ });
166
+ }
167
+ } else {
168
+ // orderByExpr is ColumnRef
169
+ refsColumns.set(`${orderByExpr.table}.${orderByExpr.column}`, {
170
+ table: orderByExpr.table,
171
+ column: orderByExpr.column,
172
+ });
173
+ }
174
+ }
175
+
176
+ const includeAliases = new Set(args.includes?.map((inc) => inc.alias) ?? []);
177
+ const projectionMap = Object.fromEntries(
178
+ args.projection.aliases.map((alias, index) => {
179
+ if (includeAliases.has(alias)) {
180
+ return [alias, `include:${alias}`];
181
+ }
182
+ const column = args.projection.columns[index];
183
+ if (!column) {
184
+ throw planInvalid(`Missing column for alias ${alias} at index ${index}`);
185
+ }
186
+ if (isExpressionBuilder(column)) {
187
+ return [alias, `operation:${column.expr.method}`];
188
+ }
189
+ // column is ColumnBuilder
190
+ const col = column as unknown as { table?: string; column?: string };
191
+ if (!col.table || !col.column) {
192
+ return [alias, `include:${alias}`];
193
+ }
194
+ return [alias, `${col.table}.${col.column}`];
195
+ }),
196
+ );
197
+
198
+ const projectionTypes: Record<string, string> = {};
199
+ for (let i = 0; i < args.projection.aliases.length; i++) {
200
+ const alias = args.projection.aliases[i];
201
+ if (!alias || includeAliases.has(alias)) {
202
+ continue;
203
+ }
204
+ const col = args.projection.columns[i];
205
+ if (!col) {
206
+ continue;
207
+ }
208
+ if (isExpressionBuilder(col)) {
209
+ const operationExpr = col.expr;
210
+ if (operationExpr.returns.kind === 'typeId') {
211
+ projectionTypes[alias] = operationExpr.returns.type;
212
+ } else if (operationExpr.returns.kind === 'builtin') {
213
+ projectionTypes[alias] = operationExpr.returns.type;
214
+ }
215
+ } else {
216
+ const columnMeta = getColumnMeta(col);
217
+ const codecId = columnMeta?.codecId;
218
+ if (codecId) {
219
+ projectionTypes[alias] = codecId;
220
+ }
221
+ }
222
+ }
223
+
224
+ const projectionCodecs: Record<string, string> = {};
225
+ for (let i = 0; i < args.projection.aliases.length; i++) {
226
+ const alias = args.projection.aliases[i];
227
+ if (!alias || includeAliases.has(alias)) {
228
+ continue;
229
+ }
230
+ const column = args.projection.columns[i];
231
+ if (!column) {
232
+ continue;
233
+ }
234
+ if (isExpressionBuilder(column)) {
235
+ const operationExpr = column.expr;
236
+ if (operationExpr.returns.kind === 'typeId') {
237
+ projectionCodecs[alias] = operationExpr.returns.type;
238
+ }
239
+ } else {
240
+ const columnMeta = getColumnMeta(column);
241
+ const codecId = columnMeta?.codecId;
242
+ if (codecId) {
243
+ projectionCodecs[alias] = codecId;
244
+ }
245
+ }
246
+ }
247
+
248
+ const allCodecs: Record<string, string> = {
249
+ ...projectionCodecs,
250
+ ...(args.paramCodecs ? args.paramCodecs : {}),
251
+ };
252
+
253
+ return Object.freeze(
254
+ compact({
255
+ target: args.contract.target,
256
+ targetFamily: args.contract.targetFamily,
257
+ coreHash: args.contract.coreHash,
258
+ lane: 'dsl',
259
+ refs: {
260
+ tables: Array.from(refsTables),
261
+ columns: Array.from(refsColumns.values()),
262
+ },
263
+ projection: projectionMap,
264
+ projectionTypes: Object.keys(projectionTypes).length > 0 ? projectionTypes : undefined,
265
+ annotations:
266
+ Object.keys(allCodecs).length > 0
267
+ ? Object.freeze({ codecs: Object.freeze(allCodecs) })
268
+ : undefined,
269
+ paramDescriptors: args.paramDescriptors,
270
+ profileHash: args.contract.profileHash,
271
+ }) as PlanMeta,
272
+ );
273
+ }
274
+
275
+ export function createPlan<Row>(
276
+ ast: SelectAst,
277
+ lowered: { body: LoweredStatement },
278
+ paramValues: unknown[],
279
+ planMeta: PlanMeta,
280
+ ): ExecutionPlan<Row> {
281
+ return Object.freeze({
282
+ ast,
283
+ sql: lowered.body.sql,
284
+ params: lowered.body.params ?? paramValues,
285
+ meta: {
286
+ ...planMeta,
287
+ lane: 'orm',
288
+ },
289
+ });
290
+ }
291
+
292
+ export function createPlanWithExists<Row>(
293
+ ast: SelectAst,
294
+ combinedWhere: BinaryExpr | ExistsExpr | undefined,
295
+ lowered: { body: LoweredStatement },
296
+ paramValues: unknown[],
297
+ planMeta: PlanMeta,
298
+ ): ExecutionPlan<Row> {
299
+ const modifiedAst: SelectAst = {
300
+ ...ast,
301
+ ...(combinedWhere !== undefined ? { where: combinedWhere } : {}),
302
+ };
303
+ return Object.freeze({
304
+ ast: modifiedAst,
305
+ sql: lowered.body.sql,
306
+ params: lowered.body.params ?? paramValues,
307
+ meta: {
308
+ ...planMeta,
309
+ lane: 'orm',
310
+ },
311
+ });
312
+ }
@@ -0,0 +1 @@
1
+ export type {};
@@ -0,0 +1,324 @@
1
+ import type { ParamDescriptor } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
3
+ import type {
4
+ BinaryExpr,
5
+ ColumnRef,
6
+ ExistsExpr,
7
+ IncludeAst,
8
+ OperationExpr,
9
+ ParamRef,
10
+ TableRef,
11
+ } from '@prisma-next/sql-relational-core/ast';
12
+ import { compact } from '@prisma-next/sql-relational-core/ast';
13
+ import type { QueryLaneContext } from '@prisma-next/sql-relational-core/query-lane-context';
14
+ import { schema } from '@prisma-next/sql-relational-core/schema';
15
+ import type {
16
+ AnyBinaryBuilder,
17
+ AnyColumnBuilder,
18
+ AnyOrderBuilder,
19
+ BuildOptions,
20
+ NestedProjection,
21
+ } from '@prisma-next/sql-relational-core/types';
22
+ import { isExpressionBuilder } from '@prisma-next/sql-relational-core/utils/guards';
23
+ import { checkIncludeCapabilities } from '../orm/capabilities';
24
+ import type { OrmIncludeState, RelationFilter } from '../orm/state';
25
+ import { buildJoinOnExpr } from '../selection/join';
26
+ import { buildChildOrderByClause } from '../selection/ordering';
27
+ import { buildWhereExpr } from '../selection/predicates';
28
+ import {
29
+ buildProjectionState,
30
+ type ProjectionInput,
31
+ type ProjectionState,
32
+ } from '../selection/projection';
33
+ import { createColumnRef, createSelectAst } from '../utils/ast';
34
+ import {
35
+ errorChildProjectionEmpty,
36
+ errorChildProjectionMustBeSpecified,
37
+ errorColumnNotFound,
38
+ errorJoinColumnsMustBeDefined,
39
+ errorMissingAlias,
40
+ errorMissingColumn,
41
+ errorModelNotFound,
42
+ errorMultiColumnJoinsNotSupported,
43
+ errorTableNotFound,
44
+ } from '../utils/errors';
45
+
46
+ export interface IncludeState {
47
+ readonly alias: string;
48
+ readonly table: TableRef;
49
+ readonly on: {
50
+ kind: 'join-on';
51
+ left: StorageColumn;
52
+ right: StorageColumn;
53
+ };
54
+ readonly childProjection: ProjectionState;
55
+ readonly childWhere?: AnyBinaryBuilder;
56
+ readonly childOrderBy?: AnyOrderBuilder;
57
+ readonly childLimit?: number;
58
+ }
59
+
60
+ interface BuildIncludeAstsInput {
61
+ readonly includes: OrmIncludeState[];
62
+ readonly contract: SqlContract<SqlStorage>;
63
+ readonly context: QueryLaneContext<SqlContract<SqlStorage>>;
64
+ readonly modelName: string;
65
+ readonly paramsMap: Record<string, unknown>;
66
+ readonly paramDescriptors: ParamDescriptor[];
67
+ readonly paramValues: unknown[];
68
+ readonly paramCodecs: Record<string, string>;
69
+ }
70
+
71
+ export function buildIncludeAsts(input: BuildIncludeAstsInput): {
72
+ includesAst: IncludeAst[];
73
+ includesForMeta: IncludeState[];
74
+ } {
75
+ const {
76
+ includes,
77
+ contract,
78
+ context,
79
+ modelName,
80
+ paramsMap,
81
+ paramDescriptors,
82
+ paramValues,
83
+ paramCodecs,
84
+ } = input;
85
+
86
+ const includesAst: IncludeAst[] = [];
87
+ const includesForMeta: IncludeState[] = [];
88
+
89
+ for (const includeState of includes) {
90
+ checkIncludeCapabilities(contract);
91
+
92
+ const parentTableName = contract.mappings.modelToTable?.[modelName];
93
+ if (!parentTableName) {
94
+ errorModelNotFound(modelName);
95
+ }
96
+
97
+ const parentSchemaHandle = schema(context);
98
+ const parentSchemaTable = parentSchemaHandle.tables[parentTableName];
99
+ if (!parentSchemaTable) {
100
+ errorTableNotFound(parentTableName);
101
+ }
102
+ const childSchemaHandle = schema(context);
103
+ const childSchemaTable = childSchemaHandle.tables[includeState.childTable.name];
104
+ if (!childSchemaTable) {
105
+ errorTableNotFound(includeState.childTable.name);
106
+ }
107
+
108
+ if (
109
+ includeState.relation.on.parentCols.length !== 1 ||
110
+ includeState.relation.on.childCols.length !== 1
111
+ ) {
112
+ errorMultiColumnJoinsNotSupported();
113
+ }
114
+ const parentColName = includeState.relation.on.parentCols[0];
115
+ const childColName = includeState.relation.on.childCols[0];
116
+ if (!parentColName || !childColName) {
117
+ errorJoinColumnsMustBeDefined();
118
+ }
119
+ const parentCol = parentSchemaTable.columns[parentColName];
120
+ const childCol = childSchemaTable.columns[childColName];
121
+ if (!parentCol) {
122
+ errorColumnNotFound(parentColName, parentTableName);
123
+ }
124
+ if (!childCol) {
125
+ errorColumnNotFound(childColName, includeState.childTable.name);
126
+ }
127
+
128
+ const onExpr = buildJoinOnExpr(
129
+ parentTableName,
130
+ parentColName,
131
+ includeState.childTable.name,
132
+ childColName,
133
+ );
134
+
135
+ if (!includeState.childProjection) {
136
+ errorChildProjectionMustBeSpecified();
137
+ }
138
+ const filteredProjection: Record<string, AnyColumnBuilder | NestedProjection> = {};
139
+ for (const [key, value] of Object.entries(includeState.childProjection)) {
140
+ if (value !== true && value !== false) {
141
+ filteredProjection[key] = value as AnyColumnBuilder | NestedProjection;
142
+ }
143
+ }
144
+ if (Object.keys(filteredProjection).length === 0) {
145
+ errorChildProjectionEmpty();
146
+ }
147
+ const childProjectionState = buildProjectionState(
148
+ includeState.childTable,
149
+ filteredProjection as ProjectionInput,
150
+ );
151
+
152
+ let childWhere: BinaryExpr | undefined;
153
+ if (includeState.childWhere) {
154
+ const whereResult = buildWhereExpr(
155
+ includeState.childWhere,
156
+ contract,
157
+ paramsMap,
158
+ paramDescriptors,
159
+ paramValues,
160
+ );
161
+ childWhere = whereResult.expr;
162
+ if (whereResult.codecId && whereResult.paramName) {
163
+ paramCodecs[whereResult.paramName] = whereResult.codecId;
164
+ }
165
+ }
166
+
167
+ const childOrderBy = buildChildOrderByClause(includeState.childOrderBy);
168
+
169
+ const childProjectionItems: Array<{ alias: string; expr: ColumnRef | OperationExpr }> = [];
170
+ for (let i = 0; i < childProjectionState.aliases.length; i++) {
171
+ const alias = childProjectionState.aliases[i];
172
+ if (!alias) {
173
+ errorMissingAlias(i);
174
+ }
175
+ const column = childProjectionState.columns[i];
176
+ if (!column) {
177
+ errorMissingColumn(alias, i);
178
+ }
179
+ if (isExpressionBuilder(column)) {
180
+ childProjectionItems.push({ alias, expr: column.expr });
181
+ } else {
182
+ // ColumnBuilder - use toExpr() to get ColumnRef
183
+ childProjectionItems.push({ alias, expr: column.toExpr() });
184
+ }
185
+ }
186
+
187
+ const includeAst: IncludeAst = compact({
188
+ kind: 'includeMany',
189
+ alias: includeState.alias,
190
+ child: compact({
191
+ table: includeState.childTable,
192
+ on: onExpr,
193
+ project: childProjectionItems,
194
+ where: childWhere,
195
+ orderBy: childOrderBy,
196
+ limit: includeState.childLimit,
197
+ }),
198
+ }) as IncludeAst;
199
+ includesAst.push(includeAst);
200
+
201
+ const includeForMeta: IncludeState = compact({
202
+ alias: includeState.alias,
203
+ table: includeState.childTable,
204
+ on: {
205
+ kind: 'join-on',
206
+ left: parentCol as unknown as StorageColumn,
207
+ right: childCol as unknown as StorageColumn,
208
+ },
209
+ childProjection: childProjectionState,
210
+ childWhere: includeState.childWhere,
211
+ childOrderBy: includeState.childOrderBy,
212
+ childLimit: includeState.childLimit,
213
+ }) as IncludeState;
214
+ includesForMeta.push(includeForMeta);
215
+ }
216
+
217
+ return { includesAst, includesForMeta };
218
+ }
219
+
220
+ export function buildExistsSubqueries(
221
+ relationFilters: RelationFilter[],
222
+ contract: SqlContract<SqlStorage>,
223
+ modelName: string,
224
+ options?: BuildOptions,
225
+ ): ExistsExpr[] {
226
+ const existsExprs: ExistsExpr[] = [];
227
+
228
+ for (const filter of relationFilters) {
229
+ const childTableName = contract.mappings.modelToTable?.[filter.childModelName];
230
+ if (!childTableName) {
231
+ errorModelNotFound(filter.childModelName);
232
+ }
233
+
234
+ const childTable: TableRef = { kind: 'table', name: childTableName };
235
+ const parentTableName = contract.mappings.modelToTable?.[modelName];
236
+ if (!parentTableName) {
237
+ errorModelNotFound(modelName);
238
+ }
239
+
240
+ const joinConditions: Array<{ left: ColumnRef; right: ColumnRef }> = [];
241
+ for (let i = 0; i < filter.relation.on.parentCols.length; i++) {
242
+ const parentCol = filter.relation.on.parentCols[i];
243
+ const childCol = filter.relation.on.childCols[i];
244
+ if (!parentCol || !childCol) {
245
+ continue;
246
+ }
247
+ joinConditions.push({
248
+ left: { kind: 'col', table: parentTableName, column: parentCol },
249
+ right: { kind: 'col', table: childTableName, column: childCol },
250
+ });
251
+ }
252
+
253
+ let childWhere: BinaryExpr | undefined;
254
+ if (filter.childWhere) {
255
+ const paramsMap = (options?.params ?? {}) as Record<string, unknown>;
256
+ const paramDescriptors: ParamDescriptor[] = [];
257
+ const paramValues: unknown[] = [];
258
+ const whereResult = buildWhereExpr(
259
+ filter.childWhere,
260
+ contract,
261
+ paramsMap,
262
+ paramDescriptors,
263
+ paramValues,
264
+ );
265
+ childWhere = whereResult.expr;
266
+ }
267
+
268
+ let subqueryWhere: BinaryExpr | undefined = childWhere;
269
+ if (joinConditions.length > 0) {
270
+ const firstJoinCondition = joinConditions[0];
271
+ if (firstJoinCondition) {
272
+ const joinWhere: BinaryExpr = {
273
+ kind: 'bin',
274
+ op: 'eq',
275
+ left: firstJoinCondition.left,
276
+ right: firstJoinCondition.right as unknown as ParamRef,
277
+ };
278
+ if (childWhere) {
279
+ subqueryWhere = joinWhere;
280
+ } else {
281
+ subqueryWhere = joinWhere;
282
+ }
283
+ }
284
+ }
285
+ const projectionColumn = joinConditions[0]?.right ?? createColumnRef(childTableName, 'id');
286
+ const subquery = createSelectAst({
287
+ from: childTable,
288
+ project: [{ alias: '_exists', expr: projectionColumn }],
289
+ where: subqueryWhere,
290
+ } as {
291
+ from: TableRef;
292
+ project: ReadonlyArray<{ alias: string; expr: ColumnRef }>;
293
+ where?: BinaryExpr | ExistsExpr;
294
+ });
295
+
296
+ const notExists = filter.filterType === 'none' || filter.filterType === 'every';
297
+
298
+ const existsExpr: ExistsExpr = {
299
+ kind: 'exists',
300
+ subquery,
301
+ not: notExists,
302
+ };
303
+
304
+ existsExprs.push(existsExpr);
305
+ }
306
+
307
+ return existsExprs;
308
+ }
309
+
310
+ export function combineWhereClauses(
311
+ mainWhere: BinaryExpr | ExistsExpr | undefined,
312
+ existsExprs: ExistsExpr[],
313
+ ): BinaryExpr | ExistsExpr | undefined {
314
+ if (existsExprs.length === 1) {
315
+ return existsExprs[0];
316
+ }
317
+ if (mainWhere) {
318
+ return mainWhere;
319
+ }
320
+ if (existsExprs.length > 0) {
321
+ return existsExprs[0];
322
+ }
323
+ return undefined;
324
+ }
@@ -0,0 +1,13 @@
1
+ import type { JoinOnExpr } from '@prisma-next/sql-relational-core/ast';
2
+ import { createColumnRef, createJoinOnExpr } from '../utils/ast';
3
+
4
+ export function buildJoinOnExpr(
5
+ parentTableName: string,
6
+ parentColName: string,
7
+ childTableName: string,
8
+ childColName: string,
9
+ ): JoinOnExpr {
10
+ const leftCol = createColumnRef(parentTableName, parentColName);
11
+ const rightCol = createColumnRef(childTableName, childColName);
12
+ return createJoinOnExpr(leftCol, rightCol);
13
+ }
@@ -0,0 +1,52 @@
1
+ import type { StorageColumn } from '@prisma-next/sql-contract/types';
2
+ import type { ColumnRef, Direction, OperationExpr } from '@prisma-next/sql-relational-core/ast';
3
+ import type { AnyOrderBuilder, OrderBuilder } from '@prisma-next/sql-relational-core/types';
4
+ import {
5
+ extractBaseColumnRef,
6
+ isOperationExpr,
7
+ } from '@prisma-next/sql-relational-core/utils/guards';
8
+ import { createColumnRef, createOrderByItem } from '../utils/ast';
9
+
10
+ export function buildOrderByClause(orderBy: AnyOrderBuilder | undefined):
11
+ | ReadonlyArray<{
12
+ expr: ColumnRef | OperationExpr;
13
+ dir: Direction;
14
+ }>
15
+ | undefined {
16
+ if (!orderBy) {
17
+ return undefined;
18
+ }
19
+
20
+ const orderByBuilder = orderBy as OrderBuilder<string, StorageColumn, unknown>;
21
+ const orderExpr = orderByBuilder.expr;
22
+ const expr: ColumnRef | OperationExpr = isOperationExpr(orderExpr)
23
+ ? orderExpr
24
+ : (() => {
25
+ const colBuilder = orderExpr as { table: string; column: string };
26
+ return createColumnRef(colBuilder.table, colBuilder.column);
27
+ })();
28
+ return [createOrderByItem(expr, orderByBuilder.dir)];
29
+ }
30
+
31
+ export function buildChildOrderByClause(orderBy: AnyOrderBuilder | undefined):
32
+ | ReadonlyArray<{
33
+ expr: ColumnRef | OperationExpr;
34
+ dir: Direction;
35
+ }>
36
+ | undefined {
37
+ if (!orderBy) {
38
+ return undefined;
39
+ }
40
+
41
+ const orderByBuilder = orderBy as OrderBuilder<string, StorageColumn, unknown>;
42
+ const orderExpr = orderByBuilder.expr;
43
+ const expr: ColumnRef | OperationExpr = (() => {
44
+ if (isOperationExpr(orderExpr)) {
45
+ const baseCol = extractBaseColumnRef(orderExpr);
46
+ return createColumnRef(baseCol.table, baseCol.column);
47
+ }
48
+ const colBuilder = orderExpr as { table: string; column: string };
49
+ return createColumnRef(colBuilder.table, colBuilder.column);
50
+ })();
51
+ return [createOrderByItem(expr, orderByBuilder.dir)];
52
+ }