@prisma-next/sql-lane 0.3.0-dev.7 → 0.3.0-dev.71
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 +20 -3
- package/dist/builder-DLGrrQ5F.mjs +1254 -0
- package/dist/builder-DLGrrQ5F.mjs.map +1 -0
- package/dist/builder-DizPddCD.d.mts +121 -0
- package/dist/builder-DizPddCD.d.mts.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 +28 -24
- package/src/raw.ts +9 -2
- package/src/sql/context.ts +3 -3
- package/src/sql/include-builder.ts +3 -3
- package/src/sql/mutation-builder.ts +61 -10
- package/src/sql/plan.ts +117 -102
- package/src/sql/predicate-builder.ts +94 -40
- package/src/sql/projection.ts +15 -10
- package/src/sql/select-builder.ts +26 -41
- package/src/types/internal.ts +5 -4
- package/src/utils/errors.ts +1 -1
- 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
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
createUpdateAst,
|
|
11
11
|
} from '@prisma-next/sql-relational-core/ast';
|
|
12
12
|
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
13
|
-
import type {
|
|
13
|
+
import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
|
|
14
14
|
import type {
|
|
15
15
|
AnyColumnBuilder,
|
|
16
16
|
BinaryBuilder,
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
InferReturningRow,
|
|
19
19
|
ParamPlaceholder,
|
|
20
20
|
SqlBuilderOptions,
|
|
21
|
+
UnaryBuilder,
|
|
21
22
|
} from '@prisma-next/sql-relational-core/types';
|
|
22
23
|
import { checkReturningCapability } from '../utils/capabilities';
|
|
23
24
|
import {
|
|
@@ -48,7 +49,7 @@ export interface UpdateBuilder<
|
|
|
48
49
|
CodecTypes extends Record<string, { readonly output: unknown }> = Record<string, never>,
|
|
49
50
|
Row = unknown,
|
|
50
51
|
> {
|
|
51
|
-
where(predicate: BinaryBuilder): UpdateBuilder<TContract, CodecTypes, Row>;
|
|
52
|
+
where(predicate: BinaryBuilder | UnaryBuilder): UpdateBuilder<TContract, CodecTypes, Row>;
|
|
52
53
|
returning<const Columns extends readonly AnyColumnBuilder[]>(
|
|
53
54
|
...columns: Columns
|
|
54
55
|
): UpdateBuilder<TContract, CodecTypes, InferReturningRow<Columns>>;
|
|
@@ -60,7 +61,7 @@ export interface DeleteBuilder<
|
|
|
60
61
|
CodecTypes extends Record<string, { readonly output: unknown }> = Record<string, never>,
|
|
61
62
|
Row = unknown,
|
|
62
63
|
> {
|
|
63
|
-
where(predicate: BinaryBuilder): DeleteBuilder<TContract, CodecTypes, Row>;
|
|
64
|
+
where(predicate: BinaryBuilder | UnaryBuilder): DeleteBuilder<TContract, CodecTypes, Row>;
|
|
64
65
|
returning<const Columns extends readonly AnyColumnBuilder[]>(
|
|
65
66
|
...columns: Columns
|
|
66
67
|
): DeleteBuilder<TContract, CodecTypes, InferReturningRow<Columns>>;
|
|
@@ -74,7 +75,7 @@ export class InsertBuilderImpl<
|
|
|
74
75
|
> implements InsertBuilder<TContract, CodecTypes, Row>
|
|
75
76
|
{
|
|
76
77
|
private readonly contract: TContract;
|
|
77
|
-
private readonly context:
|
|
78
|
+
private readonly context: ExecutionContext<TContract>;
|
|
78
79
|
private readonly table: TableRef;
|
|
79
80
|
private readonly values: Record<string, ParamPlaceholder>;
|
|
80
81
|
private returningColumns: AnyColumnBuilder[] = [];
|
|
@@ -149,6 +150,31 @@ export class InsertBuilderImpl<
|
|
|
149
150
|
values[columnName] = createParamRef(index, paramName);
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
const appliedDefaults = this.context.applyMutationDefaults({
|
|
154
|
+
op: 'create',
|
|
155
|
+
table: this.table.name,
|
|
156
|
+
values,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
for (const defaultValue of appliedDefaults) {
|
|
160
|
+
const columnMeta = contractTable.columns[defaultValue.column];
|
|
161
|
+
if (!columnMeta) {
|
|
162
|
+
errorUnknownColumn(defaultValue.column, this.table.name);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const index = paramValues.push(defaultValue.value);
|
|
166
|
+
paramCodecs[defaultValue.column] = columnMeta.codecId;
|
|
167
|
+
paramDescriptors.push({
|
|
168
|
+
name: defaultValue.column,
|
|
169
|
+
source: 'dsl',
|
|
170
|
+
refs: { table: this.table.name, column: defaultValue.column },
|
|
171
|
+
codecId: columnMeta.codecId,
|
|
172
|
+
nativeType: columnMeta.nativeType,
|
|
173
|
+
nullable: columnMeta.nullable,
|
|
174
|
+
});
|
|
175
|
+
values[defaultValue.column] = createParamRef(index, defaultValue.column);
|
|
176
|
+
}
|
|
177
|
+
|
|
152
178
|
const returning: ColumnRef[] = this.returningColumns.map((col) => {
|
|
153
179
|
// TypeScript can't narrow ColumnBuilder properly
|
|
154
180
|
const c = col as unknown as { table: string; column: string };
|
|
@@ -203,10 +229,10 @@ export class UpdateBuilderImpl<
|
|
|
203
229
|
> implements UpdateBuilder<TContract, CodecTypes, Row>
|
|
204
230
|
{
|
|
205
231
|
private readonly contract: TContract;
|
|
206
|
-
private readonly context:
|
|
232
|
+
private readonly context: ExecutionContext<TContract>;
|
|
207
233
|
private readonly table: TableRef;
|
|
208
234
|
private readonly set: Record<string, ParamPlaceholder>;
|
|
209
|
-
private wherePredicate?: BinaryBuilder;
|
|
235
|
+
private wherePredicate?: BinaryBuilder | UnaryBuilder;
|
|
210
236
|
private returningColumns: AnyColumnBuilder[] = [];
|
|
211
237
|
|
|
212
238
|
constructor(
|
|
@@ -220,7 +246,7 @@ export class UpdateBuilderImpl<
|
|
|
220
246
|
this.set = set;
|
|
221
247
|
}
|
|
222
248
|
|
|
223
|
-
where(predicate: BinaryBuilder): UpdateBuilder<TContract, CodecTypes, Row> {
|
|
249
|
+
where(predicate: BinaryBuilder | UnaryBuilder): UpdateBuilder<TContract, CodecTypes, Row> {
|
|
224
250
|
const builder = new UpdateBuilderImpl<TContract, CodecTypes, Row>(
|
|
225
251
|
{
|
|
226
252
|
context: this.context,
|
|
@@ -299,6 +325,31 @@ export class UpdateBuilderImpl<
|
|
|
299
325
|
set[columnName] = createParamRef(index, paramName);
|
|
300
326
|
}
|
|
301
327
|
|
|
328
|
+
const appliedDefaults = this.context.applyMutationDefaults({
|
|
329
|
+
op: 'update',
|
|
330
|
+
table: this.table.name,
|
|
331
|
+
values: set,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
for (const defaultValue of appliedDefaults) {
|
|
335
|
+
const columnMeta = contractTable.columns[defaultValue.column];
|
|
336
|
+
if (!columnMeta) {
|
|
337
|
+
errorUnknownColumn(defaultValue.column, this.table.name);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const index = paramValues.push(defaultValue.value);
|
|
341
|
+
paramCodecs[defaultValue.column] = columnMeta.codecId;
|
|
342
|
+
paramDescriptors.push({
|
|
343
|
+
name: defaultValue.column,
|
|
344
|
+
source: 'dsl',
|
|
345
|
+
refs: { table: this.table.name, column: defaultValue.column },
|
|
346
|
+
codecId: columnMeta.codecId,
|
|
347
|
+
nativeType: columnMeta.nativeType,
|
|
348
|
+
nullable: columnMeta.nullable,
|
|
349
|
+
});
|
|
350
|
+
set[defaultValue.column] = createParamRef(index, defaultValue.column);
|
|
351
|
+
}
|
|
352
|
+
|
|
302
353
|
const whereResult = buildWhereExpr(
|
|
303
354
|
this.contract,
|
|
304
355
|
this.wherePredicate,
|
|
@@ -372,9 +423,9 @@ export class DeleteBuilderImpl<
|
|
|
372
423
|
> implements DeleteBuilder<TContract, CodecTypes, Row>
|
|
373
424
|
{
|
|
374
425
|
private readonly contract: TContract;
|
|
375
|
-
private readonly context:
|
|
426
|
+
private readonly context: ExecutionContext<TContract>;
|
|
376
427
|
private readonly table: TableRef;
|
|
377
|
-
private wherePredicate?: BinaryBuilder;
|
|
428
|
+
private wherePredicate?: BinaryBuilder | UnaryBuilder;
|
|
378
429
|
private returningColumns: AnyColumnBuilder[] = [];
|
|
379
430
|
|
|
380
431
|
constructor(options: SqlBuilderOptions<TContract>, table: TableRef) {
|
|
@@ -383,7 +434,7 @@ export class DeleteBuilderImpl<
|
|
|
383
434
|
this.table = table;
|
|
384
435
|
}
|
|
385
436
|
|
|
386
|
-
where(predicate: BinaryBuilder): DeleteBuilder<TContract, CodecTypes, Row> {
|
|
437
|
+
where(predicate: BinaryBuilder | UnaryBuilder): DeleteBuilder<TContract, CodecTypes, Row> {
|
|
387
438
|
const builder = new DeleteBuilderImpl<TContract, CodecTypes, Row>(
|
|
388
439
|
{
|
|
389
440
|
context: this.context,
|
package/src/sql/plan.ts
CHANGED
|
@@ -1,42 +1,72 @@
|
|
|
1
1
|
import type { PlanMeta } from '@prisma-next/contract/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Expression } from '@prisma-next/sql-relational-core/ast';
|
|
3
3
|
import { compact } from '@prisma-next/sql-relational-core/ast';
|
|
4
|
-
import type {
|
|
4
|
+
import type { AnyExpressionSource } from '@prisma-next/sql-relational-core/types';
|
|
5
5
|
import {
|
|
6
6
|
collectColumnRefs,
|
|
7
|
-
getColumnInfo,
|
|
8
|
-
getOperationExpr,
|
|
9
7
|
isColumnBuilder,
|
|
8
|
+
isExpressionBuilder,
|
|
10
9
|
isOperationExpr,
|
|
11
10
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
12
11
|
import type { MetaBuildArgs } from '../types/internal';
|
|
13
12
|
import { assertColumnBuilder } from '../utils/assertions';
|
|
14
13
|
import { errorMissingColumnForAlias } from '../utils/errors';
|
|
15
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Extracts column references from an ExpressionSource (ColumnBuilder or ExpressionBuilder).
|
|
17
|
+
*/
|
|
18
|
+
function collectRefsFromExpressionSource(
|
|
19
|
+
source: AnyExpressionSource,
|
|
20
|
+
refsColumns: Map<string, { table: string; column: string }>,
|
|
21
|
+
): void {
|
|
22
|
+
if (isExpressionBuilder(source)) {
|
|
23
|
+
// ExpressionBuilder has an OperationExpr - collect all column refs
|
|
24
|
+
const allRefs = collectColumnRefs(source.expr);
|
|
25
|
+
for (const ref of allRefs) {
|
|
26
|
+
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
27
|
+
table: ref.table,
|
|
28
|
+
column: ref.column,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
} else if (isColumnBuilder(source)) {
|
|
32
|
+
// ColumnBuilder - use table and column directly
|
|
33
|
+
const col = source as unknown as { table: string; column: string };
|
|
34
|
+
refsColumns.set(`${col.table}.${col.column}`, {
|
|
35
|
+
table: col.table,
|
|
36
|
+
column: col.column,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extracts column references from an Expression (AST node).
|
|
43
|
+
*/
|
|
44
|
+
function collectRefsFromExpression(
|
|
45
|
+
expr: Expression,
|
|
46
|
+
refsColumns: Map<string, { table: string; column: string }>,
|
|
47
|
+
): void {
|
|
48
|
+
if (isOperationExpr(expr)) {
|
|
49
|
+
const allRefs = collectColumnRefs(expr);
|
|
50
|
+
for (const ref of allRefs) {
|
|
51
|
+
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
52
|
+
table: ref.table,
|
|
53
|
+
column: ref.column,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
} else if (expr.kind === 'col') {
|
|
57
|
+
refsColumns.set(`${expr.table}.${expr.column}`, {
|
|
58
|
+
table: expr.table,
|
|
59
|
+
column: expr.column,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
16
64
|
export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
17
65
|
const refsColumns = new Map<string, { table: string; column: string }>();
|
|
18
66
|
const refsTables = new Set<string>([args.table.name]);
|
|
19
67
|
|
|
20
68
|
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
|
-
}
|
|
69
|
+
collectRefsFromExpressionSource(column, refsColumns);
|
|
40
70
|
}
|
|
41
71
|
|
|
42
72
|
if (args.joins) {
|
|
@@ -81,53 +111,40 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
81
111
|
}
|
|
82
112
|
// Add child WHERE columns if present
|
|
83
113
|
if (include.childWhere) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
114
|
+
// Handle UnaryBuilder (e.g., NullCheckBuilder) - it only has 'expr' property
|
|
115
|
+
if (include.childWhere.kind === 'nullCheck') {
|
|
116
|
+
const expr: Expression = include.childWhere.expr;
|
|
117
|
+
collectRefsFromExpression(expr, refsColumns);
|
|
118
|
+
} else {
|
|
119
|
+
// BinaryBuilder - has 'left' and 'right' properties
|
|
120
|
+
// childWhere.left is Expression (already converted at builder creation time)
|
|
121
|
+
collectRefsFromExpression(include.childWhere.left, refsColumns);
|
|
122
|
+
// Handle right side of child WHERE clause - can be ParamPlaceholder or ExpressionSource
|
|
123
|
+
const childWhereRight = include.childWhere.right;
|
|
124
|
+
if (isColumnBuilder(childWhereRight) || isExpressionBuilder(childWhereRight)) {
|
|
125
|
+
collectRefsFromExpressionSource(childWhereRight, refsColumns);
|
|
126
|
+
}
|
|
97
127
|
}
|
|
98
128
|
}
|
|
99
129
|
// Add child ORDER BY columns if present
|
|
100
130
|
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
|
-
}
|
|
131
|
+
// childOrderBy.expr is Expression (already converted at builder creation time)
|
|
132
|
+
collectRefsFromExpression(include.childOrderBy.expr, refsColumns);
|
|
111
133
|
}
|
|
112
134
|
}
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
if (args.where) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for (const ref of allRefs) {
|
|
121
|
-
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
122
|
-
table: ref.table,
|
|
123
|
-
column: ref.column,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
138
|
+
// Handle UnaryBuilder (e.g., NullCheckBuilder) - it only has 'expr' property
|
|
139
|
+
if (args.where.kind === 'nullCheck') {
|
|
140
|
+
const expr: Expression = args.where.expr;
|
|
141
|
+
collectRefsFromExpression(expr, refsColumns);
|
|
126
142
|
} else {
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
// BinaryBuilder - has 'left' and 'right' properties
|
|
144
|
+
// args.where.left is Expression (already converted at builder creation time)
|
|
145
|
+
const leftExpr: Expression = args.where.left;
|
|
146
|
+
if (isOperationExpr(leftExpr)) {
|
|
147
|
+
const allRefs = collectColumnRefs(leftExpr);
|
|
131
148
|
for (const ref of allRefs) {
|
|
132
149
|
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
133
150
|
table: ref.table,
|
|
@@ -135,45 +152,38 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
135
152
|
});
|
|
136
153
|
}
|
|
137
154
|
} else {
|
|
138
|
-
|
|
139
|
-
refsColumns.set(`${
|
|
140
|
-
table:
|
|
141
|
-
column:
|
|
155
|
+
// leftExpr is ColumnRef
|
|
156
|
+
refsColumns.set(`${leftExpr.table}.${leftExpr.column}`, {
|
|
157
|
+
table: leftExpr.table,
|
|
158
|
+
column: leftExpr.column,
|
|
142
159
|
});
|
|
143
160
|
}
|
|
144
|
-
}
|
|
145
161
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
table: colInfo.table,
|
|
152
|
-
column: colInfo.column,
|
|
153
|
-
});
|
|
162
|
+
// Handle right side of WHERE clause - can be ParamPlaceholder or AnyExpressionSource
|
|
163
|
+
const whereRight = args.where.right;
|
|
164
|
+
if (isColumnBuilder(whereRight) || isExpressionBuilder(whereRight)) {
|
|
165
|
+
collectRefsFromExpressionSource(whereRight, refsColumns);
|
|
166
|
+
}
|
|
154
167
|
}
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
if (args.orderBy) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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,
|
|
171
|
+
// args.orderBy.expr is Expression (already converted at builder creation time)
|
|
172
|
+
const orderByExpr: Expression = args.orderBy.expr;
|
|
173
|
+
if (isOperationExpr(orderByExpr)) {
|
|
174
|
+
const allRefs = collectColumnRefs(orderByExpr);
|
|
175
|
+
for (const ref of allRefs) {
|
|
176
|
+
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
177
|
+
table: ref.table,
|
|
178
|
+
column: ref.column,
|
|
175
179
|
});
|
|
176
180
|
}
|
|
181
|
+
} else {
|
|
182
|
+
// orderByExpr is ColumnRef
|
|
183
|
+
refsColumns.set(`${orderByExpr.table}.${orderByExpr.column}`, {
|
|
184
|
+
table: orderByExpr.table,
|
|
185
|
+
column: orderByExpr.column,
|
|
186
|
+
});
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
189
|
|
|
@@ -191,13 +201,18 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
191
201
|
// This shouldn't happen if projection building is correct, but handle gracefully
|
|
192
202
|
errorMissingColumnForAlias(alias, index);
|
|
193
203
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
204
|
+
// Check if column is an ExpressionBuilder (operation result)
|
|
205
|
+
if (isExpressionBuilder(column)) {
|
|
206
|
+
return [alias, `operation:${column.expr.method}`];
|
|
207
|
+
}
|
|
208
|
+
// column is ColumnBuilder
|
|
209
|
+
const col = column as unknown as { table?: string; column?: string };
|
|
210
|
+
if (!col.table || !col.column) {
|
|
211
|
+
// This is a placeholder column for an include - skip it
|
|
212
|
+
return [alias, `include:${alias}`];
|
|
198
213
|
}
|
|
199
214
|
|
|
200
|
-
return [alias, `${
|
|
215
|
+
return [alias, `${col.table}.${col.column}`];
|
|
201
216
|
}),
|
|
202
217
|
);
|
|
203
218
|
|
|
@@ -213,15 +228,15 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
213
228
|
if (!column) {
|
|
214
229
|
continue;
|
|
215
230
|
}
|
|
216
|
-
|
|
217
|
-
|
|
231
|
+
if (isExpressionBuilder(column)) {
|
|
232
|
+
const operationExpr = column.expr;
|
|
218
233
|
if (operationExpr.returns.kind === 'typeId') {
|
|
219
234
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
220
235
|
} else if (operationExpr.returns.kind === 'builtin') {
|
|
221
236
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
222
237
|
}
|
|
223
238
|
} else {
|
|
224
|
-
//
|
|
239
|
+
// column is ColumnBuilder
|
|
225
240
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
226
241
|
const columnMeta = col.columnMeta;
|
|
227
242
|
const codecId = columnMeta?.codecId;
|
|
@@ -243,14 +258,14 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
243
258
|
if (!column) {
|
|
244
259
|
continue;
|
|
245
260
|
}
|
|
246
|
-
|
|
247
|
-
|
|
261
|
+
if (isExpressionBuilder(column)) {
|
|
262
|
+
const operationExpr = column.expr;
|
|
248
263
|
if (operationExpr.returns.kind === 'typeId') {
|
|
249
264
|
projectionCodecs[alias] = operationExpr.returns.type;
|
|
250
265
|
}
|
|
251
266
|
} else {
|
|
252
267
|
// Use columnMeta.codecId directly as typeId (already canonicalized)
|
|
253
|
-
//
|
|
268
|
+
// column is ColumnBuilder
|
|
254
269
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
255
270
|
const columnMeta = col.columnMeta;
|
|
256
271
|
const codecId = columnMeta?.codecId;
|
|
@@ -270,7 +285,7 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
270
285
|
compact({
|
|
271
286
|
target: args.contract.target,
|
|
272
287
|
targetFamily: args.contract.targetFamily,
|
|
273
|
-
|
|
288
|
+
storageHash: args.contract.storageHash,
|
|
274
289
|
lane: 'dsl',
|
|
275
290
|
refs: {
|
|
276
291
|
tables: Array.from(refsTables),
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import type { ParamDescriptor } from '@prisma-next/contract/types';
|
|
2
2
|
import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
|
|
3
3
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
OperationExpr,
|
|
4
|
+
Expression,
|
|
5
|
+
NullCheckExpr,
|
|
7
6
|
ParamRef,
|
|
7
|
+
WhereExpr,
|
|
8
8
|
} from '@prisma-next/sql-relational-core/ast';
|
|
9
9
|
import {
|
|
10
10
|
createBinaryExpr,
|
|
11
|
-
|
|
11
|
+
createNullCheckExpr,
|
|
12
12
|
createParamRef,
|
|
13
13
|
} from '@prisma-next/sql-relational-core/ast';
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
BinaryBuilder,
|
|
16
|
+
NullCheckBuilder,
|
|
17
|
+
ParamPlaceholder,
|
|
18
|
+
UnaryBuilder,
|
|
19
|
+
} from '@prisma-next/sql-relational-core/types';
|
|
15
20
|
import {
|
|
16
|
-
getColumnInfo,
|
|
17
|
-
getOperationExpr,
|
|
18
21
|
isColumnBuilder,
|
|
22
|
+
isExpressionBuilder,
|
|
19
23
|
isParamPlaceholder,
|
|
20
24
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
21
25
|
import {
|
|
@@ -26,32 +30,83 @@ import {
|
|
|
26
30
|
} from '../utils/errors';
|
|
27
31
|
|
|
28
32
|
export interface BuildWhereExprResult {
|
|
29
|
-
expr:
|
|
33
|
+
expr: WhereExpr;
|
|
30
34
|
codecId: string | undefined;
|
|
31
35
|
paramName: string;
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Type guard to check if a builder is a NullCheckBuilder (unary).
|
|
40
|
+
*/
|
|
41
|
+
function isNullCheckBuilder(builder: BinaryBuilder | UnaryBuilder): builder is NullCheckBuilder {
|
|
42
|
+
return builder.kind === 'nullCheck';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Builds a NullCheckExpr from a NullCheckBuilder.
|
|
47
|
+
*/
|
|
48
|
+
function buildNullCheckExpr(
|
|
49
|
+
contract: SqlContract<SqlStorage>,
|
|
50
|
+
where: NullCheckBuilder,
|
|
51
|
+
): NullCheckExpr {
|
|
52
|
+
const expr = where.expr;
|
|
53
|
+
|
|
54
|
+
// Validate column exists in contract if it's a ColumnRef
|
|
55
|
+
if (expr.kind === 'col') {
|
|
56
|
+
const { table, column } = expr;
|
|
57
|
+
const contractTable = contract.storage.tables[table];
|
|
58
|
+
if (!contractTable) {
|
|
59
|
+
errorUnknownTable(table);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
63
|
+
if (!columnMeta) {
|
|
64
|
+
errorUnknownColumn(column, table);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return createNullCheckExpr(expr, where.isNull);
|
|
69
|
+
}
|
|
70
|
+
|
|
34
71
|
export function buildWhereExpr(
|
|
35
72
|
contract: SqlContract<SqlStorage>,
|
|
36
|
-
where: BinaryBuilder,
|
|
73
|
+
where: BinaryBuilder | UnaryBuilder,
|
|
37
74
|
paramsMap: Record<string, unknown>,
|
|
38
75
|
descriptors: ParamDescriptor[],
|
|
39
76
|
values: unknown[],
|
|
40
77
|
): BuildWhereExprResult {
|
|
41
|
-
|
|
78
|
+
// Handle NullCheckBuilder (unary expression)
|
|
79
|
+
if (isNullCheckBuilder(where)) {
|
|
80
|
+
return {
|
|
81
|
+
expr: buildNullCheckExpr(contract, where),
|
|
82
|
+
codecId: undefined,
|
|
83
|
+
paramName: '',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle BinaryBuilder (binary expression)
|
|
88
|
+
let leftExpr: Expression;
|
|
42
89
|
let codecId: string | undefined;
|
|
43
|
-
let rightExpr:
|
|
90
|
+
let rightExpr: Expression | ParamRef;
|
|
44
91
|
let paramName: string;
|
|
45
92
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
93
|
+
// Validate where.left is a valid Expression (col or operation)
|
|
94
|
+
const validExpressionKinds = ['col', 'operation'];
|
|
95
|
+
if (
|
|
96
|
+
!where.left ||
|
|
97
|
+
typeof where.left !== 'object' ||
|
|
98
|
+
!validExpressionKinds.includes((where.left as { kind?: string }).kind ?? '')
|
|
99
|
+
) {
|
|
100
|
+
errorFailedToBuildWhereClause();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// where.left is an Expression (already converted at builder creation time)
|
|
104
|
+
// It could be a ColumnRef or OperationExpr
|
|
105
|
+
leftExpr = where.left;
|
|
54
106
|
|
|
107
|
+
// If the left expression is a column reference, extract codecId for param descriptors
|
|
108
|
+
if (leftExpr.kind === 'col') {
|
|
109
|
+
const { table, column } = leftExpr;
|
|
55
110
|
const contractTable = contract.storage.tables[table];
|
|
56
111
|
if (!contractTable) {
|
|
57
112
|
errorUnknownTable(table);
|
|
@@ -63,13 +118,9 @@ export function buildWhereExpr(
|
|
|
63
118
|
}
|
|
64
119
|
|
|
65
120
|
codecId = columnMeta.codecId;
|
|
66
|
-
leftExpr = createColumnRef(table, column);
|
|
67
|
-
} else {
|
|
68
|
-
// where.left is neither OperationExpr nor ColumnBuilder - invalid state
|
|
69
|
-
errorFailedToBuildWhereClause();
|
|
70
121
|
}
|
|
71
122
|
|
|
72
|
-
// Handle where.right - can be ParamPlaceholder or
|
|
123
|
+
// Handle where.right - can be ParamPlaceholder or ExpressionSource
|
|
73
124
|
if (isParamPlaceholder(where.right)) {
|
|
74
125
|
// Handle param placeholder (existing logic)
|
|
75
126
|
const placeholder: ParamPlaceholder = where.right;
|
|
@@ -82,9 +133,9 @@ export function buildWhereExpr(
|
|
|
82
133
|
const value = paramsMap[paramName];
|
|
83
134
|
const index = values.push(value);
|
|
84
135
|
|
|
85
|
-
// Construct descriptor directly from validated StorageColumn
|
|
86
|
-
if (
|
|
87
|
-
const { table, column } =
|
|
136
|
+
// Construct descriptor directly from validated StorageColumn if left is a column
|
|
137
|
+
if (leftExpr.kind === 'col') {
|
|
138
|
+
const { table, column } = leftExpr;
|
|
88
139
|
const contractTable = contract.storage.tables[table];
|
|
89
140
|
const columnMeta = contractTable?.columns[column];
|
|
90
141
|
if (columnMeta) {
|
|
@@ -100,25 +151,28 @@ export function buildWhereExpr(
|
|
|
100
151
|
}
|
|
101
152
|
|
|
102
153
|
rightExpr = createParamRef(index, paramName);
|
|
103
|
-
} else if (isColumnBuilder(where.right)) {
|
|
104
|
-
// Handle
|
|
105
|
-
|
|
154
|
+
} else if (isColumnBuilder(where.right) || isExpressionBuilder(where.right)) {
|
|
155
|
+
// Handle ExpressionSource (ColumnBuilder or ExpressionBuilder) on the right
|
|
156
|
+
rightExpr = where.right.toExpr();
|
|
106
157
|
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
158
|
+
// Validate column exists in contract if it's a ColumnRef
|
|
159
|
+
if (rightExpr.kind === 'col') {
|
|
160
|
+
const { table, column } = rightExpr;
|
|
161
|
+
const contractTable = contract.storage.tables[table];
|
|
162
|
+
if (!contractTable) {
|
|
163
|
+
errorUnknownTable(table);
|
|
164
|
+
}
|
|
111
165
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
166
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
167
|
+
if (!columnMeta) {
|
|
168
|
+
errorUnknownColumn(column, table);
|
|
169
|
+
}
|
|
115
170
|
}
|
|
116
171
|
|
|
117
|
-
|
|
118
|
-
// Use a placeholder paramName for column references (not used for params)
|
|
172
|
+
// Use a placeholder paramName for expression references (not used for params)
|
|
119
173
|
paramName = '';
|
|
120
174
|
} else {
|
|
121
|
-
// where.right is neither ParamPlaceholder nor
|
|
175
|
+
// where.right is neither ParamPlaceholder nor ExpressionSource - invalid state
|
|
122
176
|
errorFailedToBuildWhereClause();
|
|
123
177
|
}
|
|
124
178
|
|