@vibeorm/sql 5.5.0 → 5.7.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
@@ -1103,6 +1103,14 @@ export type OnConflictClause = OnConflictUpdate | OnConflictDoNothing;
1103
1103
  export type UpdateStatement = {
1104
1104
  readonly kind: "update";
1105
1105
  readonly table: TableRef;
1106
+ /** The target's alias; required with {@link UpdateStatement.from}, where two relations are in scope. */
1107
+ readonly alias?: string;
1108
+ /**
1109
+ * A joined source: `UPDATE … FROM` on PostgreSQL/SQLite (its `on` AND-ed
1110
+ * before `where`), `UPDATE … JOIN … ON` on MySQL. A
1111
+ * {@link SourceColumnRef} set value reads its columns.
1112
+ */
1113
+ readonly from?: UpdateSource;
1106
1114
  /**
1107
1115
  * Column → parameter value, or an atomic {@link SetOperation}. As a
1108
1116
  * {@link ChainStep} only (statement-maxx lane C3), a value may be a
@@ -1227,6 +1235,23 @@ export declare function stepColumnRef(params: {
1227
1235
  step: string;
1228
1236
  column: string;
1229
1237
  }): StepColumnRef;
1238
+ /** The joined relation of an {@link UpdateStatement}: its table, alias and join condition. */
1239
+ export type UpdateSource = {
1240
+ readonly table: TableRef;
1241
+ readonly alias: string;
1242
+ readonly on: SqlExpr;
1243
+ };
1244
+ /** An UPDATE set value read from the joined source's `column` (`UPDATE … FROM`). */
1245
+ export type SourceColumnRef = {
1246
+ readonly __sourceColumn: true;
1247
+ readonly column: string;
1248
+ };
1249
+ /** Read `column` of an {@link UpdateStatement}'s joined source as a set value. */
1250
+ export declare function sourceColumnRef(params: {
1251
+ column: string;
1252
+ }): SourceColumnRef;
1253
+ /** Is this UPDATE set value a {@link SourceColumnRef}? */
1254
+ export declare function isSourceColumnRef(value: unknown): value is SourceColumnRef;
1230
1255
  /** Is this INSERT … SELECT cell a {@link StepColumnRef}? */
1231
1256
  export declare function isStepColumnRef(value: unknown): value is StepColumnRef;
1232
1257
  /**
@@ -1368,7 +1393,10 @@ export type ChainResult = {
1368
1393
  * array_agg("__vibe_i")::text FROM (SELECT DISTINCT ARRAY["c0", "c1"] AS
1369
1394
  * "__vibe_i" FROM "step" LIMIT $n) AS "__vibe_d")` — a two-dimensional text
1370
1395
  * array literal of at most `limit` distinct images, NULL when the step
1371
- * returned no row. A before image skips the rows whose arm marker is true.
1396
+ * returned no row. With `inserted`, both read the update arm's rows only
1397
+ * (`WHERE NOT` the marker), and `_a` is left out with `insertedOnly`;
1398
+ * - `<alias>_ia` (with `inserted` and `after`): the insert arm's after images
1399
+ * (`WHERE` the marker), so an upsert's after images arrive per arm.
1372
1400
  *
1373
1401
  * `before` / `after` name columns the step RETURNS (its
1374
1402
  * {@link CaptureReturningColumn} aliases), in image order. `limit` is bound once.
@@ -1378,7 +1406,7 @@ export type ChainCapture = {
1378
1406
  readonly alias: string;
1379
1407
  /** The step's `"inserted"` arm-marker column, for an `INSERT … ON CONFLICT DO UPDATE` step. */
1380
1408
  readonly inserted?: string;
1381
- /** With `inserted`: images of inserted rows only — `after` reads the rows whose marker is true. */
1409
+ /** With `inserted`: images of inserted rows only — `<alias>_ia`, no `<alias>_a`. */
1382
1410
  readonly insertedOnly?: true;
1383
1411
  readonly before?: readonly string[];
1384
1412
  readonly after?: readonly string[];
package/dist/index.d.ts CHANGED
@@ -40,8 +40,8 @@ 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, AdvisoryLocksStatement, AggFn, AggRef, AggSelect, ChainResult, ChainRowSource, ChainCapture, CaptureReturningColumn, 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
- 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, isCaptureReturningColumn, jsonParamCell, returningColumnName, stepColumnRef, } from "./ast.ts";
43
+ export type { AdvisoryLockKey, AdvisoryLockStatement, AdvisoryLocksStatement, AggFn, AggRef, AggSelect, ChainResult, ChainRowSource, ChainCapture, CaptureReturningColumn, 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, SourceColumnRef, UpdateSource, TableRef, UpdateStatement, } from "./ast.ts";
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, isSourceColumnRef, sourceColumnRef, isCaptureReturningColumn, 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, renderAdvisoryLocks, renderRowLockClause } from "./locks.ts";
47
47
  export { JSON_TRANSPORT, projectionOutputName } from "./json-transport.ts";
package/dist/index.js CHANGED
@@ -2903,6 +2903,15 @@ var ADVISORY_LOCK_RESULT_COLUMN = "acquired";
2903
2903
  function stepColumnRef(params) {
2904
2904
  return { __stepColumn: true, step: params.step, column: params.column };
2905
2905
  }
2906
+ function sourceColumnRef(params) {
2907
+ return Object.freeze({ __sourceColumn: true, column: params.column });
2908
+ }
2909
+ function isSourceColumnRef(value) {
2910
+ if (typeof value !== "object" || value === null)
2911
+ return false;
2912
+ const candidate = value;
2913
+ return candidate.__sourceColumn === true && typeof candidate.column === "string" && Object.keys(value).length === 2;
2914
+ }
2906
2915
  function isStepColumnRef(value) {
2907
2916
  return typeof value === "object" && value !== null && value.__stepColumn === true;
2908
2917
  }
@@ -4960,17 +4969,18 @@ function applyReturning(params) {
4960
4969
  renderCaptureReturning({ dialect, column: capture });
4961
4970
  return { suffix: "", status: "omitted" };
4962
4971
  }
4972
+ const read = (name) => params.qualify === undefined ? name : `${params.qualify}.${name}`;
4963
4973
  const columns = requested.map((column) => {
4964
4974
  if (typeof column === "string")
4965
- return dialect.quoteIdent({ name: column });
4975
+ return read(dialect.quoteIdent({ name: column }));
4966
4976
  if ("capture" in column)
4967
4977
  return renderCaptureReturning({ dialect, column });
4968
4978
  if ("transform" in column) {
4969
4979
  const name = dialect.quoteIdent({ name: column.name });
4970
- return `${dialect.jsonTextProjection({ expression: name })} AS ${name}`;
4980
+ return `${dialect.jsonTextProjection({ expression: read(name) })} AS ${name}`;
4971
4981
  }
4972
4982
  const name = dialect.quoteIdent({ name: column.name });
4973
- return `${textProjection({ dialect, expression: name })} AS ${name}`;
4983
+ return `${textProjection({ dialect, expression: read(name) })} AS ${name}`;
4974
4984
  }).join(", ");
4975
4985
  return { suffix: ` RETURNING ${columns}`, status: "native" };
4976
4986
  }
@@ -5404,6 +5414,18 @@ function renderUpdate(params) {
5404
5414
  if (assignments.length === 0) {
5405
5415
  throw internalError4({ message: "update must set at least one column" });
5406
5416
  }
5417
+ const source = statement.from;
5418
+ if (source !== undefined) {
5419
+ if (statement.alias === undefined || statement.alias === source.alias)
5420
+ throw internalError4({ message: "UPDATE \u2026 FROM needs distinct target and source aliases" });
5421
+ const target = dialect.quoteIdent({ name: statement.alias });
5422
+ const joined = dialect.quoteIdent({ name: source.alias });
5423
+ const assign = () => renderAssignments({ dialect, entries: assignments, collector, selfRef: target, source: joined, ...dialect.name === "mysql" ? { qualifyWrite: target } : {}, ...params.earlier === undefined ? {} : { steps: params.earlier } });
5424
+ const where = () => statement.where === undefined ? "" : ` AND ${renderExpr({ dialect, expr: statement.where, collector })}`;
5425
+ const text = dialect.name === "mysql" ? `UPDATE ${renderTable(dialect, statement.table)} AS ${target} JOIN ${renderTable(dialect, source.table)} AS ${joined} ON ${renderExpr({ dialect, expr: source.on, collector })} SET ${assign()}${statement.where === undefined ? "" : ` WHERE ${renderExpr({ dialect, expr: statement.where, collector })}`}` : `UPDATE ${renderTable(dialect, statement.table)} AS ${target} SET ${assign()} FROM ${renderTable(dialect, source.table)} AS ${joined} WHERE ${renderExpr({ dialect, expr: source.on, collector })}${where()}`;
5426
+ const returning = applyReturning({ dialect, requested: statement.returning, ...dialect.name === "postgres" ? { qualify: target } : {} });
5427
+ return { text: text + returning.suffix, values: collector.values, returning: returning.status };
5428
+ }
5407
5429
  const setSql = renderAssignments({ dialect, entries: assignments, collector, ...params.earlier === undefined ? {} : { steps: params.earlier } });
5408
5430
  let text = `UPDATE ${renderTable(dialect, statement.table)} SET ${setSql}`;
5409
5431
  if (statement.where !== undefined) {
@@ -5423,6 +5445,11 @@ function renderAssignments(params) {
5423
5445
  return entries.map(([name, value]) => {
5424
5446
  const column = dialect.quoteIdent({ name });
5425
5447
  const read = selfRef === undefined ? column : `${selfRef}.${column}`;
5448
+ if (isSourceColumnRef(value)) {
5449
+ if (params.source === undefined)
5450
+ throw internalError4({ message: `an UPDATE set value reads the joined source's column ${value.column}, but the statement joins no source` });
5451
+ return `${column} = ${params.source}.${dialect.quoteIdent({ name: value.column })}`;
5452
+ }
5426
5453
  if (isStepColumnRef(value)) {
5427
5454
  if (params.steps === undefined || !params.steps.has(value.step)) {
5428
5455
  throw internalError4({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
@@ -5466,7 +5493,7 @@ function renderAssignments(params) {
5466
5493
  return `${column} = ${read} || ${collector.add(value.value)}`;
5467
5494
  }
5468
5495
  return `${column} = ${collector.add(value)}`;
5469
- }).join(", ");
5496
+ }).map((assignment) => params.qualifyWrite === undefined ? assignment : `${params.qualifyWrite}.${assignment}`).join(", ");
5470
5497
  }
5471
5498
  function renderDelete(params) {
5472
5499
  const { dialect, statement, collector } = params;
@@ -5569,8 +5596,12 @@ function captureAliases(params) {
5569
5596
  out.push(`${entry.alias}_i`);
5570
5597
  if (entry.before !== undefined && entry.before.length > 0)
5571
5598
  out.push(`${entry.alias}_b`);
5572
- if (entry.after !== undefined && entry.after.length > 0)
5573
- out.push(`${entry.alias}_a`);
5599
+ if (entry.after !== undefined && entry.after.length > 0) {
5600
+ if (entry.inserted === undefined || entry.insertedOnly !== true)
5601
+ out.push(`${entry.alias}_a`);
5602
+ if (entry.inserted !== undefined)
5603
+ out.push(`${entry.alias}_ia`);
5604
+ }
5574
5605
  }
5575
5606
  return out;
5576
5607
  }
@@ -5595,10 +5626,13 @@ function renderChainCaptures(params) {
5595
5626
  const limit = collector.add(entry.limit);
5596
5627
  const item = quote("__vibe_i");
5597
5628
  const image = (columns, filter) => `(SELECT array_agg(${item})::text FROM (SELECT DISTINCT ARRAY[${columns.map(quote).join(", ")}] AS ${item} FROM ${step}${filter} LIMIT ${limit}) AS ${quote("__vibe_d")})`;
5629
+ const updatedRows = marker === undefined ? "" : ` WHERE NOT ${marker}`;
5598
5630
  if (before.length > 0)
5599
- out.push(`${image(before, marker === undefined ? "" : ` WHERE NOT ${marker}`)} AS ${quote(`${entry.alias}_b`)}`);
5600
- if (after.length > 0)
5601
- out.push(`${image(after, marker !== undefined && entry.insertedOnly === true ? ` WHERE ${marker}` : "")} AS ${quote(`${entry.alias}_a`)}`);
5631
+ out.push(`${image(before, updatedRows)} AS ${quote(`${entry.alias}_b`)}`);
5632
+ if (after.length > 0 && (marker === undefined || entry.insertedOnly !== true))
5633
+ out.push(`${image(after, updatedRows)} AS ${quote(`${entry.alias}_a`)}`);
5634
+ if (after.length > 0 && marker !== undefined)
5635
+ out.push(`${image(after, ` WHERE ${marker}`)} AS ${quote(`${entry.alias}_ia`)}`);
5602
5636
  }
5603
5637
  return out;
5604
5638
  }
@@ -6413,6 +6447,7 @@ export {
6413
6447
  isReusableSqlCommand,
6414
6448
  isReusableSqlQuery,
6415
6449
  isSetOperation,
6450
+ isSourceColumnRef,
6416
6451
  isSqlBindingCollector,
6417
6452
  isSqlCommand,
6418
6453
  isSqlDefaultCell,
@@ -6532,6 +6567,7 @@ export {
6532
6567
  sealSqlInputScope,
6533
6568
  shortestFloat32,
6534
6569
  snapshotSqlValue,
6570
+ sourceColumnRef,
6535
6571
  sql,
6536
6572
  sqlInputContract,
6537
6573
  sqlParamContract,
@@ -6551,4 +6587,4 @@ export {
6551
6587
  toPgArrayLiteral
6552
6588
  };
6553
6589
 
6554
- //# debugId=98E9DEF83EE89CED64756E2164756E21
6590
+ //# debugId=0EA38AD633C52F8C64756E2164756E21