@prisma-next/sql-lane 0.3.0-pr.93.4 → 0.3.0-pr.94.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder-C87NiJaP.mjs +1175 -0
- package/dist/builder-C87NiJaP.mjs.map +1 -0
- package/dist/builder-C8ExdG4j.d.mts +121 -0
- package/dist/builder-C8ExdG4j.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 +20 -18
- package/src/sql/include-builder.ts +1 -1
- package/src/sql/plan.ts +102 -101
- package/src/sql/predicate-builder.ts +41 -44
- package/src/sql/projection.ts +15 -10
- package/src/sql/select-builder.ts +16 -35
- package/src/types/internal.ts +2 -2
- package/src/utils/errors.ts +1 -1
- package/src/utils/state.ts +2 -4
- 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,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,42 +111,27 @@ 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
|
-
column: colInfo.column,
|
|
88
|
-
});
|
|
89
|
-
// Handle right side of child WHERE clause
|
|
114
|
+
// childWhere.left is Expression (already converted at builder creation time)
|
|
115
|
+
collectRefsFromExpression(include.childWhere.left, refsColumns);
|
|
116
|
+
// Handle right side of child WHERE clause - can be ParamPlaceholder or ExpressionSource
|
|
90
117
|
const childWhereRight = include.childWhere.right;
|
|
91
|
-
if (isColumnBuilder(childWhereRight)) {
|
|
92
|
-
|
|
93
|
-
refsColumns.set(`${rightColInfo.table}.${rightColInfo.column}`, {
|
|
94
|
-
table: rightColInfo.table,
|
|
95
|
-
column: rightColInfo.column,
|
|
96
|
-
});
|
|
118
|
+
if (isColumnBuilder(childWhereRight) || isExpressionBuilder(childWhereRight)) {
|
|
119
|
+
collectRefsFromExpressionSource(childWhereRight, refsColumns);
|
|
97
120
|
}
|
|
98
121
|
}
|
|
99
122
|
// Add child ORDER BY columns if present
|
|
100
123
|
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
|
-
}
|
|
124
|
+
// childOrderBy.expr is Expression (already converted at builder creation time)
|
|
125
|
+
collectRefsFromExpression(include.childOrderBy.expr, refsColumns);
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
|
|
115
130
|
if (args.where) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (isOperationExpr(
|
|
119
|
-
const allRefs = collectColumnRefs(
|
|
131
|
+
// args.where.left is Expression (already converted at builder creation time)
|
|
132
|
+
const leftExpr: Expression = args.where.left;
|
|
133
|
+
if (isOperationExpr(leftExpr)) {
|
|
134
|
+
const allRefs = collectColumnRefs(leftExpr);
|
|
120
135
|
for (const ref of allRefs) {
|
|
121
136
|
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
122
137
|
table: ref.table,
|
|
@@ -124,56 +139,37 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
124
139
|
});
|
|
125
140
|
}
|
|
126
141
|
} else {
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
});
|
|
143
|
-
}
|
|
142
|
+
// leftExpr is ColumnRef
|
|
143
|
+
refsColumns.set(`${leftExpr.table}.${leftExpr.column}`, {
|
|
144
|
+
table: leftExpr.table,
|
|
145
|
+
column: leftExpr.column,
|
|
146
|
+
});
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
// Handle right side of WHERE clause - can be ParamPlaceholder or
|
|
149
|
+
// Handle right side of WHERE clause - can be ParamPlaceholder or AnyExpressionSource
|
|
147
150
|
const whereRight = args.where.right;
|
|
148
|
-
if (isColumnBuilder(whereRight)) {
|
|
149
|
-
|
|
150
|
-
refsColumns.set(`${colInfo.table}.${colInfo.column}`, {
|
|
151
|
-
table: colInfo.table,
|
|
152
|
-
column: colInfo.column,
|
|
153
|
-
});
|
|
151
|
+
if (isColumnBuilder(whereRight) || isExpressionBuilder(whereRight)) {
|
|
152
|
+
collectRefsFromExpressionSource(whereRight, refsColumns);
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
155
|
|
|
157
156
|
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,
|
|
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,
|
|
175
165
|
});
|
|
176
166
|
}
|
|
167
|
+
} else {
|
|
168
|
+
// orderByExpr is ColumnRef
|
|
169
|
+
refsColumns.set(`${orderByExpr.table}.${orderByExpr.column}`, {
|
|
170
|
+
table: orderByExpr.table,
|
|
171
|
+
column: orderByExpr.column,
|
|
172
|
+
});
|
|
177
173
|
}
|
|
178
174
|
}
|
|
179
175
|
|
|
@@ -191,13 +187,18 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
191
187
|
// This shouldn't happen if projection building is correct, but handle gracefully
|
|
192
188
|
errorMissingColumnForAlias(alias, index);
|
|
193
189
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
// Check if column is an ExpressionBuilder (operation result)
|
|
191
|
+
if (isExpressionBuilder(column)) {
|
|
192
|
+
return [alias, `operation:${column.expr.method}`];
|
|
193
|
+
}
|
|
194
|
+
// column is ColumnBuilder
|
|
195
|
+
const col = column as unknown as { table?: string; column?: string };
|
|
196
|
+
if (!col.table || !col.column) {
|
|
197
|
+
// This is a placeholder column for an include - skip it
|
|
198
|
+
return [alias, `include:${alias}`];
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
return [alias, `${
|
|
201
|
+
return [alias, `${col.table}.${col.column}`];
|
|
201
202
|
}),
|
|
202
203
|
);
|
|
203
204
|
|
|
@@ -213,15 +214,15 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
213
214
|
if (!column) {
|
|
214
215
|
continue;
|
|
215
216
|
}
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
if (isExpressionBuilder(column)) {
|
|
218
|
+
const operationExpr = column.expr;
|
|
218
219
|
if (operationExpr.returns.kind === 'typeId') {
|
|
219
220
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
220
221
|
} else if (operationExpr.returns.kind === 'builtin') {
|
|
221
222
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
222
223
|
}
|
|
223
224
|
} else {
|
|
224
|
-
//
|
|
225
|
+
// column is ColumnBuilder
|
|
225
226
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
226
227
|
const columnMeta = col.columnMeta;
|
|
227
228
|
const codecId = columnMeta?.codecId;
|
|
@@ -243,14 +244,14 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
243
244
|
if (!column) {
|
|
244
245
|
continue;
|
|
245
246
|
}
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
if (isExpressionBuilder(column)) {
|
|
248
|
+
const operationExpr = column.expr;
|
|
248
249
|
if (operationExpr.returns.kind === 'typeId') {
|
|
249
250
|
projectionCodecs[alias] = operationExpr.returns.type;
|
|
250
251
|
}
|
|
251
252
|
} else {
|
|
252
253
|
// Use columnMeta.codecId directly as typeId (already canonicalized)
|
|
253
|
-
//
|
|
254
|
+
// column is ColumnBuilder
|
|
254
255
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
255
256
|
const columnMeta = col.columnMeta;
|
|
256
257
|
const codecId = columnMeta?.codecId;
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import type { ParamDescriptor } from '@prisma-next/contract/types';
|
|
2
2
|
import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
ColumnRef,
|
|
6
|
-
OperationExpr,
|
|
7
|
-
ParamRef,
|
|
8
|
-
} from '@prisma-next/sql-relational-core/ast';
|
|
9
|
-
import {
|
|
10
|
-
createBinaryExpr,
|
|
11
|
-
createColumnRef,
|
|
12
|
-
createParamRef,
|
|
13
|
-
} from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import type { BinaryExpr, Expression, ParamRef } from '@prisma-next/sql-relational-core/ast';
|
|
4
|
+
import { createBinaryExpr, createParamRef } from '@prisma-next/sql-relational-core/ast';
|
|
14
5
|
import type { BinaryBuilder, ParamPlaceholder } from '@prisma-next/sql-relational-core/types';
|
|
15
6
|
import {
|
|
16
|
-
getColumnInfo,
|
|
17
|
-
getOperationExpr,
|
|
18
7
|
isColumnBuilder,
|
|
8
|
+
isExpressionBuilder,
|
|
19
9
|
isParamPlaceholder,
|
|
20
10
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
21
11
|
import {
|
|
@@ -38,20 +28,28 @@ export function buildWhereExpr(
|
|
|
38
28
|
descriptors: ParamDescriptor[],
|
|
39
29
|
values: unknown[],
|
|
40
30
|
): BuildWhereExprResult {
|
|
41
|
-
let leftExpr:
|
|
31
|
+
let leftExpr: Expression;
|
|
42
32
|
let codecId: string | undefined;
|
|
43
|
-
let rightExpr:
|
|
33
|
+
let rightExpr: Expression | ParamRef;
|
|
44
34
|
let paramName: string;
|
|
45
35
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
// Validate where.left is a valid Expression (col or operation)
|
|
37
|
+
const validExpressionKinds = ['col', 'operation'];
|
|
38
|
+
if (
|
|
39
|
+
!where.left ||
|
|
40
|
+
typeof where.left !== 'object' ||
|
|
41
|
+
!validExpressionKinds.includes((where.left as { kind?: string }).kind ?? '')
|
|
42
|
+
) {
|
|
43
|
+
errorFailedToBuildWhereClause();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// where.left is an Expression (already converted at builder creation time)
|
|
47
|
+
// It could be a ColumnRef or OperationExpr
|
|
48
|
+
leftExpr = where.left;
|
|
54
49
|
|
|
50
|
+
// If the left expression is a column reference, extract codecId for param descriptors
|
|
51
|
+
if (leftExpr.kind === 'col') {
|
|
52
|
+
const { table, column } = leftExpr;
|
|
55
53
|
const contractTable = contract.storage.tables[table];
|
|
56
54
|
if (!contractTable) {
|
|
57
55
|
errorUnknownTable(table);
|
|
@@ -63,13 +61,9 @@ export function buildWhereExpr(
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
codecId = columnMeta.codecId;
|
|
66
|
-
leftExpr = createColumnRef(table, column);
|
|
67
|
-
} else {
|
|
68
|
-
// where.left is neither OperationExpr nor ColumnBuilder - invalid state
|
|
69
|
-
errorFailedToBuildWhereClause();
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
// Handle where.right - can be ParamPlaceholder or
|
|
66
|
+
// Handle where.right - can be ParamPlaceholder or ExpressionSource
|
|
73
67
|
if (isParamPlaceholder(where.right)) {
|
|
74
68
|
// Handle param placeholder (existing logic)
|
|
75
69
|
const placeholder: ParamPlaceholder = where.right;
|
|
@@ -82,9 +76,9 @@ export function buildWhereExpr(
|
|
|
82
76
|
const value = paramsMap[paramName];
|
|
83
77
|
const index = values.push(value);
|
|
84
78
|
|
|
85
|
-
// Construct descriptor directly from validated StorageColumn
|
|
86
|
-
if (
|
|
87
|
-
const { table, column } =
|
|
79
|
+
// Construct descriptor directly from validated StorageColumn if left is a column
|
|
80
|
+
if (leftExpr.kind === 'col') {
|
|
81
|
+
const { table, column } = leftExpr;
|
|
88
82
|
const contractTable = contract.storage.tables[table];
|
|
89
83
|
const columnMeta = contractTable?.columns[column];
|
|
90
84
|
if (columnMeta) {
|
|
@@ -100,25 +94,28 @@ export function buildWhereExpr(
|
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
rightExpr = createParamRef(index, paramName);
|
|
103
|
-
} else if (isColumnBuilder(where.right)) {
|
|
104
|
-
// Handle
|
|
105
|
-
|
|
97
|
+
} else if (isColumnBuilder(where.right) || isExpressionBuilder(where.right)) {
|
|
98
|
+
// Handle ExpressionSource (ColumnBuilder or ExpressionBuilder) on the right
|
|
99
|
+
rightExpr = where.right.toExpr();
|
|
106
100
|
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
// Validate column exists in contract if it's a ColumnRef
|
|
102
|
+
if (rightExpr.kind === 'col') {
|
|
103
|
+
const { table, column } = rightExpr;
|
|
104
|
+
const contractTable = contract.storage.tables[table];
|
|
105
|
+
if (!contractTable) {
|
|
106
|
+
errorUnknownTable(table);
|
|
107
|
+
}
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
110
|
+
if (!columnMeta) {
|
|
111
|
+
errorUnknownColumn(column, table);
|
|
112
|
+
}
|
|
115
113
|
}
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
// Use a placeholder paramName for column references (not used for params)
|
|
115
|
+
// Use a placeholder paramName for expression references (not used for params)
|
|
119
116
|
paramName = '';
|
|
120
117
|
} else {
|
|
121
|
-
// where.right is neither ParamPlaceholder nor
|
|
118
|
+
// where.right is neither ParamPlaceholder nor ExpressionSource - invalid state
|
|
122
119
|
errorFailedToBuildWhereClause();
|
|
123
120
|
}
|
|
124
121
|
|
package/src/sql/projection.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TableRef } from '@prisma-next/sql-relational-core/ast';
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
2
|
+
import type { AnyExpressionSource, NestedProjection } from '@prisma-next/sql-relational-core/types';
|
|
3
|
+
import { isExpressionSource } from '@prisma-next/sql-relational-core/utils/guards';
|
|
4
4
|
import type { ProjectionInput } from '../types/internal';
|
|
5
5
|
import {
|
|
6
6
|
errorAliasCollision,
|
|
@@ -47,14 +47,14 @@ export function flattenProjection(
|
|
|
47
47
|
projection: NestedProjection,
|
|
48
48
|
tracker: AliasTracker,
|
|
49
49
|
currentPath: string[] = [],
|
|
50
|
-
): { aliases: string[]; columns:
|
|
50
|
+
): { aliases: string[]; columns: AnyExpressionSource[] } {
|
|
51
51
|
const aliases: string[] = [];
|
|
52
|
-
const columns:
|
|
52
|
+
const columns: AnyExpressionSource[] = [];
|
|
53
53
|
|
|
54
54
|
for (const [key, value] of Object.entries(projection)) {
|
|
55
55
|
const path = [...currentPath, key];
|
|
56
56
|
|
|
57
|
-
if (
|
|
57
|
+
if (isExpressionSource(value)) {
|
|
58
58
|
const alias = tracker.register(path);
|
|
59
59
|
aliases.push(alias);
|
|
60
60
|
columns.push(value);
|
|
@@ -77,7 +77,7 @@ export function buildProjectionState(
|
|
|
77
77
|
): ProjectionState {
|
|
78
78
|
const tracker = new AliasTracker();
|
|
79
79
|
const aliases: string[] = [];
|
|
80
|
-
const columns:
|
|
80
|
+
const columns: AnyExpressionSource[] = [];
|
|
81
81
|
|
|
82
82
|
for (const [key, value] of Object.entries(projection)) {
|
|
83
83
|
if (value === true) {
|
|
@@ -86,12 +86,17 @@ export function buildProjectionState(
|
|
|
86
86
|
if (!matchingInclude) {
|
|
87
87
|
errorIncludeAliasNotFound(key);
|
|
88
88
|
}
|
|
89
|
-
// For include references, we track the alias but use
|
|
89
|
+
// For include references, we track the alias but use a placeholder object
|
|
90
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: () => ({ kind: 'col', table: matchingInclude.table.name, column: '' }),
|
|
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);
|
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
TableRef,
|
|
12
12
|
} from '@prisma-next/sql-relational-core/ast';
|
|
13
13
|
import {
|
|
14
|
-
createColumnRef,
|
|
15
14
|
createJoinOnBuilder,
|
|
16
15
|
createOrderByItem,
|
|
17
16
|
createSelectAst,
|
|
@@ -31,9 +30,8 @@ import type {
|
|
|
31
30
|
OrderBuilder,
|
|
32
31
|
SqlBuilderOptions,
|
|
33
32
|
} from '@prisma-next/sql-relational-core/types';
|
|
34
|
-
import {
|
|
33
|
+
import { isExpressionBuilder } from '@prisma-next/sql-relational-core/utils/guards';
|
|
35
34
|
import type { ProjectionInput } from '../types/internal';
|
|
36
|
-
import { assertColumnBuilder } from '../utils/assertions';
|
|
37
35
|
import { checkIncludeCapabilities } from '../utils/capabilities';
|
|
38
36
|
import {
|
|
39
37
|
errorChildProjectionEmpty,
|
|
@@ -41,7 +39,6 @@ import {
|
|
|
41
39
|
errorIncludeAliasCollision,
|
|
42
40
|
errorLimitMustBeNonNegativeInteger,
|
|
43
41
|
errorMissingAlias,
|
|
44
|
-
errorMissingColumnForAlias,
|
|
45
42
|
errorSelectMustBeCalled,
|
|
46
43
|
errorSelfJoinNotSupported,
|
|
47
44
|
errorUnknownTable,
|
|
@@ -320,15 +317,8 @@ export class SelectBuilderImpl<
|
|
|
320
317
|
const orderByClause = this.state.orderBy
|
|
321
318
|
? (() => {
|
|
322
319
|
const orderBy = this.state.orderBy as OrderBuilder<string, StorageColumn, unknown>;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const expr: ColumnRef | OperationExpr = operationExpr
|
|
326
|
-
? operationExpr
|
|
327
|
-
: (() => {
|
|
328
|
-
const colBuilder = orderExpr as { table: string; column: string };
|
|
329
|
-
return createColumnRef(colBuilder.table, colBuilder.column);
|
|
330
|
-
})();
|
|
331
|
-
return [createOrderByItem(expr, orderBy.dir)];
|
|
320
|
+
// orderBy.expr is already an Expression (ColumnRef or OperationExpr)
|
|
321
|
+
return [createOrderByItem(orderBy.expr, orderBy.dir)];
|
|
332
322
|
})()
|
|
333
323
|
: undefined;
|
|
334
324
|
|
|
@@ -357,28 +347,19 @@ export class SelectBuilderImpl<
|
|
|
357
347
|
alias,
|
|
358
348
|
expr: { kind: 'includeRef', alias },
|
|
359
349
|
});
|
|
360
|
-
} else {
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
// This is a regular column
|
|
374
|
-
// TypeScript can't narrow ColumnBuilder properly
|
|
375
|
-
const col = column as { table: string; column: string };
|
|
376
|
-
assertColumnBuilder(col, 'projection column');
|
|
377
|
-
projectEntries.push({
|
|
378
|
-
alias,
|
|
379
|
-
expr: createColumnRef(col.table, col.column),
|
|
380
|
-
});
|
|
381
|
-
}
|
|
350
|
+
} else if (column && isExpressionBuilder(column)) {
|
|
351
|
+
// This is an ExpressionBuilder (operation result) - use its expr
|
|
352
|
+
projectEntries.push({
|
|
353
|
+
alias,
|
|
354
|
+
expr: column.expr,
|
|
355
|
+
});
|
|
356
|
+
} else if (column) {
|
|
357
|
+
// This is a regular ColumnBuilder - use toExpr() to get ColumnRef
|
|
358
|
+
const columnRef = column.toExpr();
|
|
359
|
+
projectEntries.push({
|
|
360
|
+
alias,
|
|
361
|
+
expr: columnRef,
|
|
362
|
+
});
|
|
382
363
|
}
|
|
383
364
|
}
|
|
384
365
|
|
package/src/types/internal.ts
CHANGED
|
@@ -3,13 +3,13 @@ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
|
3
3
|
import type { TableRef } from '@prisma-next/sql-relational-core/ast';
|
|
4
4
|
import type {
|
|
5
5
|
AnyBinaryBuilder,
|
|
6
|
-
|
|
6
|
+
AnyExpressionSource,
|
|
7
7
|
AnyOrderBuilder,
|
|
8
8
|
NestedProjection,
|
|
9
9
|
} from '@prisma-next/sql-relational-core/types';
|
|
10
10
|
import type { ProjectionState } from '../utils/state';
|
|
11
11
|
|
|
12
|
-
export type ProjectionInput = Record<string,
|
|
12
|
+
export type ProjectionInput = Record<string, AnyExpressionSource | boolean | NestedProjection>;
|
|
13
13
|
|
|
14
14
|
export interface MetaBuildArgs {
|
|
15
15
|
readonly contract: SqlContract<SqlStorage>;
|
package/src/utils/errors.ts
CHANGED
|
@@ -98,7 +98,7 @@ export function errorMissingParameter(paramName: string): never {
|
|
|
98
98
|
|
|
99
99
|
export function errorInvalidProjectionValue(path: string[]): never {
|
|
100
100
|
throw planInvalid(
|
|
101
|
-
`Invalid projection value at path ${path.join('.')}: expected ColumnBuilder or nested object`,
|
|
101
|
+
`Invalid projection value at path ${path.join('.')}: expected ExpressionSource (ColumnBuilder or ExpressionBuilder) or nested object`,
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
|
package/src/utils/state.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { TableRef } from '@prisma-next/sql-relational-core/ast';
|
|
2
2
|
import type {
|
|
3
3
|
AnyBinaryBuilder,
|
|
4
|
-
|
|
4
|
+
AnyExpressionSource,
|
|
5
5
|
AnyOrderBuilder,
|
|
6
6
|
JoinOnPredicate,
|
|
7
7
|
} from '@prisma-next/sql-relational-core/types';
|
|
8
8
|
|
|
9
9
|
export interface ProjectionState {
|
|
10
10
|
readonly aliases: string[];
|
|
11
|
-
|
|
12
|
-
// This maintains array alignment but avoids creating invalid ColumnBuilders
|
|
13
|
-
readonly columns: (AnyColumnBuilder | null)[];
|
|
11
|
+
readonly columns: AnyExpressionSource[];
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export interface JoinState {
|