@prisma-next/sql-relational-core 0.13.0-dev.4 → 0.13.0-dev.40

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.
Files changed (53) hide show
  1. package/dist/{ddl-types-D3vlEjMA.d.mts → ddl-types-BVO002FS.d.mts} +6 -2
  2. package/dist/ddl-types-BVO002FS.d.mts.map +1 -0
  3. package/dist/{ddl-types-X9_XHUl0.mjs → ddl-types-DAox2c8w.mjs} +4 -1
  4. package/dist/ddl-types-DAox2c8w.mjs.map +1 -0
  5. package/dist/{errors-CPLfzKkw.d.mts → errors-BbatEYjA.d.mts} +2 -2
  6. package/dist/{errors-CPLfzKkw.d.mts.map → errors-BbatEYjA.d.mts.map} +1 -1
  7. package/dist/exports/ast.d.mts +11 -3
  8. package/dist/exports/ast.d.mts.map +1 -1
  9. package/dist/exports/ast.mjs +3 -3
  10. package/dist/exports/codec-descriptor-registry.d.mts.map +1 -1
  11. package/dist/exports/codec-descriptor-registry.mjs +2 -13
  12. package/dist/exports/codec-descriptor-registry.mjs.map +1 -1
  13. package/dist/exports/contract-free.d.mts +60 -3
  14. package/dist/exports/contract-free.d.mts.map +1 -1
  15. package/dist/exports/contract-free.mjs +123 -3
  16. package/dist/exports/contract-free.mjs.map +1 -1
  17. package/dist/exports/errors.d.mts +1 -1
  18. package/dist/exports/expression.d.mts +1 -1
  19. package/dist/exports/expression.mjs +1 -1
  20. package/dist/exports/middleware.d.mts +1 -1
  21. package/dist/exports/plan.d.mts +2 -2
  22. package/dist/exports/types.d.mts +3 -3
  23. package/dist/index.d.mts +9 -9
  24. package/dist/index.mjs +3 -3
  25. package/dist/{middleware-BXSFukKU.d.mts → middleware-Dyyo4IP1.d.mts} +2 -2
  26. package/dist/{middleware-BXSFukKU.d.mts.map → middleware-Dyyo4IP1.d.mts.map} +1 -1
  27. package/dist/{plan-DObuWSWi.d.mts → plan-DUjdGLY3.d.mts} +2 -2
  28. package/dist/{plan-DObuWSWi.d.mts.map → plan-DUjdGLY3.d.mts.map} +1 -1
  29. package/dist/{sql-execution-plan-JwVeAzXt.d.mts → sql-execution-plan-DjMEAjKK.d.mts} +2 -2
  30. package/dist/{sql-execution-plan-JwVeAzXt.d.mts.map → sql-execution-plan-DjMEAjKK.d.mts.map} +1 -1
  31. package/dist/types-BQ_zfC-S.d.mts +233 -0
  32. package/dist/types-BQ_zfC-S.d.mts.map +1 -0
  33. package/dist/{types-LGikJRYV.d.mts → types-CUHnDsdV.d.mts} +3 -3
  34. package/dist/{types-LGikJRYV.d.mts.map → types-CUHnDsdV.d.mts.map} +1 -1
  35. package/dist/{types-BbGUx5Bi.d.mts → types-DQrmgP8Y.d.mts} +17 -5
  36. package/dist/{types-BbGUx5Bi.d.mts.map → types-DQrmgP8Y.d.mts.map} +1 -1
  37. package/dist/{types-D72v8s92.mjs → types-lJUc6cY-.mjs} +78 -18
  38. package/dist/types-lJUc6cY-.mjs.map +1 -0
  39. package/package.json +11 -11
  40. package/src/ast/ddl-types.ts +5 -0
  41. package/src/ast/driver-types.ts +8 -0
  42. package/src/ast/types.ts +98 -19
  43. package/src/codec-ref-for-column.ts +2 -35
  44. package/src/contract-free/column.ts +2 -0
  45. package/src/contract-free/dml.ts +5 -0
  46. package/src/contract-free/table.ts +191 -1
  47. package/src/exports/contract-free.ts +5 -0
  48. package/src/types.ts +134 -22
  49. package/dist/ddl-types-D3vlEjMA.d.mts.map +0 -1
  50. package/dist/ddl-types-X9_XHUl0.mjs.map +0 -1
  51. package/dist/types-CQVke4QO.d.mts +0 -181
  52. package/dist/types-CQVke4QO.d.mts.map +0 -1
  53. package/dist/types-D72v8s92.mjs.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import type { ColumnDefaultLiteralInputValue } from '@prisma-next/contract/types';
2
2
  import { isColumnDefaultLiteralInputValue } from '@prisma-next/contract/types';
3
3
  import type { ReferentialAction } from '@prisma-next/sql-contract/types';
4
+ import type { CodecRef } from './codec-types';
4
5
  import type { AnyParamRef } from './types';
5
6
 
6
7
  /**
@@ -71,6 +72,8 @@ export class DdlColumn {
71
72
  readonly notNull?: boolean | undefined;
72
73
  readonly primaryKey?: boolean | undefined;
73
74
  readonly default?: AnyDdlColumnDefault | undefined;
75
+ /** Codec identity for this column. When present, the DDL walker resolves the codec via `codecLookup.get(codecRef.codecId)` and calls `codec.encode(default.value, {})` to obtain the wire value before inlining the literal default into the DDL string. When absent, literal defaults follow RawSqlLiteral wire-scalar semantics (string / number / boolean / bigint / null / Uint8Array / Date inlined directly). */
76
+ readonly codecRef?: CodecRef | undefined;
74
77
 
75
78
  constructor(options: {
76
79
  readonly name: string;
@@ -78,12 +81,14 @@ export class DdlColumn {
78
81
  readonly notNull?: boolean;
79
82
  readonly primaryKey?: boolean;
80
83
  readonly default?: AnyDdlColumnDefault;
84
+ readonly codecRef?: CodecRef;
81
85
  }) {
82
86
  this.name = options.name;
83
87
  this.type = options.type;
84
88
  this.notNull = options.notNull;
85
89
  this.primaryKey = options.primaryKey;
86
90
  this.default = options.default;
91
+ this.codecRef = options.codecRef;
87
92
  Object.freeze(this);
88
93
  }
89
94
  }
@@ -1,3 +1,11 @@
1
+ /**
2
+ * A fully lowered SQL statement ready for a driver to execute: SQL text plus
3
+ * driver-ready (codec-encoded) parameter values. The output of the control
4
+ * adapter's `lowerToExecuteRequest`, handed to a driver via `SqlQueryable.query`
5
+ * or `execute`. Inline-substituted positions (e.g. DDL `DEFAULT`
6
+ * clauses) carry no param; `params` holds the wire values for the `$N`/`?`
7
+ * positions, in order.
8
+ */
1
9
  export interface SqlExecuteRequest {
2
10
  readonly sql: string;
3
11
  readonly params?: readonly unknown[];
package/src/ast/types.ts CHANGED
@@ -378,6 +378,35 @@ export class DerivedTableSource extends FromSource {
378
378
  }
379
379
  }
380
380
 
381
+ export class FunctionSource extends FromSource {
382
+ readonly kind = 'function-source' as const;
383
+ readonly fn: string;
384
+ readonly args: ReadonlyArray<AnyExpression>;
385
+ readonly alias: string | undefined;
386
+
387
+ protected constructor(fn: string, args: ReadonlyArray<AnyExpression>, alias?: string) {
388
+ super();
389
+ this.fn = fn;
390
+ this.args = frozenArrayCopy(args);
391
+ this.alias = alias;
392
+ this.freeze();
393
+ }
394
+
395
+ static of(fn: string, args: ReadonlyArray<AnyExpression>, alias?: string): FunctionSource {
396
+ return new FunctionSource(fn, args, alias);
397
+ }
398
+
399
+ override rewrite(rewriter: AstRewriter): AnyFromSource {
400
+ const rewrittenArgs = this.args.map((arg) => rewriteComparable(arg, rewriter));
401
+ if (rewrittenArgs.every((arg, i) => arg === this.args[i])) return this;
402
+ return new FunctionSource(this.fn, rewrittenArgs, this.alias);
403
+ }
404
+
405
+ override toFromSource(): AnyFromSource {
406
+ return this;
407
+ }
408
+ }
409
+
381
410
  export class ColumnRef extends Expression {
382
411
  readonly kind = 'column-ref' as const;
383
412
  readonly table: string;
@@ -1299,7 +1328,7 @@ export class ProjectionItem extends AstNode {
1299
1328
  export type LimitOffsetValue = number | AnyExpression;
1300
1329
 
1301
1330
  export interface SelectAstOptions {
1302
- readonly from: AnyFromSource;
1331
+ readonly from?: AnyFromSource;
1303
1332
  readonly joins: ReadonlyArray<JoinAst> | undefined;
1304
1333
  readonly projection: ReadonlyArray<ProjectionItem>;
1305
1334
  readonly where: AnyExpression | undefined;
@@ -1315,7 +1344,7 @@ export interface SelectAstOptions {
1315
1344
 
1316
1345
  export class SelectAst extends QueryAst {
1317
1346
  readonly kind = 'select' as const;
1318
- readonly from: AnyFromSource;
1347
+ readonly from: AnyFromSource | undefined;
1319
1348
  readonly joins: ReadonlyArray<JoinAst> | undefined;
1320
1349
  readonly projection: ReadonlyArray<ProjectionItem>;
1321
1350
  readonly where: AnyExpression | undefined;
@@ -1368,79 +1397,113 @@ export class SelectAst extends QueryAst {
1368
1397
  });
1369
1398
  }
1370
1399
 
1400
+ static noFrom(): SelectAst {
1401
+ return new SelectAst({
1402
+ joins: undefined,
1403
+ projection: [],
1404
+ where: undefined,
1405
+ orderBy: undefined,
1406
+ distinct: undefined,
1407
+ distinctOn: undefined,
1408
+ groupBy: undefined,
1409
+ having: undefined,
1410
+ limit: undefined,
1411
+ offset: undefined,
1412
+ selectAllIntent: undefined,
1413
+ });
1414
+ }
1415
+
1416
+ private toOptions(): SelectAstOptions {
1417
+ return {
1418
+ ...(this.from !== undefined ? { from: this.from } : {}),
1419
+ joins: this.joins,
1420
+ projection: this.projection,
1421
+ where: this.where,
1422
+ orderBy: this.orderBy,
1423
+ distinct: this.distinct,
1424
+ distinctOn: this.distinctOn,
1425
+ groupBy: this.groupBy,
1426
+ having: this.having,
1427
+ limit: this.limit,
1428
+ offset: this.offset,
1429
+ selectAllIntent: this.selectAllIntent,
1430
+ };
1431
+ }
1432
+
1371
1433
  withFrom(from: AnyFromSource): SelectAst {
1372
- return new SelectAst({ ...this, from });
1434
+ return new SelectAst({ ...this.toOptions(), from });
1373
1435
  }
1374
1436
 
1375
1437
  withJoins(joins: ReadonlyArray<JoinAst>): SelectAst {
1376
1438
  return new SelectAst({
1377
- ...this,
1439
+ ...this.toOptions(),
1378
1440
  joins: joins.length > 0 ? joins : undefined,
1379
1441
  });
1380
1442
  }
1381
1443
 
1382
1444
  withProjection(projection: ReadonlyArray<ProjectionItem>): SelectAst {
1383
- return new SelectAst({ ...this, projection });
1445
+ return new SelectAst({ ...this.toOptions(), projection });
1384
1446
  }
1385
1447
 
1386
1448
  addProjection(alias: string, expr: ProjectionExpr): SelectAst {
1387
1449
  return new SelectAst({
1388
- ...this,
1450
+ ...this.toOptions(),
1389
1451
  projection: [...this.projection, new ProjectionItem(alias, expr)],
1390
1452
  });
1391
1453
  }
1392
1454
 
1393
1455
  withWhere(where: AnyExpression | undefined): SelectAst {
1394
- return new SelectAst({ ...this, where });
1456
+ return new SelectAst({ ...this.toOptions(), where });
1395
1457
  }
1396
1458
 
1397
1459
  withOrderBy(orderBy: ReadonlyArray<OrderByItem>): SelectAst {
1398
1460
  return new SelectAst({
1399
- ...this,
1461
+ ...this.toOptions(),
1400
1462
  orderBy: orderBy.length > 0 ? orderBy : undefined,
1401
1463
  });
1402
1464
  }
1403
1465
 
1404
1466
  withDistinct(enabled = true): SelectAst {
1405
1467
  return new SelectAst({
1406
- ...this,
1468
+ ...this.toOptions(),
1407
1469
  distinct: enabled ? true : undefined,
1408
1470
  });
1409
1471
  }
1410
1472
 
1411
1473
  withDistinctOn(distinctOn: ReadonlyArray<AnyExpression>): SelectAst {
1412
1474
  return new SelectAst({
1413
- ...this,
1475
+ ...this.toOptions(),
1414
1476
  distinctOn: distinctOn.length > 0 ? distinctOn : undefined,
1415
1477
  });
1416
1478
  }
1417
1479
 
1418
1480
  withGroupBy(groupBy: ReadonlyArray<AnyExpression>): SelectAst {
1419
1481
  return new SelectAst({
1420
- ...this,
1482
+ ...this.toOptions(),
1421
1483
  groupBy: groupBy.length > 0 ? groupBy : undefined,
1422
1484
  });
1423
1485
  }
1424
1486
 
1425
1487
  withHaving(having: AnyExpression | undefined): SelectAst {
1426
- return new SelectAst({ ...this, having });
1488
+ return new SelectAst({ ...this.toOptions(), having });
1427
1489
  }
1428
1490
 
1429
1491
  withLimit(limit: LimitOffsetValue | undefined): SelectAst {
1430
- return new SelectAst({ ...this, limit });
1492
+ return new SelectAst({ ...this.toOptions(), limit });
1431
1493
  }
1432
1494
 
1433
1495
  withOffset(offset: LimitOffsetValue | undefined): SelectAst {
1434
- return new SelectAst({ ...this, offset });
1496
+ return new SelectAst({ ...this.toOptions(), offset });
1435
1497
  }
1436
1498
 
1437
1499
  withSelectAllIntent(selectAllIntent: { readonly table?: string } | undefined): SelectAst {
1438
- return new SelectAst({ ...this, selectAllIntent });
1500
+ return new SelectAst({ ...this.toOptions(), selectAllIntent });
1439
1501
  }
1440
1502
 
1441
1503
  rewrite(rewriter: AstRewriter): SelectAst {
1504
+ const rewrittenFrom = this.from?.rewrite(rewriter);
1442
1505
  const rewritten = new SelectAst({
1443
- from: this.from.rewrite(rewriter),
1506
+ ...(rewrittenFrom !== undefined ? { from: rewrittenFrom } : {}),
1444
1507
  joins: this.joins?.map((join) => join.rewrite(rewriter)),
1445
1508
  projection: this.projection.map(
1446
1509
  (projection) =>
@@ -1474,8 +1537,12 @@ export class SelectAst extends QueryAst {
1474
1537
  refs.push(...columns);
1475
1538
  };
1476
1539
 
1477
- if (this.from.kind === 'derived-table-source') {
1540
+ if (this.from?.kind === 'derived-table-source') {
1478
1541
  pushRefs(this.from.query.collectColumnRefs());
1542
+ } else if (this.from?.kind === 'function-source') {
1543
+ for (const arg of this.from.args) {
1544
+ pushRefs(arg.collectColumnRefs());
1545
+ }
1479
1546
  }
1480
1547
 
1481
1548
  for (const projection of this.projection) {
@@ -1502,6 +1569,10 @@ export class SelectAst extends QueryAst {
1502
1569
  for (const join of this.joins ?? []) {
1503
1570
  if (join.source.kind === 'derived-table-source') {
1504
1571
  pushRefs(join.source.query.collectColumnRefs());
1572
+ } else if (join.source.kind === 'function-source') {
1573
+ for (const arg of join.source.args) {
1574
+ pushRefs(arg.collectColumnRefs());
1575
+ }
1505
1576
  }
1506
1577
  if (join.on.kind === 'eq-col-join-on') {
1507
1578
  refs.push(join.on.left, join.on.right);
@@ -1525,8 +1596,12 @@ export class SelectAst extends QueryAst {
1525
1596
  refs.push(...params);
1526
1597
  };
1527
1598
 
1528
- if (this.from.kind === 'derived-table-source') {
1599
+ if (this.from?.kind === 'derived-table-source') {
1529
1600
  pushRefs(this.from.query.collectParamRefs());
1601
+ } else if (this.from?.kind === 'function-source') {
1602
+ for (const arg of this.from.args) {
1603
+ pushRefs(arg.collectParamRefs());
1604
+ }
1530
1605
  }
1531
1606
 
1532
1607
  for (const projection of this.projection) {
@@ -1553,6 +1628,10 @@ export class SelectAst extends QueryAst {
1553
1628
  for (const join of this.joins ?? []) {
1554
1629
  if (join.source.kind === 'derived-table-source') {
1555
1630
  pushRefs(join.source.query.collectParamRefs());
1631
+ } else if (join.source.kind === 'function-source') {
1632
+ for (const arg of join.source.args) {
1633
+ pushRefs(arg.collectParamRefs());
1634
+ }
1556
1635
  }
1557
1636
  if (!(join.on.kind === 'eq-col-join-on')) {
1558
1637
  pushRefs(join.on.collectParamRefs());
@@ -1899,7 +1978,7 @@ export class RawSqlExpr extends QueryAst {
1899
1978
  }
1900
1979
 
1901
1980
  export type AnyQueryAst = SelectAst | InsertAst | UpdateAst | DeleteAst | RawSqlExpr;
1902
- export type AnyFromSource = TableSource | DerivedTableSource;
1981
+ export type AnyFromSource = TableSource | DerivedTableSource | FunctionSource;
1903
1982
  export type AnyExpression =
1904
1983
  | ColumnRef
1905
1984
  | IdentifierRef
@@ -1,11 +1,7 @@
1
1
  import type { JsonValue } from '@prisma-next/contract/types';
2
2
  import type { CodecRef } from '@prisma-next/framework-components/codec';
3
3
  import { resolveStorageTable } from '@prisma-next/sql-contract/resolve-storage-table';
4
- import {
5
- isPostgresEnumStorageEntry,
6
- isStorageTypeInstance,
7
- type SqlStorage,
8
- } from '@prisma-next/sql-contract/types';
4
+ import { isStorageTypeInstance, type SqlStorage } from '@prisma-next/sql-contract/types';
9
5
 
10
6
  /**
11
7
  * Derive the canonical {@link CodecRef} for a `(table, column)` pair against a {@link SqlStorage}. This is the build-time path every column-bound `ParamRef` / `ProjectionItem` uses to stamp its `codec` slot before the AST is handed to the runtime — the runtime resolver then materialises a memoised {@link import('@prisma-next/sql-relational-core/ast').Codec} for the same `CodecRef` via `forCodecRef`.
@@ -34,38 +30,9 @@ export function codecRefForStorageColumn(
34
30
  const columnDef = tableDef.columns[columnName];
35
31
  if (!columnDef) return undefined;
36
32
  if (columnDef.typeRef !== undefined) {
37
- let instance: unknown = storage.types?.[columnDef.typeRef];
38
- if (!instance) {
39
- for (const ns of Object.values(storage.namespaces)) {
40
- const typeSlot = (ns.entries as { type?: Record<string, unknown> }).type;
41
- const nsEntry = typeSlot?.[columnDef.typeRef];
42
- if (nsEntry !== undefined) {
43
- instance = nsEntry;
44
- break;
45
- }
46
- }
47
- }
33
+ const instance = storage.types?.[columnDef.typeRef];
48
34
  if (!instance) return undefined;
49
- if (isPostgresEnumStorageEntry(instance)) {
50
- // Canonical path: the entry is a live `PostgresEnumType` IR
51
- // instance reached through the per-target serializer's
52
- // hydration. Raw JSON envelopes carrying `kind: 'postgres-enum'`
53
- // never reach this site — `SqlStorage.normaliseTypeEntry`
54
- // rejects them upstream (F09). Read `codecId` and `values` off
55
- // the structural shape (enumerable own properties on the live
56
- // instance) so the dispatch stays layered against the family
57
- // alphabet rather than a target-specific class import.
58
- return {
59
- codecId: instance.codecId,
60
- typeParams: { values: instance.values } as unknown as JsonValue,
61
- };
62
- }
63
35
  if (isStorageTypeInstance(instance)) {
64
- // Empty-state canonicalization: a `StorageTypeInstance` with absent
65
- // (or empty) `typeParams` produces a `CodecRef` with no `typeParams`
66
- // field. Equivalent to the non-parameterized-column branch below.
67
- // Carrying `{}` here would break content-keyed memoisation and trip
68
- // the runtime validator against non-parameterized codec descriptors.
69
36
  const instanceParams = instance.typeParams;
70
37
  const hasParamKeys = instanceParams !== undefined && Object.keys(instanceParams).length > 0;
71
38
  return hasParamKeys
@@ -1,5 +1,6 @@
1
1
  import type { ColumnDefaultLiteralInputValue } from '@prisma-next/contract/types';
2
2
  import type { ReferentialAction } from '@prisma-next/sql-contract/types';
3
+ import type { CodecRef } from '../ast/codec-types';
3
4
  import type { AnyDdlColumnDefault } from '../ast/ddl-types';
4
5
  import {
5
6
  DdlColumn,
@@ -14,6 +15,7 @@ export interface DdlColumnOptions {
14
15
  readonly notNull?: boolean;
15
16
  readonly primaryKey?: boolean;
16
17
  readonly default?: AnyDdlColumnDefault;
18
+ readonly codecRef?: CodecRef;
17
19
  }
18
20
 
19
21
  export function lit(value: ColumnDefaultLiteralInputValue): LiteralColumnDefault {
@@ -1,6 +1,8 @@
1
1
  export {
2
2
  CfConflictClause,
3
3
  CfExpr,
4
+ CfExprSelectQuery,
5
+ type CfFnOptions,
4
6
  CfInsertQuery,
5
7
  CfSelectQuery,
6
8
  CfUpdateQuery,
@@ -9,7 +11,10 @@ export {
9
11
  type ColumnDescriptor,
10
12
  type ColumnProxy,
11
13
  type ColumnSchema,
14
+ cfExpr,
15
+ cfTable,
12
16
  type ExcludedProxy,
17
+ exprSelect,
13
18
  type TableHandle,
14
19
  type TableInsertRow,
15
20
  type TableSetValues,
@@ -1,19 +1,28 @@
1
+ import type { ParamSpec } from '@prisma-next/operations';
1
2
  import { blindCast } from '@prisma-next/utils/casts';
2
3
  import {
4
+ AggregateExpr,
3
5
  AndExpr,
4
6
  type AnyExpression,
7
+ type AnyFromSource,
5
8
  BinaryExpr,
6
9
  ColumnRef,
10
+ ExistsExpr,
11
+ IdentifierRef,
7
12
  InsertAst,
8
13
  InsertOnConflict,
9
14
  type InsertValue,
15
+ JoinAst,
16
+ LiteralExpr,
10
17
  NullCheckExpr,
18
+ OperationExpr,
11
19
  OrderByItem,
12
20
  OrExpr,
13
21
  ParamRef,
14
22
  ProjectionItem,
23
+ RawExpr,
15
24
  SelectAst,
16
- type TableSource,
25
+ TableSource,
17
26
  UpdateAst,
18
27
  } from '../ast/types';
19
28
 
@@ -43,6 +52,187 @@ export class CfExpr {
43
52
  not(): CfExpr {
44
53
  return new CfExpr(this.ast.not());
45
54
  }
55
+
56
+ isNull(): CfExpr {
57
+ return new CfExpr(NullCheckExpr.isNull(this.ast));
58
+ }
59
+
60
+ isNotNull(): CfExpr {
61
+ return new CfExpr(NullCheckExpr.isNotNull(this.ast));
62
+ }
63
+
64
+ eqLit(value: number | string | boolean): CfExpr {
65
+ return new CfExpr(BinaryExpr.eq(this.ast, LiteralExpr.of(value)));
66
+ }
67
+
68
+ gtLit(value: number | string | boolean): CfExpr {
69
+ return new CfExpr(BinaryExpr.gt(this.ast, LiteralExpr.of(value)));
70
+ }
71
+
72
+ eqParam(value: unknown, codecId: string): CfExpr {
73
+ return new CfExpr(BinaryExpr.eq(this.ast, ParamRef.of(value, { codec: { codecId } })));
74
+ }
75
+
76
+ eqExpr(other: CfExpr): CfExpr {
77
+ return new CfExpr(BinaryExpr.eq(this.ast, other.ast));
78
+ }
79
+ }
80
+
81
+ export interface CfFnOptions {
82
+ readonly method: string;
83
+ readonly template: string;
84
+ readonly self: CfExpr;
85
+ readonly args?: ReadonlyArray<CfExpr>;
86
+ readonly returns: ParamSpec;
87
+ }
88
+
89
+ export const cfExpr = {
90
+ countStar(): CfExpr {
91
+ return new CfExpr(AggregateExpr.count());
92
+ },
93
+ lit(value: number | string | boolean): CfExpr {
94
+ return new CfExpr(LiteralExpr.of(value));
95
+ },
96
+ identifierRef(name: string): CfExpr {
97
+ return new CfExpr(IdentifierRef.of(name));
98
+ },
99
+ param(value: unknown, codecId: string): CfExpr {
100
+ return new CfExpr(ParamRef.of(value, { codec: { codecId } }));
101
+ },
102
+ /**
103
+ * Catalog function call lowered via a `'function'`-strategy template
104
+ * (e.g. `to_regclass({{self}})`). Owns the `OperationExpr` assembly so
105
+ * target packages only supply vocabulary: template, codec'd operands,
106
+ * and return spec.
107
+ */
108
+ fn(options: CfFnOptions): CfExpr {
109
+ return new CfExpr(
110
+ new OperationExpr({
111
+ method: options.method,
112
+ self: options.self.ast,
113
+ args: options.args?.map((arg) => arg.ast),
114
+ returns: options.returns,
115
+ lowering: {
116
+ targetFamily: 'sql',
117
+ strategy: 'function',
118
+ template: options.template,
119
+ },
120
+ }),
121
+ );
122
+ },
123
+ columnRef(qualifier: string, name: string): CfExpr {
124
+ return new CfExpr(ColumnRef.of(qualifier, name));
125
+ },
126
+ allOf(exprs: ReadonlyArray<CfExpr>): CfExpr {
127
+ return new CfExpr(AndExpr.of(exprs.map((expr) => expr.ast)));
128
+ },
129
+ /**
130
+ * Opaque DB-side SQL expression (e.g. `current_schema()`) carried as a
131
+ * `RawExpr`. For zero-operand catalog functions where a `'function'`
132
+ * lowering template has nothing to substitute.
133
+ */
134
+ raw(sql: string, returns: ParamSpec): CfExpr {
135
+ return new CfExpr(new RawExpr({ parts: [sql], returns }));
136
+ },
137
+ exists(query: CfExprSelectQuery): CfExpr {
138
+ return new CfExpr(ExistsExpr.exists(query.build()));
139
+ },
140
+ notExists(query: CfExprSelectQuery): CfExpr {
141
+ return new CfExpr(ExistsExpr.notExists(query.build()));
142
+ },
143
+ };
144
+
145
+ /** Aliased table source for catalog queries (no namespace coordinate). */
146
+ export function cfTable(name: string, alias?: string): TableSource {
147
+ return TableSource.named(name, alias);
148
+ }
149
+
150
+ export class CfExprSelectQuery {
151
+ constructor(
152
+ private readonly src: AnyFromSource | undefined,
153
+ private readonly projectionItems: ReadonlyArray<ProjectionItem>,
154
+ private readonly whereExpr: CfExpr | undefined,
155
+ private readonly joinItems: ReadonlyArray<JoinAst> = [],
156
+ private readonly limitValue: number | undefined = undefined,
157
+ ) {}
158
+
159
+ from(source: AnyFromSource): CfExprSelectQuery {
160
+ return new CfExprSelectQuery(
161
+ source,
162
+ this.projectionItems,
163
+ this.whereExpr,
164
+ this.joinItems,
165
+ this.limitValue,
166
+ );
167
+ }
168
+
169
+ join(source: AnyFromSource, on: CfExpr): CfExprSelectQuery {
170
+ return new CfExprSelectQuery(
171
+ this.src,
172
+ this.projectionItems,
173
+ this.whereExpr,
174
+ [...this.joinItems, JoinAst.inner(source, on.ast)],
175
+ this.limitValue,
176
+ );
177
+ }
178
+
179
+ leftJoin(source: AnyFromSource, on: CfExpr): CfExprSelectQuery {
180
+ return new CfExprSelectQuery(
181
+ this.src,
182
+ this.projectionItems,
183
+ this.whereExpr,
184
+ [...this.joinItems, JoinAst.left(source, on.ast)],
185
+ this.limitValue,
186
+ );
187
+ }
188
+
189
+ project(alias: string, expr: CfExpr): CfExprSelectQuery {
190
+ return new CfExprSelectQuery(
191
+ this.src,
192
+ [...this.projectionItems, ProjectionItem.of(alias, expr.ast)],
193
+ this.whereExpr,
194
+ this.joinItems,
195
+ this.limitValue,
196
+ );
197
+ }
198
+
199
+ where(expr: CfExpr): CfExprSelectQuery {
200
+ return new CfExprSelectQuery(
201
+ this.src,
202
+ this.projectionItems,
203
+ expr,
204
+ this.joinItems,
205
+ this.limitValue,
206
+ );
207
+ }
208
+
209
+ limit(value: number): CfExprSelectQuery {
210
+ return new CfExprSelectQuery(
211
+ this.src,
212
+ this.projectionItems,
213
+ this.whereExpr,
214
+ this.joinItems,
215
+ value,
216
+ );
217
+ }
218
+
219
+ build(): SelectAst {
220
+ if (this.joinItems.length > 0 && this.src === undefined) {
221
+ throw new Error('CfExprSelectQuery: cannot add a JOIN without a FROM clause');
222
+ }
223
+ const base =
224
+ this.src !== undefined
225
+ ? SelectAst.from(this.src).withProjection(this.projectionItems)
226
+ : SelectAst.noFrom().withProjection(this.projectionItems);
227
+ const withJoins = this.joinItems.length > 0 ? base.withJoins(this.joinItems) : base;
228
+ const withWhere =
229
+ this.whereExpr !== undefined ? withJoins.withWhere(this.whereExpr.ast) : withJoins;
230
+ return this.limitValue !== undefined ? withWhere.withLimit(this.limitValue) : withWhere;
231
+ }
232
+ }
233
+
234
+ export function exprSelect(): CfExprSelectQuery {
235
+ return new CfExprSelectQuery(undefined, [], undefined);
46
236
  }
47
237
 
48
238
  /**
@@ -10,6 +10,8 @@ export {
10
10
  export {
11
11
  CfConflictClause,
12
12
  CfExpr,
13
+ CfExprSelectQuery,
14
+ type CfFnOptions,
13
15
  CfInsertQuery,
14
16
  CfSelectQuery,
15
17
  CfUpdateQuery,
@@ -18,7 +20,10 @@ export {
18
20
  type ColumnDescriptor,
19
21
  type ColumnProxy,
20
22
  type ColumnSchema,
23
+ cfExpr,
24
+ cfTable,
21
25
  type ExcludedProxy,
26
+ exprSelect,
22
27
  type TableHandle,
23
28
  type TableInsertRow,
24
29
  type TableSetValues,