@vibeorm/sql 5.1.0 → 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 +30 -4
- package/dist/dialect.d.ts +10 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +48 -14
- package/dist/index.js.map +7 -7
- package/package.json +2 -2
package/dist/ast.d.ts
CHANGED
|
@@ -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,
|
|
@@ -93,6 +95,8 @@ export type AggRef = {
|
|
|
93
95
|
} | {
|
|
94
96
|
readonly fn: AggFn;
|
|
95
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";
|
|
96
100
|
};
|
|
97
101
|
/**
|
|
98
102
|
* One aggregate projection: `FN("col") AS "alias"`. The alias is part of the
|
|
@@ -463,18 +467,27 @@ export type OrderByAggregate = {
|
|
|
463
467
|
};
|
|
464
468
|
/**
|
|
465
469
|
* ORDER BY a correlated scalar subquery — relation orderBy
|
|
466
|
-
* (`orderBy: { posts: { _count: "desc" } }` → `(SELECT COUNT(*) …)
|
|
470
|
+
* (`orderBy: { posts: { _count: "desc" } }` → `(SELECT COUNT(*) …)`,
|
|
471
|
+
* `orderBy: { posts: { _max: { createdAt: "desc" } } }` → `(SELECT MAX(…) …)`,
|
|
467
472
|
* `orderBy: { author: { email: "asc" } }` → `(SELECT "email" …)`).
|
|
468
473
|
* Portable: all three dialects order by scalar subqueries. The subquery
|
|
469
474
|
* correlates to the outer scope through table-qualified `cmpCol` predicates,
|
|
470
475
|
* exactly like `exists`; its parameters merge into the statement's single
|
|
471
|
-
* sequence in render order.
|
|
472
|
-
*
|
|
473
|
-
* missing
|
|
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.
|
|
474
484
|
*/
|
|
475
485
|
export type OrderBySubquery = {
|
|
476
486
|
readonly select: SelectStatement;
|
|
487
|
+
/** A count behind a missing to-one row is zero, not NULL. */
|
|
488
|
+
readonly coalesce?: 0;
|
|
477
489
|
readonly direction: "asc" | "desc";
|
|
490
|
+
readonly nulls?: "first" | "last";
|
|
478
491
|
};
|
|
479
492
|
export type OrderTerm = OrderByColumn | OrderByAggregate | OrderBySubquery;
|
|
480
493
|
/**
|
|
@@ -519,6 +532,17 @@ export type LateralJoin = {
|
|
|
519
532
|
/** The correlated child select — may reference outer columns via table-qualified refs. */
|
|
520
533
|
readonly select: SelectStatement;
|
|
521
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
|
+
};
|
|
522
546
|
/**
|
|
523
547
|
* One correlated scalar `COUNT(*)` projected into a select list:
|
|
524
548
|
* `(SELECT COUNT(*) FROM … WHERE <correlation>) AS "alias"` — the relation
|
|
@@ -765,6 +789,8 @@ export type SelectStatement = {
|
|
|
765
789
|
readonly aggregates?: readonly AggSelect[];
|
|
766
790
|
/** Correlated `COUNT(*)` subqueries, rendered after `aggregates` — see {@link CountSubquery}. */
|
|
767
791
|
readonly countSubqueries?: readonly CountSubquery[];
|
|
792
|
+
/** Scalar values projected after count subqueries and before a row number. */
|
|
793
|
+
readonly scalarSubqueries?: readonly ScalarSubquery[];
|
|
768
794
|
/**
|
|
769
795
|
* `SELECT DISTINCT ON (cols)` — postgres only (`capabilities.distinctOn`;
|
|
770
796
|
* other dialects refuse at render time and the runtime emulates client-side
|
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, DateTruncation, 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";
|
package/dist/index.js
CHANGED
|
@@ -2402,6 +2402,12 @@ var sqliteDialect = {
|
|
|
2402
2402
|
quoteStringLiteral({ value }) {
|
|
2403
2403
|
return `'${value.replaceAll("'", "''")}'`;
|
|
2404
2404
|
},
|
|
2405
|
+
renderEnumOrdinal({ column }) {
|
|
2406
|
+
return column;
|
|
2407
|
+
},
|
|
2408
|
+
renderEnumMinMax({ fn, column }) {
|
|
2409
|
+
return `${fn === "min" ? "MIN" : "MAX"}(${column})`;
|
|
2410
|
+
},
|
|
2405
2411
|
placeholder() {
|
|
2406
2412
|
return "?";
|
|
2407
2413
|
},
|
|
@@ -3075,6 +3081,12 @@ var postgresDialect = {
|
|
|
3075
3081
|
quoteStringLiteral({ value }) {
|
|
3076
3082
|
return `'${value.replaceAll("'", "''")}'`;
|
|
3077
3083
|
},
|
|
3084
|
+
renderEnumOrdinal({ column }) {
|
|
3085
|
+
return column;
|
|
3086
|
+
},
|
|
3087
|
+
renderEnumMinMax({ fn, column }) {
|
|
3088
|
+
return `${fn === "min" ? "MIN" : "MAX"}(${column})`;
|
|
3089
|
+
},
|
|
3078
3090
|
placeholder({ index }) {
|
|
3079
3091
|
return `$${index}`;
|
|
3080
3092
|
},
|
|
@@ -3148,6 +3160,15 @@ var mysqlDialect = {
|
|
|
3148
3160
|
quoteStringLiteral({ value }) {
|
|
3149
3161
|
return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "''")}'`;
|
|
3150
3162
|
},
|
|
3163
|
+
renderEnumOrdinal({ column }) {
|
|
3164
|
+
return `${column} + 0`;
|
|
3165
|
+
},
|
|
3166
|
+
renderEnumMinMax({ fn, column, result }) {
|
|
3167
|
+
const aggregate = fn === "min" ? "MIN" : "MAX";
|
|
3168
|
+
if (result === "ordinal")
|
|
3169
|
+
return `${aggregate}(${column} + 0)`;
|
|
3170
|
+
return `SUBSTRING(${aggregate}(CONCAT(LPAD(${column} + 0, 5, '0'), ${column})), 6)`;
|
|
3171
|
+
},
|
|
3151
3172
|
placeholder() {
|
|
3152
3173
|
return "?";
|
|
3153
3174
|
},
|
|
@@ -4129,6 +4150,8 @@ function quoteCol(dialect, column) {
|
|
|
4129
4150
|
const ref = column.table === undefined ? name : `${dialect.quoteIdent({ name: column.table })}.${name}`;
|
|
4130
4151
|
if (column.truncate !== undefined)
|
|
4131
4152
|
return renderDateTruncation({ dialect, reference: ref, truncation: column.truncate });
|
|
4153
|
+
if (column.enumOrdinal === true)
|
|
4154
|
+
return dialect.renderEnumOrdinal({ column: ref });
|
|
4132
4155
|
if (column.castReal === true) {
|
|
4133
4156
|
if (dialect.name !== "sqlite") {
|
|
4134
4157
|
throw internalError4({ message: "ColRef.castReal is a sqlite Decimal construct" });
|
|
@@ -4229,7 +4252,11 @@ var AGG_FN_SQL = {
|
|
|
4229
4252
|
function renderAggRef(dialect, agg) {
|
|
4230
4253
|
if (agg.fn === "countAll")
|
|
4231
4254
|
return "COUNT(*)";
|
|
4232
|
-
|
|
4255
|
+
const column = quoteCol(dialect, agg.column);
|
|
4256
|
+
if ((agg.fn === "min" || agg.fn === "max") && agg.enumOrder !== undefined) {
|
|
4257
|
+
return dialect.renderEnumMinMax({ fn: agg.fn, column, result: agg.enumOrder });
|
|
4258
|
+
}
|
|
4259
|
+
return `${AGG_FN_SQL[agg.fn]}(${column})`;
|
|
4233
4260
|
}
|
|
4234
4261
|
function assertScalarArrays(dialect, feature) {
|
|
4235
4262
|
if (dialect.capabilities.scalarArrays)
|
|
@@ -4770,23 +4797,20 @@ function renderOrderTerm(params) {
|
|
|
4770
4797
|
if ("agg" in term) {
|
|
4771
4798
|
return `${renderAggRef(dialect, term.agg)} ${term.direction === "asc" ? "ASC" : "DESC"}`;
|
|
4772
4799
|
}
|
|
4773
|
-
|
|
4774
|
-
const sub = renderSelect({ dialect, statement: term.select, collector });
|
|
4775
|
-
return `(${sub.text}) ${term.direction === "asc" ? "ASC" : "DESC"}`;
|
|
4776
|
-
}
|
|
4777
|
-
const column = quoteCol(dialect, term.column);
|
|
4800
|
+
const expression = "select" in term ? () => renderScalarSubquery({ dialect, subquery: term, collector }) : () => quoteCol(dialect, term.column);
|
|
4778
4801
|
const direction = term.direction === "asc" ? "ASC" : "DESC";
|
|
4779
4802
|
if (term.nulls === undefined) {
|
|
4780
|
-
return `${
|
|
4803
|
+
return `${expression()} ${direction}`;
|
|
4781
4804
|
}
|
|
4782
4805
|
if (dialect.orderByNullsMode === "native") {
|
|
4783
|
-
return `${
|
|
4806
|
+
return `${expression()} ${direction} NULLS ${term.nulls === "first" ? "FIRST" : "LAST"}`;
|
|
4784
4807
|
}
|
|
4785
4808
|
if (term.nulls === "first" === (term.direction === "asc")) {
|
|
4786
|
-
return `${
|
|
4809
|
+
return `${expression()} ${direction}`;
|
|
4787
4810
|
}
|
|
4788
|
-
const
|
|
4789
|
-
|
|
4811
|
+
const tested = expression();
|
|
4812
|
+
const nullsTerm = term.nulls === "first" ? `${tested} IS NULL DESC` : `${tested} IS NULL`;
|
|
4813
|
+
return `${nullsTerm}, ${expression()} ${direction}`;
|
|
4790
4814
|
}
|
|
4791
4815
|
function renderSelectExpr(params) {
|
|
4792
4816
|
const { dialect, expr } = params;
|
|
@@ -4837,7 +4861,7 @@ function renderLateralJoin(params) {
|
|
|
4837
4861
|
const json = lateralJsonOf({ dialect });
|
|
4838
4862
|
let aggOrder = "";
|
|
4839
4863
|
if (json.order === "inAggregate" && join.agg === "jsonArray" && join.select.orderBy !== undefined && join.select.orderBy.length > 0) {
|
|
4840
|
-
const projected = new Set(join.select.columns.filter((column) => column.transform === undefined && column.castReal !== true).map((column) => column.name));
|
|
4864
|
+
const projected = new Set(join.select.columns.filter((column) => column.transform === undefined && column.castReal !== true).map((column) => column.name).concat((join.select.scalarSubqueries ?? []).map((subquery) => subquery.alias)));
|
|
4841
4865
|
const terms = join.select.orderBy;
|
|
4842
4866
|
const columnTerms = terms.filter((term) => ("column" in term));
|
|
4843
4867
|
if (columnTerms.length === terms.length && columnTerms.every((term) => projected.has(term.column.name))) {
|
|
@@ -4863,12 +4887,17 @@ function renderLateralJoin(params) {
|
|
|
4863
4887
|
function renderCountSubquery(params) {
|
|
4864
4888
|
const { dialect, subquery, collector } = params;
|
|
4865
4889
|
const select = subquery.select;
|
|
4866
|
-
if (select.columns.length !== 0 || select.selectExpr?.kind !== "countAll" || (select.aggregates?.length ?? 0) > 0 || select.rowNumber !== undefined || select.groupBy !== undefined || select.having !== undefined || select.limit !== undefined || select.offset !== undefined || select.distinctOn !== undefined || select.distinct !== undefined || select.rowLock !== undefined || select.countSubqueries !== undefined) {
|
|
4890
|
+
if (select.columns.length !== 0 || select.selectExpr?.kind !== "countAll" || (select.aggregates?.length ?? 0) > 0 || select.rowNumber !== undefined || select.groupBy !== undefined || select.having !== undefined || select.limit !== undefined || select.offset !== undefined || select.distinctOn !== undefined || select.distinct !== undefined || select.rowLock !== undefined || select.countSubqueries !== undefined || select.scalarSubqueries !== undefined) {
|
|
4867
4891
|
throw internalError4({ message: "count subquery must be one ungrouped COUNT(*)" });
|
|
4868
4892
|
}
|
|
4869
4893
|
const inner = renderSelect({ dialect, statement: select, collector });
|
|
4870
4894
|
return `(${inner.text}) AS ${dialect.quoteIdent({ name: subquery.alias })}`;
|
|
4871
4895
|
}
|
|
4896
|
+
function renderScalarSubquery(params) {
|
|
4897
|
+
const { dialect, subquery, collector } = params;
|
|
4898
|
+
const value = `(${renderSelect({ dialect, statement: subquery.select, collector }).text})`;
|
|
4899
|
+
return subquery.coalesce === 0 ? `COALESCE(${value}, 0)` : value;
|
|
4900
|
+
}
|
|
4872
4901
|
function applyReturning(params) {
|
|
4873
4902
|
const { dialect, requested } = params;
|
|
4874
4903
|
if (requested === undefined || requested.length === 0) {
|
|
@@ -4977,6 +5006,11 @@ function renderSelect(params) {
|
|
|
4977
5006
|
columnList.push(renderCountSubquery({ dialect, subquery, collector }));
|
|
4978
5007
|
}
|
|
4979
5008
|
}
|
|
5009
|
+
if (statement.scalarSubqueries !== undefined) {
|
|
5010
|
+
for (const subquery of statement.scalarSubqueries) {
|
|
5011
|
+
columnList.push(`${renderScalarSubquery({ dialect, subquery, collector })} AS ${dialect.quoteIdent({ name: subquery.alias })}`);
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
4980
5014
|
if (statement.rowNumber !== undefined) {
|
|
4981
5015
|
const rowNumber = statement.rowNumber;
|
|
4982
5016
|
if (rowNumber.partitionBy.length === 0 && rowNumber.orderBy.length === 0) {
|
|
@@ -6380,4 +6414,4 @@ export {
|
|
|
6380
6414
|
toPgArrayLiteral
|
|
6381
6415
|
};
|
|
6382
6416
|
|
|
6383
|
-
//# debugId=
|
|
6417
|
+
//# debugId=7A09C0BE2E07A46664756E2164756E21
|