@prisma-next/sql-lane 0.3.0-pr.95.1 → 0.3.0-pr.98.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/{chunk-72PNERR5.js → chunk-AWSKRSFP.js} +160 -132
- package/dist/chunk-AWSKRSFP.js.map +1 -0
- package/dist/exports/sql.js +1 -1
- package/dist/index.js +1 -1
- package/dist/sql/plan.d.ts.map +1 -1
- package/dist/sql/predicate-builder.d.ts.map +1 -1
- package/dist/sql/projection.d.ts +2 -2
- package/dist/sql/projection.d.ts.map +1 -1
- package/dist/sql/select-builder.d.ts.map +1 -1
- package/dist/types/internal.d.ts +2 -2
- package/dist/types/internal.d.ts.map +1 -1
- package/dist/utils/state.d.ts +2 -2
- package/dist/utils/state.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/sql/include-builder.ts +1 -1
- package/src/sql/plan.ts +101 -102
- package/src/sql/predicate-builder.ts +44 -41
- package/src/sql/projection.ts +10 -15
- package/src/sql/select-builder.ts +35 -16
- package/src/types/internal.ts +2 -2
- package/src/utils/errors.ts +1 -1
- package/src/utils/state.ts +4 -2
- package/dist/chunk-72PNERR5.js.map +0 -1
package/src/sql/plan.ts
CHANGED
|
@@ -1,72 +1,42 @@
|
|
|
1
1
|
import type { PlanMeta } from '@prisma-next/contract/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OperationExpr } from '@prisma-next/sql-relational-core/ast';
|
|
3
3
|
import { compact } from '@prisma-next/sql-relational-core/ast';
|
|
4
|
-
import type {
|
|
4
|
+
import type { AnyColumnBuilder } from '@prisma-next/sql-relational-core/types';
|
|
5
5
|
import {
|
|
6
6
|
collectColumnRefs,
|
|
7
|
+
getColumnInfo,
|
|
8
|
+
getOperationExpr,
|
|
7
9
|
isColumnBuilder,
|
|
8
|
-
isExpressionBuilder,
|
|
9
10
|
isOperationExpr,
|
|
10
11
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
11
12
|
import type { MetaBuildArgs } from '../types/internal';
|
|
12
13
|
import { assertColumnBuilder } from '../utils/assertions';
|
|
13
14
|
import { errorMissingColumnForAlias } from '../utils/errors';
|
|
14
15
|
|
|
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
|
-
|
|
64
16
|
export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
65
17
|
const refsColumns = new Map<string, { table: string; column: string }>();
|
|
66
18
|
const refsTables = new Set<string>([args.table.name]);
|
|
67
19
|
|
|
68
20
|
for (const column of args.projection.columns) {
|
|
69
|
-
|
|
21
|
+
// Skip null columns (include placeholders)
|
|
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
|
+
}
|
|
70
40
|
}
|
|
71
41
|
|
|
72
42
|
if (args.joins) {
|
|
@@ -111,27 +81,42 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
111
81
|
}
|
|
112
82
|
// Add child WHERE columns if present
|
|
113
83
|
if (include.childWhere) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
84
|
+
const colInfo = getColumnInfo(include.childWhere.left);
|
|
85
|
+
refsColumns.set(`${colInfo.table}.${colInfo.column}`, {
|
|
86
|
+
table: colInfo.table,
|
|
87
|
+
column: colInfo.column,
|
|
88
|
+
});
|
|
89
|
+
// Handle right side of child WHERE clause
|
|
117
90
|
const childWhereRight = include.childWhere.right;
|
|
118
|
-
if (isColumnBuilder(childWhereRight)
|
|
119
|
-
|
|
91
|
+
if (isColumnBuilder(childWhereRight)) {
|
|
92
|
+
const rightColInfo = getColumnInfo(childWhereRight);
|
|
93
|
+
refsColumns.set(`${rightColInfo.table}.${rightColInfo.column}`, {
|
|
94
|
+
table: rightColInfo.table,
|
|
95
|
+
column: rightColInfo.column,
|
|
96
|
+
});
|
|
120
97
|
}
|
|
121
98
|
}
|
|
122
99
|
// Add child ORDER BY columns if present
|
|
123
100
|
if (include.childOrderBy) {
|
|
124
|
-
|
|
125
|
-
|
|
101
|
+
const orderBy = include.childOrderBy as unknown as {
|
|
102
|
+
expr?: AnyColumnBuilder | OperationExpr;
|
|
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
|
+
}
|
|
126
111
|
}
|
|
127
112
|
}
|
|
128
113
|
}
|
|
129
114
|
|
|
130
115
|
if (args.where) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (isOperationExpr(
|
|
134
|
-
const allRefs = collectColumnRefs(
|
|
116
|
+
const whereLeft = args.where.left;
|
|
117
|
+
// Check if whereLeft is an OperationExpr directly (not wrapped in ColumnBuilder)
|
|
118
|
+
if (isOperationExpr(whereLeft)) {
|
|
119
|
+
const allRefs = collectColumnRefs(whereLeft);
|
|
135
120
|
for (const ref of allRefs) {
|
|
136
121
|
refsColumns.set(`${ref.table}.${ref.column}`, {
|
|
137
122
|
table: ref.table,
|
|
@@ -139,37 +124,56 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
139
124
|
});
|
|
140
125
|
}
|
|
141
126
|
} else {
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
127
|
+
// Check if whereLeft is a ColumnBuilder with an _operationExpr property
|
|
128
|
+
const operationExpr = (whereLeft as { _operationExpr?: OperationExpr })._operationExpr;
|
|
129
|
+
if (operationExpr) {
|
|
130
|
+
const allRefs = collectColumnRefs(operationExpr);
|
|
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
|
+
});
|
|
143
|
+
}
|
|
147
144
|
}
|
|
148
145
|
|
|
149
|
-
// Handle right side of WHERE clause - can be ParamPlaceholder or
|
|
146
|
+
// Handle right side of WHERE clause - can be ParamPlaceholder or AnyColumnBuilder
|
|
150
147
|
const whereRight = args.where.right;
|
|
151
|
-
if (isColumnBuilder(whereRight)
|
|
152
|
-
|
|
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
|
+
});
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
if (args.orderBy) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
const orderBy = args.orderBy as unknown as {
|
|
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,
|
|
165
175
|
});
|
|
166
176
|
}
|
|
167
|
-
} else {
|
|
168
|
-
// orderByExpr is ColumnRef
|
|
169
|
-
refsColumns.set(`${orderByExpr.table}.${orderByExpr.column}`, {
|
|
170
|
-
table: orderByExpr.table,
|
|
171
|
-
column: orderByExpr.column,
|
|
172
|
-
});
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
|
|
@@ -187,18 +191,13 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
187
191
|
// This shouldn't happen if projection building is correct, but handle gracefully
|
|
188
192
|
errorMissingColumnForAlias(alias, index);
|
|
189
193
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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}`];
|
|
194
|
+
|
|
195
|
+
const operationExpr = getOperationExpr(column);
|
|
196
|
+
if (operationExpr) {
|
|
197
|
+
return [alias, `operation:${operationExpr.method}`];
|
|
199
198
|
}
|
|
200
199
|
|
|
201
|
-
return [alias, `${
|
|
200
|
+
return [alias, `${column.table}.${column.column}`];
|
|
202
201
|
}),
|
|
203
202
|
);
|
|
204
203
|
|
|
@@ -214,15 +213,15 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
214
213
|
if (!column) {
|
|
215
214
|
continue;
|
|
216
215
|
}
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
const operationExpr = (column as { _operationExpr?: OperationExpr })._operationExpr;
|
|
217
|
+
if (operationExpr) {
|
|
219
218
|
if (operationExpr.returns.kind === 'typeId') {
|
|
220
219
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
221
220
|
} else if (operationExpr.returns.kind === 'builtin') {
|
|
222
221
|
projectionTypes[alias] = operationExpr.returns.type;
|
|
223
222
|
}
|
|
224
223
|
} else {
|
|
225
|
-
//
|
|
224
|
+
// TypeScript can't narrow ColumnBuilder properly
|
|
226
225
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
227
226
|
const columnMeta = col.columnMeta;
|
|
228
227
|
const codecId = columnMeta?.codecId;
|
|
@@ -244,14 +243,14 @@ export function buildMeta(args: MetaBuildArgs): PlanMeta {
|
|
|
244
243
|
if (!column) {
|
|
245
244
|
continue;
|
|
246
245
|
}
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
const operationExpr = (column as { _operationExpr?: OperationExpr })._operationExpr;
|
|
247
|
+
if (operationExpr) {
|
|
249
248
|
if (operationExpr.returns.kind === 'typeId') {
|
|
250
249
|
projectionCodecs[alias] = operationExpr.returns.type;
|
|
251
250
|
}
|
|
252
251
|
} else {
|
|
253
252
|
// Use columnMeta.codecId directly as typeId (already canonicalized)
|
|
254
|
-
//
|
|
253
|
+
// TypeScript can't narrow ColumnBuilder properly
|
|
255
254
|
const col = column as unknown as { columnMeta?: { codecId: string } };
|
|
256
255
|
const columnMeta = col.columnMeta;
|
|
257
256
|
const codecId = columnMeta?.codecId;
|
|
@@ -1,11 +1,21 @@
|
|
|
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
|
-
|
|
3
|
+
import type {
|
|
4
|
+
BinaryExpr,
|
|
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';
|
|
5
14
|
import type { BinaryBuilder, ParamPlaceholder } from '@prisma-next/sql-relational-core/types';
|
|
6
15
|
import {
|
|
16
|
+
getColumnInfo,
|
|
17
|
+
getOperationExpr,
|
|
7
18
|
isColumnBuilder,
|
|
8
|
-
isExpressionBuilder,
|
|
9
19
|
isParamPlaceholder,
|
|
10
20
|
} from '@prisma-next/sql-relational-core/utils/guards';
|
|
11
21
|
import {
|
|
@@ -28,28 +38,20 @@ export function buildWhereExpr(
|
|
|
28
38
|
descriptors: ParamDescriptor[],
|
|
29
39
|
values: unknown[],
|
|
30
40
|
): BuildWhereExprResult {
|
|
31
|
-
let leftExpr:
|
|
41
|
+
let leftExpr: ColumnRef | OperationExpr;
|
|
32
42
|
let codecId: string | undefined;
|
|
33
|
-
let rightExpr:
|
|
43
|
+
let rightExpr: ColumnRef | ParamRef;
|
|
34
44
|
let paramName: string;
|
|
35
45
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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;
|
|
46
|
+
// Check if where.left is an OperationExpr directly (from operation.eq())
|
|
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);
|
|
49
54
|
|
|
50
|
-
// If the left expression is a column reference, extract codecId for param descriptors
|
|
51
|
-
if (leftExpr.kind === 'col') {
|
|
52
|
-
const { table, column } = leftExpr;
|
|
53
55
|
const contractTable = contract.storage.tables[table];
|
|
54
56
|
if (!contractTable) {
|
|
55
57
|
errorUnknownTable(table);
|
|
@@ -61,9 +63,13 @@ export function buildWhereExpr(
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
codecId = columnMeta.codecId;
|
|
66
|
+
leftExpr = createColumnRef(table, column);
|
|
67
|
+
} else {
|
|
68
|
+
// where.left is neither OperationExpr nor ColumnBuilder - invalid state
|
|
69
|
+
errorFailedToBuildWhereClause();
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
// Handle where.right - can be ParamPlaceholder or
|
|
72
|
+
// Handle where.right - can be ParamPlaceholder or AnyColumnBuilder
|
|
67
73
|
if (isParamPlaceholder(where.right)) {
|
|
68
74
|
// Handle param placeholder (existing logic)
|
|
69
75
|
const placeholder: ParamPlaceholder = where.right;
|
|
@@ -76,9 +82,9 @@ export function buildWhereExpr(
|
|
|
76
82
|
const value = paramsMap[paramName];
|
|
77
83
|
const index = values.push(value);
|
|
78
84
|
|
|
79
|
-
// Construct descriptor directly from validated StorageColumn
|
|
80
|
-
if (
|
|
81
|
-
const { table, column } =
|
|
85
|
+
// Construct descriptor directly from validated StorageColumn
|
|
86
|
+
if (isColumnBuilder(where.left)) {
|
|
87
|
+
const { table, column } = getColumnInfo(where.left);
|
|
82
88
|
const contractTable = contract.storage.tables[table];
|
|
83
89
|
const columnMeta = contractTable?.columns[column];
|
|
84
90
|
if (columnMeta) {
|
|
@@ -94,28 +100,25 @@ export function buildWhereExpr(
|
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
rightExpr = createParamRef(index, paramName);
|
|
97
|
-
} else if (isColumnBuilder(where.right)
|
|
98
|
-
// Handle
|
|
99
|
-
|
|
103
|
+
} else if (isColumnBuilder(where.right)) {
|
|
104
|
+
// Handle column builder on the right
|
|
105
|
+
const { table, column } = getColumnInfo(where.right);
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!contractTable) {
|
|
106
|
-
errorUnknownTable(table);
|
|
107
|
-
}
|
|
107
|
+
const contractTable = contract.storage.tables[table];
|
|
108
|
+
if (!contractTable) {
|
|
109
|
+
errorUnknownTable(table);
|
|
110
|
+
}
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
112
|
+
const columnMeta: StorageColumn | undefined = contractTable.columns[column];
|
|
113
|
+
if (!columnMeta) {
|
|
114
|
+
errorUnknownColumn(column, table);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
rightExpr = createColumnRef(table, column);
|
|
118
|
+
// Use a placeholder paramName for column references (not used for params)
|
|
116
119
|
paramName = '';
|
|
117
120
|
} else {
|
|
118
|
-
// where.right is neither ParamPlaceholder nor
|
|
121
|
+
// where.right is neither ParamPlaceholder nor ColumnBuilder - invalid state
|
|
119
122
|
errorFailedToBuildWhereClause();
|
|
120
123
|
}
|
|
121
124
|
|
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 { AnyColumnBuilder, NestedProjection } from '@prisma-next/sql-relational-core/types';
|
|
3
|
+
import { isColumnBuilder } 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: AnyColumnBuilder[] } {
|
|
51
51
|
const aliases: string[] = [];
|
|
52
|
-
const columns:
|
|
52
|
+
const columns: AnyColumnBuilder[] = [];
|
|
53
53
|
|
|
54
54
|
for (const [key, value] of Object.entries(projection)) {
|
|
55
55
|
const path = [...currentPath, key];
|
|
56
56
|
|
|
57
|
-
if (
|
|
57
|
+
if (isColumnBuilder(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: (AnyColumnBuilder | null)[] = [];
|
|
81
81
|
|
|
82
82
|
for (const [key, value] of Object.entries(projection)) {
|
|
83
83
|
if (value === true) {
|
|
@@ -86,17 +86,12 @@ 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 null as placeholder
|
|
90
90
|
// The actual handling happens in AST building where we create includeRef
|
|
91
|
+
// Using null instead of invalid ColumnBuilder avoids code smell
|
|
91
92
|
aliases.push(key);
|
|
92
|
-
columns.push(
|
|
93
|
-
|
|
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)) {
|
|
93
|
+
columns.push(null);
|
|
94
|
+
} else if (isColumnBuilder(value)) {
|
|
100
95
|
const alias = tracker.register([key]);
|
|
101
96
|
aliases.push(alias);
|
|
102
97
|
columns.push(value);
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
TableRef,
|
|
12
12
|
} from '@prisma-next/sql-relational-core/ast';
|
|
13
13
|
import {
|
|
14
|
+
createColumnRef,
|
|
14
15
|
createJoinOnBuilder,
|
|
15
16
|
createOrderByItem,
|
|
16
17
|
createSelectAst,
|
|
@@ -30,8 +31,9 @@ import type {
|
|
|
30
31
|
OrderBuilder,
|
|
31
32
|
SqlBuilderOptions,
|
|
32
33
|
} from '@prisma-next/sql-relational-core/types';
|
|
33
|
-
import {
|
|
34
|
+
import { getOperationExpr } from '@prisma-next/sql-relational-core/utils/guards';
|
|
34
35
|
import type { ProjectionInput } from '../types/internal';
|
|
36
|
+
import { assertColumnBuilder } from '../utils/assertions';
|
|
35
37
|
import { checkIncludeCapabilities } from '../utils/capabilities';
|
|
36
38
|
import {
|
|
37
39
|
errorChildProjectionEmpty,
|
|
@@ -39,6 +41,7 @@ import {
|
|
|
39
41
|
errorIncludeAliasCollision,
|
|
40
42
|
errorLimitMustBeNonNegativeInteger,
|
|
41
43
|
errorMissingAlias,
|
|
44
|
+
errorMissingColumnForAlias,
|
|
42
45
|
errorSelectMustBeCalled,
|
|
43
46
|
errorSelfJoinNotSupported,
|
|
44
47
|
errorUnknownTable,
|
|
@@ -317,8 +320,15 @@ export class SelectBuilderImpl<
|
|
|
317
320
|
const orderByClause = this.state.orderBy
|
|
318
321
|
? (() => {
|
|
319
322
|
const orderBy = this.state.orderBy as OrderBuilder<string, StorageColumn, unknown>;
|
|
320
|
-
|
|
321
|
-
|
|
323
|
+
const orderExpr = orderBy.expr;
|
|
324
|
+
const operationExpr = getOperationExpr(orderExpr);
|
|
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)];
|
|
322
332
|
})()
|
|
323
333
|
: undefined;
|
|
324
334
|
|
|
@@ -347,19 +357,28 @@ export class SelectBuilderImpl<
|
|
|
347
357
|
alias,
|
|
348
358
|
expr: { kind: 'includeRef', alias },
|
|
349
359
|
});
|
|
350
|
-
} else
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
alias,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
360
|
+
} else {
|
|
361
|
+
// Not an include - column must not be null
|
|
362
|
+
if (!column) {
|
|
363
|
+
errorMissingColumnForAlias(alias, i);
|
|
364
|
+
}
|
|
365
|
+
// Check if this column has an operation expression
|
|
366
|
+
const operationExpr = (column as { _operationExpr?: OperationExpr })._operationExpr;
|
|
367
|
+
if (operationExpr) {
|
|
368
|
+
projectEntries.push({
|
|
369
|
+
alias,
|
|
370
|
+
expr: operationExpr,
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
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
|
+
}
|
|
363
382
|
}
|
|
364
383
|
}
|
|
365
384
|
|
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
|
+
AnyColumnBuilder,
|
|
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, AnyColumnBuilder | 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
|
|
101
|
+
`Invalid projection value at path ${path.join('.')}: expected ColumnBuilder or nested object`,
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
|
package/src/utils/state.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { TableRef } from '@prisma-next/sql-relational-core/ast';
|
|
2
2
|
import type {
|
|
3
3
|
AnyBinaryBuilder,
|
|
4
|
-
|
|
4
|
+
AnyColumnBuilder,
|
|
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
|
-
|
|
11
|
+
// columns can be null for include placeholders (when alias matches an include)
|
|
12
|
+
// This maintains array alignment but avoids creating invalid ColumnBuilders
|
|
13
|
+
readonly columns: (AnyColumnBuilder | null)[];
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface JoinState {
|