@prisma-next/sql-lane 0.3.0-dev.11 → 0.3.0-dev.114
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/LICENSE +201 -0
- package/README.md +18 -1
- package/dist/builder-DpxuiFO4.d.mts +118 -0
- package/dist/builder-DpxuiFO4.d.mts.map +1 -0
- package/dist/builder-MYJh5-Gn.mjs +1153 -0
- package/dist/builder-MYJh5-Gn.mjs.map +1 -0
- package/dist/exports/sql.d.mts +3 -0
- package/dist/exports/sql.mjs +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/package.json +29 -24
- package/src/raw.ts +9 -2
- package/src/sql/builder.ts +12 -9
- package/src/sql/context.ts +3 -3
- package/src/sql/include-builder.ts +109 -70
- package/src/sql/join-builder.ts +21 -11
- package/src/sql/mutation-builder.ts +94 -107
- package/src/sql/plan.ts +88 -128
- package/src/sql/predicate-builder.ts +74 -65
- package/src/sql/projection.ts +17 -12
- package/src/sql/select-builder.ts +64 -102
- package/src/types/internal.ts +6 -5
- package/src/utils/errors.ts +2 -2
- package/src/utils/state.ts +5 -6
- package/dist/chunk-AWSKRSFP.js +0 -1569
- package/dist/chunk-AWSKRSFP.js.map +0 -1
- package/dist/exports/sql.d.ts +0 -5
- package/dist/exports/sql.d.ts.map +0 -1
- package/dist/exports/sql.js +0 -11
- package/dist/exports/sql.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/raw.d.ts +0 -11
- package/dist/raw.d.ts.map +0 -1
- package/dist/sql/builder.d.ts +0 -11
- package/dist/sql/builder.d.ts.map +0 -1
- package/dist/sql/context.d.ts +0 -5
- package/dist/sql/context.d.ts.map +0 -1
- package/dist/sql/include-builder.d.ts +0 -35
- package/dist/sql/include-builder.d.ts.map +0 -1
- package/dist/sql/join-builder.d.ts +0 -4
- package/dist/sql/join-builder.d.ts.map +0 -1
- package/dist/sql/mutation-builder.d.ts +0 -64
- package/dist/sql/mutation-builder.d.ts.map +0 -1
- package/dist/sql/plan.d.ts +0 -4
- package/dist/sql/plan.d.ts.map +0 -1
- package/dist/sql/predicate-builder.d.ts +0 -11
- package/dist/sql/predicate-builder.d.ts.map +0 -1
- package/dist/sql/projection.d.ts +0 -18
- package/dist/sql/projection.d.ts.map +0 -1
- package/dist/sql/select-builder.d.ts +0 -35
- package/dist/sql/select-builder.d.ts.map +0 -1
- package/dist/types/internal.d.ts +0 -35
- package/dist/types/internal.d.ts.map +0 -1
- package/dist/types/public.d.ts +0 -18
- package/dist/types/public.d.ts.map +0 -1
- package/dist/utils/assertions.d.ts +0 -28
- package/dist/utils/assertions.d.ts.map +0 -1
- package/dist/utils/capabilities.d.ts +0 -4
- package/dist/utils/capabilities.d.ts.map +0 -1
- package/dist/utils/errors.d.ts +0 -30
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/state.d.ts +0 -30
- package/dist/utils/state.d.ts.map +0 -1
package/src/sql/plan.ts
CHANGED
|
@@ -1,42 +1,60 @@
|
|
|
1
1
|
import type { PlanMeta } from '@prisma-next/contract/types';
|
|
2
|
-
import type
|
|
3
|
-
import {
|
|
4
|
-
import type { AnyColumnBuilder } from '@prisma-next/sql-relational-core/types';
|
|
2
|
+
import { type AnyExpression, compact } from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import type { AnyExpressionSource } from '@prisma-next/sql-relational-core/types';
|
|
5
4
|
import {
|
|
6
|
-
collectColumnRefs,
|
|
7
|
-
getColumnInfo,
|
|
8
|
-
getOperationExpr,
|
|
9
5
|
isColumnBuilder,
|
|
10
|
-
|
|
6
|
+
isExpressionBuilder,
|
|
11
7
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
12
8
|
import type { MetaBuildArgs } from '../types/internal';
|
|
13
9
|
import { assertColumnBuilder } from '../utils/assertions';
|
|
14
10
|
import { errorMissingColumnForAlias } from '../utils/errors';
|
|
15
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Extracts column references from an ExpressionSource (ColumnBuilder or ExpressionBuilder).
|
|
14
|
+
*/
|
|
15
|
+
function collectRefsFromExpressionSource(
|
|
16
|
+
source: AnyExpressionSource,
|
|
17
|
+
refsColumns: Map<string, { table: string; column: string }>,
|
|
18
|
+
): void {
|
|
19
|
+
if (isExpressionBuilder(source)) {
|
|
20
|
+
const allRefs = source.expr.collectColumnRefs();
|
|
21
|
+
for (const ref of allRefs) {
|
|
22
|
+
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
23
|
+
table: ref.table,
|
|
24
|
+
column: ref.column,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
} else if (isColumnBuilder(source)) {
|
|
28
|
+
// ColumnBuilder - use table and column directly
|
|
29
|
+
const col = source as unknown as { table: string; column: string };
|
|
30
|
+
refsColumns.set(`${col.table}.${col.column}`, {
|
|
31
|
+
table: col.table,
|
|
32
|
+
column: col.column,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extracts column references from an Expression (AST node).
|
|
39
|
+
*/
|
|
40
|
+
function collectRefsFromExpression(
|
|
41
|
+
expr: AnyExpression,
|
|
42
|
+
refsColumns: Map<string, { table: string; column: string }>,
|
|
43
|
+
): void {
|
|
44
|
+
for (const ref of expr.collectColumnRefs()) {
|
|
45
|
+
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
46
|
+
table: ref.table,
|
|
47
|
+
column: ref.column,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
16
52
|
export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
17
53
|
const refsColumns = new Map<string, { table: string; column: string }>();
|
|
18
54
|
const refsTables = new Set<string>([args.table.name]);
|
|
19
55
|
|
|
20
56
|
for (const column of args.projection.columns) {
|
|
21
|
-
|
|
22
|
-
if (!column) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
const operationExpr = getOperationExpr(column);
|
|
26
|
-
if (operationExpr) {
|
|
27
|
-
const allRefs = collectColumnRefs(operationExpr);
|
|
28
|
-
for (const ref of allRefs) {
|
|
29
|
-
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
30
|
-
table: ref.table,
|
|
31
|
-
column: ref.column,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
refsColumns.set(`${column.table}.${column.column}`, {
|
|
36
|
-
table: column.table,
|
|
37
|
-
column: column.column,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
57
|
+
collectRefsFromExpressionSource(column, refsColumns);
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
if (args.joins) {
|
|
@@ -81,100 +99,38 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
81
99
|
}
|
|
82
100
|
// Add child WHERE columns if present
|
|
83
101
|
if (include.childWhere) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const rightColInfo = getColumnInfo(childWhereRight);
|
|
93
|
-
refsColumns.set(`${rightColInfo.table}.${rightColInfo.column}`, {
|
|
94
|
-
table: rightColInfo.table,
|
|
95
|
-
column: rightColInfo.column,
|
|
96
|
-
});
|
|
102
|
+
if (include.childWhere.kind === 'nullCheck') {
|
|
103
|
+
collectRefsFromExpression(include.childWhere.expr, refsColumns);
|
|
104
|
+
} else {
|
|
105
|
+
collectRefsFromExpression(include.childWhere.left, refsColumns);
|
|
106
|
+
const childWhereRight = include.childWhere.right;
|
|
107
|
+
if (isColumnBuilder(childWhereRight) || isExpressionBuilder(childWhereRight)) {
|
|
108
|
+
collectRefsFromExpressionSource(childWhereRight, refsColumns);
|
|
109
|
+
}
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
// Add child ORDER BY columns if present
|
|
100
113
|
if (include.childOrderBy) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
};
|
|
104
|
-
if (orderBy.expr) {
|
|
105
|
-
const colInfo = getColumnInfo(orderBy.expr);
|
|
106
|
-
refsColumns.set(`${colInfo.table}.${colInfo.column}`, {
|
|
107
|
-
table: colInfo.table,
|
|
108
|
-
column: colInfo.column,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
114
|
+
// childOrderBy.expr is Expression (already converted at builder creation time)
|
|
115
|
+
collectRefsFromExpression(include.childOrderBy.expr, refsColumns);
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
if (args.where) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (isOperationExpr(whereLeft)) {
|
|
119
|
-
const allRefs = collectColumnRefs(whereLeft);
|
|
120
|
-
for (const ref of allRefs) {
|
|
121
|
-
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
122
|
-
table: ref.table,
|
|
123
|
-
column: ref.column,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
121
|
+
if (args.where.kind === 'nullCheck') {
|
|
122
|
+
collectRefsFromExpression(args.where.expr, refsColumns);
|
|
126
123
|
} else {
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
for (const ref of allRefs) {
|
|
132
|
-
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
133
|
-
table: ref.table,
|
|
134
|
-
column: ref.column,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
const colBuilder = assertColumnBuilder(whereLeft, 'where clause must be a ColumnBuilder');
|
|
139
|
-
refsColumns.set(`${colBuilder.table}.${colBuilder.column}`, {
|
|
140
|
-
table: colBuilder.table,
|
|
141
|
-
column: colBuilder.column,
|
|
142
|
-
});
|
|
124
|
+
collectRefsFromExpression(args.where.left, refsColumns);
|
|
125
|
+
const whereRight = args.where.right;
|
|
126
|
+
if (isColumnBuilder(whereRight) || isExpressionBuilder(whereRight)) {
|
|
127
|
+
collectRefsFromExpressionSource(whereRight, refsColumns);
|
|
143
128
|
}
|
|
144
129
|
}
|
|
145
|
-
|
|
146
|
-
// Handle right side of WHERE clause - can be ParamPlaceholder or AnyColumnBuilder
|
|
147
|
-
const whereRight = args.where.right;
|
|
148
|
-
if (isColumnBuilder(whereRight)) {
|
|
149
|
-
const colInfo = getColumnInfo(whereRight);
|
|
150
|
-
refsColumns.set(`${colInfo.table}.${colInfo.column}`, {
|
|
151
|
-
table: colInfo.table,
|
|
152
|
-
column: colInfo.column,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
130
|
}
|
|
156
131
|
|
|
157
132
|
if (args.orderBy) {
|
|
158
|
-
|
|
159
|
-
expr?: AnyColumnBuilder | OperationExpr;
|
|
160
|
-
};
|
|
161
|
-
const orderByExpr = orderBy.expr;
|
|
162
|
-
if (orderByExpr) {
|
|
163
|
-
if (isOperationExpr(orderByExpr)) {
|
|
164
|
-
const allRefs = collectColumnRefs(orderByExpr);
|
|
165
|
-
for (const ref of allRefs) {
|
|
166
|
-
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
167
|
-
table: ref.table,
|
|
168
|
-
column: ref.column,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
refsColumns.set(`${orderByExpr.table}.${orderByExpr.column}`, {
|
|
173
|
-
table: orderByExpr.table,
|
|
174
|
-
column: orderByExpr.column,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
133
|
+
collectRefsFromExpression(args.orderBy.expr, refsColumns);
|
|
178
134
|
}
|
|
179
135
|
|
|
180
136
|
// Build projection map - mark include aliases with special marker
|
|
@@ -191,13 +147,18 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
191
147
|
// This shouldn't happen if projection building is correct, but handle gracefully
|
|
192
148
|
errorMissingColumnForAlias(alias, index);
|
|
193
149
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
150
|
+
// Check if column is an ExpressionBuilder (operation result)
|
|
151
|
+
if (isExpressionBuilder(column)) {
|
|
152
|
+
return [alias, `operation:${column.expr.method}`];
|
|
153
|
+
}
|
|
154
|
+
// column is ColumnBuilder
|
|
155
|
+
const col = column as unknown as { table?: string; column?: string };
|
|
156
|
+
if (!col.table || !col.column) {
|
|
157
|
+
// This is a placeholder column for an include - skip it
|
|
158
|
+
return [alias, `include:${alias}`];
|
|
198
159
|
}
|
|
199
160
|
|
|
200
|
-
return [alias, `${
|
|
161
|
+
return [alias, `${col.table}.${col.column}`];
|
|
201
162
|
}),
|
|
202
163
|
);
|
|
203
164
|
|
|
@@ -213,15 +174,15 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
213
174
|
if (!column) {
|
|
214
175
|
continue;
|
|
215
176
|
}
|
|
216
|
-
|
|
217
|
-
|
|
177
|
+
if (isExpressionBuilder(column)) {
|
|
178
|
+
const operationExpr = column.expr;
|
|
218
179
|
if (operationExpr.returns.kind === 'typeId') {
|
|
219
180
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
220
181
|
} else if (operationExpr.returns.kind === 'builtin') {
|
|
221
182
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
222
183
|
}
|
|
223
184
|
} else {
|
|
224
|
-
//
|
|
185
|
+
// column is ColumnBuilder
|
|
225
186
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
226
187
|
const columnMeta = col.columnMeta;
|
|
227
188
|
const codecId = columnMeta?.codecId;
|
|
@@ -233,7 +194,7 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
233
194
|
|
|
234
195
|
// Build codec assignments from column types
|
|
235
196
|
// Skip include aliases - they don't need codec entries
|
|
236
|
-
const
|
|
197
|
+
const codecs: Record<string, string> = {};
|
|
237
198
|
for (let i = 0; i < args.projection.aliases.length; i++) {
|
|
238
199
|
const alias = args.projection.aliases[i];
|
|
239
200
|
if (!alias || includeAliases.has(alias)) {
|
|
@@ -243,34 +204,28 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
243
204
|
if (!column) {
|
|
244
205
|
continue;
|
|
245
206
|
}
|
|
246
|
-
|
|
247
|
-
|
|
207
|
+
if (isExpressionBuilder(column)) {
|
|
208
|
+
const operationExpr = column.expr;
|
|
248
209
|
if (operationExpr.returns.kind === 'typeId') {
|
|
249
|
-
|
|
210
|
+
codecs[alias] = operationExpr.returns.type;
|
|
250
211
|
}
|
|
251
212
|
} else {
|
|
252
213
|
// Use columnMeta.codecId directly as typeId (already canonicalized)
|
|
253
|
-
//
|
|
214
|
+
// column is ColumnBuilder
|
|
254
215
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
255
216
|
const columnMeta = col.columnMeta;
|
|
256
217
|
const codecId = columnMeta?.codecId;
|
|
257
218
|
if (codecId) {
|
|
258
|
-
|
|
219
|
+
codecs[alias] = codecId;
|
|
259
220
|
}
|
|
260
221
|
}
|
|
261
222
|
}
|
|
262
223
|
|
|
263
|
-
// Merge projection and parameter codecs
|
|
264
|
-
const allCodecs: Record<string, string> = {
|
|
265
|
-
...projectionCodecs,
|
|
266
|
-
...(args.paramCodecs ? args.paramCodecs : {}),
|
|
267
|
-
};
|
|
268
|
-
|
|
269
224
|
return Object.freeze(
|
|
270
225
|
compact({
|
|
271
226
|
target: args.contract.target,
|
|
272
227
|
targetFamily: args.contract.targetFamily,
|
|
273
|
-
|
|
228
|
+
storageHash: args.contract.storageHash,
|
|
274
229
|
lane: 'dsl',
|
|
275
230
|
refs: {
|
|
276
231
|
tables: Array.from(refsTables),
|
|
@@ -279,8 +234,13 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
279
234
|
projection: projectionMap,
|
|
280
235
|
projectionTypes: Object.keys(projectionTypes).length > 0 ? projectionTypes : undefined,
|
|
281
236
|
annotations:
|
|
282
|
-
Object.keys(
|
|
283
|
-
? Object.freeze(
|
|
237
|
+
Object.keys(codecs).length > 0 || args.limit !== undefined
|
|
238
|
+
? Object.freeze(
|
|
239
|
+
compact({
|
|
240
|
+
codecs: Object.keys(codecs).length > 0 ? Object.freeze(codecs) : undefined,
|
|
241
|
+
limit: args.limit,
|
|
242
|
+
}),
|
|
243
|
+
)
|
|
284
244
|
: undefined,
|
|
285
245
|
paramDescriptors: args.paramDescriptors,
|
|
286
246
|
profileHash: args.contract.profileHash,
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { planInvalid } from '@prisma-next/plan';
|
|
2
2
|
import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
|
|
3
|
-
import type {
|
|
4
|
-
BinaryExpr,
|
|
5
|
-
ColumnRef,
|
|
6
|
-
OperationExpr,
|
|
7
|
-
ParamRef,
|
|
8
|
-
} from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import type { AnyExpression, NullCheckExpr } from '@prisma-next/sql-relational-core/ast';
|
|
9
4
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
BinaryExpr,
|
|
6
|
+
NullCheckExpr as NullCheckExprNode,
|
|
7
|
+
ParamRef as ParamRefNode,
|
|
13
8
|
} from '@prisma-next/sql-relational-core/ast';
|
|
14
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
BinaryBuilder,
|
|
11
|
+
NullCheckBuilder,
|
|
12
|
+
ParamPlaceholder,
|
|
13
|
+
UnaryBuilder,
|
|
14
|
+
} from '@prisma-next/sql-relational-core/types';
|
|
15
15
|
import {
|
|
16
|
-
getColumnInfo,
|
|
17
|
-
getOperationExpr,
|
|
18
16
|
isColumnBuilder,
|
|
17
|
+
isExpressionBuilder,
|
|
19
18
|
isParamPlaceholder,
|
|
20
19
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
21
20
|
import {
|
|
@@ -26,32 +25,59 @@ import {
|
|
|
26
25
|
} from '../utils/errors';
|
|
27
26
|
|
|
28
27
|
export interface BuildWhereExprResult {
|
|
29
|
-
expr:
|
|
28
|
+
expr: AnyExpression;
|
|
30
29
|
codecId: string | undefined;
|
|
31
30
|
paramName: string;
|
|
32
31
|
}
|
|
33
32
|
|
|
33
|
+
function isNullCheckBuilder(builder: BinaryBuilder | UnaryBuilder): builder is NullCheckBuilder {
|
|
34
|
+
return builder.kind === 'nullCheck';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function buildNullCheckExpr(
|
|
38
|
+
contract: SqlContract<SqlStorage>,
|
|
39
|
+
where: NullCheckBuilder,
|
|
40
|
+
): NullCheckExpr {
|
|
41
|
+
const expr = where.expr;
|
|
42
|
+
|
|
43
|
+
if (expr.kind === 'column-ref') {
|
|
44
|
+
const { table, column } = expr;
|
|
45
|
+
const contractTable = contract.storage.tables[table];
|
|
46
|
+
if (!contractTable) {
|
|
47
|
+
errorUnknownTable(table);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
51
|
+
if (!columnMeta) {
|
|
52
|
+
errorUnknownColumn(column, table);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return where.isNull ? NullCheckExprNode.isNull(expr) : NullCheckExprNode.isNotNull(expr);
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
export function buildWhereExpr(
|
|
35
60
|
contract: SqlContract<SqlStorage>,
|
|
36
|
-
where: BinaryBuilder,
|
|
61
|
+
where: BinaryBuilder | UnaryBuilder,
|
|
37
62
|
paramsMap: Record<string, unknown>,
|
|
38
|
-
descriptors: ParamDescriptor[],
|
|
39
|
-
values: unknown[],
|
|
40
63
|
): BuildWhereExprResult {
|
|
41
|
-
|
|
64
|
+
if (isNullCheckBuilder(where)) {
|
|
65
|
+
return {
|
|
66
|
+
expr: buildNullCheckExpr(contract, where),
|
|
67
|
+
codecId: undefined,
|
|
68
|
+
paramName: '',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let leftExpr: AnyExpression;
|
|
42
73
|
let codecId: string | undefined;
|
|
43
|
-
let rightExpr:
|
|
74
|
+
let rightExpr: AnyExpression;
|
|
44
75
|
let paramName: string;
|
|
45
76
|
|
|
46
|
-
|
|
47
|
-
// or a ColumnBuilder with _operationExpr property
|
|
48
|
-
const operationExpr = getOperationExpr(where.left);
|
|
49
|
-
if (operationExpr) {
|
|
50
|
-
leftExpr = operationExpr;
|
|
51
|
-
} else if (isColumnBuilder(where.left)) {
|
|
52
|
-
// where.left is a ColumnBuilder - use proper type narrowing
|
|
53
|
-
const { table, column } = getColumnInfo(where.left);
|
|
77
|
+
leftExpr = where.left;
|
|
54
78
|
|
|
79
|
+
if (leftExpr.kind === 'column-ref') {
|
|
80
|
+
const { table, column } = leftExpr;
|
|
55
81
|
const contractTable = contract.storage.tables[table];
|
|
56
82
|
if (!contractTable) {
|
|
57
83
|
errorUnknownTable(table);
|
|
@@ -63,15 +89,11 @@ export function buildWhereExpr(
|
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
codecId = columnMeta.codecId;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// where.left is neither OperationExpr nor ColumnBuilder - invalid state
|
|
69
|
-
errorFailedToBuildWhereClause();
|
|
92
|
+
} else if (leftExpr.kind === 'operation') {
|
|
93
|
+
codecId = leftExpr.returns.kind === 'typeId' ? leftExpr.returns.type : leftExpr.forTypeId;
|
|
70
94
|
}
|
|
71
95
|
|
|
72
|
-
// Handle where.right - can be ParamPlaceholder or AnyColumnBuilder
|
|
73
96
|
if (isParamPlaceholder(where.right)) {
|
|
74
|
-
// Handle param placeholder (existing logic)
|
|
75
97
|
const placeholder: ParamPlaceholder = where.right;
|
|
76
98
|
paramName = placeholder.name;
|
|
77
99
|
|
|
@@ -80,50 +102,37 @@ export function buildWhereExpr(
|
|
|
80
102
|
}
|
|
81
103
|
|
|
82
104
|
const value = paramsMap[paramName];
|
|
83
|
-
const index = values.push(value);
|
|
84
105
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const { table, column } = getColumnInfo(where.left);
|
|
88
|
-
const contractTable = contract.storage.tables[table];
|
|
89
|
-
const columnMeta = contractTable?.columns[column];
|
|
90
|
-
if (columnMeta) {
|
|
91
|
-
descriptors.push({
|
|
92
|
-
name: paramName,
|
|
93
|
-
source: 'dsl',
|
|
94
|
-
refs: { table, column },
|
|
95
|
-
nullable: columnMeta.nullable,
|
|
96
|
-
codecId: columnMeta.codecId,
|
|
97
|
-
nativeType: columnMeta.nativeType,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
106
|
+
if (!codecId) {
|
|
107
|
+
throw planInvalid(`Cannot determine codecId for parameter '${paramName}'`);
|
|
100
108
|
}
|
|
109
|
+
rightExpr = ParamRefNode.of(value, {
|
|
110
|
+
name: paramName,
|
|
111
|
+
codecId,
|
|
112
|
+
});
|
|
113
|
+
} else if (isColumnBuilder(where.right) || isExpressionBuilder(where.right)) {
|
|
114
|
+
rightExpr = where.right.toExpr();
|
|
101
115
|
|
|
102
|
-
rightExpr
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!contractTable) {
|
|
109
|
-
errorUnknownTable(table);
|
|
110
|
-
}
|
|
116
|
+
if (rightExpr.kind === 'column-ref') {
|
|
117
|
+
const { table, column } = rightExpr;
|
|
118
|
+
const contractTable = contract.storage.tables[table];
|
|
119
|
+
if (!contractTable) {
|
|
120
|
+
errorUnknownTable(table);
|
|
121
|
+
}
|
|
111
122
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
124
|
+
if (!columnMeta) {
|
|
125
|
+
errorUnknownColumn(column, table);
|
|
126
|
+
}
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
rightExpr = createColumnRef(table, column);
|
|
118
|
-
// Use a placeholder paramName for column references (not used for params)
|
|
119
129
|
paramName = '';
|
|
120
130
|
} else {
|
|
121
|
-
// where.right is neither ParamPlaceholder nor ColumnBuilder - invalid state
|
|
122
131
|
errorFailedToBuildWhereClause();
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
return {
|
|
126
|
-
expr:
|
|
135
|
+
expr: new BinaryExpr(where.op, leftExpr, rightExpr),
|
|
127
136
|
codecId,
|
|
128
137
|
paramName,
|
|
129
138
|
};
|
package/src/sql/projection.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TableRef } from '@prisma-next/sql-relational-core/ast';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { ColumnRef } from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import type { AnyExpressionSource, NestedProjection } from '@prisma-next/sql-relational-core/types';
|
|
4
|
+
import { isExpressionSource } from '@prisma-next/sql-relational-core/utils/guards';
|
|
4
5
|
import type { ProjectionInput } from '../types/internal';
|
|
5
6
|
import {
|
|
6
7
|
errorAliasCollision,
|
|
@@ -47,14 +48,14 @@ export function flattenProjection(
|
|
|
47
48
|
projection: NestedProjection,
|
|
48
49
|
tracker: AliasTracker,
|
|
49
50
|
currentPath: string[] = [],
|
|
50
|
-
): { aliases: string[]; columns:
|
|
51
|
+
): { aliases: string[]; columns: AnyExpressionSource[] } {
|
|
51
52
|
const aliases: string[] = [];
|
|
52
|
-
const columns:
|
|
53
|
+
const columns: AnyExpressionSource[] = [];
|
|
53
54
|
|
|
54
55
|
for (const [key, value] of Object.entries(projection)) {
|
|
55
56
|
const path = [...currentPath, key];
|
|
56
57
|
|
|
57
|
-
if (
|
|
58
|
+
if (isExpressionSource(value)) {
|
|
58
59
|
const alias = tracker.register(path);
|
|
59
60
|
aliases.push(alias);
|
|
60
61
|
columns.push(value);
|
|
@@ -77,21 +78,25 @@ export function buildProjectionState(
|
|
|
77
78
|
): ProjectionState {
|
|
78
79
|
const tracker = new AliasTracker();
|
|
79
80
|
const aliases: string[] = [];
|
|
80
|
-
const columns:
|
|
81
|
+
const columns: AnyExpressionSource[] = [];
|
|
81
82
|
|
|
82
83
|
for (const [key, value] of Object.entries(projection)) {
|
|
83
84
|
if (value === true) {
|
|
84
|
-
// Boolean true
|
|
85
|
+
// Boolean true marks an include projection alias. The concrete expression
|
|
86
|
+
// is stitched during include AST construction.
|
|
85
87
|
const matchingInclude = includes?.find((inc) => inc.alias === key);
|
|
86
88
|
if (!matchingInclude) {
|
|
87
89
|
errorIncludeAliasNotFound(key);
|
|
88
90
|
}
|
|
89
|
-
// For include references, we track the alias but use null as placeholder
|
|
90
|
-
// The actual handling happens in AST building where we create includeRef
|
|
91
|
-
// Using null instead of invalid ColumnBuilder avoids code smell
|
|
92
91
|
aliases.push(key);
|
|
93
|
-
columns.push(
|
|
94
|
-
|
|
92
|
+
columns.push({
|
|
93
|
+
kind: 'column',
|
|
94
|
+
table: matchingInclude.table.name,
|
|
95
|
+
column: '',
|
|
96
|
+
columnMeta: { nativeType: 'jsonb', codecId: 'core/json@1', nullable: true },
|
|
97
|
+
toExpr: () => ColumnRef.of(matchingInclude.table.name, ''),
|
|
98
|
+
} as AnyExpressionSource);
|
|
99
|
+
} else if (isExpressionSource(value)) {
|
|
95
100
|
const alias = tracker.register([key]);
|
|
96
101
|
aliases.push(alias);
|
|
97
102
|
columns.push(value);
|