@vibeorm/sql 5.0.0 → 5.1.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/index.js CHANGED
@@ -1216,7 +1216,7 @@ function renderPostgresIndexKeys(params) {
1216
1216
  throw unsupportedCapability6({ dialect: dialect.name, feature: "indexOperatorClasses" });
1217
1217
  const collector = new ParamCollector(dialect);
1218
1218
  const scope = params.table !== undefined ? `${params.expressionMetadata && params.table.namespace === "pg_temp" ? "n.oid = pg_catalog.pg_my_temp_schema()" : `n.nspname = ${collector.add(params.table.namespace)}`} AND c.relname = ${collector.add(params.table.name)}` : params.namespaces === undefined ? "n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\'" : params.namespaces.length === 0 ? "false" : `n.nspname IN (${[...new Set(params.namespaces)].map((namespace) => collector.add(namespace)).join(", ")})`;
1219
- const defaultResolution = `LEFT JOIN LATERAL (WITH RECURSIVE input_types AS (SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t WHERE t.oid = ${params.expressionMetadata ? "key_attribute.atttypid" : "a.atttypid"} UNION ALL SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t JOIN input_types previous ON t.oid = previous.typbasetype WHERE previous.typtype = 'd'), candidates AS (SELECT candidate.oid, CASE WHEN candidate.opcintype = input.oid THEN 0 WHEN class_input.typispreferred AND class_input.typcategory = input.typcategory THEN 1 ELSE 2 END AS priority FROM pg_catalog.pg_opclass candidate JOIN pg_catalog.pg_type class_input ON class_input.oid = candidate.opcintype CROSS JOIN input_types input WHERE input.typtype <> 'd' AND candidate.opcdefault AND candidate.opcmethod = idx.relam AND (candidate.opcintype = input.oid OR EXISTS (SELECT 1 FROM pg_catalog.pg_cast cast_entry WHERE cast_entry.castsource = input.oid AND cast_entry.casttarget = candidate.opcintype AND cast_entry.castmethod = 'b' AND cast_entry.castcontext = 'i') OR input.typtype = 'e' AND candidate.opcintype = 'pg_catalog.anyenum'::regtype OR input.typelem <> 0 AND candidate.opcintype = 'pg_catalog.anyarray'::regtype)) SELECT (array_agg(candidate.oid))[1] AS oid FROM candidates candidate WHERE candidate.priority = (SELECT min(priority) FROM candidates) HAVING count(*) = 1) resolved_default ON true`;
1219
+ const defaultResolution = `LEFT JOIN LATERAL (WITH RECURSIVE input_types AS (SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t WHERE t.oid = ${params.expressionMetadata ? "COALESCE(a.atttypid, key_attribute.atttypid)" : "a.atttypid"} UNION ALL SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t JOIN input_types previous ON t.oid = previous.typbasetype WHERE previous.typtype = 'd'), candidates AS (SELECT candidate.oid, CASE WHEN candidate.opcintype = input.oid THEN 0 WHEN class_input.typispreferred AND class_input.typcategory = input.typcategory THEN 1 ELSE 2 END AS priority FROM pg_catalog.pg_opclass candidate JOIN pg_catalog.pg_type class_input ON class_input.oid = candidate.opcintype CROSS JOIN input_types input WHERE input.typtype <> 'd' AND candidate.opcdefault AND candidate.opcmethod = idx.relam AND (candidate.opcintype = input.oid OR EXISTS (SELECT 1 FROM pg_catalog.pg_cast cast_entry WHERE cast_entry.castsource = input.oid AND cast_entry.casttarget = candidate.opcintype AND cast_entry.castmethod = 'b' AND cast_entry.castcontext = 'i') OR input.typtype = 'e' AND candidate.opcintype = 'pg_catalog.anyenum'::regtype OR input.typelem <> 0 AND candidate.opcintype = 'pg_catalog.anyarray'::regtype)) SELECT (array_agg(candidate.oid))[1] AS oid FROM candidates candidate WHERE candidate.priority = (SELECT min(priority) FROM candidates) HAVING count(*) = 1) resolved_default ON true`;
1220
1220
  const expressionColumns = params.expressionMetadata ? `, CASE WHEN k.attnum = 0 THEN pg_catalog.pg_get_indexdef(i.indexrelid, k.ordinality::integer, false) ELSE NULL END AS expression, pg_catalog.pg_get_indexdef(i.indexrelid, 0, false) AS index_definition, pg_catalog.format_type(key_attribute.atttypid, key_attribute.atttypmod) AS result_type, (i.indoption[k.ordinality - 1] & 2) <> 0 AS nulls_first, ((i.indoption[k.ordinality - 1] & 2) <> 0) = ((i.indoption[k.ordinality - 1] & 1) <> 0) AS default_null_order, i.indcollation[k.ordinality - 1] AS collation_oid, CASE WHEN k.attnum = 0 THEN NULL ELSE i.indcollation[k.ordinality - 1] = a.attcollation END AS default_collation, i.indisvalid AS valid, i.indisready AS ready, i.indimmediate AS immediate, i.indnkeyatts AS key_count, i.indnatts AS attribute_count` : "";
1221
1221
  const expressionJoins = params.expressionMetadata ? ` JOIN pg_catalog.pg_attribute key_attribute ON key_attribute.attrelid = idx.oid AND key_attribute.attnum = k.ordinality` : "";
1222
1222
  return {
@@ -3281,17 +3281,53 @@ function renderLocalDateCheck({ dialect, table, column }) {
3281
3281
  expression: `${quoted} IS date(${quoted}) AND (${quoted} IS NULL OR ${quoted} >= ${dialect.quoteStringLiteral({ value: "0001-01-01" })})`
3282
3282
  };
3283
3283
  }
3284
+ // packages/sql/src/date-truncation.ts
3285
+ import { DATE_TRUNCATION_UNITS, VibeError as VibeError16, internalError as internalError2, unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3286
+ var TIME_ZONE_NAME = /^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/;
3287
+ function isTimeZoneName(params) {
3288
+ return typeof params.value === "string" && TIME_ZONE_NAME.test(params.value);
3289
+ }
3290
+ function renderDateTruncation(params) {
3291
+ const { dialect, reference, truncation } = params;
3292
+ if (!dialect.capabilities.dateTruncation) {
3293
+ throw unsupportedCapability17({
3294
+ dialect: dialect.name,
3295
+ feature: "a truncated groupBy key ({ field, truncate, timeZone })",
3296
+ hint: "only PostgreSQL and PGlite truncate a timestamp in a named time zone \u2014 group by the plain field and bucket the rows in code instead"
3297
+ });
3298
+ }
3299
+ if (!DATE_TRUNCATION_UNITS.includes(truncation.unit)) {
3300
+ throw internalError2({ message: `unknown date truncation unit ${JSON.stringify(truncation.unit)}` });
3301
+ }
3302
+ if (!isTimeZoneName({ value: truncation.timeZone })) {
3303
+ throw new VibeError16({
3304
+ code: "VIBE_VALIDATION",
3305
+ message: `time zone ${JSON.stringify(truncation.timeZone)} is not a time-zone name \u2014 use an IANA name such as "Europe/Oslo" or "UTC"`,
3306
+ meta: { timeZone: truncation.timeZone }
3307
+ });
3308
+ }
3309
+ const unit = dialect.quoteStringLiteral({ value: truncation.unit });
3310
+ const zone = dialect.quoteStringLiteral({ value: truncation.timeZone });
3311
+ switch (truncation.storage) {
3312
+ case "timestamptz":
3313
+ return `date_trunc(${unit}, ${reference}, ${zone})`;
3314
+ case "timestamp":
3315
+ return `(date_trunc(${unit}, ${reference} AT TIME ZONE 'UTC', ${zone}) AT TIME ZONE 'UTC')`;
3316
+ default:
3317
+ throw internalError2({ message: `unknown date truncation storage ${JSON.stringify(truncation.storage)}` });
3318
+ }
3319
+ }
3284
3320
  // packages/sql/src/generated-columns.ts
3285
- import { VibeError as VibeError16, unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
3321
+ import { VibeError as VibeError17, unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
3286
3322
 
3287
3323
  // packages/sql/src/dialects/postgres-generated-columns.ts
3288
- import { unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3324
+ import { unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
3289
3325
  function renderPostgresGeneratedColumnClause({ generated }) {
3290
3326
  return `GENERATED ALWAYS AS (${generated.expression}) STORED`;
3291
3327
  }
3292
3328
  function renderPostgresGeneratedColumnProbe({ dialect, probe }) {
3293
3329
  if (dialect.name !== "postgres")
3294
- throw unsupportedCapability17({ dialect: dialect.name, feature: "rollbackGeneratedColumnProbe" });
3330
+ throw unsupportedCapability18({ dialect: dialect.name, feature: "rollbackGeneratedColumnProbe" });
3295
3331
  const q = ({ name }) => dialect.quoteIdent({ name });
3296
3332
  let text;
3297
3333
  switch (probe.operation) {
@@ -3312,20 +3348,20 @@ function renderPostgresGeneratedColumnProbe({ dialect, probe }) {
3312
3348
  }
3313
3349
  function renderPostgresDropGeneratedExpression({ dialect, table, column }) {
3314
3350
  if (dialect.name !== "postgres")
3315
- throw unsupportedCapability17({ dialect: dialect.name, feature: "dropGeneratedExpression" });
3351
+ throw unsupportedCapability18({ dialect: dialect.name, feature: "dropGeneratedExpression" });
3316
3352
  return { text: `ALTER TABLE ${dialect.quoteIdent({ name: table.namespace })}.${dialect.quoteIdent({ name: table.name })} ALTER COLUMN ${dialect.quoteIdent({ name: column })} DROP EXPRESSION`, values: [], returning: "none" };
3317
3353
  }
3318
3354
 
3319
3355
  // packages/sql/src/generated-columns.ts
3320
3356
  function renderGeneratedColumnClause({ dialect, generated }) {
3321
3357
  if (!("generatedColumns" in dialect.capabilities) || dialect.capabilities.generatedColumns !== true) {
3322
- throw unsupportedCapability18({ dialect: dialect.name, feature: "generatedColumns" });
3358
+ throw unsupportedCapability19({ dialect: dialect.name, feature: "generatedColumns" });
3323
3359
  }
3324
3360
  if (generated.storage !== "stored") {
3325
- throw unsupportedCapability18({ dialect: dialect.name, feature: "virtualGeneratedColumns" });
3361
+ throw unsupportedCapability19({ dialect: dialect.name, feature: "virtualGeneratedColumns" });
3326
3362
  }
3327
3363
  if (typeof generated.expression !== "string" || generated.expression.trim().length === 0 || generated.expression.includes("\x00")) {
3328
- throw new VibeError16({ code: "VIBE_SCHEMA_INVALID", message: "A generated column requires non-empty, NUL-free SQL", meta: { reason: "generated-expression-invalid" } });
3364
+ throw new VibeError17({ code: "VIBE_SCHEMA_INVALID", message: "A generated column requires non-empty, NUL-free SQL", meta: { reason: "generated-expression-invalid" } });
3329
3365
  }
3330
3366
  switch (dialect.name) {
3331
3367
  case "postgres":
@@ -3337,10 +3373,10 @@ function renderGeneratedColumnClause({ dialect, generated }) {
3337
3373
  }
3338
3374
  }
3339
3375
  // packages/sql/src/row-rules.ts
3340
- import { VibeError as VibeError20, internalError as internalError2, normalizeRowRules, rowRuleGateField, rowRuleIdentities, rowRuleProtectedFields, rowRuleShapeIssue, rowRuleTriggerName, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
3376
+ import { VibeError as VibeError21, internalError as internalError3, normalizeRowRules, rowRuleGateField, rowRuleIdentities, rowRuleProtectedFields, rowRuleShapeIssue, rowRuleTriggerName, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
3341
3377
 
3342
3378
  // packages/sql/src/dialects/postgres-row-rules.ts
3343
- import { VibeError as VibeError17, unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
3379
+ import { VibeError as VibeError18, unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
3344
3380
  function comparison({ dialect, field }) {
3345
3381
  const column = dialect.quoteIdent({ name: field.dbName ?? field.name });
3346
3382
  const cast = field.type === "Json" ? `::pg_catalog.jsonb${field.isList ? "[]" : ""}` : "";
@@ -3348,7 +3384,7 @@ function comparison({ dialect, field }) {
3348
3384
  }
3349
3385
  function renderPostgresRowRule({ dialect, context }) {
3350
3386
  if (dialect.name !== "postgres")
3351
- throw unsupportedCapability19({ dialect: dialect.name, feature: "postgresRowRules" });
3387
+ throw unsupportedCapability20({ dialect: dialect.name, feature: "postgresRowRules" });
3352
3388
  const { rule, table, functionIdentity, triggerIdentity, protectedFields, gateField } = context;
3353
3389
  const qualify = ({ name }) => `${dialect.quoteIdent({ name: name.namespace })}.${dialect.quoteIdent({ name: name.name })}`;
3354
3390
  const functionName = qualify({ name: functionIdentity.name });
@@ -3365,7 +3401,7 @@ END;`;
3365
3401
  } else {
3366
3402
  const terms = protectedFields.map((field) => comparison({ dialect, field }));
3367
3403
  if (terms.length === 0) {
3368
- throw new VibeError17({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3404
+ throw new VibeError18({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3369
3405
  }
3370
3406
  const condition = gateField === undefined ? terms.join(`
3371
3407
  OR `) : `OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
@@ -3400,19 +3436,19 @@ FOR EACH ROW EXECUTE FUNCTION ${functionName}();`;
3400
3436
  }
3401
3437
 
3402
3438
  // packages/sql/src/dialects/sqlite-row-rules.ts
3403
- import { SQLITE_ROW_RULE_ERROR_PREFIX, VibeError as VibeError18, unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
3439
+ import { SQLITE_ROW_RULE_ERROR_PREFIX, VibeError as VibeError19, unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
3404
3440
  function renderSqliteRowRule({ dialect, context }) {
3405
3441
  if (dialect.name !== "sqlite")
3406
- throw unsupportedCapability20({ dialect: dialect.name, feature: "sqliteRowRules" });
3442
+ throw unsupportedCapability21({ dialect: dialect.name, feature: "sqliteRowRules" });
3407
3443
  const { rule, table, triggers, protectedFields, gateField } = context;
3408
3444
  if (table.namespace !== "main")
3409
- throw unsupportedCapability20({ dialect: dialect.name, feature: "fixedNamespaces" });
3445
+ throw unsupportedCapability21({ dialect: dialect.name, feature: "fixedNamespaces" });
3410
3446
  const terms = protectedFields.map((field) => {
3411
3447
  const column = dialect.quoteIdent({ name: field.dbName ?? field.name });
3412
3448
  return `OLD.${column} IS NOT NEW.${column}`;
3413
3449
  });
3414
3450
  if (rule.kind !== "appendOnly" && terms.length === 0)
3415
- throw new VibeError18({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3451
+ throw new VibeError19({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3416
3452
  const condition = rule.kind === "appendOnly" ? "" : gateField === undefined ? `WHEN ${terms.join(`
3417
3453
  OR `)}
3418
3454
  ` : `WHEN OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
@@ -3437,11 +3473,11 @@ END;`,
3437
3473
  }
3438
3474
 
3439
3475
  // packages/sql/src/dialects/mysql-row-rules.ts
3440
- import { MYSQL_ROW_RULE_MESSAGE_MAX_BYTES, VibeError as VibeError19, mysqlRowRuleMessageByteLength, unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
3476
+ import { MYSQL_ROW_RULE_MESSAGE_MAX_BYTES, VibeError as VibeError20, mysqlRowRuleMessageByteLength, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
3441
3477
  function diagnosticLiteral({ name, context, dialect }) {
3442
3478
  const message = `Check constraint '${name}' is violated.`;
3443
3479
  if (mysqlRowRuleMessageByteLength({ triggerName: name }) > MYSQL_ROW_RULE_MESSAGE_MAX_BYTES)
3444
- throw new VibeError19({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${context.rule.name} exceeds MySQL's ${MYSQL_ROW_RULE_MESSAGE_MAX_BYTES}-byte diagnostic limit; supply a shorter name stem`, meta: { model: context.model, rule: context.rule.name } });
3480
+ throw new VibeError20({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${context.rule.name} exceeds MySQL's ${MYSQL_ROW_RULE_MESSAGE_MAX_BYTES}-byte diagnostic limit; supply a shorter name stem`, meta: { model: context.model, rule: context.rule.name } });
3445
3481
  if (message.includes("\\")) {
3446
3482
  const hex = Array.from(new TextEncoder().encode(message), (byte) => byte.toString(16).padStart(2, "0")).join("");
3447
3483
  return `_utf8mb4 X'${hex}'`;
@@ -3450,11 +3486,11 @@ function diagnosticLiteral({ name, context, dialect }) {
3450
3486
  }
3451
3487
  function renderMysqlRowRule({ dialect, context }) {
3452
3488
  if (dialect.name !== "mysql")
3453
- throw unsupportedCapability21({ dialect: dialect.name, feature: "mysqlRowRules" });
3489
+ throw unsupportedCapability22({ dialect: dialect.name, feature: "mysqlRowRules" });
3454
3490
  const { rule, table, triggers, protectedFields, gateField, placement } = context;
3455
3491
  for (const name of Object.keys(placement ?? {}))
3456
3492
  if (!triggers.some(({ identity }) => identity.name === name))
3457
- throw new VibeError19({ code: "VIBE_INTERNAL", message: "A MySQL trigger placement names a trigger this rule does not own", meta: { model: context.model, rule: rule.name, trigger: name } });
3493
+ throw new VibeError20({ code: "VIBE_INTERNAL", message: "A MySQL trigger placement names a trigger this rule does not own", meta: { model: context.model, rule: rule.name, trigger: name } });
3458
3494
  const position = ({ name }) => {
3459
3495
  const entry = placement?.[name];
3460
3496
  if (entry === undefined)
@@ -3467,7 +3503,7 @@ function renderMysqlRowRule({ dialect, context }) {
3467
3503
  return `NOT (OLD.${column} <=> NEW.${column})`;
3468
3504
  });
3469
3505
  if (rule.kind !== "appendOnly" && terms.length === 0)
3470
- throw new VibeError19({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3506
+ throw new VibeError20({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3471
3507
  const condition = gateField === undefined ? terms.join(`
3472
3508
  OR `) : `OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
3473
3509
  ${terms.join(`
@@ -3496,10 +3532,10 @@ END;`, values: [] };
3496
3532
  function renderRowRule({ schema, model, rule, dialect, placement }) {
3497
3533
  const issue = rowRuleShapeIssue({ value: rule });
3498
3534
  if (issue !== undefined) {
3499
- throw new VibeError20({ code: "VIBE_SCHEMA_INVALID", message: issue, meta: { model: model.name } });
3535
+ throw new VibeError21({ code: "VIBE_SCHEMA_INVALID", message: issue, meta: { model: model.name } });
3500
3536
  }
3501
3537
  if (placement !== undefined && Object.keys(placement).length > 0 && dialect.name !== "mysql")
3502
- throw unsupportedCapability22({ dialect: dialect.name, feature: "rowRuleTriggerPlacement" });
3538
+ throw unsupportedCapability23({ dialect: dialect.name, feature: "rowRuleTriggerPlacement" });
3503
3539
  const normalized = normalizeRowRules({ schema, model, rules: [rule], dialect: dialect.name })[0];
3504
3540
  switch (dialect.name) {
3505
3541
  case "postgres": {
@@ -3507,7 +3543,7 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
3507
3543
  const functionIdentity = objects.find((object) => object.role === "function")?.identity;
3508
3544
  const triggerIdentity = objects.find((object) => object.role === "trigger")?.identity;
3509
3545
  if (functionIdentity?.kind !== "function" || triggerIdentity?.kind !== "trigger")
3510
- throw internalError2({ message: "PostgreSQL row rules own one function and one trigger", meta: { model: model.name, rule: normalized.name } });
3546
+ throw internalError3({ message: "PostgreSQL row rules own one function and one trigger", meta: { model: model.name, rule: normalized.name } });
3511
3547
  const gate = rowRuleGateField({ rule: normalized });
3512
3548
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
3513
3549
  return renderPostgresRowRule({
@@ -3528,15 +3564,15 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
3528
3564
  const objects = rowRuleIdentities({ schema, model, rule: normalized, dialect: dialect.name });
3529
3565
  const triggers = objects.map(({ role, identity }) => {
3530
3566
  if (role !== "trigger" || identity.kind !== "trigger")
3531
- throw internalError2({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
3567
+ throw internalError3({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
3532
3568
  const event = ["update", "delete"].find((event) => identity.name === rowRuleTriggerName({ rule: normalized, event }));
3533
3569
  if (event === undefined)
3534
- throw internalError2({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
3570
+ throw internalError3({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
3535
3571
  return { event, identity: { kind: "trigger", table: identity.table, name: identity.name } };
3536
3572
  });
3537
3573
  const table = triggers[0]?.identity.table;
3538
3574
  if (table === undefined)
3539
- throw internalError2({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
3575
+ throw internalError3({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
3540
3576
  const gate = rowRuleGateField({ rule: normalized });
3541
3577
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
3542
3578
  const context = {
@@ -3567,7 +3603,7 @@ function renderSqliteJsonEquality(params) {
3567
3603
  }
3568
3604
 
3569
3605
  // packages/sql/src/dialects/diagnostics.ts
3570
- import { unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
3606
+ import { unsupportedCapability as unsupportedCapability24 } from "@vibeorm/schema";
3571
3607
  var QUERIES = Object.freeze({
3572
3608
  postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
3573
3609
  sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
@@ -3575,7 +3611,7 @@ var QUERIES = Object.freeze({
3575
3611
  function renderDiagnostic(params) {
3576
3612
  const query = QUERIES[params.query];
3577
3613
  if (params.dialect.name !== query.dialect) {
3578
- throw unsupportedCapability23({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3614
+ throw unsupportedCapability24({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3579
3615
  }
3580
3616
  return { text: query.text, values: [], returning: "none" };
3581
3617
  }
@@ -3584,7 +3620,7 @@ function isFixedDiagnostic(params) {
3584
3620
  }
3585
3621
 
3586
3622
  // packages/sql/src/dialects/postgres-diagnostics.ts
3587
- import { VibeError as VibeError21, unsupportedCapability as unsupportedCapability24 } from "@vibeorm/schema";
3623
+ import { VibeError as VibeError22, unsupportedCapability as unsupportedCapability25 } from "@vibeorm/schema";
3588
3624
  function renderPostgresExplain(params) {
3589
3625
  const { dialect, query, options } = params;
3590
3626
  requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
@@ -3619,7 +3655,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
3619
3655
  });
3620
3656
  function requirePostgres(params) {
3621
3657
  if (params.dialect.name !== "postgres") {
3622
- throw unsupportedCapability24({
3658
+ throw unsupportedCapability25({
3623
3659
  dialect: params.dialect.name,
3624
3660
  feature: params.feature,
3625
3661
  hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
@@ -3637,7 +3673,7 @@ function renderPostgresWorkload(params) {
3637
3673
  if (query === "statementSnapshot") {
3638
3674
  const bounded = limit ?? 20;
3639
3675
  if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
3640
- throw new VibeError21({
3676
+ throw new VibeError22({
3641
3677
  code: "VIBE_CONFIG",
3642
3678
  message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
3643
3679
  meta: { limit }
@@ -3656,7 +3692,7 @@ function renderPostgresWorkload(params) {
3656
3692
  function requireArg(params) {
3657
3693
  const { value, name, query } = params;
3658
3694
  if (value === undefined || value.length === 0) {
3659
- throw new VibeError21({
3695
+ throw new VibeError22({
3660
3696
  code: "VIBE_CONFIG",
3661
3697
  message: `the "${query}" diagnostic read requires a ${name}`,
3662
3698
  meta: { query, argument: name }
@@ -3666,10 +3702,10 @@ function requireArg(params) {
3666
3702
  }
3667
3703
 
3668
3704
  // packages/sql/src/dialects/postgres-rls-readiness.ts
3669
- import { unsupportedCapability as unsupportedCapability26 } from "@vibeorm/schema";
3705
+ import { unsupportedCapability as unsupportedCapability27 } from "@vibeorm/schema";
3670
3706
 
3671
3707
  // packages/sql/src/dialects/postgres-rls.ts
3672
- import { RlsContextError, VibeError as VibeError22, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability25, validateRlsContextIR } from "@vibeorm/schema";
3708
+ import { RlsContextError, VibeError as VibeError23, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability26, validateRlsContextIR } from "@vibeorm/schema";
3673
3709
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
3674
3710
  var COLUMN_TYPES = {
3675
3711
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -3682,18 +3718,18 @@ var COLUMN_TYPES = {
3682
3718
  };
3683
3719
  function requireRls(params) {
3684
3720
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
3685
- throw unsupportedCapability25({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3721
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3686
3722
  }
3687
3723
  }
3688
3724
  function identifier2(params) {
3689
3725
  if (typeof params.name !== "string" || params.name.length === 0 || params.name.includes("\x00") || new TextEncoder().encode(params.name).length > 63) {
3690
- throw new VibeError22({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
3726
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
3691
3727
  }
3692
3728
  return params.dialect.quoteIdent({ name: params.name });
3693
3729
  }
3694
3730
  function tableRef(params) {
3695
3731
  if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
3696
- throw unsupportedCapability25({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
3732
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
3697
3733
  }
3698
3734
  return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
3699
3735
  }
@@ -3734,7 +3770,7 @@ function renderRlsColumnType(params) {
3734
3770
  if (native !== undefined)
3735
3771
  return native;
3736
3772
  }
3737
- throw unsupportedCapability25({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3773
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3738
3774
  }
3739
3775
  function protectedModel(params) {
3740
3776
  requireRls(params);
@@ -3742,7 +3778,7 @@ function protectedModel(params) {
3742
3778
  assertValidSchemaIR({ schema: params.schema });
3743
3779
  const model = params.schema.models.find((entry) => entry.name === params.model);
3744
3780
  if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
3745
- throw new VibeError22({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
3781
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
3746
3782
  }
3747
3783
  return model;
3748
3784
  }
@@ -3775,7 +3811,7 @@ function predicate(params) {
3775
3811
  }).join(" AND ");
3776
3812
  return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
3777
3813
  }
3778
- throw new VibeError22({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
3814
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
3779
3815
  }
3780
3816
  function renderRlsPolicy(params) {
3781
3817
  const model = protectedModel(params);
@@ -3816,7 +3852,7 @@ function renderRlsContext(params) {
3816
3852
  }
3817
3853
  function runtimeRoleOf(params) {
3818
3854
  if (typeof params.role !== "string" || params.role.trim().length === 0 || params.role.includes("\x00")) {
3819
- throw new VibeError22({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
3855
+ throw new VibeError23({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
3820
3856
  }
3821
3857
  return params.role;
3822
3858
  }
@@ -3973,7 +4009,7 @@ ORDER BY c.relname, p.polname`,
3973
4009
  });
3974
4010
  function requireRls2(params) {
3975
4011
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
3976
- throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
4012
+ throw unsupportedCapability27({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3977
4013
  }
3978
4014
  }
3979
4015
  function renderRlsReadinessQuery(params) {
@@ -4005,7 +4041,7 @@ function renderRlsPolicyPath(params) {
4005
4041
  }
4006
4042
 
4007
4043
  // packages/sql/src/render.ts
4008
- import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError3, unsupportedCapability as unsupportedCapability27 } from "@vibeorm/schema";
4044
+ import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError4, unsupportedCapability as unsupportedCapability28 } from "@vibeorm/schema";
4009
4045
 
4010
4046
  // packages/sql/src/like.ts
4011
4047
  function escapeLikePattern(params) {
@@ -4091,15 +4127,17 @@ function renderStatement(params) {
4091
4127
  function quoteCol(dialect, column) {
4092
4128
  const name = dialect.quoteIdent({ name: column.name });
4093
4129
  const ref = column.table === undefined ? name : `${dialect.quoteIdent({ name: column.table })}.${name}`;
4130
+ if (column.truncate !== undefined)
4131
+ return renderDateTruncation({ dialect, reference: ref, truncation: column.truncate });
4094
4132
  if (column.castReal === true) {
4095
4133
  if (dialect.name !== "sqlite") {
4096
- throw internalError3({ message: "ColRef.castReal is a sqlite Decimal construct" });
4134
+ throw internalError4({ message: "ColRef.castReal is a sqlite Decimal construct" });
4097
4135
  }
4098
4136
  return `CAST(${ref} AS REAL)`;
4099
4137
  }
4100
4138
  if (column.castJsonb !== undefined) {
4101
4139
  if (dialect.name !== "postgres") {
4102
- throw internalError3({ message: "ColRef.castJsonb is a postgres json construct" });
4140
+ throw internalError4({ message: "ColRef.castJsonb is a postgres json construct" });
4103
4141
  }
4104
4142
  return `${ref}::${column.castJsonb}`;
4105
4143
  }
@@ -4107,7 +4145,7 @@ function quoteCol(dialect, column) {
4107
4145
  }
4108
4146
  function typedOrderRef(params) {
4109
4147
  const { statement, column } = params;
4110
- if (column.table !== undefined || column.castReal === true || column.castJsonb !== undefined)
4148
+ if (column.table !== undefined || column.castReal === true || column.castJsonb !== undefined || column.truncate !== undefined)
4111
4149
  return column;
4112
4150
  const renamed = statement.columns.some((projected) => (projected.alias ?? projected.name) === column.name && (projected.transform !== undefined || projected.alias !== undefined));
4113
4151
  return renamed ? { ...column, table: statement.table.alias ?? statement.table.name } : column;
@@ -4116,16 +4154,17 @@ function jsonCellCast(dialect, cell) {
4116
4154
  if (cell.storage !== "json" || dialect.jsonParamCast === "")
4117
4155
  return dialect.jsonParamCast;
4118
4156
  if (dialect.name !== "postgres") {
4119
- throw internalError3({ message: "JsonParamCell storage json is a postgres construct" });
4157
+ throw internalError4({ message: "JsonParamCell storage json is a postgres construct" });
4120
4158
  }
4121
4159
  return "::text::json";
4122
4160
  }
4123
4161
  function quoteProjection(dialect, column) {
4124
4162
  if (column.transform === undefined) {
4125
4163
  const base = quoteCol(dialect, column);
4126
- if (column.alias === undefined)
4164
+ const alias = column.alias ?? (column.truncate === undefined ? undefined : column.name);
4165
+ if (alias === undefined)
4127
4166
  return base;
4128
- return `${base} AS ${dialect.quoteIdent({ name: column.alias })}`;
4167
+ return `${base} AS ${dialect.quoteIdent({ name: alias })}`;
4129
4168
  }
4130
4169
  if (column.transform === "jsonText") {
4131
4170
  const ref = quoteCol(dialect, column);
@@ -4139,7 +4178,7 @@ function quoteProjection(dialect, column) {
4139
4178
  if (column.transform === "encodeBase64" && dialect.name === "mysql")
4140
4179
  return `TO_BASE64(${ref}) AS ${alias}`;
4141
4180
  if (dialect.name !== "postgres") {
4142
- throw internalError3({ message: `${column.transform} is a postgres construct` });
4181
+ throw internalError4({ message: `${column.transform} is a postgres construct` });
4143
4182
  }
4144
4183
  if (column.transform === "encodeArrayBase64")
4145
4184
  return `encode(array_send(${ref}), 'base64') AS ${alias}`;
@@ -4163,7 +4202,7 @@ function renderTable(dialect, table) {
4163
4202
  }
4164
4203
  function assertCount(value, what) {
4165
4204
  if (!Number.isSafeInteger(value) || value < 0) {
4166
- throw internalError3({ message: `${what} must be a non-negative integer, got ${value}` });
4205
+ throw internalError4({ message: `${what} must be a non-negative integer, got ${value}` });
4167
4206
  }
4168
4207
  }
4169
4208
  var COMPARISON_SQL2 = {
@@ -4195,7 +4234,7 @@ function renderAggRef(dialect, agg) {
4195
4234
  function assertScalarArrays(dialect, feature) {
4196
4235
  if (dialect.capabilities.scalarArrays)
4197
4236
  return;
4198
- throw unsupportedCapability27({
4237
+ throw unsupportedCapability28({
4199
4238
  dialect: dialect.name,
4200
4239
  feature,
4201
4240
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -4207,7 +4246,7 @@ function renderExpr(params) {
4207
4246
  case "cmp": {
4208
4247
  const op = COMPARISON_SQL2[expr.op];
4209
4248
  if (op === undefined) {
4210
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4249
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4211
4250
  }
4212
4251
  if (isDbNowMarker(expr.value)) {
4213
4252
  return `${quoteCol(dialect, expr.column)} ${op} ${dialect.renderDbNow({ offsetSeconds: expr.value.offsetSeconds, collector })}`;
@@ -4218,7 +4257,7 @@ function renderExpr(params) {
4218
4257
  case "cmpCol": {
4219
4258
  const op = COMPARISON_SQL2[expr.op];
4220
4259
  if (op === undefined) {
4221
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4260
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4222
4261
  }
4223
4262
  return `${quoteCol(dialect, expr.left)} ${op} ${quoteCol(dialect, expr.right)}`;
4224
4263
  }
@@ -4242,11 +4281,11 @@ function renderExpr(params) {
4242
4281
  }
4243
4282
  case "tupleIn": {
4244
4283
  if (expr.columns.length === 0) {
4245
- throw internalError3({ message: "tupleIn needs at least one column" });
4284
+ throw internalError4({ message: "tupleIn needs at least one column" });
4246
4285
  }
4247
4286
  for (const row of expr.rows) {
4248
4287
  if (row.length !== expr.columns.length) {
4249
- throw internalError3({
4288
+ throw internalError4({
4250
4289
  message: `tupleIn row has ${row.length} values for ${expr.columns.length} columns`
4251
4290
  });
4252
4291
  }
@@ -4272,7 +4311,7 @@ function renderExpr(params) {
4272
4311
  case "rowCompare": {
4273
4312
  const op = COMPARISON_SQL2[expr.op];
4274
4313
  if (op === undefined || expr.columns.length === 0 || expr.values.length !== expr.columns.length) {
4275
- throw internalError3({ message: `rowCompare needs a range operator and one value per column (${expr.columns.length} columns, ${expr.values.length} values)` });
4314
+ throw internalError4({ message: `rowCompare needs a range operator and one value per column (${expr.columns.length} columns, ${expr.values.length} values)` });
4276
4315
  }
4277
4316
  const columns = expr.columns.map((column) => quoteCol(dialect, column));
4278
4317
  const placeholders = expr.values.map((value) => collector.add(value));
@@ -4280,9 +4319,9 @@ function renderExpr(params) {
4280
4319
  }
4281
4320
  case "inSelect": {
4282
4321
  if (expr.columns.length === 0)
4283
- throw internalError3({ message: "inSelect needs at least one column" });
4322
+ throw internalError4({ message: "inSelect needs at least one column" });
4284
4323
  if (dialect.name === "mysql" && (expr.select.limit !== undefined || expr.select.offset !== undefined)) {
4285
- throw unsupportedCapability27({
4324
+ throw unsupportedCapability28({
4286
4325
  dialect: dialect.name,
4287
4326
  feature: "LIMIT inside an IN subquery",
4288
4327
  hint: "mysql refuses LIMIT directly inside an IN subquery (error 1235); wrap the limited select in a derived table"
@@ -4353,7 +4392,7 @@ function renderExpr(params) {
4353
4392
  case "aggCmp": {
4354
4393
  const op = COMPARISON_SQL2[expr.op];
4355
4394
  if (op === undefined) {
4356
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4395
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4357
4396
  }
4358
4397
  return `${renderAggRef(dialect, expr.agg)} ${op} ${collector.add(expr.value)}`;
4359
4398
  }
@@ -4435,13 +4474,13 @@ function renderKeyList(params) {
4435
4474
  const { dialect, expr, collector } = params;
4436
4475
  const arity = expr.columns.length;
4437
4476
  if (arity === 0)
4438
- throw internalError3({ message: "keyList needs at least one column" });
4477
+ throw internalError4({ message: "keyList needs at least one column" });
4439
4478
  if (expr.members.length !== arity) {
4440
- throw internalError3({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4479
+ throw internalError4({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4441
4480
  }
4442
4481
  for (const row of expr.rows) {
4443
4482
  if (row.length !== arity)
4444
- throw internalError3({ message: `keyList row has ${row.length} values for ${arity} columns` });
4483
+ throw internalError4({ message: `keyList row has ${row.length} values for ${arity} columns` });
4445
4484
  }
4446
4485
  if (expr.rows.length === 0)
4447
4486
  return "1 = 0";
@@ -4473,25 +4512,25 @@ function renderKeyPage(params) {
4473
4512
  const { select } = page;
4474
4513
  const arity = page.keyColumns.length;
4475
4514
  if (arity === 0 || page.members.length !== arity) {
4476
- throw internalError3({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4515
+ throw internalError4({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4477
4516
  }
4478
4517
  if (page.rows.length === 0)
4479
- throw internalError3({ message: "keyPage needs at least one key" });
4518
+ throw internalError4({ message: "keyPage needs at least one key" });
4480
4519
  for (const row of page.rows) {
4481
4520
  if (row.length !== arity)
4482
- throw internalError3({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4521
+ throw internalError4({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4483
4522
  }
4484
4523
  if (page.identity.length === 0)
4485
- throw internalError3({ message: "keyPage needs the child's identity columns" });
4524
+ throw internalError4({ message: "keyPage needs the child's identity columns" });
4486
4525
  const order = select.orderBy ?? [];
4487
4526
  if (order.length === 0 || order.some((term) => !("column" in term))) {
4488
- throw internalError3({ message: "keyPage needs a non-empty order of plain column terms" });
4527
+ throw internalError4({ message: "keyPage needs a non-empty order of plain column terms" });
4489
4528
  }
4490
4529
  if (select.table.alias !== undefined || Object.keys(select).some((member) => !KEY_PAGE_SELECT_MEMBERS.has(member))) {
4491
- throw internalError3({ message: "keyPage select carries a member the per-key page cannot render" });
4530
+ throw internalError4({ message: "keyPage select carries a member the per-key page cannot render" });
4492
4531
  }
4493
4532
  if (dialect.keyPageStrategy === "window") {
4494
- throw unsupportedCapability27({
4533
+ throw unsupportedCapability28({
4495
4534
  dialect: dialect.name,
4496
4535
  feature: "a per-key page (keyPage)",
4497
4536
  hint: "the runtime keeps the ROW_NUMBER window on this dialect"
@@ -4519,7 +4558,7 @@ function renderKeyPage(params) {
4519
4558
  const keys = dialect.quoteIdent({ name: keyAlias });
4520
4559
  if (dialect.keyPageStrategy === "lateral") {
4521
4560
  if (keyListBindsUntypedRows({ members: page.members })) {
4522
- throw unsupportedCapability27({
4561
+ throw unsupportedCapability28({
4523
4562
  dialect: dialect.name,
4524
4563
  feature: "a per-key page over a DateTime key without a declared native type",
4525
4564
  hint: "the runtime keeps the ROW_NUMBER window for such keys"
@@ -4547,22 +4586,22 @@ function renderKeyPage(params) {
4547
4586
  function renderKeyRows(params) {
4548
4587
  const { dialect, statement, keys, collector } = params;
4549
4588
  if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.fromUnion !== undefined || statement.table.alias !== undefined) {
4550
- throw internalError3({ message: "fromKeys uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion and table.alias must be unset" });
4589
+ throw internalError4({ message: "fromKeys uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion and table.alias must be unset" });
4551
4590
  }
4552
4591
  if (statement.rowLock !== undefined)
4553
- throw internalError3({ message: "a locking read cannot read from key rows" });
4592
+ throw internalError4({ message: "a locking read cannot read from key rows" });
4554
4593
  const arity = keys.columns.length;
4555
4594
  if (arity === 0 || keys.members.length !== arity) {
4556
- throw internalError3({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
4595
+ throw internalError4({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
4557
4596
  }
4558
4597
  if (keys.rows.length === 0)
4559
- throw internalError3({ message: "fromKeys needs at least one key row" });
4598
+ throw internalError4({ message: "fromKeys needs at least one key row" });
4560
4599
  for (const row of keys.rows) {
4561
4600
  if (row.length !== arity)
4562
- throw internalError3({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
4601
+ throw internalError4({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
4563
4602
  }
4564
4603
  if (dialect.keyListStrategy !== "unnest" || keyListBindsUntypedRows({ members: keys.members })) {
4565
- throw unsupportedCapability27({
4604
+ throw unsupportedCapability28({
4566
4605
  dialect: dialect.name,
4567
4606
  feature: dialect.keyListStrategy !== "unnest" ? "a key-rows FROM (fromKeys)" : "a key-rows FROM over a DateTime key without a declared native type",
4568
4607
  hint: "the runtime keeps the per-relation loads for this write"
@@ -4578,20 +4617,20 @@ function renderUnionPage(params) {
4578
4617
  const { dialect, statement, collector } = params;
4579
4618
  const branches = statement.fromUnion ?? [];
4580
4619
  if (branches.length < 2)
4581
- throw internalError3({ message: "fromUnion needs at least two branches" });
4620
+ throw internalError4({ message: "fromUnion needs at least two branches" });
4582
4621
  if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.table.alias !== undefined) {
4583
- throw internalError3({ message: "fromUnion uses table.name as the derived-table alias \u2014 fromSelect, keyPage and table.alias must be unset" });
4622
+ throw internalError4({ message: "fromUnion uses table.name as the derived-table alias \u2014 fromSelect, keyPage and table.alias must be unset" });
4584
4623
  }
4585
4624
  if (statement.rowLock !== undefined)
4586
- throw internalError3({ message: "a locking read cannot read from a union page" });
4625
+ throw internalError4({ message: "a locking read cannot read from a union page" });
4587
4626
  const outputNames = (branch) => branch.columns.map((column) => column.alias ?? column.name).join("\x00");
4588
4627
  const names = outputNames(branches[0]);
4589
4628
  return branches.map((branch, index) => {
4590
4629
  if (branch.columns.length === 0 || Object.keys(branch).some((member) => !UNION_BRANCH_MEMBERS.has(member))) {
4591
- throw internalError3({ message: "a fromUnion branch must be a plain SELECT <columns> FROM <table> [WHERE] [ORDER BY] [LIMIT]" });
4630
+ throw internalError4({ message: "a fromUnion branch must be a plain SELECT <columns> FROM <table> [WHERE] [ORDER BY] [LIMIT]" });
4592
4631
  }
4593
4632
  if (outputNames(branch) !== names)
4594
- throw internalError3({ message: "fromUnion branches must project the same output names in the same order" });
4633
+ throw internalError4({ message: "fromUnion branches must project the same output names in the same order" });
4595
4634
  const inner = renderSelect({ dialect, statement: branch, collector });
4596
4635
  return `SELECT * FROM (${inner.text}) AS ${dialect.quoteIdent({ name: `__vibe_u${index}` })}`;
4597
4636
  }).join(" UNION ALL ");
@@ -4599,7 +4638,7 @@ function renderUnionPage(params) {
4599
4638
  function jsonStringPattern(params) {
4600
4639
  const { expr } = params;
4601
4640
  if (typeof expr.value !== "string") {
4602
- throw internalError3({ message: `json ${expr.op} expects a string operand` });
4641
+ throw internalError4({ message: `json ${expr.op} expects a string operand` });
4603
4642
  }
4604
4643
  if (expr.op === "string_contains")
4605
4644
  return containsPattern({ raw: expr.value });
@@ -4623,7 +4662,7 @@ function renderJsonLike(params) {
4623
4662
  function renderJsonFilter(params) {
4624
4663
  const { dialect, expr, collector } = params;
4625
4664
  if (!dialect.supportedJsonOps.includes(expr.op)) {
4626
- throw unsupportedCapability27({
4665
+ throw unsupportedCapability28({
4627
4666
  dialect: dialect.name,
4628
4667
  feature: `the json filter "${expr.op}"`,
4629
4668
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -4690,7 +4729,7 @@ function renderJsonFilter(params) {
4690
4729
  const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
4691
4730
  const tests = elements.map((element) => {
4692
4731
  if (element === null || typeof element === "object" && element !== null) {
4693
- throw unsupportedCapability27({
4732
+ throw unsupportedCapability28({
4694
4733
  dialect: dialect.name,
4695
4734
  feature: "json array_contains with object/array/null elements",
4696
4735
  hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
@@ -4704,7 +4743,7 @@ function renderJsonFilter(params) {
4704
4743
  return tests.length === 0 ? guard : `(${[guard, ...tests].join(" AND ")})`;
4705
4744
  }
4706
4745
  default:
4707
- throw internalError3({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
4746
+ throw internalError4({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
4708
4747
  }
4709
4748
  }
4710
4749
  const column = quoteCol(dialect, expr.column);
@@ -4762,11 +4801,11 @@ function renderPositionalRow(params) {
4762
4801
  const position = join.select.rowNumber;
4763
4802
  const positioned = position !== undefined && json.order === "positionSlot" && join.agg === "jsonArray" && position.partitionBy.length === 0 && position.orderBy.length > 0;
4764
4803
  if (join.select.columns.length === 0 || join.select.selectExpr !== undefined || (join.select.aggregates?.length ?? 0) > 0 || position !== undefined && !positioned) {
4765
- throw internalError3({ message: "positional lateral rows require an explicit column-only projection" });
4804
+ throw internalError4({ message: "positional lateral rows require an explicit column-only projection" });
4766
4805
  }
4767
4806
  const names = [...join.select.columns.map((column) => projectionOutputName({ column })), ...positioned ? [position.alias] : []];
4768
4807
  if (new Set(names).size !== names.length) {
4769
- throw internalError3({ message: "positional lateral projection has duplicate output names" });
4808
+ throw internalError4({ message: "positional lateral projection has duplicate output names" });
4770
4809
  }
4771
4810
  const values = names.map((name) => `${subAlias}.${dialect.quoteIdent({ name })}`);
4772
4811
  const chunks = [];
@@ -4774,14 +4813,14 @@ function renderPositionalRow(params) {
4774
4813
  chunks.push(`${json.rowArray}(${values.slice(index, index + JSON_TRANSPORT.chunkSize).join(", ")})`);
4775
4814
  }
4776
4815
  if (chunks.length > JSON_TRANSPORT.chunkSize) {
4777
- throw internalError3({ message: "positional lateral projection exceeds the transport width limit" });
4816
+ throw internalError4({ message: "positional lateral projection exceeds the transport width limit" });
4778
4817
  }
4779
4818
  return chunks.length === 1 ? chunks[0] : `${json.rowArray}(${chunks.join(", ")})`;
4780
4819
  }
4781
4820
  function lateralJsonOf(params) {
4782
4821
  const json = params.dialect.lateralJson;
4783
4822
  if (json === null)
4784
- throw internalError3({ message: "lateral JSON rendering on a dialect without LATERAL" });
4823
+ throw internalError4({ message: "lateral JSON rendering on a dialect without LATERAL" });
4785
4824
  return json;
4786
4825
  }
4787
4826
  function renderLateralJoin(params) {
@@ -4789,7 +4828,7 @@ function renderLateralJoin(params) {
4789
4828
  const inner = renderSelect({ dialect, statement: join.select, collector });
4790
4829
  if (join.agg === "count") {
4791
4830
  if (join.rowFormat === "positional" || join.select.columns.length !== 0 || join.select.selectExpr?.kind !== "countAll" || join.select.selectExpr.alias !== join.columnAlias || join.select.groupBy !== undefined || join.select.having !== undefined || join.select.limit !== undefined || join.select.offset !== undefined || join.select.distinctOn !== undefined || join.select.distinct !== undefined) {
4792
- throw internalError3({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
4831
+ throw internalError4({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
4793
4832
  }
4794
4833
  return `LEFT JOIN LATERAL (${inner.text}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
4795
4834
  }
@@ -4814,7 +4853,7 @@ function renderLateralJoin(params) {
4814
4853
  }
4815
4854
  }
4816
4855
  if (join.rowFormat !== "positional" && json.order !== "inAggregate") {
4817
- throw unsupportedCapability27({ dialect: dialect.name, feature: "object-format lateral rows", hint: 'lateral relation rows render positional (rowFormat "positional") on this dialect' });
4856
+ throw unsupportedCapability28({ dialect: dialect.name, feature: "object-format lateral rows", hint: 'lateral relation rows render positional (rowFormat "positional") on this dialect' });
4818
4857
  }
4819
4858
  const rowJson = join.rowFormat === "positional" ? renderPositionalRow({ dialect, join, subAlias }) : `to_json(${subAlias}.*)`;
4820
4859
  const projection = join.agg === "jsonArray" ? `COALESCE(${json.arrayAgg}(${rowJson}${aggOrder}), ${json.emptyArray}) AS ${columnAlias}` : `${rowJson} AS ${columnAlias}`;
@@ -4825,7 +4864,7 @@ function renderCountSubquery(params) {
4825
4864
  const { dialect, subquery, collector } = params;
4826
4865
  const select = subquery.select;
4827
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) {
4828
- throw internalError3({ message: "count subquery must be one ungrouped COUNT(*)" });
4867
+ throw internalError4({ message: "count subquery must be one ungrouped COUNT(*)" });
4829
4868
  }
4830
4869
  const inner = renderSelect({ dialect, statement: select, collector });
4831
4870
  return `(${inner.text}) AS ${dialect.quoteIdent({ name: subquery.alias })}`;
@@ -4855,27 +4894,27 @@ function renderKeyTableJoin(params) {
4855
4894
  const postgresMember = (member) => member.kind === "citext" || member.kind === "text" || member.kind === "integer";
4856
4895
  const admitted = dialect.name === "mysql" ? join.members.every((member) => member.kind !== "citext" && member.kind !== "text") : dialect.name === "postgres" && join.members.every(postgresMember);
4857
4896
  if (!admitted) {
4858
- throw unsupportedCapability27({
4897
+ throw unsupportedCapability28({
4859
4898
  dialect: dialect.name,
4860
4899
  feature: "relation key table",
4861
4900
  hint: "the key-table join is mysql's relation association \u2014 this dialect compares relation keys by value"
4862
4901
  });
4863
4902
  }
4864
4903
  if (join.columns.length === 0 || join.columns.length !== join.members.length) {
4865
- throw internalError3({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
4904
+ throw internalError4({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
4866
4905
  }
4867
4906
  if (join.rows.length === 0)
4868
- throw internalError3({ message: "key table needs at least one key row" });
4907
+ throw internalError4({ message: "key table needs at least one key row" });
4869
4908
  for (const member of join.members) {
4870
4909
  if (member.kind === "decimal" && !(Number.isInteger(member.scale) && member.scale >= 0 && member.scale <= 30)) {
4871
- throw internalError3({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
4910
+ throw internalError4({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
4872
4911
  }
4873
4912
  }
4874
4913
  const alias = dialect.quoteIdent({ name: join.alias });
4875
4914
  const memberNames = join.members.map((_, index) => dialect.quoteIdent({ name: `__vibe_k${index}` }));
4876
4915
  const rows = join.rows.map((row, ordinal) => {
4877
4916
  if (row.length !== join.members.length) {
4878
- throw internalError3({ message: `key table row has ${row.length} values for ${join.members.length} members` });
4917
+ throw internalError4({ message: `key table row has ${row.length} values for ${join.members.length} members` });
4879
4918
  }
4880
4919
  const cells = row.map((value, index) => {
4881
4920
  const placeholder = collector.add(value);
@@ -4905,10 +4944,10 @@ function renderKeyTableJoin(params) {
4905
4944
  function renderLeftJoin(params) {
4906
4945
  const { dialect, join, collector } = params;
4907
4946
  if (join.on.length === 0) {
4908
- throw internalError3({ message: "left join needs at least one ON pair" });
4947
+ throw internalError4({ message: "left join needs at least one ON pair" });
4909
4948
  }
4910
4949
  if (join.select.columns.length === 0 || join.select.columns.some((column) => column.alias === undefined)) {
4911
- throw internalError3({ message: "a left join's derived select must alias every projected column" });
4950
+ throw internalError4({ message: "a left join's derived select must alias every projected column" });
4912
4951
  }
4913
4952
  const inner = renderSelect({ dialect, statement: join.select, collector });
4914
4953
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
@@ -4917,10 +4956,10 @@ function renderLeftJoin(params) {
4917
4956
  function renderSelect(params) {
4918
4957
  const { dialect, statement, collector } = params;
4919
4958
  if (statement.selectExpr !== undefined && statement.columns.length > 0 && statement.selectExpr.kind !== "countAll") {
4920
- throw internalError3({ message: "only countAll can combine with a column list" });
4959
+ throw internalError4({ message: "only countAll can combine with a column list" });
4921
4960
  }
4922
4961
  if (statement.selectExpr !== undefined && statement.aggregates !== undefined && statement.aggregates.length > 0) {
4923
- throw internalError3({ message: "selectExpr and aggregates cannot combine" });
4962
+ throw internalError4({ message: "selectExpr and aggregates cannot combine" });
4924
4963
  }
4925
4964
  const columnList = statement.columns.map((column) => quoteProjection(dialect, column));
4926
4965
  if (statement.selectExpr !== undefined) {
@@ -4941,7 +4980,7 @@ function renderSelect(params) {
4941
4980
  if (statement.rowNumber !== undefined) {
4942
4981
  const rowNumber = statement.rowNumber;
4943
4982
  if (rowNumber.partitionBy.length === 0 && rowNumber.orderBy.length === 0) {
4944
- throw internalError3({ message: "rowNumber needs a partition column or an order" });
4983
+ throw internalError4({ message: "rowNumber needs a partition column or an order" });
4945
4984
  }
4946
4985
  const partition = rowNumber.partitionBy.length === 0 ? "" : `PARTITION BY ${rowNumber.partitionBy.map((column) => quoteCol(dialect, column)).join(", ")}`;
4947
4986
  const order = rowNumber.orderBy.length === 0 ? "" : `${partition === "" ? "" : " "}ORDER BY ${rowNumber.orderBy.map((term) => renderOrderTerm({ dialect, term, collector })).join(", ")}`;
@@ -4949,11 +4988,11 @@ function renderSelect(params) {
4949
4988
  }
4950
4989
  let distinctPrefix = statement.distinct === true ? "DISTINCT " : "";
4951
4990
  if (statement.distinct === true && statement.distinctOn !== undefined) {
4952
- throw internalError3({ message: "distinct and distinctOn cannot combine" });
4991
+ throw internalError4({ message: "distinct and distinctOn cannot combine" });
4953
4992
  }
4954
4993
  if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
4955
4994
  if (!dialect.capabilities.distinctOn) {
4956
- throw unsupportedCapability27({
4995
+ throw unsupportedCapability28({
4957
4996
  dialect: dialect.name,
4958
4997
  feature: "SELECT DISTINCT ON",
4959
4998
  hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
@@ -4969,12 +5008,12 @@ function renderSelect(params) {
4969
5008
  from = `(${renderUnionPage({ dialect, statement, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
4970
5009
  } else if (statement.keyPage !== undefined) {
4971
5010
  if (statement.fromSelect !== undefined || statement.table.alias !== undefined) {
4972
- throw internalError3({ message: "keyPage uses table.name as the derived-table alias \u2014 fromSelect and table.alias must be unset" });
5011
+ throw internalError4({ message: "keyPage uses table.name as the derived-table alias \u2014 fromSelect and table.alias must be unset" });
4973
5012
  }
4974
5013
  from = `(${renderKeyPage({ dialect, page: statement.keyPage, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
4975
5014
  } else if (statement.fromSelect !== undefined) {
4976
5015
  if (statement.table.alias !== undefined) {
4977
- throw internalError3({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
5016
+ throw internalError4({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
4978
5017
  }
4979
5018
  const inner = renderSelect({ dialect, statement: statement.fromSelect, collector });
4980
5019
  from = `(${inner.text}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
@@ -4985,7 +5024,7 @@ function renderSelect(params) {
4985
5024
  if (statement.innerJoins !== undefined && statement.innerJoins.length > 0) {
4986
5025
  for (const join of statement.innerJoins) {
4987
5026
  if (join.on.length === 0) {
4988
- throw internalError3({ message: "inner join needs at least one ON pair" });
5027
+ throw internalError4({ message: "inner join needs at least one ON pair" });
4989
5028
  }
4990
5029
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
4991
5030
  text += ` INNER JOIN ${renderTable(dialect, join.table)} ON ${on}`;
@@ -4996,7 +5035,7 @@ function renderSelect(params) {
4996
5035
  }
4997
5036
  if (statement.leftJoins !== undefined && statement.leftJoins.length > 0) {
4998
5037
  if (statement.rowLock !== undefined) {
4999
- throw internalError3({ message: "a locking read cannot carry a left join \u2014 postgres refuses FOR UPDATE on the nullable side of an outer join" });
5038
+ throw internalError4({ message: "a locking read cannot carry a left join \u2014 postgres refuses FOR UPDATE on the nullable side of an outer join" });
5000
5039
  }
5001
5040
  for (const join of statement.leftJoins) {
5002
5041
  text += ` ${renderLeftJoin({ dialect, join, collector })}`;
@@ -5004,7 +5043,7 @@ function renderSelect(params) {
5004
5043
  }
5005
5044
  if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
5006
5045
  if (!dialect.capabilities.lateralJoin || dialect.lateralJson === null) {
5007
- throw unsupportedCapability27({
5046
+ throw unsupportedCapability28({
5008
5047
  dialect: dialect.name,
5009
5048
  feature: "LATERAL JOIN relation loading",
5010
5049
  hint: 'use the portable "query" relation strategy on this dialect'
@@ -5053,7 +5092,7 @@ function unnestElement(params) {
5053
5092
  return null;
5054
5093
  if (isJsonParamCell(value)) {
5055
5094
  if (jsonStorage === undefined || (value.storage ?? "jsonb") !== jsonStorage) {
5056
- throw internalError3({ message: `unnest insert column "${column}" got a Json cell its declared type does not take` });
5095
+ throw internalError4({ message: `unnest insert column "${column}" got a Json cell its declared type does not take` });
5057
5096
  }
5058
5097
  return value.text;
5059
5098
  }
@@ -5069,24 +5108,24 @@ function unnestElement(params) {
5069
5108
  return value;
5070
5109
  }
5071
5110
  }
5072
- throw internalError3({ message: `unnest insert column "${column}" got a cell that is not a plain value (DEFAULT, the database clock, a document) \u2014 such rows keep VALUES` });
5111
+ throw internalError4({ message: `unnest insert column "${column}" got a cell that is not a plain value (DEFAULT, the database clock, a document) \u2014 such rows keep VALUES` });
5073
5112
  }
5074
5113
  function renderUnnestRows(params) {
5075
5114
  const { dialect, statement, members, collector } = params;
5076
5115
  if (dialect.bulkInsertForm !== "unnest") {
5077
- throw unsupportedCapability27({
5116
+ throw unsupportedCapability28({
5078
5117
  dialect: dialect.name,
5079
5118
  feature: "a bulk insert bound as typed arrays (INSERT \u2026 SELECT FROM unnest)",
5080
5119
  hint: "this dialect binds createMany rows as VALUES"
5081
5120
  });
5082
5121
  }
5083
5122
  if (statement.columns.length === 0)
5084
- throw internalError3({ message: "an unnest insert needs at least one column" });
5123
+ throw internalError4({ message: "an unnest insert needs at least one column" });
5085
5124
  if (members.length !== statement.columns.length) {
5086
- throw internalError3({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5125
+ throw internalError4({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5087
5126
  }
5088
5127
  if (statement.onConflict !== undefined && !("doNothing" in statement.onConflict)) {
5089
- throw internalError3({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5128
+ throw internalError4({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5090
5129
  }
5091
5130
  const aliases = statement.columns.map((_column, index) => dialect.quoteIdent({ name: `c${index + 1}` }));
5092
5131
  const arrays = members.map((member, index) => {
@@ -5102,14 +5141,14 @@ function renderUnnestRows(params) {
5102
5141
  function renderInsert(params) {
5103
5142
  const { dialect, statement, collector } = params;
5104
5143
  if (statement.rows.length === 0) {
5105
- throw internalError3({ message: "insert requires at least one row" });
5144
+ throw internalError4({ message: "insert requires at least one row" });
5106
5145
  }
5107
5146
  const defaultValues = statement.columns.length === 0 && dialect.emptyInsert.form === "defaultValues";
5108
5147
  if (defaultValues && statement.rows.length !== 1) {
5109
- throw internalError3({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5148
+ throw internalError4({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5110
5149
  }
5111
5150
  if (defaultValues && statement.onConflict !== undefined && !(dialect.emptyInsert.form === "defaultValues" && dialect.emptyInsert.conflictClause)) {
5112
- throw unsupportedCapability27({
5151
+ throw unsupportedCapability28({
5113
5152
  dialect: dialect.name,
5114
5153
  feature: "a conflict clause on an all-default row (DEFAULT VALUES)",
5115
5154
  hint: "supply at least one column value, or drop the conflict clause"
@@ -5117,7 +5156,7 @@ function renderInsert(params) {
5117
5156
  }
5118
5157
  for (const row of statement.rows) {
5119
5158
  if (row.length !== statement.columns.length) {
5120
- throw internalError3({
5159
+ throw internalError4({
5121
5160
  message: `insert row has ${row.length} values but ${statement.columns.length} columns`
5122
5161
  });
5123
5162
  }
@@ -5135,7 +5174,7 @@ function renderInsert(params) {
5135
5174
  if (!isSqlDefaultCell(value))
5136
5175
  return collector.add(value);
5137
5176
  if (!dialect.rowDefaultKeyword) {
5138
- throw internalError3({
5177
+ throw internalError4({
5139
5178
  message: `${dialect.name} cannot render a per-row DEFAULT cell \u2014 rows must be grouped by column set`
5140
5179
  });
5141
5180
  }
@@ -5151,7 +5190,7 @@ function renderInsert(params) {
5151
5190
  let target = "";
5152
5191
  if (onConflict.constraint !== undefined) {
5153
5192
  if (dialect.name !== "postgres") {
5154
- throw unsupportedCapability27({
5193
+ throw unsupportedCapability28({
5155
5194
  dialect: dialect.name,
5156
5195
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
5157
5196
  hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
@@ -5167,7 +5206,7 @@ function renderInsert(params) {
5167
5206
  text += ` ON CONFLICT${target} DO NOTHING`;
5168
5207
  } else {
5169
5208
  if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
5170
- throw unsupportedCapability27({
5209
+ throw unsupportedCapability28({
5171
5210
  dialect: dialect.name,
5172
5211
  feature: "a refined conflict target (constraint / partial-index predicate) on ON DUPLICATE KEY UPDATE",
5173
5212
  hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
@@ -5175,52 +5214,69 @@ function renderInsert(params) {
5175
5214
  }
5176
5215
  const keepName = onConflict.keepColumn ?? statement.columns[0];
5177
5216
  if (keepName === undefined) {
5178
- throw internalError3({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
5217
+ throw internalError4({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
5179
5218
  }
5180
5219
  const keep = dialect.quoteIdent({ name: keepName });
5181
5220
  const current = `${dialect.quoteIdent({ name: statement.table.name })}.${keep}`;
5182
5221
  const flag = onConflict.duplicateFlag;
5183
5222
  if (flag !== undefined && !/^\d{1,20}$/.test(flag)) {
5184
- throw internalError3({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
5223
+ throw internalError4({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
5185
5224
  }
5186
5225
  text += flag === undefined ? ` ON DUPLICATE KEY UPDATE ${keep} = ${current}` : ` ON DUPLICATE KEY UPDATE ${keep} = IF(LAST_INSERT_ID(${flag}), ${current}, ${current})`;
5187
5226
  }
5188
5227
  } else {
5189
5228
  const updates = Object.entries(onConflict.update);
5190
5229
  if (updates.length === 0) {
5191
- throw internalError3({ message: "upsert update must set at least one column" });
5230
+ throw internalError4({ message: "upsert update must set at least one column" });
5192
5231
  }
5193
5232
  const selfRef = dialect.quoteIdent({ name: statement.table.name });
5194
5233
  const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true, tableName: statement.table.name });
5195
5234
  if (dialect.upsertForm === "onConflict") {
5196
5235
  if (onConflict.lastInsertIdColumn !== undefined) {
5197
- throw internalError3({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
5236
+ throw internalError4({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
5198
5237
  }
5199
5238
  if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
5200
- throw unsupportedCapability27({
5239
+ throw unsupportedCapability28({
5201
5240
  dialect: dialect.name,
5202
5241
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
5203
5242
  hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
5204
5243
  });
5205
5244
  }
5206
5245
  if (onConflict.constraint !== undefined && onConflict.conflictWhere !== undefined) {
5207
- throw internalError3({
5246
+ throw internalError4({
5208
5247
  message: "a named-constraint conflict target already identifies the index \u2014 it cannot carry a partial predicate too"
5209
5248
  });
5210
5249
  }
5211
5250
  const partial = onConflict.conflictWhere === undefined ? "" : ` WHERE ${onConflict.conflictWhere}`;
5212
5251
  const target = onConflict.constraint !== undefined ? ` ON CONSTRAINT ${dialect.quoteIdent({ name: onConflict.constraint })}` : ` (${onConflict.columns.map((name) => dialect.quoteIdent({ name })).join(", ")})${partial}`;
5213
5252
  text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
5253
+ if (onConflict.updateWhere !== undefined) {
5254
+ if (!dialect.capabilities.conflictUpdateWhere) {
5255
+ throw unsupportedCapability28({
5256
+ dialect: dialect.name,
5257
+ feature: "a guarded conflict update (ON CONFLICT \u2026 DO UPDATE \u2026 WHERE)",
5258
+ hint: "run the guarded upsert as a find-then-write in a transaction instead"
5259
+ });
5260
+ }
5261
+ text += ` WHERE ${renderExpr({ dialect, expr: onConflict.updateWhere, collector })}`;
5262
+ }
5214
5263
  } else {
5264
+ if (onConflict.updateWhere !== undefined) {
5265
+ throw unsupportedCapability28({
5266
+ dialect: dialect.name,
5267
+ feature: "a guarded conflict update (ON DUPLICATE KEY UPDATE has no WHERE)",
5268
+ hint: "run the guarded upsert as a find-then-write in a transaction instead \u2014 the runtime does this when the dialect lacks conflictUpdateWhere"
5269
+ });
5270
+ }
5215
5271
  if (onConflict.constraint !== undefined) {
5216
- throw unsupportedCapability27({
5272
+ throw unsupportedCapability28({
5217
5273
  dialect: dialect.name,
5218
5274
  feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
5219
5275
  hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
5220
5276
  });
5221
5277
  }
5222
5278
  if (onConflict.conflictWhere !== undefined) {
5223
- throw unsupportedCapability27({
5279
+ throw unsupportedCapability28({
5224
5280
  dialect: dialect.name,
5225
5281
  feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
5226
5282
  hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
@@ -5238,7 +5294,7 @@ function renderUpdate(params) {
5238
5294
  const { dialect, statement, collector } = params;
5239
5295
  const assignments = Object.entries(statement.set);
5240
5296
  if (assignments.length === 0) {
5241
- throw internalError3({ message: "update must set at least one column" });
5297
+ throw internalError4({ message: "update must set at least one column" });
5242
5298
  }
5243
5299
  const setSql = renderAssignments({ dialect, entries: assignments, collector, ...params.earlier === undefined ? {} : { steps: params.earlier } });
5244
5300
  let text = `UPDATE ${renderTable(dialect, statement.table)} SET ${setSql}`;
@@ -5261,18 +5317,18 @@ function renderAssignments(params) {
5261
5317
  const read = selfRef === undefined ? column : `${selfRef}.${column}`;
5262
5318
  if (isStepColumnRef(value)) {
5263
5319
  if (params.steps === undefined || !params.steps.has(value.step)) {
5264
- throw internalError3({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
5320
+ throw internalError4({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
5265
5321
  }
5266
5322
  return `${column} = (SELECT ${dialect.quoteIdent({ name: value.column })} FROM ${dialect.quoteIdent({ name: value.step })})`;
5267
5323
  }
5268
5324
  if (isSqlFragment(value)) {
5269
5325
  if (params.allowExcluded !== true || selfRef === undefined)
5270
- throw internalError3({ message: "SQL assignment fragments require a conflict update" });
5326
+ throw internalError4({ message: "SQL assignment fragments require a conflict update" });
5271
5327
  return `${column} = ${renderSqlFragment({ dialect, fragment: value, collector, conflict: { table: params.tableName } })}`;
5272
5328
  }
5273
5329
  if (isExcludedRef(value)) {
5274
5330
  if (params.allowExcluded !== true) {
5275
- throw internalError3({
5331
+ throw internalError4({
5276
5332
  message: `an incoming-row reference (${value.column}) is only meaningful inside an upsert's update arm`
5277
5333
  });
5278
5334
  }
@@ -5280,7 +5336,7 @@ function renderAssignments(params) {
5280
5336
  }
5281
5337
  if (isSqlKeepCell(value)) {
5282
5338
  if (params.allowExcluded !== true || selfRef === undefined)
5283
- throw internalError3({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
5339
+ throw internalError4({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
5284
5340
  return `${column} = ${read}`;
5285
5341
  }
5286
5342
  if (isJsonParamCell(value)) {
@@ -5318,19 +5374,19 @@ var CHAIN_ONE_ALIAS = "__vibe_one";
5318
5374
  function renderChain(params) {
5319
5375
  const { dialect, statement, collector } = params;
5320
5376
  if (dialect.statementChain === null) {
5321
- throw unsupportedCapability27({
5377
+ throw unsupportedCapability28({
5322
5378
  dialect: dialect.name,
5323
5379
  feature: "a statement chain (data-modifying WITH steps)",
5324
5380
  hint: "this dialect accepts only SELECT inside WITH \u2014 gate on SqlDialect.statementChain and keep the multi-statement path"
5325
5381
  });
5326
5382
  }
5327
5383
  if (statement.steps.length === 0)
5328
- throw internalError3({ message: "a statement chain needs at least one step" });
5384
+ throw internalError4({ message: "a statement chain needs at least one step" });
5329
5385
  const defined = new Set;
5330
5386
  const steps = [];
5331
5387
  for (const step of statement.steps) {
5332
5388
  if (step.name === CHAIN_ROWS_ALIAS || step.name === CHAIN_ONE_ALIAS || defined.has(step.name)) {
5333
- throw internalError3({ message: `chain step name "${step.name}" is reserved or repeated` });
5389
+ throw internalError4({ message: `chain step name "${step.name}" is reserved or repeated` });
5334
5390
  }
5335
5391
  const body = renderChainStep({ dialect, statement: step.statement, collector, earlier: defined });
5336
5392
  steps.push(`${dialect.quoteIdent({ name: step.name })} AS (${body})`);
@@ -5338,11 +5394,11 @@ function renderChain(params) {
5338
5394
  }
5339
5395
  const result = statement.result;
5340
5396
  if (result.rowsFrom.length === 0 || result.columns.length === 0) {
5341
- throw internalError3({ message: "a chain result needs at least one source step and one column" });
5397
+ throw internalError4({ message: "a chain result needs at least one source step and one column" });
5342
5398
  }
5343
5399
  for (const name of result.rowsFrom) {
5344
5400
  if (!defined.has(name))
5345
- throw internalError3({ message: `chain result reads undefined step "${name}"` });
5401
+ throw internalError4({ message: `chain result reads undefined step "${name}"` });
5346
5402
  }
5347
5403
  const columnList = result.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
5348
5404
  const union = result.rowsFrom.map((name) => `SELECT ${columnList} FROM ${dialect.quoteIdent({ name })}`).join(" UNION ALL ");
@@ -5351,7 +5407,7 @@ function renderChain(params) {
5351
5407
  const extra = [];
5352
5408
  for (const alias of [result.verdict?.alias, result.tally?.alias]) {
5353
5409
  if (alias !== undefined && result.columns.includes(alias)) {
5354
- throw internalError3({ message: `chain result alias "${alias}" collides with a result column` });
5410
+ throw internalError4({ message: `chain result alias "${alias}" collides with a result column` });
5355
5411
  }
5356
5412
  }
5357
5413
  const rows = dialect.quoteIdent({ name: CHAIN_ROWS_ALIAS });
@@ -5388,21 +5444,21 @@ function renderSelectCell(params) {
5388
5444
  if (isDbNowMarker(value))
5389
5445
  return dialect.renderDbNow({ offsetSeconds: value.offsetSeconds, collector });
5390
5446
  if (isSqlDefaultCell(value)) {
5391
- throw internalError3({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
5447
+ throw internalError4({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
5392
5448
  }
5393
5449
  return collector.add(value);
5394
5450
  }
5395
5451
  function renderRowSource(params) {
5396
5452
  const { dialect, source, collector } = params;
5397
5453
  if (source.columns.length === 0 || source.rows.length === 0) {
5398
- throw internalError3({ message: "a row source needs at least one column and one row" });
5454
+ throw internalError4({ message: "a row source needs at least one column and one row" });
5399
5455
  }
5400
5456
  const typedBy = renderTable(dialect, source.typedBy);
5401
5457
  const columns = source.columns.map((name) => dialect.quoteIdent({ name }));
5402
5458
  const rows = [`(${columns.map((column) => `(SELECT ${column} FROM ${typedBy} WHERE FALSE)`).join(", ")})`];
5403
5459
  for (const row of source.rows) {
5404
5460
  if (row.length !== source.columns.length) {
5405
- throw internalError3({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
5461
+ throw internalError4({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
5406
5462
  }
5407
5463
  rows.push(`(${row.map((value) => renderSelectCell({ dialect, value, collector })).join(", ")})`);
5408
5464
  }
@@ -5414,12 +5470,12 @@ function renderTallyColumn(params) {
5414
5470
  const { dialect, result, defined, collector } = params;
5415
5471
  const tally = result.tally;
5416
5472
  if (tally === undefined)
5417
- throw internalError3({ message: "a chain veto needs a tally column to guard" });
5473
+ throw internalError4({ message: "a chain veto needs a tally column to guard" });
5418
5474
  if (tally.steps.length === 0)
5419
- throw internalError3({ message: "a chain tally needs at least one step" });
5475
+ throw internalError4({ message: "a chain tally needs at least one step" });
5420
5476
  const step = (name) => {
5421
5477
  if (!defined.has(name))
5422
- throw internalError3({ message: `chain tally or veto reads undefined step "${name}"` });
5478
+ throw internalError4({ message: `chain tally or veto reads undefined step "${name}"` });
5423
5479
  return dialect.quoteIdent({ name });
5424
5480
  };
5425
5481
  const count = (name) => `(SELECT count(*) FROM ${step(name)})`;
@@ -5429,12 +5485,12 @@ function renderTallyColumn(params) {
5429
5485
  return `${sum} AS ${dialect.quoteIdent({ name: tally.alias })}`;
5430
5486
  const arms = vetoes.map((veto) => {
5431
5487
  if (!CHAIN_VETO_TOKEN_PATTERN.test(veto.token))
5432
- throw internalError3({ message: `chain veto token "${veto.token}" is not a lower-case word` });
5488
+ throw internalError4({ message: `chain veto token "${veto.token}" is not a lower-case word` });
5433
5489
  if (typeof veto.nonce !== "string" || veto.nonce === "")
5434
- throw internalError3({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
5490
+ throw internalError4({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
5435
5491
  const minRows = veto.minRows ?? 1;
5436
5492
  if (!Number.isSafeInteger(minRows) || minRows < 1)
5437
- throw internalError3({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
5493
+ throw internalError4({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
5438
5494
  const when = minRows === 1 ? `WHEN NOT EXISTS (SELECT 1 FROM ${step(veto.step)})` : `WHEN ${count(veto.step)} < ${minRows}`;
5439
5495
  return `${when} THEN CAST('${CHAIN_VETO_PREFIX}${veto.token}:' || ${collector.add(veto.nonce)} || ':' || ${count(veto.step)} AS integer)`;
5440
5496
  });
@@ -5443,25 +5499,25 @@ function renderTallyColumn(params) {
5443
5499
  function renderInsertSelect(params) {
5444
5500
  const { dialect, statement, collector } = params;
5445
5501
  if (statement.columns.length === 0)
5446
- throw internalError3({ message: "insert requires at least one column" });
5502
+ throw internalError4({ message: "insert requires at least one column" });
5447
5503
  if (statement.cells.length !== statement.columns.length) {
5448
- throw internalError3({
5504
+ throw internalError4({
5449
5505
  message: `insert row has ${statement.cells.length} values but ${statement.columns.length} columns`
5450
5506
  });
5451
5507
  }
5452
5508
  const from = statement.from ?? [];
5453
5509
  for (const name of from) {
5454
5510
  if (!params.earlier.has(name))
5455
- throw internalError3({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
5511
+ throw internalError4({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
5456
5512
  }
5457
5513
  const source = statement.source;
5458
5514
  if (source !== undefined && (params.earlier.has(source.alias) || from.includes(source.alias))) {
5459
- throw internalError3({ message: `row source alias "${source.alias}" collides with a chain step` });
5515
+ throw internalError4({ message: `row source alias "${source.alias}" collides with a chain step` });
5460
5516
  }
5461
5517
  const cells = statement.cells.map((value) => {
5462
5518
  if (isStepColumnRef(value)) {
5463
5519
  if (!from.includes(value.step) && value.step !== source?.alias) {
5464
- throw internalError3({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
5520
+ throw internalError4({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
5465
5521
  }
5466
5522
  return `${dialect.quoteIdent({ name: value.step })}.${dialect.quoteIdent({ name: value.column })}`;
5467
5523
  }
@@ -5480,9 +5536,9 @@ function renderInsertSelect(params) {
5480
5536
  const conflict = statement.conflictDoNothing;
5481
5537
  if (conflict !== undefined) {
5482
5538
  if (conflict.columns.length === 0)
5483
- throw internalError3({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
5539
+ throw internalError4({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
5484
5540
  if (dialect.doNothingForm !== "onConflictDoNothing") {
5485
- throw unsupportedCapability27({
5541
+ throw unsupportedCapability28({
5486
5542
  dialect: dialect.name,
5487
5543
  feature: "ON CONFLICT DO NOTHING on an INSERT \u2026 SELECT chain step",
5488
5544
  hint: "gate on SqlDialect.statementChain and keep the multi-statement path"
@@ -5493,7 +5549,7 @@ function renderInsertSelect(params) {
5493
5549
  return text + applyReturning({ dialect, requested: statement.returning }).suffix;
5494
5550
  }
5495
5551
  // packages/sql/src/dialects/postgres-type-identity.ts
5496
- import { unsupportedCapability as unsupportedCapability28 } from "@vibeorm/schema";
5552
+ import { unsupportedCapability as unsupportedCapability29 } from "@vibeorm/schema";
5497
5553
  var IDENTIFIER = '(?:"(?:[^"\\x00]|"")+"|[a-zA-Z_][a-zA-Z_0-9$]*)';
5498
5554
  var MODIFIER_VALUE = `(?:[+-]?\\d+|${IDENTIFIER}|'(?:[^'\\x00]|'')*')`;
5499
5555
  var TYPE_NAME = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*(\\(\\s*${MODIFIER_VALUE}(?:\\s*,\\s*${MODIFIER_VALUE})*\\s*\\))?\\s*((?:\\[\\s*\\]\\s*)*)$`);
@@ -5520,13 +5576,13 @@ function parsePostgresTypeName(params) {
5520
5576
  }
5521
5577
  function renderPostgresTypeName(params) {
5522
5578
  if (params.dialect.name !== "postgres")
5523
- throw unsupportedCapability28({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5579
+ throw unsupportedCapability29({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5524
5580
  const { dialect, type } = params;
5525
5581
  return `${type.namespace === undefined ? "" : `${dialect.quoteIdent({ name: type.namespace })}.`}${dialect.quoteIdent({ name: type.name })}${type.modifier}${type.array ? "[]" : ""}`;
5526
5582
  }
5527
5583
  function renderPostgresTypeIdentity(params) {
5528
5584
  if (params.dialect.name !== "postgres")
5529
- throw unsupportedCapability28({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5585
+ throw unsupportedCapability29({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5530
5586
  const collector = new ParamCollector(params.dialect);
5531
5587
  const { source } = params;
5532
5588
  let text;
@@ -5539,11 +5595,11 @@ function renderPostgresTypeIdentity(params) {
5539
5595
  return { text, values: collector.values, returning: "none" };
5540
5596
  }
5541
5597
  // packages/sql/src/dialects/postgres-default-identity.ts
5542
- import { unsupportedCapability as unsupportedCapability29 } from "@vibeorm/schema";
5598
+ import { unsupportedCapability as unsupportedCapability30 } from "@vibeorm/schema";
5543
5599
  function renderPostgresDefaultFunctionIdentity(params) {
5544
5600
  const { dialect, table, column } = params;
5545
5601
  if (dialect.name !== "postgres")
5546
- throw unsupportedCapability29({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
5602
+ throw unsupportedCapability30({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
5547
5603
  const collector = new ParamCollector(dialect);
5548
5604
  const namespace = collector.add(table.namespace);
5549
5605
  const name = collector.add(table.name);
@@ -5567,28 +5623,28 @@ WHERE n.nspname = ${namespace} AND c.relname = ${name} AND a.attname = ${field}
5567
5623
  };
5568
5624
  }
5569
5625
  // packages/sql/src/dialects/constraint-owned-index.ts
5570
- import { unsupportedCapability as unsupportedCapability30 } from "@vibeorm/schema";
5626
+ import { unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
5571
5627
  function renderConstraintOwnedIndexDrop(params) {
5572
5628
  if (params.dialect.name !== "postgres")
5573
- throw unsupportedCapability30({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
5629
+ throw unsupportedCapability31({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
5574
5630
  const quote = (name) => params.dialect.quoteIdent({ name });
5575
5631
  return { text: `ALTER TABLE ${quote(params.table)} DROP CONSTRAINT ${quote(params.constraint)};`, values: [], returning: "none" };
5576
5632
  }
5577
5633
  // packages/sql/src/dialects/mysql-check-probe.ts
5578
- import { unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
5634
+ import { unsupportedCapability as unsupportedCapability32 } from "@vibeorm/schema";
5579
5635
  function renderMysqlCheckProbe(params) {
5580
5636
  if (params.dialect.name !== "mysql")
5581
- throw unsupportedCapability31({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
5637
+ throw unsupportedCapability32({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
5582
5638
  const q = (name) => params.dialect.quoteIdent({ name });
5583
5639
  const probe = q(params.probe);
5584
5640
  switch (params.operation) {
5585
5641
  case "clone":
5586
5642
  if (params.table === undefined)
5587
- throw unsupportedCapability31({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
5643
+ throw unsupportedCapability32({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
5588
5644
  return { text: `CREATE TEMPORARY TABLE ${probe} LIKE ${q(params.table.namespace)}.${q(params.table.name)}`, values: [], returning: "none" };
5589
5645
  case "add": {
5590
5646
  if (params.expression === undefined || params.definition === undefined)
5591
- throw unsupportedCapability31({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
5647
+ throw unsupportedCapability32({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
5592
5648
  const parts = tableDefinitionParts({ sql: params.definition, backslashEscapes: true });
5593
5649
  return { text: `CREATE TEMPORARY TABLE ${probe} (
5594
5650
  ${[...parts.clauses, `CONSTRAINT ${probe} CHECK (${params.expression})`].join(`,
@@ -5634,33 +5690,33 @@ function sqliteTableConstraints(params) {
5634
5690
  return result;
5635
5691
  }
5636
5692
  // packages/sql/src/dialects/migration-visibility.ts
5637
- import { unsupportedCapability as unsupportedCapability33 } from "@vibeorm/schema";
5693
+ import { unsupportedCapability as unsupportedCapability34 } from "@vibeorm/schema";
5638
5694
 
5639
5695
  // packages/sql/src/dialects/migration-inventory.ts
5640
- import { unsupportedCapability as unsupportedCapability32 } from "@vibeorm/schema";
5696
+ import { unsupportedCapability as unsupportedCapability33 } from "@vibeorm/schema";
5641
5697
  var MYSQL_NO_ROW_LIMIT = " LIMIT 18446744073709551615";
5642
5698
  function renderSqliteColumnInventory(params) {
5643
5699
  if (params.dialect.name !== "sqlite")
5644
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5700
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5645
5701
  const collector = new ParamCollector(params.dialect);
5646
5702
  return { text: `SELECT name, hidden, pk, dflt_value AS definition FROM pragma_table_xinfo(${collector.add(params.table)}, ${collector.add(params.namespace)}) ORDER BY cid`, values: collector.values, returning: "none" };
5647
5703
  }
5648
5704
  function renderSqliteForeignKeyInventory(params) {
5649
5705
  if (params.dialect.name !== "sqlite")
5650
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5706
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5651
5707
  const collector = new ParamCollector(params.dialect);
5652
5708
  return { text: `SELECT id, seq, "from", "to", "table", on_update, on_delete, match FROM pragma_foreign_key_list(${collector.add(params.table)}, ${collector.add(params.namespace)}) ORDER BY id, seq`, values: collector.values, returning: "none" };
5653
5709
  }
5654
5710
  function renderSqliteColumnInventoryBatch(params) {
5655
5711
  if (params.dialect.name !== "sqlite")
5656
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5712
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5657
5713
  const collector = new ParamCollector(params.dialect);
5658
5714
  const request = params.dialect.quoteIdent({ name: "vibe_request" });
5659
5715
  return { text: `SELECT j.key AS ${request}, x.name AS name, x.hidden AS hidden, x.pk AS pk, x.dflt_value AS definition FROM json_each(${collector.add(JSON.stringify(params.tables))}) j LEFT JOIN pragma_table_xinfo(j.value, ${collector.add(params.namespace)}) x ORDER BY j.key, x.cid`, values: collector.values, returning: "none" };
5660
5716
  }
5661
5717
  function renderSqliteForeignKeyInventoryBatch(params) {
5662
5718
  if (params.dialect.name !== "sqlite")
5663
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5719
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5664
5720
  const collector = new ParamCollector(params.dialect);
5665
5721
  const request = params.dialect.quoteIdent({ name: "vibe_request" });
5666
5722
  return { text: `SELECT j.key AS ${request}, f.id AS id, f.seq AS seq, f."from" AS "from", f."to" AS "to", f."table" AS "table", f.on_update AS on_update, f.on_delete AS on_delete, f.match AS match FROM json_each(${collector.add(JSON.stringify(params.tables))}) j LEFT JOIN pragma_foreign_key_list(j.value, ${collector.add(params.namespace)}) f ORDER BY j.key, f.id, f.seq`, values: collector.values, returning: "none" };
@@ -5673,15 +5729,15 @@ var SQLITE_INTROSPECTION_PRAGMA_ORDER = Object.freeze({
5673
5729
  });
5674
5730
  function renderSqliteIntrospectionPragmaBatch(params) {
5675
5731
  if (params.dialect.name !== "sqlite")
5676
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteIntrospectionPragmaBatch" });
5732
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteIntrospectionPragmaBatch" });
5677
5733
  const order = SQLITE_INTROSPECTION_PRAGMA_ORDER[params.pragma];
5678
5734
  if (order === undefined)
5679
- throw unsupportedCapability32({ dialect: "sqlite", feature: `introspectionPragma:${String(params.pragma)}` });
5735
+ throw unsupportedCapability33({ dialect: "sqlite", feature: `introspectionPragma:${String(params.pragma)}` });
5680
5736
  return { text: `SELECT j.key AS ${params.dialect.quoteIdent({ name: "vibe_request" })}, p.* FROM json_each(${params.dialect.quoteStringLiteral({ value: JSON.stringify(params.names) })}) j LEFT JOIN pragma_${params.pragma}(j.value) p ORDER BY j.key, ${order}`, values: [], returning: "none" };
5681
5737
  }
5682
5738
  function renderSqliteRebuildObjects(params) {
5683
5739
  if (params.dialect.name !== "sqlite")
5684
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
5740
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
5685
5741
  return { text: "SELECT name, tbl_name, type, sql FROM main.sqlite_schema WHERE type IN ('index', 'trigger') AND sql IS NOT NULL ORDER BY type, name", values: [], returning: "none" };
5686
5742
  }
5687
5743
  function renderMigrationInventory(params) {
@@ -5723,13 +5779,13 @@ ORDER BY kind, parent, name${MYSQL_NO_ROW_LIMIT}`, values: collector.values, ret
5723
5779
  // packages/sql/src/dialects/migration-visibility.ts
5724
5780
  function renderMigrationVisibility(params) {
5725
5781
  if (params.dialect.name !== "mysql")
5726
- throw unsupportedCapability33({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
5782
+ throw unsupportedCapability34({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
5727
5783
  const collector = new ParamCollector(params.dialect);
5728
5784
  const grantee = "CONCAT(QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)), '@', QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', -1)))";
5729
5785
  return { text: `SELECT PRIVILEGE_TYPE AS privilege FROM information_schema.USER_PRIVILEGES WHERE GRANTEE = ${grantee} UNION SELECT PRIVILEGE_TYPE AS privilege FROM information_schema.SCHEMA_PRIVILEGES WHERE GRANTEE = ${grantee} AND ${collector.add(params.namespace)} LIKE TABLE_SCHEMA${MYSQL_NO_ROW_LIMIT}`, values: collector.values, returning: "none" };
5730
5786
  }
5731
5787
  // packages/sql/src/dialects/migration-objects.ts
5732
- import { internalError as internalError4, unsupportedCapability as unsupportedCapability34 } from "@vibeorm/schema";
5788
+ import { internalError as internalError5, unsupportedCapability as unsupportedCapability35 } from "@vibeorm/schema";
5733
5789
  function renderMigrationObjectQuery(params) {
5734
5790
  return objectQuery({ ...params, collector: new ParamCollector(params.dialect) });
5735
5791
  }
@@ -5737,7 +5793,7 @@ function objectQuery(params) {
5737
5793
  const { dialect, identity: object, collector } = params;
5738
5794
  if (object.kind === "namespace") {
5739
5795
  if (dialect.name !== "postgres")
5740
- throw unsupportedCapability34({ dialect: dialect.name, feature: "fixedNamespaces" });
5796
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "fixedNamespaces" });
5741
5797
  return { text: `SELECT n.nspname::text AS "name" FROM pg_catalog.pg_namespace n WHERE n.nspname = ${collector.add(object.name)}`, values: collector.values, returning: "none" };
5742
5798
  }
5743
5799
  const physical = "table" in object ? object.table : object.name;
@@ -5785,18 +5841,18 @@ function objectQuery(params) {
5785
5841
  }
5786
5842
  } else if (dialect.name === "mysql") {
5787
5843
  if (object.kind === "constraint" && object.constraintType === "exclusion")
5788
- throw unsupportedCapability34({ dialect: "mysql", feature: "exclusionConstraints" });
5844
+ throw unsupportedCapability35({ dialect: "mysql", feature: "exclusionConstraints" });
5789
5845
  const mysql = mysqlFactsText({ dialect, identity: object, namespace, name, collector });
5790
5846
  if (mysql === undefined)
5791
- throw unsupportedCapability34({ dialect: "mysql", feature: `inspect:${object.kind}` });
5847
+ throw unsupportedCapability35({ dialect: "mysql", feature: `inspect:${object.kind}` });
5792
5848
  text = mysql;
5793
5849
  } else {
5794
5850
  if (physical.namespace !== "main" && physical.namespace !== "temp")
5795
- throw unsupportedCapability34({ dialect: "sqlite", feature: "attachedSchemaInspection" });
5851
+ throw unsupportedCapability35({ dialect: "sqlite", feature: "attachedSchemaInspection" });
5796
5852
  const catalog = `${dialect.quoteIdent({ name: physical.namespace })}.sqlite_schema`;
5797
5853
  const kind = object.kind === "table" || object.kind === "view" ? object.kind : object.kind === "trigger" ? "trigger" : object.kind === "index" ? "index" : "table";
5798
5854
  if (["function", "sequence", "postgresExtension", "enum"].includes(object.kind) || object.kind === "constraint" && object.constraintType === "exclusion")
5799
- throw unsupportedCapability34({ dialect: "sqlite", feature: `inspect:${object.kind}` });
5855
+ throw unsupportedCapability35({ dialect: "sqlite", feature: `inspect:${object.kind}` });
5800
5856
  const sqliteParams = new ParamCollector(dialect);
5801
5857
  if (object.kind === "column" || object.kind === "columnDefault" || object.kind === "generatedColumn") {
5802
5858
  text = `SELECT x.type AS type, x."notnull" AS "notNull", x.pk AS primaryKeyPosition, x.hidden AS generated, x.dflt_value AS definition, (SELECT sql FROM ${catalog} WHERE name = ${sqliteParams.add(physical.name)} AND type = 'table') AS tableDefinition FROM pragma_table_xinfo(${sqliteParams.add(physical.name)}, ${sqliteParams.add(physical.namespace)}) x WHERE x.name = ${sqliteParams.add(object.column)}`;
@@ -5871,7 +5927,7 @@ function mysqlBatches(params) {
5871
5927
  function renderMysqlObjectBatch(params) {
5872
5928
  const { dialect } = params;
5873
5929
  if (dialect.name !== "mysql")
5874
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5930
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5875
5931
  const position = dialect.quoteIdent({ name: "vibe_position" });
5876
5932
  const groups = new Map;
5877
5933
  for (const [index, identity] of params.identities.entries()) {
@@ -5891,7 +5947,7 @@ function renderMysqlObjectBatch(params) {
5891
5947
  function renderMysqlObjectMembersBatch(params) {
5892
5948
  const { dialect } = params;
5893
5949
  if (dialect.name !== "mysql")
5894
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5950
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5895
5951
  const position = dialect.quoteIdent({ name: "vibe_position" });
5896
5952
  const ordinal = dialect.quoteIdent({ name: "vibe_ordinal" });
5897
5953
  const groups = new Map;
@@ -5911,7 +5967,7 @@ function renderMigrationDependencies(params) {
5911
5967
  }
5912
5968
  function dependencyQuery(params) {
5913
5969
  if (params.dialect.name !== "postgres")
5914
- throw unsupportedCapability34({ dialect: params.dialect.name, feature: "catalogDependencies" });
5970
+ throw unsupportedCapability35({ dialect: params.dialect.name, feature: "catalogDependencies" });
5915
5971
  const object = params.identity;
5916
5972
  const collector = params.collector;
5917
5973
  if (object.kind === "namespace") {
@@ -5968,7 +6024,7 @@ function inspectionReferences(params) {
5968
6024
  function renderMigrationObjectBatch(params) {
5969
6025
  const { dialect } = params;
5970
6026
  if (dialect.name !== "postgres")
5971
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
6027
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5972
6028
  const groups = new Map;
5973
6029
  for (const [position, identity] of params.identities.entries()) {
5974
6030
  const kind = identity.kind === "constraint" ? `${identity.kind}:${identity.constraintType}` : identity.kind === "generatedColumn" ? "columnDefault" : identity.kind;
@@ -5981,7 +6037,7 @@ function renderMigrationObjectBatch(params) {
5981
6037
  const facts = objectQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
5982
6038
  const dependencies = dependencyQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
5983
6039
  if (JSON.stringify(dependencies.values) !== JSON.stringify(facts.values))
5984
- throw internalError4({ message: "Batched catalog queries bind their identity in different orders", meta: { kind: identity.kind } });
6040
+ throw internalError5({ message: "Batched catalog queries bind their identity in different orders", meta: { kind: identity.kind } });
5985
6041
  const columns = facts.values.map((value, index) => `${dialect.quoteIdent({ name: `p${index + 1}` })} ${Array.isArray(value) ? "text[]" : "text"}`);
5986
6042
  const requests = group.map(({ identity, position }) => ({ position, ...Object.fromEntries(renderMigrationObjectQuery({ dialect, identity }).values.map((value, index) => [`p${index + 1}`, value])) }));
5987
6043
  const collector = new ParamCollector(dialect);
@@ -6001,16 +6057,16 @@ function renderMigrationNamespace(params) {
6001
6057
  }
6002
6058
  function renderCheckProbe(params) {
6003
6059
  if (params.dialect.name !== "postgres")
6004
- throw unsupportedCapability34({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
6060
+ throw unsupportedCapability35({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
6005
6061
  const q = (name) => params.dialect.quoteIdent({ name });
6006
6062
  return { text: `ALTER TABLE ${q(params.table.namespace)}.${q(params.table.name)} ADD CONSTRAINT ${q(params.name)} CHECK (${params.expression}) NOT VALID`, values: [], returning: "none" };
6007
6063
  }
6008
6064
  // packages/sql/src/dialects/postgres-migration.ts
6009
- import { VibeError as VibeError23, unsupportedCapability as unsupportedCapability35 } from "@vibeorm/schema";
6065
+ import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability36 } from "@vibeorm/schema";
6010
6066
  var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
6011
6067
  function requirePostgres2(params) {
6012
6068
  if (params.dialect.name !== "postgres") {
6013
- throw unsupportedCapability35({ dialect: params.dialect.name, feature: params.feature });
6069
+ throw unsupportedCapability36({ dialect: params.dialect.name, feature: params.feature });
6014
6070
  }
6015
6071
  }
6016
6072
  function renderMigrationSessionRead(params) {
@@ -6025,7 +6081,7 @@ function renderMigrationSessionSet(params) {
6025
6081
  requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
6026
6082
  const { milliseconds, setting } = params;
6027
6083
  if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
6028
- throw new VibeError23({
6084
+ throw new VibeError24({
6029
6085
  code: "VIBE_MIGRATION",
6030
6086
  message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
6031
6087
  meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
@@ -6196,6 +6252,7 @@ export {
6196
6252
  isSqlParam,
6197
6253
  isSqlQuery,
6198
6254
  isStepColumnRef,
6255
+ isTimeZoneName,
6199
6256
  jsonListParameter,
6200
6257
  jsonParamCell,
6201
6258
  keyListBindsUntypedRows,
@@ -6222,6 +6279,7 @@ export {
6222
6279
  renderColumnStateQuery,
6223
6280
  renderConstraintOwnedIndexDrop,
6224
6281
  renderConstraintStateQuery,
6282
+ renderDateTruncation,
6225
6283
  renderDiagnostic,
6226
6284
  renderExclusionCatalog,
6227
6285
  renderExclusionDdl,
@@ -6322,4 +6380,4 @@ export {
6322
6380
  toPgArrayLiteral
6323
6381
  };
6324
6382
 
6325
- //# debugId=0FD86C4E8226D05064756E2164756E21
6383
+ //# debugId=DB7441716F57EC8864756E2164756E21