@vibeorm/sql 5.0.1 → 5.2.0

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/ast.d.ts CHANGED
@@ -10,7 +10,7 @@ import type { RlsReadinessQuery } from "./dialects/postgres-rls-readiness.ts";
10
10
  * grows with the runtime in P2/P3. Every shape added here needs a catalog case
11
11
  * (tests/catalog/cases.ts) in the same change.
12
12
  */
13
- import type { AdvisoryLockMode, RlsContextIR, RowLockStrength, RowLockWaitPolicy, ScalarType, SchemaIR, SqlFragmentSpec } from "@vibeorm/schema";
13
+ import type { AdvisoryLockMode, DateTruncationStorage, DateTruncationUnit, RlsContextIR, RowLockStrength, RowLockWaitPolicy, ScalarType, SchemaIR, SqlFragmentSpec } from "@vibeorm/schema";
14
14
  /** A column reference, optionally table-qualified. */
15
15
  export type ColRef = {
16
16
  readonly table?: string;
@@ -45,6 +45,8 @@ export type ColRef = {
45
45
  * storage and read-back stay the exact text.
46
46
  */
47
47
  readonly castReal?: boolean;
48
+ /** Native enum order key through a scalar subquery: MySQL reads its member index, other dialects keep the column. */
49
+ readonly enumOrdinal?: true;
48
50
  /**
49
51
  * Order/distinct/compare cast, postgres `json` columns only (`Json @db.Json`,
50
52
  * release-3.0 D3): `json` has no ordering or equality operator, so ORDER BY,
@@ -53,6 +55,17 @@ export type ColRef = {
53
55
  * column. Never set on projections, so read-back keeps the stored text.
54
56
  */
55
57
  readonly castJsonb?: "jsonb" | "jsonb[]";
58
+ /**
59
+ * Calendar truncation of a timestamp column, for a `groupBy` date key. The
60
+ * reference renders as the start of its period in a named time zone
61
+ * (`date_trunc('day', "col", 'Europe/Oslo')`; see `renderDateTruncation`),
62
+ * in the column's own SQL type. Set on the projection, GROUP BY and ORDER BY
63
+ * refs of one key, which then repeat the identical expression text
64
+ * (PostgreSQL matches GROUP BY expressions by structure); a projection keeps
65
+ * `alias ?? name` as its output name. Never set on filter refs. Dialects
66
+ * without the `dateTruncation` capability refuse it when rendering.
67
+ */
68
+ readonly truncate?: DateTruncation;
56
69
  /**
57
70
  * Projection alias: renders `ref AS "alias"`. PROJECTION-ONLY — filter,
58
71
  * order and correlation refs never set it (the renderer ignores it outside
@@ -61,6 +74,13 @@ export type ColRef = {
61
74
  */
62
75
  readonly alias?: string;
63
76
  };
77
+ /** A {@link ColRef.truncate} request: the calendar unit, the zone that defines the calendar, and the column's storage form. */
78
+ export type DateTruncation = {
79
+ readonly unit: DateTruncationUnit;
80
+ /** A time-zone name (`"Europe/Oslo"`, `"UTC"`), rendered as a string literal after a strict character check. */
81
+ readonly timeZone: string;
82
+ readonly storage: DateTruncationStorage;
83
+ };
64
84
  export type TableRef = {
65
85
  readonly name: string;
66
86
  /** Explicit fixed PostgreSQL placement; absence preserves existing SQL. */
@@ -75,6 +95,8 @@ export type AggRef = {
75
95
  } | {
76
96
  readonly fn: AggFn;
77
97
  readonly column: ColRef;
98
+ /** Native enum MIN/MAX: projections and HAVING return the database label; order keys keep its ordinal. */
99
+ readonly enumOrder?: "value" | "ordinal";
78
100
  };
79
101
  /**
80
102
  * One aggregate projection: `FN("col") AS "alias"`. The alias is part of the
@@ -445,18 +467,27 @@ export type OrderByAggregate = {
445
467
  };
446
468
  /**
447
469
  * ORDER BY a correlated scalar subquery — relation orderBy
448
- * (`orderBy: { posts: { _count: "desc" } }` → `(SELECT COUNT(*) …)` /
470
+ * (`orderBy: { posts: { _count: "desc" } }` → `(SELECT COUNT(*) …)`,
471
+ * `orderBy: { posts: { _max: { createdAt: "desc" } } }` → `(SELECT MAX(…) …)`,
449
472
  * `orderBy: { author: { email: "asc" } }` → `(SELECT "email" …)`).
450
473
  * Portable: all three dialects order by scalar subqueries. The subquery
451
474
  * correlates to the outer scope through table-qualified `cmpCol` predicates,
452
475
  * exactly like `exists`; its parameters merge into the statement's single
453
- * sequence in render order. NULLS placement is deliberately unsupported here
454
- * (emulating it would render the subquery twice and duplicate its params) —
455
- * missing relations sort at each engine's native NULL position.
476
+ * sequence in render order.
477
+ *
478
+ * `nulls` places the NULL a missing to-one row or a parent without related
479
+ * rows (under MIN/MAX/AVG/SUM) sorts as. Without it, NULL sorts at the
480
+ * engine's native position. Postgres and sqlite render `NULLS FIRST|LAST`;
481
+ * mysql renders the bare term when its native placement already matches, and
482
+ * otherwise a leading `(<subquery>) IS NULL` term — the subquery then renders
483
+ * twice and its parameters bind twice, in text order.
456
484
  */
457
485
  export type OrderBySubquery = {
458
486
  readonly select: SelectStatement;
487
+ /** A count behind a missing to-one row is zero, not NULL. */
488
+ readonly coalesce?: 0;
459
489
  readonly direction: "asc" | "desc";
490
+ readonly nulls?: "first" | "last";
460
491
  };
461
492
  export type OrderTerm = OrderByColumn | OrderByAggregate | OrderBySubquery;
462
493
  /**
@@ -501,6 +532,17 @@ export type LateralJoin = {
501
532
  /** The correlated child select — may reference outer columns via table-qualified refs. */
502
533
  readonly select: SelectStatement;
503
534
  };
535
+ /**
536
+ * One scalar subquery in a projection. The builder guarantees a single column
537
+ * and at most one row. Hidden sort values stay inside derived tables rather
538
+ * than becoming result fields. `coalesce` preserves a count across a missing
539
+ * to-one hop.
540
+ */
541
+ export type ScalarSubquery = {
542
+ readonly alias: string;
543
+ readonly select: SelectStatement;
544
+ readonly coalesce?: 0;
545
+ };
504
546
  /**
505
547
  * One correlated scalar `COUNT(*)` projected into a select list:
506
548
  * `(SELECT COUNT(*) FROM … WHERE <correlation>) AS "alias"` — the relation
@@ -747,6 +789,8 @@ export type SelectStatement = {
747
789
  readonly aggregates?: readonly AggSelect[];
748
790
  /** Correlated `COUNT(*)` subqueries, rendered after `aggregates` — see {@link CountSubquery}. */
749
791
  readonly countSubqueries?: readonly CountSubquery[];
792
+ /** Scalar values projected after count subqueries and before a row number. */
793
+ readonly scalarSubqueries?: readonly ScalarSubquery[];
750
794
  /**
751
795
  * `SELECT DISTINCT ON (cols)` — postgres only (`capabilities.distinctOn`;
752
796
  * other dialects refuse at render time and the runtime emulates client-side
@@ -969,6 +1013,16 @@ export type OnConflictUpdate = {
969
1013
  */
970
1014
  readonly conflictWhere?: string;
971
1015
  readonly update: Readonly<Record<string, unknown>>;
1016
+ /**
1017
+ * Guard on the update arm, BOUND parameters: `ON CONFLICT (…) DO UPDATE SET
1018
+ * … WHERE <guard>` — the conflicting row is updated (and returned) only when
1019
+ * the guard admits it; otherwise the statement writes and returns nothing.
1020
+ * Postgres has both the target row and `excluded` in scope there, so every
1021
+ * column ref must carry `table` (the target table's name), exactly as the
1022
+ * SET self-references do. Needs the `conflictUpdateWhere` capability:
1023
+ * mysql's ODKU has no WHERE and refuses it (`VIBE_UNSUPPORTED_CAPABILITY`).
1024
+ */
1025
+ readonly updateWhere?: SqlExpr;
972
1026
  /**
973
1027
  * MySQL only (`upsertForm: "onDuplicateKey"`): append
974
1028
  * `` `col` = LAST_INSERT_ID(`col`) `` to the update list so the UPDATE arm
@@ -0,0 +1,27 @@
1
+ import type { DateTruncation } from "./ast.ts";
2
+ import type { SqlDialect } from "./dialect.ts";
3
+ /** Is this value a time-zone name the renderer will spell as a literal? (See {@link TIME_ZONE_NAME}.) */
4
+ export declare function isTimeZoneName(params: {
5
+ readonly value: unknown;
6
+ }): params is {
7
+ readonly value: string;
8
+ };
9
+ /**
10
+ * The start of the period that holds `reference`'s value, in the column's own
11
+ * SQL type, so the field's codec decodes it unchanged:
12
+ *
13
+ * - `timestamptz` → `date_trunc('day', "col", 'Europe/Oslo')` — the 3-argument
14
+ * form (PostgreSQL 12 or later) truncates in the named zone, never in the
15
+ * session's `TimeZone`;
16
+ * - `timestamp` (zone-less, UTC wall-clock by contract) →
17
+ * `(date_trunc('day', "col" AT TIME ZONE 'UTC', 'Europe/Oslo') AT TIME ZONE 'UTC')`.
18
+ *
19
+ * The unit and the zone are literals, so every site of one key (projection,
20
+ * GROUP BY, ORDER BY) repeats the identical text and binds no parameter.
21
+ */
22
+ export declare function renderDateTruncation(params: {
23
+ readonly dialect: SqlDialect;
24
+ readonly reference: string;
25
+ readonly truncation: DateTruncation;
26
+ }): string;
27
+ //# sourceMappingURL=date-truncation.d.ts.map
package/dist/dialect.d.ts CHANGED
@@ -446,6 +446,16 @@ export type SqlDialect = {
446
446
  quoteStringLiteral(params: {
447
447
  value: string;
448
448
  }): string;
449
+ /** Keep a native enum's ordering through a scalar subquery that may lose its physical enum type. */
450
+ renderEnumOrdinal(params: {
451
+ column: string;
452
+ }): string;
453
+ /** Render native enum MIN/MAX in database declaration order, returning its label or an order key. */
454
+ renderEnumMinMax(params: {
455
+ fn: "min" | "max";
456
+ column: string;
457
+ result: "value" | "ordinal";
458
+ }): string;
449
459
  /** Positional placeholder for the 1-based parameter index ($n or ?). */
450
460
  placeholder(params: {
451
461
  index: number;
package/dist/index.d.ts CHANGED
@@ -40,7 +40,7 @@ export type { ResultDecoder, ResultReadDecoder, ResultParameterEncoder, ResultSc
40
40
  * lives in this package; see the root sql-catalog.md for the reviewable surface.
41
41
  */
42
42
  export { compileExclusion, renderExclusionSetup, renderExclusionDdl, renderExclusionCatalog, renderExclusionProbe } from "./exclusion.ts";
43
- export type { AdvisoryLockKey, AdvisoryLockStatement, AggFn, AggRef, AggSelect, ChainResult, ChainRowSource, ChainStatement, ChainStep, ChainStepStatement, ChainTally, ChainVeto, ColRef, ComparisonOp, CountSubquery, DeleteStatement, ExcludedRef, InnerJoin, KeyListMember, KeyPage, KeyRows, KeyTableJoin, KeyTableMember, InsertSelectStatement, InsertStatement, ReturningColumn, JsonFilterOp, LateralJoin, LeftJoin, ListSetOperation, OnConflictClause, OnConflictDoNothing, OnConflictUpdate, OrderByAggregate, OrderByColumn, OrderBySubquery, OrderTerm, RowLock, RowNumberProjection, DbNowMarker, JsonParamCell, SelectExpr, SelectStatement, SetOperation, SetOperator, SqlExpr, SqlStatement, StepColumnRef, TableRef, UpdateStatement, } from "./ast.ts";
43
+ export type { AdvisoryLockKey, AdvisoryLockStatement, AggFn, AggRef, AggSelect, ChainResult, ChainRowSource, ChainStatement, ChainStep, ChainStepStatement, ChainTally, ChainVeto, ColRef, ComparisonOp, DateTruncation, CountSubquery, ScalarSubquery, DeleteStatement, ExcludedRef, InnerJoin, KeyListMember, KeyPage, KeyRows, KeyTableJoin, KeyTableMember, InsertSelectStatement, InsertStatement, ReturningColumn, JsonFilterOp, LateralJoin, LeftJoin, ListSetOperation, OnConflictClause, OnConflictDoNothing, OnConflictUpdate, OrderByAggregate, OrderByColumn, OrderBySubquery, OrderTerm, RowLock, RowNumberProjection, DbNowMarker, JsonParamCell, SelectExpr, SelectStatement, SetOperation, SetOperator, SqlExpr, SqlStatement, StepColumnRef, TableRef, UpdateStatement, } from "./ast.ts";
44
44
  export { ADVISORY_LOCK_RESULT_COLUMN, CHAIN_VETO_PREFIX, CHAIN_VETO_TOKEN_PATTERN, SQL_DEFAULT_CELL, SQL_KEEP_CELL, chainVetoToken, dbNowMarker, excludedRef, isDbNowMarker, isExcludedRef, isJsonParamCell, isListSetOperation, isSetOperation, isSqlDefaultCell, isSqlKeepCell, isStepColumnRef, jsonParamCell, returningColumnName, stepColumnRef, } from "./ast.ts";
45
45
  export type { AdvisoryLockFunctions, AdvisoryLockModeFunctions, RowLockSyntax, EmptyInsertSyntax, SqlDialect, } from "./dialect.ts";
46
46
  export { ADVISORY_KEY_BOUNDS, describeRowLock, renderAdvisoryLock, renderRowLockClause } from "./locks.ts";
@@ -53,6 +53,7 @@ export { tableDefinitionParts } from "./dialects/sqlite-table-definition.ts";
53
53
  export { keyListBindsUntypedRows, postgresBulkInsertArrayCastOf, postgresKeyListElementTypeOf } from "./key-list.ts";
54
54
  export { renderPredicate, renderSchemaExpression } from "./predicate.ts";
55
55
  export { renderLocalDateCheck } from "./local-date.ts";
56
+ export { isTimeZoneName, renderDateTruncation } from "./date-truncation.ts";
56
57
  export { renderGeneratedColumnClause } from "./generated-columns.ts";
57
58
  export type { GeneratedColumnDeclaration, ExtractedGeneratedColumn } from "./generated-columns.ts";
58
59
  export { renderPostgresGeneratedColumnProbe, renderPostgresDropGeneratedExpression } from "./dialects/postgres-generated-columns.ts";