@prisma-next/sql-relational-core 0.5.0-dev.3 → 0.5.0-dev.31
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/README.md +67 -1
- package/dist/codec-types-DJEaWT36.d.mts +313 -0
- package/dist/codec-types-DJEaWT36.d.mts.map +1 -0
- package/dist/{errors-ChY_dHam.d.mts → errors-BRt5yHo9.d.mts} +2 -2
- package/dist/errors-BRt5yHo9.d.mts.map +1 -0
- package/dist/{errors-D3xmG4h-.mjs → errors-D6kqqjHM.mjs} +1 -1
- package/dist/{errors-D3xmG4h-.mjs.map → errors-D6kqqjHM.mjs.map} +1 -1
- package/dist/exports/ast.d.mts +27 -12
- package/dist/exports/ast.d.mts.map +1 -1
- package/dist/exports/ast.mjs +63 -1089
- package/dist/exports/ast.mjs.map +1 -1
- package/dist/exports/errors.d.mts +4 -4
- package/dist/exports/errors.mjs +1 -1
- package/dist/exports/expression.d.mts +79 -0
- package/dist/exports/expression.d.mts.map +1 -0
- package/dist/exports/expression.mjs +41 -0
- package/dist/exports/expression.mjs.map +1 -0
- package/dist/exports/plan.d.mts +3 -2
- package/dist/exports/plan.mjs +1 -17
- package/dist/exports/query-lane-context.d.mts +3 -3
- package/dist/exports/types.d.mts +5 -4
- package/dist/index.d.mts +11 -9
- package/dist/index.mjs +6 -4
- package/dist/plan-C7SiEWkN.d.mts +25 -0
- package/dist/plan-C7SiEWkN.d.mts.map +1 -0
- package/dist/{query-lane-context-UlR8vOkd.d.mts → query-lane-context-BF-wuc0r.d.mts} +53 -3
- package/dist/query-lane-context-BF-wuc0r.d.mts.map +1 -0
- package/dist/sql-execution-plan-Dgx7BGin.d.mts +33 -0
- package/dist/sql-execution-plan-Dgx7BGin.d.mts.map +1 -0
- package/dist/{types-C3Hg-CVz.d.mts → types-B4dL4lc3.d.mts} +17 -22
- package/dist/types-B4dL4lc3.d.mts.map +1 -0
- package/dist/types-BUlUvdIU.d.mts +24 -0
- package/dist/types-BUlUvdIU.d.mts.map +1 -0
- package/dist/{types-k9pir8XY.d.mts → types-BWOCTYd8.d.mts} +12 -19
- package/dist/types-BWOCTYd8.d.mts.map +1 -0
- package/dist/types-DUL-3vy6.mjs +1064 -0
- package/dist/types-DUL-3vy6.mjs.map +1 -0
- package/package.json +11 -10
- package/src/ast/adapter-types.ts +8 -0
- package/src/ast/codec-types.ts +251 -45
- package/src/ast/sql-codecs.ts +20 -3
- package/src/ast/types.ts +142 -172
- package/src/ast/util.ts +23 -0
- package/src/exports/expression.ts +1 -0
- package/src/exports/plan.ts +1 -0
- package/src/exports/types.ts +1 -0
- package/src/expression.ts +117 -0
- package/src/index.ts +1 -0
- package/src/plan.ts +11 -30
- package/src/query-lane-context.ts +52 -1
- package/src/runtime-scope.ts +20 -0
- package/src/sql-execution-plan.ts +28 -0
- package/src/types.ts +9 -22
- package/dist/codec-types-DcEITed4.d.mts +0 -144
- package/dist/codec-types-DcEITed4.d.mts.map +0 -1
- package/dist/errors-ChY_dHam.d.mts.map +0 -1
- package/dist/exports/plan.mjs.map +0 -1
- package/dist/plan-Cs65hb-E.d.mts +0 -28
- package/dist/plan-Cs65hb-E.d.mts.map +0 -1
- package/dist/query-lane-context-UlR8vOkd.d.mts.map +0 -1
- package/dist/types-C3Hg-CVz.d.mts.map +0 -1
- package/dist/types-k9pir8XY.d.mts.map +0 -1
package/src/ast/sql-codecs.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JsonValue } from '@prisma-next/contract/types';
|
|
1
2
|
import { type as arktype } from 'arktype';
|
|
2
3
|
import { codec, defineCodecs } from './codec-types';
|
|
3
4
|
|
|
@@ -104,12 +105,28 @@ const sqlTextCodec = codec({
|
|
|
104
105
|
decode: (wire: string): string => wire,
|
|
105
106
|
});
|
|
106
107
|
|
|
107
|
-
const sqlTimestampCodec = codec
|
|
108
|
+
const sqlTimestampCodec = codec<
|
|
109
|
+
typeof SQL_TIMESTAMP_CODEC_ID,
|
|
110
|
+
readonly ['equality', 'order'],
|
|
111
|
+
Date,
|
|
112
|
+
Date
|
|
113
|
+
>({
|
|
108
114
|
typeId: SQL_TIMESTAMP_CODEC_ID,
|
|
109
115
|
targetTypes: ['timestamp'],
|
|
110
116
|
traits: ['equality', 'order'],
|
|
111
|
-
encode: (value:
|
|
112
|
-
decode: (wire:
|
|
117
|
+
encode: (value: Date): Date => value,
|
|
118
|
+
decode: (wire: Date): Date => wire,
|
|
119
|
+
encodeJson: (value: Date): JsonValue => value.toISOString(),
|
|
120
|
+
decodeJson: (json: JsonValue): Date => {
|
|
121
|
+
if (typeof json !== 'string') {
|
|
122
|
+
throw new Error(`Expected ISO date string for sql/timestamp@1, got ${typeof json}`);
|
|
123
|
+
}
|
|
124
|
+
const date = new Date(json);
|
|
125
|
+
if (Number.isNaN(date.getTime())) {
|
|
126
|
+
throw new Error(`Invalid ISO date string for sql/timestamp@1: ${json}`);
|
|
127
|
+
}
|
|
128
|
+
return date;
|
|
129
|
+
},
|
|
113
130
|
paramsSchema: precisionParamsSchema,
|
|
114
131
|
renderOutputType: (typeParams) => {
|
|
115
132
|
const precision = typeParams['precision'];
|
package/src/ast/types.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { PlanRefs } from '@prisma-next/contract/types';
|
|
2
1
|
import type { ParamSpec } from '@prisma-next/operations';
|
|
3
2
|
import type { SqlLoweringSpec } from '@prisma-next/sql-operations';
|
|
4
3
|
|
|
@@ -146,54 +145,89 @@ function collectParamRefsWith<TNode extends Expression>(node: TNode): ParamRef[]
|
|
|
146
145
|
});
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
): PlanRefs {
|
|
153
|
-
const sortedTables = [...tables].sort((a, b) => a.localeCompare(b));
|
|
154
|
-
const sortedColumns = [...columns.values()].sort((a, b) => {
|
|
155
|
-
const tableCompare = a.table.localeCompare(b.table);
|
|
156
|
-
if (tableCompare !== 0) {
|
|
157
|
-
return tableCompare;
|
|
158
|
-
}
|
|
159
|
-
return a.column.localeCompare(b.column);
|
|
160
|
-
});
|
|
148
|
+
function rewriteTableSource(table: TableSource, rewriter: AstRewriter): TableSource {
|
|
149
|
+
return rewriter.tableSource ? rewriter.tableSource(table) : table;
|
|
150
|
+
}
|
|
161
151
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
):
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
table: columnRef.table,
|
|
181
|
-
column: columnRef.column,
|
|
182
|
-
});
|
|
152
|
+
function rewriteProjectionItem(item: ProjectionItem, rewriter: AstRewriter): ProjectionItem {
|
|
153
|
+
const rewrittenExpr =
|
|
154
|
+
item.expr.kind === 'literal'
|
|
155
|
+
? rewriter.literal
|
|
156
|
+
? rewriter.literal(item.expr)
|
|
157
|
+
: item.expr
|
|
158
|
+
: item.expr.rewrite(rewriter);
|
|
159
|
+
return new ProjectionItem(item.alias, rewrittenExpr, item.codecId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function rewriteInsertValue(value: InsertValue, rewriter: AstRewriter): InsertValue {
|
|
163
|
+
switch (value.kind) {
|
|
164
|
+
case 'param-ref':
|
|
165
|
+
return rewriter.paramRef ? rewriteParamRefForInsert(value, rewriter) : value;
|
|
166
|
+
case 'column-ref':
|
|
167
|
+
return rewriter.columnRef ? rewriteColumnRefForInsert(value, rewriter) : value;
|
|
168
|
+
case 'default-value':
|
|
169
|
+
return value;
|
|
183
170
|
}
|
|
184
171
|
}
|
|
185
172
|
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
173
|
+
function rewriteParamRefForInsert(value: ParamRef, rewriter: AstRewriter): InsertValue {
|
|
174
|
+
const rewritten = rewriter.paramRef ? rewriter.paramRef(value) : value;
|
|
175
|
+
return rewritten.kind === 'param-ref' ? rewritten : value;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function rewriteColumnRefForInsert(value: ColumnRef, rewriter: AstRewriter): InsertValue {
|
|
179
|
+
const rewritten = rewriter.columnRef ? rewriter.columnRef(value) : value;
|
|
180
|
+
return rewritten.kind === 'column-ref' ? rewritten : value;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function rewriteInsertRow(
|
|
184
|
+
row: Readonly<Record<string, InsertValue>>,
|
|
185
|
+
rewriter: AstRewriter,
|
|
186
|
+
): Record<string, InsertValue> {
|
|
187
|
+
const result: Record<string, InsertValue> = {};
|
|
188
|
+
for (const [key, value] of Object.entries(row)) {
|
|
189
|
+
result[key] = rewriteInsertValue(value, rewriter);
|
|
193
190
|
}
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function rewriteUpdateSetValue(
|
|
195
|
+
value: ColumnRef | ParamRef,
|
|
196
|
+
rewriter: AstRewriter,
|
|
197
|
+
): ColumnRef | ParamRef {
|
|
198
|
+
if (value.kind === 'column-ref') {
|
|
199
|
+
const rewritten = rewriter.columnRef ? rewriter.columnRef(value) : value;
|
|
200
|
+
return rewritten.kind === 'column-ref' ? rewritten : value;
|
|
201
|
+
}
|
|
202
|
+
const rewritten = rewriter.paramRef ? rewriter.paramRef(value) : value;
|
|
203
|
+
return rewritten.kind === 'param-ref' ? rewritten : value;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function rewriteUpdateSet(
|
|
207
|
+
set: Readonly<Record<string, ColumnRef | ParamRef>>,
|
|
208
|
+
rewriter: AstRewriter,
|
|
209
|
+
): Record<string, ColumnRef | ParamRef> {
|
|
210
|
+
const result: Record<string, ColumnRef | ParamRef> = {};
|
|
211
|
+
for (const [key, value] of Object.entries(set)) {
|
|
212
|
+
result[key] = rewriteUpdateSetValue(value, rewriter);
|
|
196
213
|
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function rewriteOnConflict(onConflict: InsertOnConflict, rewriter: AstRewriter): InsertOnConflict {
|
|
218
|
+
const columns = onConflict.columns.map((columnRef) => {
|
|
219
|
+
const rewritten = rewriter.columnRef ? rewriter.columnRef(columnRef) : columnRef;
|
|
220
|
+
return rewritten.kind === 'column-ref' ? rewritten : columnRef;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (onConflict.action.kind === 'do-nothing') {
|
|
224
|
+
return new InsertOnConflict(columns, new DoNothingConflictAction());
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return new InsertOnConflict(
|
|
228
|
+
columns,
|
|
229
|
+
new DoUpdateSetConflictAction(rewriteUpdateSet(onConflict.action.set, rewriter)),
|
|
230
|
+
);
|
|
197
231
|
}
|
|
198
232
|
|
|
199
233
|
abstract class AstNode {
|
|
@@ -205,18 +239,11 @@ abstract class AstNode {
|
|
|
205
239
|
}
|
|
206
240
|
|
|
207
241
|
abstract class QueryAst extends AstNode {
|
|
208
|
-
abstract collectRefs(): PlanRefs;
|
|
209
242
|
abstract collectParamRefs(): ParamRef[];
|
|
210
243
|
abstract toQueryAst(): AnyQueryAst;
|
|
211
|
-
|
|
212
|
-
collectColumnRefs(): ColumnRef[] {
|
|
213
|
-
const refs = this.collectRefs().columns ?? [];
|
|
214
|
-
return refs.map((ref) => new ColumnRef(ref.table, ref.column));
|
|
215
|
-
}
|
|
216
244
|
}
|
|
217
245
|
|
|
218
246
|
abstract class FromSource extends AstNode {
|
|
219
|
-
abstract collectRefs(): PlanRefs;
|
|
220
247
|
abstract rewrite(rewriter: AstRewriter): AnyFromSource;
|
|
221
248
|
abstract toFromSource(): AnyFromSource;
|
|
222
249
|
}
|
|
@@ -270,13 +297,6 @@ export class TableSource extends FromSource {
|
|
|
270
297
|
override toFromSource(): AnyFromSource {
|
|
271
298
|
return this;
|
|
272
299
|
}
|
|
273
|
-
|
|
274
|
-
override collectRefs(): PlanRefs {
|
|
275
|
-
return {
|
|
276
|
-
tables: [this.name],
|
|
277
|
-
columns: [],
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
300
|
}
|
|
281
301
|
|
|
282
302
|
export interface TableRef {
|
|
@@ -310,10 +330,6 @@ export class DerivedTableSource extends FromSource {
|
|
|
310
330
|
override toFromSource(): AnyFromSource {
|
|
311
331
|
return this;
|
|
312
332
|
}
|
|
313
|
-
|
|
314
|
-
override collectRefs(): PlanRefs {
|
|
315
|
-
return this.query.collectRefs();
|
|
316
|
-
}
|
|
317
333
|
}
|
|
318
334
|
|
|
319
335
|
export class ColumnRef extends Expression {
|
|
@@ -1052,16 +1068,22 @@ export class ProjectionItem extends AstNode {
|
|
|
1052
1068
|
readonly kind = 'projection-item' as const;
|
|
1053
1069
|
readonly alias: string;
|
|
1054
1070
|
readonly expr: ProjectionExpr;
|
|
1071
|
+
readonly codecId: string | undefined;
|
|
1055
1072
|
|
|
1056
|
-
constructor(alias: string, expr: ProjectionExpr) {
|
|
1073
|
+
constructor(alias: string, expr: ProjectionExpr, codecId?: string) {
|
|
1057
1074
|
super();
|
|
1058
1075
|
this.alias = alias;
|
|
1059
1076
|
this.expr = expr;
|
|
1077
|
+
this.codecId = codecId;
|
|
1060
1078
|
this.freeze();
|
|
1061
1079
|
}
|
|
1062
1080
|
|
|
1063
|
-
static of(alias: string, expr: ProjectionExpr): ProjectionItem {
|
|
1064
|
-
return new ProjectionItem(alias, expr);
|
|
1081
|
+
static of(alias: string, expr: ProjectionExpr, codecId?: string): ProjectionItem {
|
|
1082
|
+
return new ProjectionItem(alias, expr, codecId);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
withCodecId(codecId: string | undefined): ProjectionItem {
|
|
1086
|
+
return new ProjectionItem(this.alias, this.expr, codecId);
|
|
1065
1087
|
}
|
|
1066
1088
|
}
|
|
1067
1089
|
|
|
@@ -1218,6 +1240,7 @@ export class SelectAst extends QueryAst {
|
|
|
1218
1240
|
? rewriter.literal(projection.expr)
|
|
1219
1241
|
: projection.expr
|
|
1220
1242
|
: projection.expr.rewrite(rewriter),
|
|
1243
|
+
projection.codecId,
|
|
1221
1244
|
),
|
|
1222
1245
|
),
|
|
1223
1246
|
where: this.where?.rewrite(rewriter),
|
|
@@ -1234,7 +1257,7 @@ export class SelectAst extends QueryAst {
|
|
|
1234
1257
|
return rewriter.select ? rewriter.select(rewritten) : rewritten;
|
|
1235
1258
|
}
|
|
1236
1259
|
|
|
1237
|
-
|
|
1260
|
+
collectColumnRefs(): ColumnRef[] {
|
|
1238
1261
|
const refs: ColumnRef[] = [];
|
|
1239
1262
|
const pushRefs = (columns: ReadonlyArray<ColumnRef>) => {
|
|
1240
1263
|
refs.push(...columns);
|
|
@@ -1322,35 +1345,6 @@ export class SelectAst extends QueryAst {
|
|
|
1322
1345
|
return refs;
|
|
1323
1346
|
}
|
|
1324
1347
|
|
|
1325
|
-
override collectRefs(): PlanRefs {
|
|
1326
|
-
const tables = new Set<string>();
|
|
1327
|
-
const columns = new Map<string, { table: string; column: string }>();
|
|
1328
|
-
|
|
1329
|
-
const addSource = (source: AnyFromSource) => {
|
|
1330
|
-
mergeRefsInto(source.collectRefs(), tables, columns);
|
|
1331
|
-
};
|
|
1332
|
-
|
|
1333
|
-
addSource(this.from);
|
|
1334
|
-
|
|
1335
|
-
for (const join of this.joins ?? []) {
|
|
1336
|
-
addSource(join.source);
|
|
1337
|
-
if (join.on.kind === 'eq-col-join-on') {
|
|
1338
|
-
addColumnRefToRefSets(join.on.left, tables, columns);
|
|
1339
|
-
addColumnRefToRefSets(join.on.right, tables, columns);
|
|
1340
|
-
} else {
|
|
1341
|
-
for (const columnRef of join.on.collectColumnRefs()) {
|
|
1342
|
-
addColumnRefToRefSets(columnRef, tables, columns);
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
for (const columnRef of this.collectColumnRefs()) {
|
|
1348
|
-
addColumnRefToRefSets(columnRef, tables, columns);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
return sortRefs(tables, columns);
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
1348
|
override toQueryAst(): AnyQueryAst {
|
|
1355
1349
|
return this;
|
|
1356
1350
|
}
|
|
@@ -1418,13 +1412,13 @@ export class InsertAst extends QueryAst {
|
|
|
1418
1412
|
readonly table: TableSource;
|
|
1419
1413
|
readonly rows: ReadonlyArray<Readonly<Record<string, InsertValue>>>;
|
|
1420
1414
|
readonly onConflict: InsertOnConflict | undefined;
|
|
1421
|
-
readonly returning: ReadonlyArray<
|
|
1415
|
+
readonly returning: ReadonlyArray<ProjectionItem> | undefined;
|
|
1422
1416
|
|
|
1423
1417
|
constructor(
|
|
1424
1418
|
table: TableSource,
|
|
1425
1419
|
rows: ReadonlyArray<Record<string, InsertValue>> = [{}],
|
|
1426
1420
|
onConflict?: InsertOnConflict,
|
|
1427
|
-
returning?: ReadonlyArray<
|
|
1421
|
+
returning?: ReadonlyArray<ProjectionItem>,
|
|
1428
1422
|
) {
|
|
1429
1423
|
super();
|
|
1430
1424
|
this.table = table;
|
|
@@ -1451,7 +1445,7 @@ export class InsertAst extends QueryAst {
|
|
|
1451
1445
|
);
|
|
1452
1446
|
}
|
|
1453
1447
|
|
|
1454
|
-
withReturning(returning: ReadonlyArray<
|
|
1448
|
+
withReturning(returning: ReadonlyArray<ProjectionItem> | undefined): InsertAst {
|
|
1455
1449
|
return new InsertAst(
|
|
1456
1450
|
this.table,
|
|
1457
1451
|
this.rows.map((row) => ({ ...row })),
|
|
@@ -1469,6 +1463,15 @@ export class InsertAst extends QueryAst {
|
|
|
1469
1463
|
);
|
|
1470
1464
|
}
|
|
1471
1465
|
|
|
1466
|
+
rewrite(rewriter: AstRewriter): InsertAst {
|
|
1467
|
+
return new InsertAst(
|
|
1468
|
+
rewriteTableSource(this.table, rewriter),
|
|
1469
|
+
this.rows.map((row) => rewriteInsertRow(row, rewriter)),
|
|
1470
|
+
this.onConflict ? rewriteOnConflict(this.onConflict, rewriter) : undefined,
|
|
1471
|
+
this.returning?.map((item) => rewriteProjectionItem(item, rewriter)),
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1472
1475
|
override collectParamRefs(): ParamRef[] {
|
|
1473
1476
|
const refs: ParamRef[] = [];
|
|
1474
1477
|
for (const row of this.rows) {
|
|
@@ -1485,44 +1488,12 @@ export class InsertAst extends QueryAst {
|
|
|
1485
1488
|
}
|
|
1486
1489
|
}
|
|
1487
1490
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
override collectRefs(): PlanRefs {
|
|
1492
|
-
const tables = new Set<string>([this.table.name]);
|
|
1493
|
-
const columns = new Map<string, { table: string; column: string }>();
|
|
1494
|
-
|
|
1495
|
-
const addColumn = (columnRef: ColumnRef) => addColumnRefToRefSets(columnRef, tables, columns);
|
|
1496
|
-
const addValue = (value: InsertValue) => {
|
|
1497
|
-
if (value.kind === 'column-ref') {
|
|
1498
|
-
addColumn(value);
|
|
1491
|
+
for (const item of this.returning ?? []) {
|
|
1492
|
+
if (item.expr.kind !== 'literal') {
|
|
1493
|
+
refs.push(...item.expr.collectParamRefs());
|
|
1499
1494
|
}
|
|
1500
|
-
};
|
|
1501
|
-
|
|
1502
|
-
for (const row of this.rows) {
|
|
1503
|
-
for (const value of Object.values(row)) {
|
|
1504
|
-
addValue(value);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
for (const columnRef of this.returning ?? []) {
|
|
1509
|
-
addColumn(columnRef);
|
|
1510
1495
|
}
|
|
1511
|
-
|
|
1512
|
-
if (this.onConflict) {
|
|
1513
|
-
for (const columnRef of this.onConflict.columns) {
|
|
1514
|
-
addColumn(columnRef);
|
|
1515
|
-
}
|
|
1516
|
-
if (this.onConflict.action.kind === 'do-update-set') {
|
|
1517
|
-
for (const value of Object.values(this.onConflict.action.set)) {
|
|
1518
|
-
if (value.kind === 'column-ref') {
|
|
1519
|
-
addColumn(value);
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
return sortRefs(tables, columns);
|
|
1496
|
+
return refs;
|
|
1526
1497
|
}
|
|
1527
1498
|
|
|
1528
1499
|
override toQueryAst(): AnyQueryAst {
|
|
@@ -1535,13 +1506,13 @@ export class UpdateAst extends QueryAst {
|
|
|
1535
1506
|
readonly table: TableSource;
|
|
1536
1507
|
readonly set: Readonly<Record<string, ColumnRef | ParamRef>>;
|
|
1537
1508
|
readonly where: AnyExpression | undefined;
|
|
1538
|
-
readonly returning: ReadonlyArray<
|
|
1509
|
+
readonly returning: ReadonlyArray<ProjectionItem> | undefined;
|
|
1539
1510
|
|
|
1540
1511
|
constructor(
|
|
1541
1512
|
table: TableSource,
|
|
1542
1513
|
set: Readonly<Record<string, ColumnRef | ParamRef>> = {},
|
|
1543
1514
|
where?: AnyExpression,
|
|
1544
|
-
returning?: ReadonlyArray<
|
|
1515
|
+
returning?: ReadonlyArray<ProjectionItem>,
|
|
1545
1516
|
) {
|
|
1546
1517
|
super();
|
|
1547
1518
|
this.table = table;
|
|
@@ -1563,10 +1534,19 @@ export class UpdateAst extends QueryAst {
|
|
|
1563
1534
|
return new UpdateAst(this.table, this.set, where, this.returning);
|
|
1564
1535
|
}
|
|
1565
1536
|
|
|
1566
|
-
withReturning(returning: ReadonlyArray<
|
|
1537
|
+
withReturning(returning: ReadonlyArray<ProjectionItem> | undefined): UpdateAst {
|
|
1567
1538
|
return new UpdateAst(this.table, this.set, this.where, returning);
|
|
1568
1539
|
}
|
|
1569
1540
|
|
|
1541
|
+
rewrite(rewriter: AstRewriter): UpdateAst {
|
|
1542
|
+
return new UpdateAst(
|
|
1543
|
+
rewriteTableSource(this.table, rewriter),
|
|
1544
|
+
rewriteUpdateSet(this.set, rewriter),
|
|
1545
|
+
this.where?.rewrite(rewriter),
|
|
1546
|
+
this.returning?.map((item) => rewriteProjectionItem(item, rewriter)),
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1570
1550
|
override collectParamRefs(): ParamRef[] {
|
|
1571
1551
|
const refs: ParamRef[] = [];
|
|
1572
1552
|
for (const value of Object.values(this.set)) {
|
|
@@ -1577,28 +1557,12 @@ export class UpdateAst extends QueryAst {
|
|
|
1577
1557
|
if (this.where) {
|
|
1578
1558
|
refs.push(...this.where.collectParamRefs());
|
|
1579
1559
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
override collectRefs(): PlanRefs {
|
|
1584
|
-
const tables = new Set<string>([this.table.name]);
|
|
1585
|
-
const columns = new Map<string, { table: string; column: string }>();
|
|
1586
|
-
|
|
1587
|
-
for (const value of Object.values(this.set)) {
|
|
1588
|
-
if (value.kind === 'column-ref') {
|
|
1589
|
-
addColumnRefToRefSets(value, tables, columns);
|
|
1560
|
+
for (const item of this.returning ?? []) {
|
|
1561
|
+
if (item.expr.kind !== 'literal') {
|
|
1562
|
+
refs.push(...item.expr.collectParamRefs());
|
|
1590
1563
|
}
|
|
1591
1564
|
}
|
|
1592
|
-
|
|
1593
|
-
for (const columnRef of this.where?.collectColumnRefs() ?? []) {
|
|
1594
|
-
addColumnRefToRefSets(columnRef, tables, columns);
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
for (const columnRef of this.returning ?? []) {
|
|
1598
|
-
addColumnRefToRefSets(columnRef, tables, columns);
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
return sortRefs(tables, columns);
|
|
1565
|
+
return refs;
|
|
1602
1566
|
}
|
|
1603
1567
|
|
|
1604
1568
|
override toQueryAst(): AnyQueryAst {
|
|
@@ -1610,9 +1574,13 @@ export class DeleteAst extends QueryAst {
|
|
|
1610
1574
|
readonly kind = 'delete' as const;
|
|
1611
1575
|
readonly table: TableSource;
|
|
1612
1576
|
readonly where: AnyExpression | undefined;
|
|
1613
|
-
readonly returning: ReadonlyArray<
|
|
1577
|
+
readonly returning: ReadonlyArray<ProjectionItem> | undefined;
|
|
1614
1578
|
|
|
1615
|
-
constructor(
|
|
1579
|
+
constructor(
|
|
1580
|
+
table: TableSource,
|
|
1581
|
+
where?: AnyExpression,
|
|
1582
|
+
returning?: ReadonlyArray<ProjectionItem>,
|
|
1583
|
+
) {
|
|
1616
1584
|
super();
|
|
1617
1585
|
this.table = table;
|
|
1618
1586
|
this.where = where;
|
|
@@ -1628,27 +1596,29 @@ export class DeleteAst extends QueryAst {
|
|
|
1628
1596
|
return new DeleteAst(this.table, where, this.returning);
|
|
1629
1597
|
}
|
|
1630
1598
|
|
|
1631
|
-
withReturning(returning: ReadonlyArray<
|
|
1599
|
+
withReturning(returning: ReadonlyArray<ProjectionItem> | undefined): DeleteAst {
|
|
1632
1600
|
return new DeleteAst(this.table, this.where, returning);
|
|
1633
1601
|
}
|
|
1634
1602
|
|
|
1635
|
-
|
|
1636
|
-
return
|
|
1603
|
+
rewrite(rewriter: AstRewriter): DeleteAst {
|
|
1604
|
+
return new DeleteAst(
|
|
1605
|
+
rewriteTableSource(this.table, rewriter),
|
|
1606
|
+
this.where?.rewrite(rewriter),
|
|
1607
|
+
this.returning?.map((item) => rewriteProjectionItem(item, rewriter)),
|
|
1608
|
+
);
|
|
1637
1609
|
}
|
|
1638
1610
|
|
|
1639
|
-
override
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
for (const columnRef of this.where?.collectColumnRefs() ?? []) {
|
|
1644
|
-
addColumnRefToRefSets(columnRef, tables, columns);
|
|
1611
|
+
override collectParamRefs(): ParamRef[] {
|
|
1612
|
+
const refs: ParamRef[] = [];
|
|
1613
|
+
if (this.where) {
|
|
1614
|
+
refs.push(...this.where.collectParamRefs());
|
|
1645
1615
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1616
|
+
for (const item of this.returning ?? []) {
|
|
1617
|
+
if (item.expr.kind !== 'literal') {
|
|
1618
|
+
refs.push(...item.expr.collectParamRefs());
|
|
1619
|
+
}
|
|
1649
1620
|
}
|
|
1650
|
-
|
|
1651
|
-
return sortRefs(tables, columns);
|
|
1621
|
+
return refs;
|
|
1652
1622
|
}
|
|
1653
1623
|
|
|
1654
1624
|
override toQueryAst(): AnyQueryAst {
|
package/src/ast/util.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AnyQueryAst, ParamRef } from './types';
|
|
2
|
+
|
|
1
3
|
export function compact<T extends Record<string, unknown>>(o: T): T {
|
|
2
4
|
const out: Record<string, unknown> = {};
|
|
3
5
|
for (const [k, v] of Object.entries(o)) {
|
|
@@ -7,3 +9,24 @@ export function compact<T extends Record<string, unknown>>(o: T): T {
|
|
|
7
9
|
}
|
|
8
10
|
return out as T;
|
|
9
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Walks an AST's parameter references in first-encounter order and dedupes
|
|
15
|
+
* by ParamRef identity. The single canonical helper used by every consumer
|
|
16
|
+
* that aligns `plan.params` with metadata-by-index — the SQL builder lane,
|
|
17
|
+
* the SQL ORM client, the SQL runtime encoder, and the Postgres renderer's
|
|
18
|
+
* `$N` index map — so the four walks cannot drift in dedupe semantics.
|
|
19
|
+
*
|
|
20
|
+
* SQLite's `?`-placeholder renderer intentionally does NOT use this helper
|
|
21
|
+
* because it needs one params entry per occurrence in the SQL.
|
|
22
|
+
*/
|
|
23
|
+
export function collectOrderedParamRefs(ast: AnyQueryAst): ReadonlyArray<ParamRef> {
|
|
24
|
+
const seen = new Set<ParamRef>();
|
|
25
|
+
const ordered: ParamRef[] = [];
|
|
26
|
+
for (const ref of ast.collectParamRefs()) {
|
|
27
|
+
if (seen.has(ref)) continue;
|
|
28
|
+
seen.add(ref);
|
|
29
|
+
ordered.push(ref);
|
|
30
|
+
}
|
|
31
|
+
return Object.freeze(ordered);
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../expression';
|
package/src/exports/plan.ts
CHANGED
package/src/exports/types.ts
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ParamSpec } from '@prisma-next/operations';
|
|
2
|
+
import type { QueryOperationReturn } from '@prisma-next/sql-contract/types';
|
|
3
|
+
import type { SqlLoweringSpec } from '@prisma-next/sql-operations';
|
|
4
|
+
import type { AnyExpression as AstExpression } from './ast/types';
|
|
5
|
+
import { OperationExpr, ParamRef } from './ast/types';
|
|
6
|
+
|
|
7
|
+
export type ScopeField = { codecId: string; nullable: boolean };
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A typed SQL expression. Identity is carried by the `returnType` descriptor
|
|
11
|
+
* (inherited from `QueryOperationReturn` and narrowed to `T`) — distinct `T`
|
|
12
|
+
* makes distinct Expression types structurally. `buildAst()` materialises the
|
|
13
|
+
* underlying AST node.
|
|
14
|
+
*/
|
|
15
|
+
export type Expression<T extends ScopeField> = QueryOperationReturn & {
|
|
16
|
+
readonly returnType: T;
|
|
17
|
+
buildAst(): AstExpression;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type CodecIdsWithTrait<
|
|
21
|
+
CT extends Record<string, { readonly input: unknown }>,
|
|
22
|
+
RequiredTraits extends readonly string[],
|
|
23
|
+
> = {
|
|
24
|
+
[K in keyof CT & string]: CT[K] extends { readonly traits: infer T }
|
|
25
|
+
? [RequiredTraits[number]] extends [T]
|
|
26
|
+
? K
|
|
27
|
+
: never
|
|
28
|
+
: never;
|
|
29
|
+
}[keyof CT & string];
|
|
30
|
+
|
|
31
|
+
type NullSuffix<N> = N extends true ? null : never;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An expression or literal value targeting a specific codec.
|
|
35
|
+
*
|
|
36
|
+
* Accepts any of:
|
|
37
|
+
* - An `Expression` whose codec matches exactly
|
|
38
|
+
* - A raw JS value of the codec's `input` type
|
|
39
|
+
* - `null` when `Nullable` is true
|
|
40
|
+
*/
|
|
41
|
+
export type CodecExpression<
|
|
42
|
+
CodecId extends string,
|
|
43
|
+
Nullable extends boolean,
|
|
44
|
+
CT extends Record<string, { readonly input: unknown }>,
|
|
45
|
+
> =
|
|
46
|
+
| Expression<{ codecId: CodecId; nullable: Nullable }>
|
|
47
|
+
| (CodecId extends keyof CT ? CT[CodecId]['input'] : never)
|
|
48
|
+
| NullSuffix<Nullable>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An expression or literal value targeting any codec whose trait set contains
|
|
52
|
+
* all the required traits.
|
|
53
|
+
*
|
|
54
|
+
* Resolves the trait set to the union of matching codec identities via
|
|
55
|
+
* `CodecIdsWithTrait`, then reuses `CodecExpression` for the codec-id form.
|
|
56
|
+
*/
|
|
57
|
+
export type TraitExpression<
|
|
58
|
+
Traits extends readonly string[],
|
|
59
|
+
Nullable extends boolean,
|
|
60
|
+
CT extends Record<string, { readonly input: unknown }>,
|
|
61
|
+
> = CodecExpression<CodecIdsWithTrait<CT, Traits>, Nullable, CT>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a raw value or an Expression into an AST expression node.
|
|
65
|
+
*
|
|
66
|
+
* When `value` is an Expression (duck-typed by its `buildAst` method), the AST
|
|
67
|
+
* it wraps is returned. Otherwise the value is embedded as a ParamRef tagged
|
|
68
|
+
* with `codecId` (if given). Pass `codecId` to encode the literal with a
|
|
69
|
+
* specific codec — most operations do.
|
|
70
|
+
*/
|
|
71
|
+
export function toExpr(value: unknown, codecId?: string): AstExpression {
|
|
72
|
+
if (isExpressionLike(value)) {
|
|
73
|
+
return value.buildAst();
|
|
74
|
+
}
|
|
75
|
+
return ParamRef.of(value, codecId ? { codecId } : undefined);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isExpressionLike(value: unknown): value is Expression<ScopeField> {
|
|
79
|
+
return (
|
|
80
|
+
typeof value === 'object' &&
|
|
81
|
+
value !== null &&
|
|
82
|
+
'buildAst' in value &&
|
|
83
|
+
typeof (value as { buildAst: unknown }).buildAst === 'function'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface BuildOperationSpec<R extends ScopeField> {
|
|
88
|
+
readonly method: string;
|
|
89
|
+
/**
|
|
90
|
+
* The operation's arguments. The first element is the self argument (the
|
|
91
|
+
* value the operation is being applied to); the rest are the remaining
|
|
92
|
+
* user-supplied arguments.
|
|
93
|
+
*/
|
|
94
|
+
readonly args: readonly [AstExpression, ...AstExpression[]];
|
|
95
|
+
readonly returns: R & ParamSpec;
|
|
96
|
+
readonly lowering: SqlLoweringSpec;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Construct an OperationExpr AST node and wrap it as a typed Expression.
|
|
101
|
+
* Operation implementations use this to turn their user-facing arguments into
|
|
102
|
+
* the AST node the compilation pipeline eventually lowers to SQL.
|
|
103
|
+
*/
|
|
104
|
+
export function buildOperation<R extends ScopeField>(spec: BuildOperationSpec<R>): Expression<R> {
|
|
105
|
+
const [self, ...rest] = spec.args;
|
|
106
|
+
const op = new OperationExpr({
|
|
107
|
+
method: spec.method,
|
|
108
|
+
self,
|
|
109
|
+
args: rest.length > 0 ? rest : undefined,
|
|
110
|
+
returns: spec.returns,
|
|
111
|
+
lowering: spec.lowering,
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
returnType: spec.returns,
|
|
115
|
+
buildAst: () => op,
|
|
116
|
+
};
|
|
117
|
+
}
|