@vibeorm/sql 5.2.0 → 5.4.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 +22 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +46 -2
- package/dist/index.js.map +5 -5
- package/dist/locks.d.ts +12 -1
- package/dist/query/index.js.map +1 -1
- package/package.json +2 -2
package/dist/ast.d.ts
CHANGED
|
@@ -868,10 +868,21 @@ export type SelectStatement = {
|
|
|
868
868
|
* The lock a locking read takes: one strength, one wait policy. Both members
|
|
869
869
|
* are REQUIRED here — defaulting happens once, in the query builder, so the
|
|
870
870
|
* rendered SQL is a function of the AST alone.
|
|
871
|
+
*
|
|
872
|
+
* `of` narrows the lock to named FROM items (`FOR UPDATE OF "Parent"`). The
|
|
873
|
+
* join relation strategy needs it: its statement also reads child rows through
|
|
874
|
+
* `LEFT JOIN LATERAL` subqueries, and an unnarrowed clause is refused by
|
|
875
|
+
* postgres ("FOR UPDATE is not allowed with aggregate functions", from the
|
|
876
|
+
* children's `json_agg`). With `of` naming the statement's own table only the
|
|
877
|
+
* parent rows are locked. Each name must be the statement's table alias, or its
|
|
878
|
+
* unqualified table name when it has no alias — the only FROM item the AST
|
|
879
|
+
* names; the renderer refuses anything else. Dialects without a locking-read
|
|
880
|
+
* clause render nothing, as for a lock without `of`.
|
|
871
881
|
*/
|
|
872
882
|
export type RowLock = {
|
|
873
883
|
readonly strength: RowLockStrength;
|
|
874
884
|
readonly wait: RowLockWaitPolicy;
|
|
885
|
+
readonly of?: readonly string[];
|
|
875
886
|
};
|
|
876
887
|
/**
|
|
877
888
|
* Sentinel VALUES cell: render the `DEFAULT` keyword instead of a placeholder
|
|
@@ -1172,6 +1183,12 @@ export type AdvisoryLockStatement = {
|
|
|
1172
1183
|
readonly wait: "wait" | "nowait";
|
|
1173
1184
|
readonly key: AdvisoryLockKey;
|
|
1174
1185
|
};
|
|
1186
|
+
/** An ordered set of transaction-scoped bigint advisory locks. */
|
|
1187
|
+
export type AdvisoryLocksStatement = {
|
|
1188
|
+
readonly kind: "advisoryLocks";
|
|
1189
|
+
readonly keys: readonly bigint[];
|
|
1190
|
+
readonly mode: AdvisoryLockMode;
|
|
1191
|
+
};
|
|
1175
1192
|
/** The column an advisory try-lock projects its verdict into. */
|
|
1176
1193
|
export declare const ADVISORY_LOCK_RESULT_COLUMN: string;
|
|
1177
1194
|
/**
|
|
@@ -1290,6 +1307,7 @@ export type ChainStep = {
|
|
|
1290
1307
|
* verdict-row outcome (the upsert fold always rolls it back).
|
|
1291
1308
|
*/
|
|
1292
1309
|
export type ChainResult = {
|
|
1310
|
+
/** Both lists may be empty for a tally-only result: one count row, no model values. */
|
|
1293
1311
|
readonly rowsFrom: readonly string[];
|
|
1294
1312
|
readonly columns: readonly string[];
|
|
1295
1313
|
readonly verdict?: {
|
|
@@ -1322,12 +1340,15 @@ export type ChainResult = {
|
|
|
1322
1340
|
* `minRows` (EPIC W) makes the veto count-sensitive: the statement fails when
|
|
1323
1341
|
* `step` returned FEWER rows than that (a multi-row INSERT a trigger shortened).
|
|
1324
1342
|
* Omitted or 1 = "no row". A positive safe integer, rendered as SQL text.
|
|
1343
|
+
* `exactRows` instead requires equality, including zero. It is a nonnegative
|
|
1344
|
+
* safe integer and cannot combine with `minRows`.
|
|
1325
1345
|
*/
|
|
1326
1346
|
export type ChainVeto = {
|
|
1327
1347
|
readonly step: string;
|
|
1328
1348
|
readonly token: string;
|
|
1329
1349
|
readonly nonce: string;
|
|
1330
1350
|
readonly minRows?: number;
|
|
1351
|
+
readonly exactRows?: number;
|
|
1331
1352
|
};
|
|
1332
1353
|
/** Count the RETURNING rows of `steps` into the result column `alias`. */
|
|
1333
1354
|
export type ChainTally = {
|
|
@@ -1367,7 +1388,7 @@ export type ChainStatement = {
|
|
|
1367
1388
|
readonly steps: readonly ChainStep[];
|
|
1368
1389
|
readonly result: ChainResult;
|
|
1369
1390
|
};
|
|
1370
|
-
export type SqlStatement = SelectStatement | InsertStatement | UpdateStatement | DeleteStatement | RlsEnableStatement | RlsForceStatement | RlsDisableStatement | RlsNoForceStatement | RlsDropPolicyStatement | RlsPolicyStatement | RlsContextStatement | RlsRoleStatement | AdvisoryLockStatement | ChainStatement;
|
|
1391
|
+
export type SqlStatement = SelectStatement | InsertStatement | UpdateStatement | DeleteStatement | RlsEnableStatement | RlsForceStatement | RlsDisableStatement | RlsNoForceStatement | RlsDropPolicyStatement | RlsPolicyStatement | RlsContextStatement | RlsRoleStatement | AdvisoryLockStatement | AdvisoryLocksStatement | ChainStatement;
|
|
1371
1392
|
/** Readiness statements remain distinct from application queries. */
|
|
1372
1393
|
export type RlsReadinessStatement = {
|
|
1373
1394
|
readonly kind: "rlsReadiness";
|
package/dist/index.d.ts
CHANGED
|
@@ -40,10 +40,10 @@ 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, 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";
|
|
43
|
+
export type { AdvisoryLockKey, AdvisoryLockStatement, AdvisoryLocksStatement, 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
|
-
export { ADVISORY_KEY_BOUNDS, describeRowLock, renderAdvisoryLock, renderRowLockClause } from "./locks.ts";
|
|
46
|
+
export { ADVISORY_KEY_BOUNDS, describeRowLock, renderAdvisoryLock, renderAdvisoryLocks, renderRowLockClause } from "./locks.ts";
|
|
47
47
|
export { JSON_TRANSPORT, projectionOutputName } from "./json-transport.ts";
|
|
48
48
|
export { postgresDialect } from "./dialects/postgres.ts";
|
|
49
49
|
export { sqliteDialect } from "./dialects/sqlite.ts";
|
package/dist/index.js
CHANGED
|
@@ -2960,6 +2960,12 @@ function renderRowLockClause(params) {
|
|
|
2960
2960
|
hint: `${dialect.name} can express: ${Object.keys(syntax.wait).join(", ")}`
|
|
2961
2961
|
});
|
|
2962
2962
|
}
|
|
2963
|
+
if (lock.of !== undefined) {
|
|
2964
|
+
if (lock.of.length === 0)
|
|
2965
|
+
throw internalError({ message: "a row lock's `of` list cannot be empty" });
|
|
2966
|
+
const tables = lock.of.map((name) => dialect.quoteIdent({ name })).join(", ");
|
|
2967
|
+
return `${strengthText} OF ${tables}${waitText}`;
|
|
2968
|
+
}
|
|
2963
2969
|
return `${strengthText}${waitText}`;
|
|
2964
2970
|
}
|
|
2965
2971
|
var ADVISORY_KEY_BOUNDS = Object.freeze({
|
|
@@ -2990,6 +2996,28 @@ function renderAdvisoryLock(params) {
|
|
|
2990
2996
|
returning: "none"
|
|
2991
2997
|
};
|
|
2992
2998
|
}
|
|
2999
|
+
function renderAdvisoryLocks(params) {
|
|
3000
|
+
const { dialect, keys, mode } = params;
|
|
3001
|
+
const functions = dialect.advisoryLockFunctions;
|
|
3002
|
+
if (functions === null)
|
|
3003
|
+
throw unsupportedCapability15({ dialect: dialect.name, feature: "advisory locks" });
|
|
3004
|
+
if (mode !== "exclusive" && mode !== "shared")
|
|
3005
|
+
throw internalError({ message: "advisory lock mode must be exclusive or shared" });
|
|
3006
|
+
if (keys.some((key) => typeof key !== "bigint" || key < ADVISORY_KEY_BOUNDS.single.min || key > ADVISORY_KEY_BOUNDS.single.max)) {
|
|
3007
|
+
throw internalError({ message: "advisory lock keys must be signed 64-bit integers" });
|
|
3008
|
+
}
|
|
3009
|
+
const collector = new ParamCollector(dialect);
|
|
3010
|
+
const binding = collector.add(JSON.stringify(keys.map((key) => key.toString())));
|
|
3011
|
+
const fn = (mode === "exclusive" ? functions.exclusive : functions.shared).wait;
|
|
3012
|
+
const table = dialect.quoteIdent({ name: "__vibe_locks" });
|
|
3013
|
+
const key = dialect.quoteIdent({ name: "key" });
|
|
3014
|
+
const order = dialect.quoteIdent({ name: "ordinal" });
|
|
3015
|
+
return {
|
|
3016
|
+
text: `SELECT ${fn}(${table}.${key}${functions.keyCast.single}) FROM jsonb_array_elements_text(${binding}::text::jsonb) WITH ORDINALITY AS ${table}(${key}, ${order}) ORDER BY ${table}.${order}`,
|
|
3017
|
+
values: collector.values,
|
|
3018
|
+
returning: "none"
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
2993
3021
|
function assertInt32(params) {
|
|
2994
3022
|
const { value, part } = params;
|
|
2995
3023
|
if (!Number.isInteger(value) || value < ADVISORY_KEY_BOUNDS.pair.min || value > ADVISORY_KEY_BOUNDS.pair.max) {
|
|
@@ -4111,6 +4139,8 @@ function renderStatement(params) {
|
|
|
4111
4139
|
}
|
|
4112
4140
|
case "select":
|
|
4113
4141
|
return renderSelect({ dialect, statement, collector });
|
|
4142
|
+
case "advisoryLocks":
|
|
4143
|
+
return renderAdvisoryLocks({ dialect, keys: statement.keys, mode: statement.mode });
|
|
4114
4144
|
case "advisoryLock":
|
|
4115
4145
|
return renderAdvisoryLock({ dialect, statement, collector });
|
|
4116
4146
|
case "insert":
|
|
@@ -5112,6 +5142,13 @@ function renderSelect(params) {
|
|
|
5112
5142
|
text += ` OFFSET ${statement.offset}`;
|
|
5113
5143
|
}
|
|
5114
5144
|
if (statement.rowLock !== undefined) {
|
|
5145
|
+
const target = statement.table.alias ?? statement.table.name;
|
|
5146
|
+
if (statement.rowLock.of !== undefined && statement.rowLock.of.some((name) => name !== target)) {
|
|
5147
|
+
throw internalError4({ message: `a row lock's \`of\` list may name only the statement's own table "${target}"` });
|
|
5148
|
+
}
|
|
5149
|
+
if (statement.rowLock.of !== undefined && (statement.fromSelect !== undefined || statement.keyPage !== undefined)) {
|
|
5150
|
+
throw internalError4({ message: "a row lock's `of` list cannot name a derived table" });
|
|
5151
|
+
}
|
|
5115
5152
|
const clause = renderRowLockClause({ dialect, lock: statement.rowLock });
|
|
5116
5153
|
if (clause !== null)
|
|
5117
5154
|
text += ` ${clause}`;
|
|
@@ -5427,6 +5464,9 @@ function renderChain(params) {
|
|
|
5427
5464
|
defined.add(step.name);
|
|
5428
5465
|
}
|
|
5429
5466
|
const result = statement.result;
|
|
5467
|
+
if (result.rowsFrom.length === 0 && result.columns.length === 0 && result.tally !== undefined && result.verdict === undefined) {
|
|
5468
|
+
return { text: `WITH ${steps.join(", ")} SELECT ${renderTallyColumn({ dialect, result, defined, collector })}`, values: collector.values, returning: "none" };
|
|
5469
|
+
}
|
|
5430
5470
|
if (result.rowsFrom.length === 0 || result.columns.length === 0) {
|
|
5431
5471
|
throw internalError4({ message: "a chain result needs at least one source step and one column" });
|
|
5432
5472
|
}
|
|
@@ -5525,7 +5565,10 @@ function renderTallyColumn(params) {
|
|
|
5525
5565
|
const minRows = veto.minRows ?? 1;
|
|
5526
5566
|
if (!Number.isSafeInteger(minRows) || minRows < 1)
|
|
5527
5567
|
throw internalError4({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
|
|
5528
|
-
|
|
5568
|
+
if (veto.exactRows !== undefined && (!Number.isSafeInteger(veto.exactRows) || veto.exactRows < 0 || veto.minRows !== undefined)) {
|
|
5569
|
+
throw internalError4({ message: `chain veto "${veto.token}" needs a nonnegative safe integer exactRows without minRows` });
|
|
5570
|
+
}
|
|
5571
|
+
const when = veto.exactRows !== undefined ? `WHEN ${count(veto.step)} <> ${veto.exactRows}` : minRows === 1 ? `WHEN NOT EXISTS (SELECT 1 FROM ${step(veto.step)})` : `WHEN ${count(veto.step)} < ${minRows}`;
|
|
5529
5572
|
return `${when} THEN CAST('${CHAIN_VETO_PREFIX}${veto.token}:' || ${collector.add(veto.nonce)} || ':' || ${count(veto.step)} AS integer)`;
|
|
5530
5573
|
});
|
|
5531
5574
|
return `CASE ${arms.join(" ")} ELSE ${sum} END AS ${dialect.quoteIdent({ name: tally.alias })}`;
|
|
@@ -6309,6 +6352,7 @@ export {
|
|
|
6309
6352
|
recordSqlBindingCheck,
|
|
6310
6353
|
registerSqlHandleCheck,
|
|
6311
6354
|
renderAdvisoryLock,
|
|
6355
|
+
renderAdvisoryLocks,
|
|
6312
6356
|
renderCheckProbe,
|
|
6313
6357
|
renderColumnStateQuery,
|
|
6314
6358
|
renderConstraintOwnedIndexDrop,
|
|
@@ -6414,4 +6458,4 @@ export {
|
|
|
6414
6458
|
toPgArrayLiteral
|
|
6415
6459
|
};
|
|
6416
6460
|
|
|
6417
|
-
//# debugId=
|
|
6461
|
+
//# debugId=11C29D1889ACA76B64756E2164756E21
|