@vibeorm/sql 5.0.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2402,6 +2402,12 @@ var sqliteDialect = {
2402
2402
  quoteStringLiteral({ value }) {
2403
2403
  return `'${value.replaceAll("'", "''")}'`;
2404
2404
  },
2405
+ renderEnumOrdinal({ column }) {
2406
+ return column;
2407
+ },
2408
+ renderEnumMinMax({ fn, column }) {
2409
+ return `${fn === "min" ? "MIN" : "MAX"}(${column})`;
2410
+ },
2405
2411
  placeholder() {
2406
2412
  return "?";
2407
2413
  },
@@ -3075,6 +3081,12 @@ var postgresDialect = {
3075
3081
  quoteStringLiteral({ value }) {
3076
3082
  return `'${value.replaceAll("'", "''")}'`;
3077
3083
  },
3084
+ renderEnumOrdinal({ column }) {
3085
+ return column;
3086
+ },
3087
+ renderEnumMinMax({ fn, column }) {
3088
+ return `${fn === "min" ? "MIN" : "MAX"}(${column})`;
3089
+ },
3078
3090
  placeholder({ index }) {
3079
3091
  return `$${index}`;
3080
3092
  },
@@ -3148,6 +3160,15 @@ var mysqlDialect = {
3148
3160
  quoteStringLiteral({ value }) {
3149
3161
  return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "''")}'`;
3150
3162
  },
3163
+ renderEnumOrdinal({ column }) {
3164
+ return `${column} + 0`;
3165
+ },
3166
+ renderEnumMinMax({ fn, column, result }) {
3167
+ const aggregate = fn === "min" ? "MIN" : "MAX";
3168
+ if (result === "ordinal")
3169
+ return `${aggregate}(${column} + 0)`;
3170
+ return `SUBSTRING(${aggregate}(CONCAT(LPAD(${column} + 0, 5, '0'), ${column})), 6)`;
3171
+ },
3151
3172
  placeholder() {
3152
3173
  return "?";
3153
3174
  },
@@ -3281,17 +3302,53 @@ function renderLocalDateCheck({ dialect, table, column }) {
3281
3302
  expression: `${quoted} IS date(${quoted}) AND (${quoted} IS NULL OR ${quoted} >= ${dialect.quoteStringLiteral({ value: "0001-01-01" })})`
3282
3303
  };
3283
3304
  }
3305
+ // packages/sql/src/date-truncation.ts
3306
+ import { DATE_TRUNCATION_UNITS, VibeError as VibeError16, internalError as internalError2, unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3307
+ var TIME_ZONE_NAME = /^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/;
3308
+ function isTimeZoneName(params) {
3309
+ return typeof params.value === "string" && TIME_ZONE_NAME.test(params.value);
3310
+ }
3311
+ function renderDateTruncation(params) {
3312
+ const { dialect, reference, truncation } = params;
3313
+ if (!dialect.capabilities.dateTruncation) {
3314
+ throw unsupportedCapability17({
3315
+ dialect: dialect.name,
3316
+ feature: "a truncated groupBy key ({ field, truncate, timeZone })",
3317
+ 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"
3318
+ });
3319
+ }
3320
+ if (!DATE_TRUNCATION_UNITS.includes(truncation.unit)) {
3321
+ throw internalError2({ message: `unknown date truncation unit ${JSON.stringify(truncation.unit)}` });
3322
+ }
3323
+ if (!isTimeZoneName({ value: truncation.timeZone })) {
3324
+ throw new VibeError16({
3325
+ code: "VIBE_VALIDATION",
3326
+ message: `time zone ${JSON.stringify(truncation.timeZone)} is not a time-zone name \u2014 use an IANA name such as "Europe/Oslo" or "UTC"`,
3327
+ meta: { timeZone: truncation.timeZone }
3328
+ });
3329
+ }
3330
+ const unit = dialect.quoteStringLiteral({ value: truncation.unit });
3331
+ const zone = dialect.quoteStringLiteral({ value: truncation.timeZone });
3332
+ switch (truncation.storage) {
3333
+ case "timestamptz":
3334
+ return `date_trunc(${unit}, ${reference}, ${zone})`;
3335
+ case "timestamp":
3336
+ return `(date_trunc(${unit}, ${reference} AT TIME ZONE 'UTC', ${zone}) AT TIME ZONE 'UTC')`;
3337
+ default:
3338
+ throw internalError2({ message: `unknown date truncation storage ${JSON.stringify(truncation.storage)}` });
3339
+ }
3340
+ }
3284
3341
  // packages/sql/src/generated-columns.ts
3285
- import { VibeError as VibeError16, unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
3342
+ import { VibeError as VibeError17, unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
3286
3343
 
3287
3344
  // packages/sql/src/dialects/postgres-generated-columns.ts
3288
- import { unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3345
+ import { unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
3289
3346
  function renderPostgresGeneratedColumnClause({ generated }) {
3290
3347
  return `GENERATED ALWAYS AS (${generated.expression}) STORED`;
3291
3348
  }
3292
3349
  function renderPostgresGeneratedColumnProbe({ dialect, probe }) {
3293
3350
  if (dialect.name !== "postgres")
3294
- throw unsupportedCapability17({ dialect: dialect.name, feature: "rollbackGeneratedColumnProbe" });
3351
+ throw unsupportedCapability18({ dialect: dialect.name, feature: "rollbackGeneratedColumnProbe" });
3295
3352
  const q = ({ name }) => dialect.quoteIdent({ name });
3296
3353
  let text;
3297
3354
  switch (probe.operation) {
@@ -3312,20 +3369,20 @@ function renderPostgresGeneratedColumnProbe({ dialect, probe }) {
3312
3369
  }
3313
3370
  function renderPostgresDropGeneratedExpression({ dialect, table, column }) {
3314
3371
  if (dialect.name !== "postgres")
3315
- throw unsupportedCapability17({ dialect: dialect.name, feature: "dropGeneratedExpression" });
3372
+ throw unsupportedCapability18({ dialect: dialect.name, feature: "dropGeneratedExpression" });
3316
3373
  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
3374
  }
3318
3375
 
3319
3376
  // packages/sql/src/generated-columns.ts
3320
3377
  function renderGeneratedColumnClause({ dialect, generated }) {
3321
3378
  if (!("generatedColumns" in dialect.capabilities) || dialect.capabilities.generatedColumns !== true) {
3322
- throw unsupportedCapability18({ dialect: dialect.name, feature: "generatedColumns" });
3379
+ throw unsupportedCapability19({ dialect: dialect.name, feature: "generatedColumns" });
3323
3380
  }
3324
3381
  if (generated.storage !== "stored") {
3325
- throw unsupportedCapability18({ dialect: dialect.name, feature: "virtualGeneratedColumns" });
3382
+ throw unsupportedCapability19({ dialect: dialect.name, feature: "virtualGeneratedColumns" });
3326
3383
  }
3327
3384
  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" } });
3385
+ throw new VibeError17({ code: "VIBE_SCHEMA_INVALID", message: "A generated column requires non-empty, NUL-free SQL", meta: { reason: "generated-expression-invalid" } });
3329
3386
  }
3330
3387
  switch (dialect.name) {
3331
3388
  case "postgres":
@@ -3337,10 +3394,10 @@ function renderGeneratedColumnClause({ dialect, generated }) {
3337
3394
  }
3338
3395
  }
3339
3396
  // 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";
3397
+ import { VibeError as VibeError21, internalError as internalError3, normalizeRowRules, rowRuleGateField, rowRuleIdentities, rowRuleProtectedFields, rowRuleShapeIssue, rowRuleTriggerName, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
3341
3398
 
3342
3399
  // packages/sql/src/dialects/postgres-row-rules.ts
3343
- import { VibeError as VibeError17, unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
3400
+ import { VibeError as VibeError18, unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
3344
3401
  function comparison({ dialect, field }) {
3345
3402
  const column = dialect.quoteIdent({ name: field.dbName ?? field.name });
3346
3403
  const cast = field.type === "Json" ? `::pg_catalog.jsonb${field.isList ? "[]" : ""}` : "";
@@ -3348,7 +3405,7 @@ function comparison({ dialect, field }) {
3348
3405
  }
3349
3406
  function renderPostgresRowRule({ dialect, context }) {
3350
3407
  if (dialect.name !== "postgres")
3351
- throw unsupportedCapability19({ dialect: dialect.name, feature: "postgresRowRules" });
3408
+ throw unsupportedCapability20({ dialect: dialect.name, feature: "postgresRowRules" });
3352
3409
  const { rule, table, functionIdentity, triggerIdentity, protectedFields, gateField } = context;
3353
3410
  const qualify = ({ name }) => `${dialect.quoteIdent({ name: name.namespace })}.${dialect.quoteIdent({ name: name.name })}`;
3354
3411
  const functionName = qualify({ name: functionIdentity.name });
@@ -3365,7 +3422,7 @@ END;`;
3365
3422
  } else {
3366
3423
  const terms = protectedFields.map((field) => comparison({ dialect, field }));
3367
3424
  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 } });
3425
+ throw new VibeError18({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3369
3426
  }
3370
3427
  const condition = gateField === undefined ? terms.join(`
3371
3428
  OR `) : `OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
@@ -3400,19 +3457,19 @@ FOR EACH ROW EXECUTE FUNCTION ${functionName}();`;
3400
3457
  }
3401
3458
 
3402
3459
  // packages/sql/src/dialects/sqlite-row-rules.ts
3403
- import { SQLITE_ROW_RULE_ERROR_PREFIX, VibeError as VibeError18, unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
3460
+ import { SQLITE_ROW_RULE_ERROR_PREFIX, VibeError as VibeError19, unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
3404
3461
  function renderSqliteRowRule({ dialect, context }) {
3405
3462
  if (dialect.name !== "sqlite")
3406
- throw unsupportedCapability20({ dialect: dialect.name, feature: "sqliteRowRules" });
3463
+ throw unsupportedCapability21({ dialect: dialect.name, feature: "sqliteRowRules" });
3407
3464
  const { rule, table, triggers, protectedFields, gateField } = context;
3408
3465
  if (table.namespace !== "main")
3409
- throw unsupportedCapability20({ dialect: dialect.name, feature: "fixedNamespaces" });
3466
+ throw unsupportedCapability21({ dialect: dialect.name, feature: "fixedNamespaces" });
3410
3467
  const terms = protectedFields.map((field) => {
3411
3468
  const column = dialect.quoteIdent({ name: field.dbName ?? field.name });
3412
3469
  return `OLD.${column} IS NOT NEW.${column}`;
3413
3470
  });
3414
3471
  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 } });
3472
+ throw new VibeError19({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3416
3473
  const condition = rule.kind === "appendOnly" ? "" : gateField === undefined ? `WHEN ${terms.join(`
3417
3474
  OR `)}
3418
3475
  ` : `WHEN OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
@@ -3437,11 +3494,11 @@ END;`,
3437
3494
  }
3438
3495
 
3439
3496
  // 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";
3497
+ import { MYSQL_ROW_RULE_MESSAGE_MAX_BYTES, VibeError as VibeError20, mysqlRowRuleMessageByteLength, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
3441
3498
  function diagnosticLiteral({ name, context, dialect }) {
3442
3499
  const message = `Check constraint '${name}' is violated.`;
3443
3500
  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 } });
3501
+ 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
3502
  if (message.includes("\\")) {
3446
3503
  const hex = Array.from(new TextEncoder().encode(message), (byte) => byte.toString(16).padStart(2, "0")).join("");
3447
3504
  return `_utf8mb4 X'${hex}'`;
@@ -3450,11 +3507,11 @@ function diagnosticLiteral({ name, context, dialect }) {
3450
3507
  }
3451
3508
  function renderMysqlRowRule({ dialect, context }) {
3452
3509
  if (dialect.name !== "mysql")
3453
- throw unsupportedCapability21({ dialect: dialect.name, feature: "mysqlRowRules" });
3510
+ throw unsupportedCapability22({ dialect: dialect.name, feature: "mysqlRowRules" });
3454
3511
  const { rule, table, triggers, protectedFields, gateField, placement } = context;
3455
3512
  for (const name of Object.keys(placement ?? {}))
3456
3513
  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 } });
3514
+ 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
3515
  const position = ({ name }) => {
3459
3516
  const entry = placement?.[name];
3460
3517
  if (entry === undefined)
@@ -3467,7 +3524,7 @@ function renderMysqlRowRule({ dialect, context }) {
3467
3524
  return `NOT (OLD.${column} <=> NEW.${column})`;
3468
3525
  });
3469
3526
  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 } });
3527
+ throw new VibeError20({ code: "VIBE_SCHEMA_INVALID", message: `Row rule ${rule.name} has no protected fields`, meta: { model: context.model, rule: rule.name } });
3471
3528
  const condition = gateField === undefined ? terms.join(`
3472
3529
  OR `) : `OLD.${dialect.quoteIdent({ name: gateField.dbName ?? gateField.name })} IS NOT NULL AND (
3473
3530
  ${terms.join(`
@@ -3496,10 +3553,10 @@ END;`, values: [] };
3496
3553
  function renderRowRule({ schema, model, rule, dialect, placement }) {
3497
3554
  const issue = rowRuleShapeIssue({ value: rule });
3498
3555
  if (issue !== undefined) {
3499
- throw new VibeError20({ code: "VIBE_SCHEMA_INVALID", message: issue, meta: { model: model.name } });
3556
+ throw new VibeError21({ code: "VIBE_SCHEMA_INVALID", message: issue, meta: { model: model.name } });
3500
3557
  }
3501
3558
  if (placement !== undefined && Object.keys(placement).length > 0 && dialect.name !== "mysql")
3502
- throw unsupportedCapability22({ dialect: dialect.name, feature: "rowRuleTriggerPlacement" });
3559
+ throw unsupportedCapability23({ dialect: dialect.name, feature: "rowRuleTriggerPlacement" });
3503
3560
  const normalized = normalizeRowRules({ schema, model, rules: [rule], dialect: dialect.name })[0];
3504
3561
  switch (dialect.name) {
3505
3562
  case "postgres": {
@@ -3507,7 +3564,7 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
3507
3564
  const functionIdentity = objects.find((object) => object.role === "function")?.identity;
3508
3565
  const triggerIdentity = objects.find((object) => object.role === "trigger")?.identity;
3509
3566
  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 } });
3567
+ throw internalError3({ message: "PostgreSQL row rules own one function and one trigger", meta: { model: model.name, rule: normalized.name } });
3511
3568
  const gate = rowRuleGateField({ rule: normalized });
3512
3569
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
3513
3570
  return renderPostgresRowRule({
@@ -3528,15 +3585,15 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
3528
3585
  const objects = rowRuleIdentities({ schema, model, rule: normalized, dialect: dialect.name });
3529
3586
  const triggers = objects.map(({ role, identity }) => {
3530
3587
  if (role !== "trigger" || identity.kind !== "trigger")
3531
- throw internalError2({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
3588
+ throw internalError3({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
3532
3589
  const event = ["update", "delete"].find((event) => identity.name === rowRuleTriggerName({ rule: normalized, event }));
3533
3590
  if (event === undefined)
3534
- throw internalError2({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
3591
+ throw internalError3({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
3535
3592
  return { event, identity: { kind: "trigger", table: identity.table, name: identity.name } };
3536
3593
  });
3537
3594
  const table = triggers[0]?.identity.table;
3538
3595
  if (table === undefined)
3539
- throw internalError2({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
3596
+ throw internalError3({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
3540
3597
  const gate = rowRuleGateField({ rule: normalized });
3541
3598
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
3542
3599
  const context = {
@@ -3567,7 +3624,7 @@ function renderSqliteJsonEquality(params) {
3567
3624
  }
3568
3625
 
3569
3626
  // packages/sql/src/dialects/diagnostics.ts
3570
- import { unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
3627
+ import { unsupportedCapability as unsupportedCapability24 } from "@vibeorm/schema";
3571
3628
  var QUERIES = Object.freeze({
3572
3629
  postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
3573
3630
  sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
@@ -3575,7 +3632,7 @@ var QUERIES = Object.freeze({
3575
3632
  function renderDiagnostic(params) {
3576
3633
  const query = QUERIES[params.query];
3577
3634
  if (params.dialect.name !== query.dialect) {
3578
- throw unsupportedCapability23({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3635
+ throw unsupportedCapability24({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3579
3636
  }
3580
3637
  return { text: query.text, values: [], returning: "none" };
3581
3638
  }
@@ -3584,7 +3641,7 @@ function isFixedDiagnostic(params) {
3584
3641
  }
3585
3642
 
3586
3643
  // packages/sql/src/dialects/postgres-diagnostics.ts
3587
- import { VibeError as VibeError21, unsupportedCapability as unsupportedCapability24 } from "@vibeorm/schema";
3644
+ import { VibeError as VibeError22, unsupportedCapability as unsupportedCapability25 } from "@vibeorm/schema";
3588
3645
  function renderPostgresExplain(params) {
3589
3646
  const { dialect, query, options } = params;
3590
3647
  requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
@@ -3619,7 +3676,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
3619
3676
  });
3620
3677
  function requirePostgres(params) {
3621
3678
  if (params.dialect.name !== "postgres") {
3622
- throw unsupportedCapability24({
3679
+ throw unsupportedCapability25({
3623
3680
  dialect: params.dialect.name,
3624
3681
  feature: params.feature,
3625
3682
  hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
@@ -3637,7 +3694,7 @@ function renderPostgresWorkload(params) {
3637
3694
  if (query === "statementSnapshot") {
3638
3695
  const bounded = limit ?? 20;
3639
3696
  if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
3640
- throw new VibeError21({
3697
+ throw new VibeError22({
3641
3698
  code: "VIBE_CONFIG",
3642
3699
  message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
3643
3700
  meta: { limit }
@@ -3656,7 +3713,7 @@ function renderPostgresWorkload(params) {
3656
3713
  function requireArg(params) {
3657
3714
  const { value, name, query } = params;
3658
3715
  if (value === undefined || value.length === 0) {
3659
- throw new VibeError21({
3716
+ throw new VibeError22({
3660
3717
  code: "VIBE_CONFIG",
3661
3718
  message: `the "${query}" diagnostic read requires a ${name}`,
3662
3719
  meta: { query, argument: name }
@@ -3666,10 +3723,10 @@ function requireArg(params) {
3666
3723
  }
3667
3724
 
3668
3725
  // packages/sql/src/dialects/postgres-rls-readiness.ts
3669
- import { unsupportedCapability as unsupportedCapability26 } from "@vibeorm/schema";
3726
+ import { unsupportedCapability as unsupportedCapability27 } from "@vibeorm/schema";
3670
3727
 
3671
3728
  // packages/sql/src/dialects/postgres-rls.ts
3672
- import { RlsContextError, VibeError as VibeError22, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability25, validateRlsContextIR } from "@vibeorm/schema";
3729
+ import { RlsContextError, VibeError as VibeError23, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability26, validateRlsContextIR } from "@vibeorm/schema";
3673
3730
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
3674
3731
  var COLUMN_TYPES = {
3675
3732
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -3682,18 +3739,18 @@ var COLUMN_TYPES = {
3682
3739
  };
3683
3740
  function requireRls(params) {
3684
3741
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
3685
- throw unsupportedCapability25({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3742
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3686
3743
  }
3687
3744
  }
3688
3745
  function identifier2(params) {
3689
3746
  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" });
3747
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
3691
3748
  }
3692
3749
  return params.dialect.quoteIdent({ name: params.name });
3693
3750
  }
3694
3751
  function tableRef(params) {
3695
3752
  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" });
3753
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
3697
3754
  }
3698
3755
  return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
3699
3756
  }
@@ -3734,7 +3791,7 @@ function renderRlsColumnType(params) {
3734
3791
  if (native !== undefined)
3735
3792
  return native;
3736
3793
  }
3737
- throw unsupportedCapability25({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3794
+ throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3738
3795
  }
3739
3796
  function protectedModel(params) {
3740
3797
  requireRls(params);
@@ -3742,7 +3799,7 @@ function protectedModel(params) {
3742
3799
  assertValidSchemaIR({ schema: params.schema });
3743
3800
  const model = params.schema.models.find((entry) => entry.name === params.model);
3744
3801
  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 } });
3802
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
3746
3803
  }
3747
3804
  return model;
3748
3805
  }
@@ -3775,7 +3832,7 @@ function predicate(params) {
3775
3832
  }).join(" AND ");
3776
3833
  return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
3777
3834
  }
3778
- throw new VibeError22({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
3835
+ throw new VibeError23({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
3779
3836
  }
3780
3837
  function renderRlsPolicy(params) {
3781
3838
  const model = protectedModel(params);
@@ -3816,7 +3873,7 @@ function renderRlsContext(params) {
3816
3873
  }
3817
3874
  function runtimeRoleOf(params) {
3818
3875
  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" });
3876
+ throw new VibeError23({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
3820
3877
  }
3821
3878
  return params.role;
3822
3879
  }
@@ -3973,7 +4030,7 @@ ORDER BY c.relname, p.polname`,
3973
4030
  });
3974
4031
  function requireRls2(params) {
3975
4032
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
3976
- throw unsupportedCapability26({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
4033
+ throw unsupportedCapability27({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3977
4034
  }
3978
4035
  }
3979
4036
  function renderRlsReadinessQuery(params) {
@@ -4005,7 +4062,7 @@ function renderRlsPolicyPath(params) {
4005
4062
  }
4006
4063
 
4007
4064
  // packages/sql/src/render.ts
4008
- import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError3, unsupportedCapability as unsupportedCapability27 } from "@vibeorm/schema";
4065
+ import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError4, unsupportedCapability as unsupportedCapability28 } from "@vibeorm/schema";
4009
4066
 
4010
4067
  // packages/sql/src/like.ts
4011
4068
  function escapeLikePattern(params) {
@@ -4091,15 +4148,19 @@ function renderStatement(params) {
4091
4148
  function quoteCol(dialect, column) {
4092
4149
  const name = dialect.quoteIdent({ name: column.name });
4093
4150
  const ref = column.table === undefined ? name : `${dialect.quoteIdent({ name: column.table })}.${name}`;
4151
+ if (column.truncate !== undefined)
4152
+ return renderDateTruncation({ dialect, reference: ref, truncation: column.truncate });
4153
+ if (column.enumOrdinal === true)
4154
+ return dialect.renderEnumOrdinal({ column: ref });
4094
4155
  if (column.castReal === true) {
4095
4156
  if (dialect.name !== "sqlite") {
4096
- throw internalError3({ message: "ColRef.castReal is a sqlite Decimal construct" });
4157
+ throw internalError4({ message: "ColRef.castReal is a sqlite Decimal construct" });
4097
4158
  }
4098
4159
  return `CAST(${ref} AS REAL)`;
4099
4160
  }
4100
4161
  if (column.castJsonb !== undefined) {
4101
4162
  if (dialect.name !== "postgres") {
4102
- throw internalError3({ message: "ColRef.castJsonb is a postgres json construct" });
4163
+ throw internalError4({ message: "ColRef.castJsonb is a postgres json construct" });
4103
4164
  }
4104
4165
  return `${ref}::${column.castJsonb}`;
4105
4166
  }
@@ -4107,7 +4168,7 @@ function quoteCol(dialect, column) {
4107
4168
  }
4108
4169
  function typedOrderRef(params) {
4109
4170
  const { statement, column } = params;
4110
- if (column.table !== undefined || column.castReal === true || column.castJsonb !== undefined)
4171
+ if (column.table !== undefined || column.castReal === true || column.castJsonb !== undefined || column.truncate !== undefined)
4111
4172
  return column;
4112
4173
  const renamed = statement.columns.some((projected) => (projected.alias ?? projected.name) === column.name && (projected.transform !== undefined || projected.alias !== undefined));
4113
4174
  return renamed ? { ...column, table: statement.table.alias ?? statement.table.name } : column;
@@ -4116,16 +4177,17 @@ function jsonCellCast(dialect, cell) {
4116
4177
  if (cell.storage !== "json" || dialect.jsonParamCast === "")
4117
4178
  return dialect.jsonParamCast;
4118
4179
  if (dialect.name !== "postgres") {
4119
- throw internalError3({ message: "JsonParamCell storage json is a postgres construct" });
4180
+ throw internalError4({ message: "JsonParamCell storage json is a postgres construct" });
4120
4181
  }
4121
4182
  return "::text::json";
4122
4183
  }
4123
4184
  function quoteProjection(dialect, column) {
4124
4185
  if (column.transform === undefined) {
4125
4186
  const base = quoteCol(dialect, column);
4126
- if (column.alias === undefined)
4187
+ const alias = column.alias ?? (column.truncate === undefined ? undefined : column.name);
4188
+ if (alias === undefined)
4127
4189
  return base;
4128
- return `${base} AS ${dialect.quoteIdent({ name: column.alias })}`;
4190
+ return `${base} AS ${dialect.quoteIdent({ name: alias })}`;
4129
4191
  }
4130
4192
  if (column.transform === "jsonText") {
4131
4193
  const ref = quoteCol(dialect, column);
@@ -4139,7 +4201,7 @@ function quoteProjection(dialect, column) {
4139
4201
  if (column.transform === "encodeBase64" && dialect.name === "mysql")
4140
4202
  return `TO_BASE64(${ref}) AS ${alias}`;
4141
4203
  if (dialect.name !== "postgres") {
4142
- throw internalError3({ message: `${column.transform} is a postgres construct` });
4204
+ throw internalError4({ message: `${column.transform} is a postgres construct` });
4143
4205
  }
4144
4206
  if (column.transform === "encodeArrayBase64")
4145
4207
  return `encode(array_send(${ref}), 'base64') AS ${alias}`;
@@ -4163,7 +4225,7 @@ function renderTable(dialect, table) {
4163
4225
  }
4164
4226
  function assertCount(value, what) {
4165
4227
  if (!Number.isSafeInteger(value) || value < 0) {
4166
- throw internalError3({ message: `${what} must be a non-negative integer, got ${value}` });
4228
+ throw internalError4({ message: `${what} must be a non-negative integer, got ${value}` });
4167
4229
  }
4168
4230
  }
4169
4231
  var COMPARISON_SQL2 = {
@@ -4190,12 +4252,16 @@ var AGG_FN_SQL = {
4190
4252
  function renderAggRef(dialect, agg) {
4191
4253
  if (agg.fn === "countAll")
4192
4254
  return "COUNT(*)";
4193
- return `${AGG_FN_SQL[agg.fn]}(${quoteCol(dialect, agg.column)})`;
4255
+ const column = quoteCol(dialect, agg.column);
4256
+ if ((agg.fn === "min" || agg.fn === "max") && agg.enumOrder !== undefined) {
4257
+ return dialect.renderEnumMinMax({ fn: agg.fn, column, result: agg.enumOrder });
4258
+ }
4259
+ return `${AGG_FN_SQL[agg.fn]}(${column})`;
4194
4260
  }
4195
4261
  function assertScalarArrays(dialect, feature) {
4196
4262
  if (dialect.capabilities.scalarArrays)
4197
4263
  return;
4198
- throw unsupportedCapability27({
4264
+ throw unsupportedCapability28({
4199
4265
  dialect: dialect.name,
4200
4266
  feature,
4201
4267
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -4207,7 +4273,7 @@ function renderExpr(params) {
4207
4273
  case "cmp": {
4208
4274
  const op = COMPARISON_SQL2[expr.op];
4209
4275
  if (op === undefined) {
4210
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4276
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4211
4277
  }
4212
4278
  if (isDbNowMarker(expr.value)) {
4213
4279
  return `${quoteCol(dialect, expr.column)} ${op} ${dialect.renderDbNow({ offsetSeconds: expr.value.offsetSeconds, collector })}`;
@@ -4218,7 +4284,7 @@ function renderExpr(params) {
4218
4284
  case "cmpCol": {
4219
4285
  const op = COMPARISON_SQL2[expr.op];
4220
4286
  if (op === undefined) {
4221
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4287
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4222
4288
  }
4223
4289
  return `${quoteCol(dialect, expr.left)} ${op} ${quoteCol(dialect, expr.right)}`;
4224
4290
  }
@@ -4242,11 +4308,11 @@ function renderExpr(params) {
4242
4308
  }
4243
4309
  case "tupleIn": {
4244
4310
  if (expr.columns.length === 0) {
4245
- throw internalError3({ message: "tupleIn needs at least one column" });
4311
+ throw internalError4({ message: "tupleIn needs at least one column" });
4246
4312
  }
4247
4313
  for (const row of expr.rows) {
4248
4314
  if (row.length !== expr.columns.length) {
4249
- throw internalError3({
4315
+ throw internalError4({
4250
4316
  message: `tupleIn row has ${row.length} values for ${expr.columns.length} columns`
4251
4317
  });
4252
4318
  }
@@ -4272,7 +4338,7 @@ function renderExpr(params) {
4272
4338
  case "rowCompare": {
4273
4339
  const op = COMPARISON_SQL2[expr.op];
4274
4340
  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)` });
4341
+ throw internalError4({ message: `rowCompare needs a range operator and one value per column (${expr.columns.length} columns, ${expr.values.length} values)` });
4276
4342
  }
4277
4343
  const columns = expr.columns.map((column) => quoteCol(dialect, column));
4278
4344
  const placeholders = expr.values.map((value) => collector.add(value));
@@ -4280,9 +4346,9 @@ function renderExpr(params) {
4280
4346
  }
4281
4347
  case "inSelect": {
4282
4348
  if (expr.columns.length === 0)
4283
- throw internalError3({ message: "inSelect needs at least one column" });
4349
+ throw internalError4({ message: "inSelect needs at least one column" });
4284
4350
  if (dialect.name === "mysql" && (expr.select.limit !== undefined || expr.select.offset !== undefined)) {
4285
- throw unsupportedCapability27({
4351
+ throw unsupportedCapability28({
4286
4352
  dialect: dialect.name,
4287
4353
  feature: "LIMIT inside an IN subquery",
4288
4354
  hint: "mysql refuses LIMIT directly inside an IN subquery (error 1235); wrap the limited select in a derived table"
@@ -4353,7 +4419,7 @@ function renderExpr(params) {
4353
4419
  case "aggCmp": {
4354
4420
  const op = COMPARISON_SQL2[expr.op];
4355
4421
  if (op === undefined) {
4356
- throw internalError3({ message: `unknown comparison operator "${expr.op}"` });
4422
+ throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4357
4423
  }
4358
4424
  return `${renderAggRef(dialect, expr.agg)} ${op} ${collector.add(expr.value)}`;
4359
4425
  }
@@ -4435,13 +4501,13 @@ function renderKeyList(params) {
4435
4501
  const { dialect, expr, collector } = params;
4436
4502
  const arity = expr.columns.length;
4437
4503
  if (arity === 0)
4438
- throw internalError3({ message: "keyList needs at least one column" });
4504
+ throw internalError4({ message: "keyList needs at least one column" });
4439
4505
  if (expr.members.length !== arity) {
4440
- throw internalError3({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4506
+ throw internalError4({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4441
4507
  }
4442
4508
  for (const row of expr.rows) {
4443
4509
  if (row.length !== arity)
4444
- throw internalError3({ message: `keyList row has ${row.length} values for ${arity} columns` });
4510
+ throw internalError4({ message: `keyList row has ${row.length} values for ${arity} columns` });
4445
4511
  }
4446
4512
  if (expr.rows.length === 0)
4447
4513
  return "1 = 0";
@@ -4473,25 +4539,25 @@ function renderKeyPage(params) {
4473
4539
  const { select } = page;
4474
4540
  const arity = page.keyColumns.length;
4475
4541
  if (arity === 0 || page.members.length !== arity) {
4476
- throw internalError3({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4542
+ throw internalError4({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4477
4543
  }
4478
4544
  if (page.rows.length === 0)
4479
- throw internalError3({ message: "keyPage needs at least one key" });
4545
+ throw internalError4({ message: "keyPage needs at least one key" });
4480
4546
  for (const row of page.rows) {
4481
4547
  if (row.length !== arity)
4482
- throw internalError3({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4548
+ throw internalError4({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4483
4549
  }
4484
4550
  if (page.identity.length === 0)
4485
- throw internalError3({ message: "keyPage needs the child's identity columns" });
4551
+ throw internalError4({ message: "keyPage needs the child's identity columns" });
4486
4552
  const order = select.orderBy ?? [];
4487
4553
  if (order.length === 0 || order.some((term) => !("column" in term))) {
4488
- throw internalError3({ message: "keyPage needs a non-empty order of plain column terms" });
4554
+ throw internalError4({ message: "keyPage needs a non-empty order of plain column terms" });
4489
4555
  }
4490
4556
  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" });
4557
+ throw internalError4({ message: "keyPage select carries a member the per-key page cannot render" });
4492
4558
  }
4493
4559
  if (dialect.keyPageStrategy === "window") {
4494
- throw unsupportedCapability27({
4560
+ throw unsupportedCapability28({
4495
4561
  dialect: dialect.name,
4496
4562
  feature: "a per-key page (keyPage)",
4497
4563
  hint: "the runtime keeps the ROW_NUMBER window on this dialect"
@@ -4519,7 +4585,7 @@ function renderKeyPage(params) {
4519
4585
  const keys = dialect.quoteIdent({ name: keyAlias });
4520
4586
  if (dialect.keyPageStrategy === "lateral") {
4521
4587
  if (keyListBindsUntypedRows({ members: page.members })) {
4522
- throw unsupportedCapability27({
4588
+ throw unsupportedCapability28({
4523
4589
  dialect: dialect.name,
4524
4590
  feature: "a per-key page over a DateTime key without a declared native type",
4525
4591
  hint: "the runtime keeps the ROW_NUMBER window for such keys"
@@ -4547,22 +4613,22 @@ function renderKeyPage(params) {
4547
4613
  function renderKeyRows(params) {
4548
4614
  const { dialect, statement, keys, collector } = params;
4549
4615
  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" });
4616
+ throw internalError4({ message: "fromKeys uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion and table.alias must be unset" });
4551
4617
  }
4552
4618
  if (statement.rowLock !== undefined)
4553
- throw internalError3({ message: "a locking read cannot read from key rows" });
4619
+ throw internalError4({ message: "a locking read cannot read from key rows" });
4554
4620
  const arity = keys.columns.length;
4555
4621
  if (arity === 0 || keys.members.length !== arity) {
4556
- throw internalError3({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
4622
+ throw internalError4({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
4557
4623
  }
4558
4624
  if (keys.rows.length === 0)
4559
- throw internalError3({ message: "fromKeys needs at least one key row" });
4625
+ throw internalError4({ message: "fromKeys needs at least one key row" });
4560
4626
  for (const row of keys.rows) {
4561
4627
  if (row.length !== arity)
4562
- throw internalError3({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
4628
+ throw internalError4({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
4563
4629
  }
4564
4630
  if (dialect.keyListStrategy !== "unnest" || keyListBindsUntypedRows({ members: keys.members })) {
4565
- throw unsupportedCapability27({
4631
+ throw unsupportedCapability28({
4566
4632
  dialect: dialect.name,
4567
4633
  feature: dialect.keyListStrategy !== "unnest" ? "a key-rows FROM (fromKeys)" : "a key-rows FROM over a DateTime key without a declared native type",
4568
4634
  hint: "the runtime keeps the per-relation loads for this write"
@@ -4578,20 +4644,20 @@ function renderUnionPage(params) {
4578
4644
  const { dialect, statement, collector } = params;
4579
4645
  const branches = statement.fromUnion ?? [];
4580
4646
  if (branches.length < 2)
4581
- throw internalError3({ message: "fromUnion needs at least two branches" });
4647
+ throw internalError4({ message: "fromUnion needs at least two branches" });
4582
4648
  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" });
4649
+ throw internalError4({ message: "fromUnion uses table.name as the derived-table alias \u2014 fromSelect, keyPage and table.alias must be unset" });
4584
4650
  }
4585
4651
  if (statement.rowLock !== undefined)
4586
- throw internalError3({ message: "a locking read cannot read from a union page" });
4652
+ throw internalError4({ message: "a locking read cannot read from a union page" });
4587
4653
  const outputNames = (branch) => branch.columns.map((column) => column.alias ?? column.name).join("\x00");
4588
4654
  const names = outputNames(branches[0]);
4589
4655
  return branches.map((branch, index) => {
4590
4656
  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]" });
4657
+ throw internalError4({ message: "a fromUnion branch must be a plain SELECT <columns> FROM <table> [WHERE] [ORDER BY] [LIMIT]" });
4592
4658
  }
4593
4659
  if (outputNames(branch) !== names)
4594
- throw internalError3({ message: "fromUnion branches must project the same output names in the same order" });
4660
+ throw internalError4({ message: "fromUnion branches must project the same output names in the same order" });
4595
4661
  const inner = renderSelect({ dialect, statement: branch, collector });
4596
4662
  return `SELECT * FROM (${inner.text}) AS ${dialect.quoteIdent({ name: `__vibe_u${index}` })}`;
4597
4663
  }).join(" UNION ALL ");
@@ -4599,7 +4665,7 @@ function renderUnionPage(params) {
4599
4665
  function jsonStringPattern(params) {
4600
4666
  const { expr } = params;
4601
4667
  if (typeof expr.value !== "string") {
4602
- throw internalError3({ message: `json ${expr.op} expects a string operand` });
4668
+ throw internalError4({ message: `json ${expr.op} expects a string operand` });
4603
4669
  }
4604
4670
  if (expr.op === "string_contains")
4605
4671
  return containsPattern({ raw: expr.value });
@@ -4623,7 +4689,7 @@ function renderJsonLike(params) {
4623
4689
  function renderJsonFilter(params) {
4624
4690
  const { dialect, expr, collector } = params;
4625
4691
  if (!dialect.supportedJsonOps.includes(expr.op)) {
4626
- throw unsupportedCapability27({
4692
+ throw unsupportedCapability28({
4627
4693
  dialect: dialect.name,
4628
4694
  feature: `the json filter "${expr.op}"`,
4629
4695
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -4690,7 +4756,7 @@ function renderJsonFilter(params) {
4690
4756
  const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
4691
4757
  const tests = elements.map((element) => {
4692
4758
  if (element === null || typeof element === "object" && element !== null) {
4693
- throw unsupportedCapability27({
4759
+ throw unsupportedCapability28({
4694
4760
  dialect: dialect.name,
4695
4761
  feature: "json array_contains with object/array/null elements",
4696
4762
  hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
@@ -4704,7 +4770,7 @@ function renderJsonFilter(params) {
4704
4770
  return tests.length === 0 ? guard : `(${[guard, ...tests].join(" AND ")})`;
4705
4771
  }
4706
4772
  default:
4707
- throw internalError3({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
4773
+ throw internalError4({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
4708
4774
  }
4709
4775
  }
4710
4776
  const column = quoteCol(dialect, expr.column);
@@ -4731,23 +4797,20 @@ function renderOrderTerm(params) {
4731
4797
  if ("agg" in term) {
4732
4798
  return `${renderAggRef(dialect, term.agg)} ${term.direction === "asc" ? "ASC" : "DESC"}`;
4733
4799
  }
4734
- if ("select" in term) {
4735
- const sub = renderSelect({ dialect, statement: term.select, collector });
4736
- return `(${sub.text}) ${term.direction === "asc" ? "ASC" : "DESC"}`;
4737
- }
4738
- const column = quoteCol(dialect, term.column);
4800
+ const expression = "select" in term ? () => renderScalarSubquery({ dialect, subquery: term, collector }) : () => quoteCol(dialect, term.column);
4739
4801
  const direction = term.direction === "asc" ? "ASC" : "DESC";
4740
4802
  if (term.nulls === undefined) {
4741
- return `${column} ${direction}`;
4803
+ return `${expression()} ${direction}`;
4742
4804
  }
4743
4805
  if (dialect.orderByNullsMode === "native") {
4744
- return `${column} ${direction} NULLS ${term.nulls === "first" ? "FIRST" : "LAST"}`;
4806
+ return `${expression()} ${direction} NULLS ${term.nulls === "first" ? "FIRST" : "LAST"}`;
4745
4807
  }
4746
4808
  if (term.nulls === "first" === (term.direction === "asc")) {
4747
- return `${column} ${direction}`;
4809
+ return `${expression()} ${direction}`;
4748
4810
  }
4749
- const nullsTerm = term.nulls === "first" ? `${column} IS NULL DESC` : `${column} IS NULL`;
4750
- return `${nullsTerm}, ${column} ${direction}`;
4811
+ const tested = expression();
4812
+ const nullsTerm = term.nulls === "first" ? `${tested} IS NULL DESC` : `${tested} IS NULL`;
4813
+ return `${nullsTerm}, ${expression()} ${direction}`;
4751
4814
  }
4752
4815
  function renderSelectExpr(params) {
4753
4816
  const { dialect, expr } = params;
@@ -4762,11 +4825,11 @@ function renderPositionalRow(params) {
4762
4825
  const position = join.select.rowNumber;
4763
4826
  const positioned = position !== undefined && json.order === "positionSlot" && join.agg === "jsonArray" && position.partitionBy.length === 0 && position.orderBy.length > 0;
4764
4827
  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" });
4828
+ throw internalError4({ message: "positional lateral rows require an explicit column-only projection" });
4766
4829
  }
4767
4830
  const names = [...join.select.columns.map((column) => projectionOutputName({ column })), ...positioned ? [position.alias] : []];
4768
4831
  if (new Set(names).size !== names.length) {
4769
- throw internalError3({ message: "positional lateral projection has duplicate output names" });
4832
+ throw internalError4({ message: "positional lateral projection has duplicate output names" });
4770
4833
  }
4771
4834
  const values = names.map((name) => `${subAlias}.${dialect.quoteIdent({ name })}`);
4772
4835
  const chunks = [];
@@ -4774,14 +4837,14 @@ function renderPositionalRow(params) {
4774
4837
  chunks.push(`${json.rowArray}(${values.slice(index, index + JSON_TRANSPORT.chunkSize).join(", ")})`);
4775
4838
  }
4776
4839
  if (chunks.length > JSON_TRANSPORT.chunkSize) {
4777
- throw internalError3({ message: "positional lateral projection exceeds the transport width limit" });
4840
+ throw internalError4({ message: "positional lateral projection exceeds the transport width limit" });
4778
4841
  }
4779
4842
  return chunks.length === 1 ? chunks[0] : `${json.rowArray}(${chunks.join(", ")})`;
4780
4843
  }
4781
4844
  function lateralJsonOf(params) {
4782
4845
  const json = params.dialect.lateralJson;
4783
4846
  if (json === null)
4784
- throw internalError3({ message: "lateral JSON rendering on a dialect without LATERAL" });
4847
+ throw internalError4({ message: "lateral JSON rendering on a dialect without LATERAL" });
4785
4848
  return json;
4786
4849
  }
4787
4850
  function renderLateralJoin(params) {
@@ -4789,7 +4852,7 @@ function renderLateralJoin(params) {
4789
4852
  const inner = renderSelect({ dialect, statement: join.select, collector });
4790
4853
  if (join.agg === "count") {
4791
4854
  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" });
4855
+ throw internalError4({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
4793
4856
  }
4794
4857
  return `LEFT JOIN LATERAL (${inner.text}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
4795
4858
  }
@@ -4798,7 +4861,7 @@ function renderLateralJoin(params) {
4798
4861
  const json = lateralJsonOf({ dialect });
4799
4862
  let aggOrder = "";
4800
4863
  if (json.order === "inAggregate" && join.agg === "jsonArray" && join.select.orderBy !== undefined && join.select.orderBy.length > 0) {
4801
- const projected = new Set(join.select.columns.filter((column) => column.transform === undefined && column.castReal !== true).map((column) => column.name));
4864
+ const projected = new Set(join.select.columns.filter((column) => column.transform === undefined && column.castReal !== true).map((column) => column.name).concat((join.select.scalarSubqueries ?? []).map((subquery) => subquery.alias)));
4802
4865
  const terms = join.select.orderBy;
4803
4866
  const columnTerms = terms.filter((term) => ("column" in term));
4804
4867
  if (columnTerms.length === terms.length && columnTerms.every((term) => projected.has(term.column.name))) {
@@ -4814,7 +4877,7 @@ function renderLateralJoin(params) {
4814
4877
  }
4815
4878
  }
4816
4879
  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' });
4880
+ throw unsupportedCapability28({ dialect: dialect.name, feature: "object-format lateral rows", hint: 'lateral relation rows render positional (rowFormat "positional") on this dialect' });
4818
4881
  }
4819
4882
  const rowJson = join.rowFormat === "positional" ? renderPositionalRow({ dialect, join, subAlias }) : `to_json(${subAlias}.*)`;
4820
4883
  const projection = join.agg === "jsonArray" ? `COALESCE(${json.arrayAgg}(${rowJson}${aggOrder}), ${json.emptyArray}) AS ${columnAlias}` : `${rowJson} AS ${columnAlias}`;
@@ -4824,12 +4887,17 @@ function renderLateralJoin(params) {
4824
4887
  function renderCountSubquery(params) {
4825
4888
  const { dialect, subquery, collector } = params;
4826
4889
  const select = subquery.select;
4827
- 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(*)" });
4890
+ if (select.columns.length !== 0 || select.selectExpr?.kind !== "countAll" || (select.aggregates?.length ?? 0) > 0 || select.rowNumber !== undefined || select.groupBy !== undefined || select.having !== undefined || select.limit !== undefined || select.offset !== undefined || select.distinctOn !== undefined || select.distinct !== undefined || select.rowLock !== undefined || select.countSubqueries !== undefined || select.scalarSubqueries !== undefined) {
4891
+ throw internalError4({ message: "count subquery must be one ungrouped COUNT(*)" });
4829
4892
  }
4830
4893
  const inner = renderSelect({ dialect, statement: select, collector });
4831
4894
  return `(${inner.text}) AS ${dialect.quoteIdent({ name: subquery.alias })}`;
4832
4895
  }
4896
+ function renderScalarSubquery(params) {
4897
+ const { dialect, subquery, collector } = params;
4898
+ const value = `(${renderSelect({ dialect, statement: subquery.select, collector }).text})`;
4899
+ return subquery.coalesce === 0 ? `COALESCE(${value}, 0)` : value;
4900
+ }
4833
4901
  function applyReturning(params) {
4834
4902
  const { dialect, requested } = params;
4835
4903
  if (requested === undefined || requested.length === 0) {
@@ -4855,27 +4923,27 @@ function renderKeyTableJoin(params) {
4855
4923
  const postgresMember = (member) => member.kind === "citext" || member.kind === "text" || member.kind === "integer";
4856
4924
  const admitted = dialect.name === "mysql" ? join.members.every((member) => member.kind !== "citext" && member.kind !== "text") : dialect.name === "postgres" && join.members.every(postgresMember);
4857
4925
  if (!admitted) {
4858
- throw unsupportedCapability27({
4926
+ throw unsupportedCapability28({
4859
4927
  dialect: dialect.name,
4860
4928
  feature: "relation key table",
4861
4929
  hint: "the key-table join is mysql's relation association \u2014 this dialect compares relation keys by value"
4862
4930
  });
4863
4931
  }
4864
4932
  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}` });
4933
+ throw internalError4({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
4866
4934
  }
4867
4935
  if (join.rows.length === 0)
4868
- throw internalError3({ message: "key table needs at least one key row" });
4936
+ throw internalError4({ message: "key table needs at least one key row" });
4869
4937
  for (const member of join.members) {
4870
4938
  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)}` });
4939
+ throw internalError4({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
4872
4940
  }
4873
4941
  }
4874
4942
  const alias = dialect.quoteIdent({ name: join.alias });
4875
4943
  const memberNames = join.members.map((_, index) => dialect.quoteIdent({ name: `__vibe_k${index}` }));
4876
4944
  const rows = join.rows.map((row, ordinal) => {
4877
4945
  if (row.length !== join.members.length) {
4878
- throw internalError3({ message: `key table row has ${row.length} values for ${join.members.length} members` });
4946
+ throw internalError4({ message: `key table row has ${row.length} values for ${join.members.length} members` });
4879
4947
  }
4880
4948
  const cells = row.map((value, index) => {
4881
4949
  const placeholder = collector.add(value);
@@ -4905,10 +4973,10 @@ function renderKeyTableJoin(params) {
4905
4973
  function renderLeftJoin(params) {
4906
4974
  const { dialect, join, collector } = params;
4907
4975
  if (join.on.length === 0) {
4908
- throw internalError3({ message: "left join needs at least one ON pair" });
4976
+ throw internalError4({ message: "left join needs at least one ON pair" });
4909
4977
  }
4910
4978
  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" });
4979
+ throw internalError4({ message: "a left join's derived select must alias every projected column" });
4912
4980
  }
4913
4981
  const inner = renderSelect({ dialect, statement: join.select, collector });
4914
4982
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
@@ -4917,10 +4985,10 @@ function renderLeftJoin(params) {
4917
4985
  function renderSelect(params) {
4918
4986
  const { dialect, statement, collector } = params;
4919
4987
  if (statement.selectExpr !== undefined && statement.columns.length > 0 && statement.selectExpr.kind !== "countAll") {
4920
- throw internalError3({ message: "only countAll can combine with a column list" });
4988
+ throw internalError4({ message: "only countAll can combine with a column list" });
4921
4989
  }
4922
4990
  if (statement.selectExpr !== undefined && statement.aggregates !== undefined && statement.aggregates.length > 0) {
4923
- throw internalError3({ message: "selectExpr and aggregates cannot combine" });
4991
+ throw internalError4({ message: "selectExpr and aggregates cannot combine" });
4924
4992
  }
4925
4993
  const columnList = statement.columns.map((column) => quoteProjection(dialect, column));
4926
4994
  if (statement.selectExpr !== undefined) {
@@ -4938,10 +5006,15 @@ function renderSelect(params) {
4938
5006
  columnList.push(renderCountSubquery({ dialect, subquery, collector }));
4939
5007
  }
4940
5008
  }
5009
+ if (statement.scalarSubqueries !== undefined) {
5010
+ for (const subquery of statement.scalarSubqueries) {
5011
+ columnList.push(`${renderScalarSubquery({ dialect, subquery, collector })} AS ${dialect.quoteIdent({ name: subquery.alias })}`);
5012
+ }
5013
+ }
4941
5014
  if (statement.rowNumber !== undefined) {
4942
5015
  const rowNumber = statement.rowNumber;
4943
5016
  if (rowNumber.partitionBy.length === 0 && rowNumber.orderBy.length === 0) {
4944
- throw internalError3({ message: "rowNumber needs a partition column or an order" });
5017
+ throw internalError4({ message: "rowNumber needs a partition column or an order" });
4945
5018
  }
4946
5019
  const partition = rowNumber.partitionBy.length === 0 ? "" : `PARTITION BY ${rowNumber.partitionBy.map((column) => quoteCol(dialect, column)).join(", ")}`;
4947
5020
  const order = rowNumber.orderBy.length === 0 ? "" : `${partition === "" ? "" : " "}ORDER BY ${rowNumber.orderBy.map((term) => renderOrderTerm({ dialect, term, collector })).join(", ")}`;
@@ -4949,11 +5022,11 @@ function renderSelect(params) {
4949
5022
  }
4950
5023
  let distinctPrefix = statement.distinct === true ? "DISTINCT " : "";
4951
5024
  if (statement.distinct === true && statement.distinctOn !== undefined) {
4952
- throw internalError3({ message: "distinct and distinctOn cannot combine" });
5025
+ throw internalError4({ message: "distinct and distinctOn cannot combine" });
4953
5026
  }
4954
5027
  if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
4955
5028
  if (!dialect.capabilities.distinctOn) {
4956
- throw unsupportedCapability27({
5029
+ throw unsupportedCapability28({
4957
5030
  dialect: dialect.name,
4958
5031
  feature: "SELECT DISTINCT ON",
4959
5032
  hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
@@ -4969,12 +5042,12 @@ function renderSelect(params) {
4969
5042
  from = `(${renderUnionPage({ dialect, statement, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
4970
5043
  } else if (statement.keyPage !== undefined) {
4971
5044
  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" });
5045
+ throw internalError4({ message: "keyPage uses table.name as the derived-table alias \u2014 fromSelect and table.alias must be unset" });
4973
5046
  }
4974
5047
  from = `(${renderKeyPage({ dialect, page: statement.keyPage, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
4975
5048
  } else if (statement.fromSelect !== undefined) {
4976
5049
  if (statement.table.alias !== undefined) {
4977
- throw internalError3({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
5050
+ throw internalError4({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
4978
5051
  }
4979
5052
  const inner = renderSelect({ dialect, statement: statement.fromSelect, collector });
4980
5053
  from = `(${inner.text}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
@@ -4985,7 +5058,7 @@ function renderSelect(params) {
4985
5058
  if (statement.innerJoins !== undefined && statement.innerJoins.length > 0) {
4986
5059
  for (const join of statement.innerJoins) {
4987
5060
  if (join.on.length === 0) {
4988
- throw internalError3({ message: "inner join needs at least one ON pair" });
5061
+ throw internalError4({ message: "inner join needs at least one ON pair" });
4989
5062
  }
4990
5063
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
4991
5064
  text += ` INNER JOIN ${renderTable(dialect, join.table)} ON ${on}`;
@@ -4996,7 +5069,7 @@ function renderSelect(params) {
4996
5069
  }
4997
5070
  if (statement.leftJoins !== undefined && statement.leftJoins.length > 0) {
4998
5071
  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" });
5072
+ 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
5073
  }
5001
5074
  for (const join of statement.leftJoins) {
5002
5075
  text += ` ${renderLeftJoin({ dialect, join, collector })}`;
@@ -5004,7 +5077,7 @@ function renderSelect(params) {
5004
5077
  }
5005
5078
  if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
5006
5079
  if (!dialect.capabilities.lateralJoin || dialect.lateralJson === null) {
5007
- throw unsupportedCapability27({
5080
+ throw unsupportedCapability28({
5008
5081
  dialect: dialect.name,
5009
5082
  feature: "LATERAL JOIN relation loading",
5010
5083
  hint: 'use the portable "query" relation strategy on this dialect'
@@ -5053,7 +5126,7 @@ function unnestElement(params) {
5053
5126
  return null;
5054
5127
  if (isJsonParamCell(value)) {
5055
5128
  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` });
5129
+ throw internalError4({ message: `unnest insert column "${column}" got a Json cell its declared type does not take` });
5057
5130
  }
5058
5131
  return value.text;
5059
5132
  }
@@ -5069,24 +5142,24 @@ function unnestElement(params) {
5069
5142
  return value;
5070
5143
  }
5071
5144
  }
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` });
5145
+ 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
5146
  }
5074
5147
  function renderUnnestRows(params) {
5075
5148
  const { dialect, statement, members, collector } = params;
5076
5149
  if (dialect.bulkInsertForm !== "unnest") {
5077
- throw unsupportedCapability27({
5150
+ throw unsupportedCapability28({
5078
5151
  dialect: dialect.name,
5079
5152
  feature: "a bulk insert bound as typed arrays (INSERT \u2026 SELECT FROM unnest)",
5080
5153
  hint: "this dialect binds createMany rows as VALUES"
5081
5154
  });
5082
5155
  }
5083
5156
  if (statement.columns.length === 0)
5084
- throw internalError3({ message: "an unnest insert needs at least one column" });
5157
+ throw internalError4({ message: "an unnest insert needs at least one column" });
5085
5158
  if (members.length !== statement.columns.length) {
5086
- throw internalError3({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5159
+ throw internalError4({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5087
5160
  }
5088
5161
  if (statement.onConflict !== undefined && !("doNothing" in statement.onConflict)) {
5089
- throw internalError3({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5162
+ throw internalError4({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5090
5163
  }
5091
5164
  const aliases = statement.columns.map((_column, index) => dialect.quoteIdent({ name: `c${index + 1}` }));
5092
5165
  const arrays = members.map((member, index) => {
@@ -5102,14 +5175,14 @@ function renderUnnestRows(params) {
5102
5175
  function renderInsert(params) {
5103
5176
  const { dialect, statement, collector } = params;
5104
5177
  if (statement.rows.length === 0) {
5105
- throw internalError3({ message: "insert requires at least one row" });
5178
+ throw internalError4({ message: "insert requires at least one row" });
5106
5179
  }
5107
5180
  const defaultValues = statement.columns.length === 0 && dialect.emptyInsert.form === "defaultValues";
5108
5181
  if (defaultValues && statement.rows.length !== 1) {
5109
- throw internalError3({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5182
+ throw internalError4({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5110
5183
  }
5111
5184
  if (defaultValues && statement.onConflict !== undefined && !(dialect.emptyInsert.form === "defaultValues" && dialect.emptyInsert.conflictClause)) {
5112
- throw unsupportedCapability27({
5185
+ throw unsupportedCapability28({
5113
5186
  dialect: dialect.name,
5114
5187
  feature: "a conflict clause on an all-default row (DEFAULT VALUES)",
5115
5188
  hint: "supply at least one column value, or drop the conflict clause"
@@ -5117,7 +5190,7 @@ function renderInsert(params) {
5117
5190
  }
5118
5191
  for (const row of statement.rows) {
5119
5192
  if (row.length !== statement.columns.length) {
5120
- throw internalError3({
5193
+ throw internalError4({
5121
5194
  message: `insert row has ${row.length} values but ${statement.columns.length} columns`
5122
5195
  });
5123
5196
  }
@@ -5135,7 +5208,7 @@ function renderInsert(params) {
5135
5208
  if (!isSqlDefaultCell(value))
5136
5209
  return collector.add(value);
5137
5210
  if (!dialect.rowDefaultKeyword) {
5138
- throw internalError3({
5211
+ throw internalError4({
5139
5212
  message: `${dialect.name} cannot render a per-row DEFAULT cell \u2014 rows must be grouped by column set`
5140
5213
  });
5141
5214
  }
@@ -5151,7 +5224,7 @@ function renderInsert(params) {
5151
5224
  let target = "";
5152
5225
  if (onConflict.constraint !== undefined) {
5153
5226
  if (dialect.name !== "postgres") {
5154
- throw unsupportedCapability27({
5227
+ throw unsupportedCapability28({
5155
5228
  dialect: dialect.name,
5156
5229
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
5157
5230
  hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
@@ -5167,7 +5240,7 @@ function renderInsert(params) {
5167
5240
  text += ` ON CONFLICT${target} DO NOTHING`;
5168
5241
  } else {
5169
5242
  if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
5170
- throw unsupportedCapability27({
5243
+ throw unsupportedCapability28({
5171
5244
  dialect: dialect.name,
5172
5245
  feature: "a refined conflict target (constraint / partial-index predicate) on ON DUPLICATE KEY UPDATE",
5173
5246
  hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
@@ -5175,52 +5248,69 @@ function renderInsert(params) {
5175
5248
  }
5176
5249
  const keepName = onConflict.keepColumn ?? statement.columns[0];
5177
5250
  if (keepName === undefined) {
5178
- throw internalError3({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
5251
+ throw internalError4({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
5179
5252
  }
5180
5253
  const keep = dialect.quoteIdent({ name: keepName });
5181
5254
  const current = `${dialect.quoteIdent({ name: statement.table.name })}.${keep}`;
5182
5255
  const flag = onConflict.duplicateFlag;
5183
5256
  if (flag !== undefined && !/^\d{1,20}$/.test(flag)) {
5184
- throw internalError3({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
5257
+ throw internalError4({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
5185
5258
  }
5186
5259
  text += flag === undefined ? ` ON DUPLICATE KEY UPDATE ${keep} = ${current}` : ` ON DUPLICATE KEY UPDATE ${keep} = IF(LAST_INSERT_ID(${flag}), ${current}, ${current})`;
5187
5260
  }
5188
5261
  } else {
5189
5262
  const updates = Object.entries(onConflict.update);
5190
5263
  if (updates.length === 0) {
5191
- throw internalError3({ message: "upsert update must set at least one column" });
5264
+ throw internalError4({ message: "upsert update must set at least one column" });
5192
5265
  }
5193
5266
  const selfRef = dialect.quoteIdent({ name: statement.table.name });
5194
5267
  const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true, tableName: statement.table.name });
5195
5268
  if (dialect.upsertForm === "onConflict") {
5196
5269
  if (onConflict.lastInsertIdColumn !== undefined) {
5197
- throw internalError3({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
5270
+ throw internalError4({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
5198
5271
  }
5199
5272
  if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
5200
- throw unsupportedCapability27({
5273
+ throw unsupportedCapability28({
5201
5274
  dialect: dialect.name,
5202
5275
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
5203
5276
  hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
5204
5277
  });
5205
5278
  }
5206
5279
  if (onConflict.constraint !== undefined && onConflict.conflictWhere !== undefined) {
5207
- throw internalError3({
5280
+ throw internalError4({
5208
5281
  message: "a named-constraint conflict target already identifies the index \u2014 it cannot carry a partial predicate too"
5209
5282
  });
5210
5283
  }
5211
5284
  const partial = onConflict.conflictWhere === undefined ? "" : ` WHERE ${onConflict.conflictWhere}`;
5212
5285
  const target = onConflict.constraint !== undefined ? ` ON CONSTRAINT ${dialect.quoteIdent({ name: onConflict.constraint })}` : ` (${onConflict.columns.map((name) => dialect.quoteIdent({ name })).join(", ")})${partial}`;
5213
5286
  text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
5287
+ if (onConflict.updateWhere !== undefined) {
5288
+ if (!dialect.capabilities.conflictUpdateWhere) {
5289
+ throw unsupportedCapability28({
5290
+ dialect: dialect.name,
5291
+ feature: "a guarded conflict update (ON CONFLICT \u2026 DO UPDATE \u2026 WHERE)",
5292
+ hint: "run the guarded upsert as a find-then-write in a transaction instead"
5293
+ });
5294
+ }
5295
+ text += ` WHERE ${renderExpr({ dialect, expr: onConflict.updateWhere, collector })}`;
5296
+ }
5214
5297
  } else {
5298
+ if (onConflict.updateWhere !== undefined) {
5299
+ throw unsupportedCapability28({
5300
+ dialect: dialect.name,
5301
+ feature: "a guarded conflict update (ON DUPLICATE KEY UPDATE has no WHERE)",
5302
+ hint: "run the guarded upsert as a find-then-write in a transaction instead \u2014 the runtime does this when the dialect lacks conflictUpdateWhere"
5303
+ });
5304
+ }
5215
5305
  if (onConflict.constraint !== undefined) {
5216
- throw unsupportedCapability27({
5306
+ throw unsupportedCapability28({
5217
5307
  dialect: dialect.name,
5218
5308
  feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
5219
5309
  hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
5220
5310
  });
5221
5311
  }
5222
5312
  if (onConflict.conflictWhere !== undefined) {
5223
- throw unsupportedCapability27({
5313
+ throw unsupportedCapability28({
5224
5314
  dialect: dialect.name,
5225
5315
  feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
5226
5316
  hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
@@ -5238,7 +5328,7 @@ function renderUpdate(params) {
5238
5328
  const { dialect, statement, collector } = params;
5239
5329
  const assignments = Object.entries(statement.set);
5240
5330
  if (assignments.length === 0) {
5241
- throw internalError3({ message: "update must set at least one column" });
5331
+ throw internalError4({ message: "update must set at least one column" });
5242
5332
  }
5243
5333
  const setSql = renderAssignments({ dialect, entries: assignments, collector, ...params.earlier === undefined ? {} : { steps: params.earlier } });
5244
5334
  let text = `UPDATE ${renderTable(dialect, statement.table)} SET ${setSql}`;
@@ -5261,18 +5351,18 @@ function renderAssignments(params) {
5261
5351
  const read = selfRef === undefined ? column : `${selfRef}.${column}`;
5262
5352
  if (isStepColumnRef(value)) {
5263
5353
  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` });
5354
+ throw internalError4({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
5265
5355
  }
5266
5356
  return `${column} = (SELECT ${dialect.quoteIdent({ name: value.column })} FROM ${dialect.quoteIdent({ name: value.step })})`;
5267
5357
  }
5268
5358
  if (isSqlFragment(value)) {
5269
5359
  if (params.allowExcluded !== true || selfRef === undefined)
5270
- throw internalError3({ message: "SQL assignment fragments require a conflict update" });
5360
+ throw internalError4({ message: "SQL assignment fragments require a conflict update" });
5271
5361
  return `${column} = ${renderSqlFragment({ dialect, fragment: value, collector, conflict: { table: params.tableName } })}`;
5272
5362
  }
5273
5363
  if (isExcludedRef(value)) {
5274
5364
  if (params.allowExcluded !== true) {
5275
- throw internalError3({
5365
+ throw internalError4({
5276
5366
  message: `an incoming-row reference (${value.column}) is only meaningful inside an upsert's update arm`
5277
5367
  });
5278
5368
  }
@@ -5280,7 +5370,7 @@ function renderAssignments(params) {
5280
5370
  }
5281
5371
  if (isSqlKeepCell(value)) {
5282
5372
  if (params.allowExcluded !== true || selfRef === undefined)
5283
- throw internalError3({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
5373
+ throw internalError4({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
5284
5374
  return `${column} = ${read}`;
5285
5375
  }
5286
5376
  if (isJsonParamCell(value)) {
@@ -5318,19 +5408,19 @@ var CHAIN_ONE_ALIAS = "__vibe_one";
5318
5408
  function renderChain(params) {
5319
5409
  const { dialect, statement, collector } = params;
5320
5410
  if (dialect.statementChain === null) {
5321
- throw unsupportedCapability27({
5411
+ throw unsupportedCapability28({
5322
5412
  dialect: dialect.name,
5323
5413
  feature: "a statement chain (data-modifying WITH steps)",
5324
5414
  hint: "this dialect accepts only SELECT inside WITH \u2014 gate on SqlDialect.statementChain and keep the multi-statement path"
5325
5415
  });
5326
5416
  }
5327
5417
  if (statement.steps.length === 0)
5328
- throw internalError3({ message: "a statement chain needs at least one step" });
5418
+ throw internalError4({ message: "a statement chain needs at least one step" });
5329
5419
  const defined = new Set;
5330
5420
  const steps = [];
5331
5421
  for (const step of statement.steps) {
5332
5422
  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` });
5423
+ throw internalError4({ message: `chain step name "${step.name}" is reserved or repeated` });
5334
5424
  }
5335
5425
  const body = renderChainStep({ dialect, statement: step.statement, collector, earlier: defined });
5336
5426
  steps.push(`${dialect.quoteIdent({ name: step.name })} AS (${body})`);
@@ -5338,11 +5428,11 @@ function renderChain(params) {
5338
5428
  }
5339
5429
  const result = statement.result;
5340
5430
  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" });
5431
+ throw internalError4({ message: "a chain result needs at least one source step and one column" });
5342
5432
  }
5343
5433
  for (const name of result.rowsFrom) {
5344
5434
  if (!defined.has(name))
5345
- throw internalError3({ message: `chain result reads undefined step "${name}"` });
5435
+ throw internalError4({ message: `chain result reads undefined step "${name}"` });
5346
5436
  }
5347
5437
  const columnList = result.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
5348
5438
  const union = result.rowsFrom.map((name) => `SELECT ${columnList} FROM ${dialect.quoteIdent({ name })}`).join(" UNION ALL ");
@@ -5351,7 +5441,7 @@ function renderChain(params) {
5351
5441
  const extra = [];
5352
5442
  for (const alias of [result.verdict?.alias, result.tally?.alias]) {
5353
5443
  if (alias !== undefined && result.columns.includes(alias)) {
5354
- throw internalError3({ message: `chain result alias "${alias}" collides with a result column` });
5444
+ throw internalError4({ message: `chain result alias "${alias}" collides with a result column` });
5355
5445
  }
5356
5446
  }
5357
5447
  const rows = dialect.quoteIdent({ name: CHAIN_ROWS_ALIAS });
@@ -5388,21 +5478,21 @@ function renderSelectCell(params) {
5388
5478
  if (isDbNowMarker(value))
5389
5479
  return dialect.renderDbNow({ offsetSeconds: value.offsetSeconds, collector });
5390
5480
  if (isSqlDefaultCell(value)) {
5391
- throw internalError3({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
5481
+ throw internalError4({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
5392
5482
  }
5393
5483
  return collector.add(value);
5394
5484
  }
5395
5485
  function renderRowSource(params) {
5396
5486
  const { dialect, source, collector } = params;
5397
5487
  if (source.columns.length === 0 || source.rows.length === 0) {
5398
- throw internalError3({ message: "a row source needs at least one column and one row" });
5488
+ throw internalError4({ message: "a row source needs at least one column and one row" });
5399
5489
  }
5400
5490
  const typedBy = renderTable(dialect, source.typedBy);
5401
5491
  const columns = source.columns.map((name) => dialect.quoteIdent({ name }));
5402
5492
  const rows = [`(${columns.map((column) => `(SELECT ${column} FROM ${typedBy} WHERE FALSE)`).join(", ")})`];
5403
5493
  for (const row of source.rows) {
5404
5494
  if (row.length !== source.columns.length) {
5405
- throw internalError3({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
5495
+ throw internalError4({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
5406
5496
  }
5407
5497
  rows.push(`(${row.map((value) => renderSelectCell({ dialect, value, collector })).join(", ")})`);
5408
5498
  }
@@ -5414,12 +5504,12 @@ function renderTallyColumn(params) {
5414
5504
  const { dialect, result, defined, collector } = params;
5415
5505
  const tally = result.tally;
5416
5506
  if (tally === undefined)
5417
- throw internalError3({ message: "a chain veto needs a tally column to guard" });
5507
+ throw internalError4({ message: "a chain veto needs a tally column to guard" });
5418
5508
  if (tally.steps.length === 0)
5419
- throw internalError3({ message: "a chain tally needs at least one step" });
5509
+ throw internalError4({ message: "a chain tally needs at least one step" });
5420
5510
  const step = (name) => {
5421
5511
  if (!defined.has(name))
5422
- throw internalError3({ message: `chain tally or veto reads undefined step "${name}"` });
5512
+ throw internalError4({ message: `chain tally or veto reads undefined step "${name}"` });
5423
5513
  return dialect.quoteIdent({ name });
5424
5514
  };
5425
5515
  const count = (name) => `(SELECT count(*) FROM ${step(name)})`;
@@ -5429,12 +5519,12 @@ function renderTallyColumn(params) {
5429
5519
  return `${sum} AS ${dialect.quoteIdent({ name: tally.alias })}`;
5430
5520
  const arms = vetoes.map((veto) => {
5431
5521
  if (!CHAIN_VETO_TOKEN_PATTERN.test(veto.token))
5432
- throw internalError3({ message: `chain veto token "${veto.token}" is not a lower-case word` });
5522
+ throw internalError4({ message: `chain veto token "${veto.token}" is not a lower-case word` });
5433
5523
  if (typeof veto.nonce !== "string" || veto.nonce === "")
5434
- throw internalError3({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
5524
+ throw internalError4({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
5435
5525
  const minRows = veto.minRows ?? 1;
5436
5526
  if (!Number.isSafeInteger(minRows) || minRows < 1)
5437
- throw internalError3({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
5527
+ throw internalError4({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
5438
5528
  const when = minRows === 1 ? `WHEN NOT EXISTS (SELECT 1 FROM ${step(veto.step)})` : `WHEN ${count(veto.step)} < ${minRows}`;
5439
5529
  return `${when} THEN CAST('${CHAIN_VETO_PREFIX}${veto.token}:' || ${collector.add(veto.nonce)} || ':' || ${count(veto.step)} AS integer)`;
5440
5530
  });
@@ -5443,25 +5533,25 @@ function renderTallyColumn(params) {
5443
5533
  function renderInsertSelect(params) {
5444
5534
  const { dialect, statement, collector } = params;
5445
5535
  if (statement.columns.length === 0)
5446
- throw internalError3({ message: "insert requires at least one column" });
5536
+ throw internalError4({ message: "insert requires at least one column" });
5447
5537
  if (statement.cells.length !== statement.columns.length) {
5448
- throw internalError3({
5538
+ throw internalError4({
5449
5539
  message: `insert row has ${statement.cells.length} values but ${statement.columns.length} columns`
5450
5540
  });
5451
5541
  }
5452
5542
  const from = statement.from ?? [];
5453
5543
  for (const name of from) {
5454
5544
  if (!params.earlier.has(name))
5455
- throw internalError3({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
5545
+ throw internalError4({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
5456
5546
  }
5457
5547
  const source = statement.source;
5458
5548
  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` });
5549
+ throw internalError4({ message: `row source alias "${source.alias}" collides with a chain step` });
5460
5550
  }
5461
5551
  const cells = statement.cells.map((value) => {
5462
5552
  if (isStepColumnRef(value)) {
5463
5553
  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` });
5554
+ throw internalError4({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
5465
5555
  }
5466
5556
  return `${dialect.quoteIdent({ name: value.step })}.${dialect.quoteIdent({ name: value.column })}`;
5467
5557
  }
@@ -5480,9 +5570,9 @@ function renderInsertSelect(params) {
5480
5570
  const conflict = statement.conflictDoNothing;
5481
5571
  if (conflict !== undefined) {
5482
5572
  if (conflict.columns.length === 0)
5483
- throw internalError3({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
5573
+ throw internalError4({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
5484
5574
  if (dialect.doNothingForm !== "onConflictDoNothing") {
5485
- throw unsupportedCapability27({
5575
+ throw unsupportedCapability28({
5486
5576
  dialect: dialect.name,
5487
5577
  feature: "ON CONFLICT DO NOTHING on an INSERT \u2026 SELECT chain step",
5488
5578
  hint: "gate on SqlDialect.statementChain and keep the multi-statement path"
@@ -5493,7 +5583,7 @@ function renderInsertSelect(params) {
5493
5583
  return text + applyReturning({ dialect, requested: statement.returning }).suffix;
5494
5584
  }
5495
5585
  // packages/sql/src/dialects/postgres-type-identity.ts
5496
- import { unsupportedCapability as unsupportedCapability28 } from "@vibeorm/schema";
5586
+ import { unsupportedCapability as unsupportedCapability29 } from "@vibeorm/schema";
5497
5587
  var IDENTIFIER = '(?:"(?:[^"\\x00]|"")+"|[a-zA-Z_][a-zA-Z_0-9$]*)';
5498
5588
  var MODIFIER_VALUE = `(?:[+-]?\\d+|${IDENTIFIER}|'(?:[^'\\x00]|'')*')`;
5499
5589
  var TYPE_NAME = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*(\\(\\s*${MODIFIER_VALUE}(?:\\s*,\\s*${MODIFIER_VALUE})*\\s*\\))?\\s*((?:\\[\\s*\\]\\s*)*)$`);
@@ -5520,13 +5610,13 @@ function parsePostgresTypeName(params) {
5520
5610
  }
5521
5611
  function renderPostgresTypeName(params) {
5522
5612
  if (params.dialect.name !== "postgres")
5523
- throw unsupportedCapability28({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5613
+ throw unsupportedCapability29({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5524
5614
  const { dialect, type } = params;
5525
5615
  return `${type.namespace === undefined ? "" : `${dialect.quoteIdent({ name: type.namespace })}.`}${dialect.quoteIdent({ name: type.name })}${type.modifier}${type.array ? "[]" : ""}`;
5526
5616
  }
5527
5617
  function renderPostgresTypeIdentity(params) {
5528
5618
  if (params.dialect.name !== "postgres")
5529
- throw unsupportedCapability28({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5619
+ throw unsupportedCapability29({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
5530
5620
  const collector = new ParamCollector(params.dialect);
5531
5621
  const { source } = params;
5532
5622
  let text;
@@ -5539,11 +5629,11 @@ function renderPostgresTypeIdentity(params) {
5539
5629
  return { text, values: collector.values, returning: "none" };
5540
5630
  }
5541
5631
  // packages/sql/src/dialects/postgres-default-identity.ts
5542
- import { unsupportedCapability as unsupportedCapability29 } from "@vibeorm/schema";
5632
+ import { unsupportedCapability as unsupportedCapability30 } from "@vibeorm/schema";
5543
5633
  function renderPostgresDefaultFunctionIdentity(params) {
5544
5634
  const { dialect, table, column } = params;
5545
5635
  if (dialect.name !== "postgres")
5546
- throw unsupportedCapability29({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
5636
+ throw unsupportedCapability30({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
5547
5637
  const collector = new ParamCollector(dialect);
5548
5638
  const namespace = collector.add(table.namespace);
5549
5639
  const name = collector.add(table.name);
@@ -5567,28 +5657,28 @@ WHERE n.nspname = ${namespace} AND c.relname = ${name} AND a.attname = ${field}
5567
5657
  };
5568
5658
  }
5569
5659
  // packages/sql/src/dialects/constraint-owned-index.ts
5570
- import { unsupportedCapability as unsupportedCapability30 } from "@vibeorm/schema";
5660
+ import { unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
5571
5661
  function renderConstraintOwnedIndexDrop(params) {
5572
5662
  if (params.dialect.name !== "postgres")
5573
- throw unsupportedCapability30({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
5663
+ throw unsupportedCapability31({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
5574
5664
  const quote = (name) => params.dialect.quoteIdent({ name });
5575
5665
  return { text: `ALTER TABLE ${quote(params.table)} DROP CONSTRAINT ${quote(params.constraint)};`, values: [], returning: "none" };
5576
5666
  }
5577
5667
  // packages/sql/src/dialects/mysql-check-probe.ts
5578
- import { unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
5668
+ import { unsupportedCapability as unsupportedCapability32 } from "@vibeorm/schema";
5579
5669
  function renderMysqlCheckProbe(params) {
5580
5670
  if (params.dialect.name !== "mysql")
5581
- throw unsupportedCapability31({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
5671
+ throw unsupportedCapability32({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
5582
5672
  const q = (name) => params.dialect.quoteIdent({ name });
5583
5673
  const probe = q(params.probe);
5584
5674
  switch (params.operation) {
5585
5675
  case "clone":
5586
5676
  if (params.table === undefined)
5587
- throw unsupportedCapability31({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
5677
+ throw unsupportedCapability32({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
5588
5678
  return { text: `CREATE TEMPORARY TABLE ${probe} LIKE ${q(params.table.namespace)}.${q(params.table.name)}`, values: [], returning: "none" };
5589
5679
  case "add": {
5590
5680
  if (params.expression === undefined || params.definition === undefined)
5591
- throw unsupportedCapability31({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
5681
+ throw unsupportedCapability32({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
5592
5682
  const parts = tableDefinitionParts({ sql: params.definition, backslashEscapes: true });
5593
5683
  return { text: `CREATE TEMPORARY TABLE ${probe} (
5594
5684
  ${[...parts.clauses, `CONSTRAINT ${probe} CHECK (${params.expression})`].join(`,
@@ -5634,33 +5724,33 @@ function sqliteTableConstraints(params) {
5634
5724
  return result;
5635
5725
  }
5636
5726
  // packages/sql/src/dialects/migration-visibility.ts
5637
- import { unsupportedCapability as unsupportedCapability33 } from "@vibeorm/schema";
5727
+ import { unsupportedCapability as unsupportedCapability34 } from "@vibeorm/schema";
5638
5728
 
5639
5729
  // packages/sql/src/dialects/migration-inventory.ts
5640
- import { unsupportedCapability as unsupportedCapability32 } from "@vibeorm/schema";
5730
+ import { unsupportedCapability as unsupportedCapability33 } from "@vibeorm/schema";
5641
5731
  var MYSQL_NO_ROW_LIMIT = " LIMIT 18446744073709551615";
5642
5732
  function renderSqliteColumnInventory(params) {
5643
5733
  if (params.dialect.name !== "sqlite")
5644
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5734
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5645
5735
  const collector = new ParamCollector(params.dialect);
5646
5736
  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
5737
  }
5648
5738
  function renderSqliteForeignKeyInventory(params) {
5649
5739
  if (params.dialect.name !== "sqlite")
5650
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5740
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5651
5741
  const collector = new ParamCollector(params.dialect);
5652
5742
  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
5743
  }
5654
5744
  function renderSqliteColumnInventoryBatch(params) {
5655
5745
  if (params.dialect.name !== "sqlite")
5656
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5746
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
5657
5747
  const collector = new ParamCollector(params.dialect);
5658
5748
  const request = params.dialect.quoteIdent({ name: "vibe_request" });
5659
5749
  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
5750
  }
5661
5751
  function renderSqliteForeignKeyInventoryBatch(params) {
5662
5752
  if (params.dialect.name !== "sqlite")
5663
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5753
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
5664
5754
  const collector = new ParamCollector(params.dialect);
5665
5755
  const request = params.dialect.quoteIdent({ name: "vibe_request" });
5666
5756
  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 +5763,15 @@ var SQLITE_INTROSPECTION_PRAGMA_ORDER = Object.freeze({
5673
5763
  });
5674
5764
  function renderSqliteIntrospectionPragmaBatch(params) {
5675
5765
  if (params.dialect.name !== "sqlite")
5676
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteIntrospectionPragmaBatch" });
5766
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteIntrospectionPragmaBatch" });
5677
5767
  const order = SQLITE_INTROSPECTION_PRAGMA_ORDER[params.pragma];
5678
5768
  if (order === undefined)
5679
- throw unsupportedCapability32({ dialect: "sqlite", feature: `introspectionPragma:${String(params.pragma)}` });
5769
+ throw unsupportedCapability33({ dialect: "sqlite", feature: `introspectionPragma:${String(params.pragma)}` });
5680
5770
  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
5771
  }
5682
5772
  function renderSqliteRebuildObjects(params) {
5683
5773
  if (params.dialect.name !== "sqlite")
5684
- throw unsupportedCapability32({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
5774
+ throw unsupportedCapability33({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
5685
5775
  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
5776
  }
5687
5777
  function renderMigrationInventory(params) {
@@ -5723,13 +5813,13 @@ ORDER BY kind, parent, name${MYSQL_NO_ROW_LIMIT}`, values: collector.values, ret
5723
5813
  // packages/sql/src/dialects/migration-visibility.ts
5724
5814
  function renderMigrationVisibility(params) {
5725
5815
  if (params.dialect.name !== "mysql")
5726
- throw unsupportedCapability33({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
5816
+ throw unsupportedCapability34({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
5727
5817
  const collector = new ParamCollector(params.dialect);
5728
5818
  const grantee = "CONCAT(QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)), '@', QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', -1)))";
5729
5819
  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
5820
  }
5731
5821
  // packages/sql/src/dialects/migration-objects.ts
5732
- import { internalError as internalError4, unsupportedCapability as unsupportedCapability34 } from "@vibeorm/schema";
5822
+ import { internalError as internalError5, unsupportedCapability as unsupportedCapability35 } from "@vibeorm/schema";
5733
5823
  function renderMigrationObjectQuery(params) {
5734
5824
  return objectQuery({ ...params, collector: new ParamCollector(params.dialect) });
5735
5825
  }
@@ -5737,7 +5827,7 @@ function objectQuery(params) {
5737
5827
  const { dialect, identity: object, collector } = params;
5738
5828
  if (object.kind === "namespace") {
5739
5829
  if (dialect.name !== "postgres")
5740
- throw unsupportedCapability34({ dialect: dialect.name, feature: "fixedNamespaces" });
5830
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "fixedNamespaces" });
5741
5831
  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
5832
  }
5743
5833
  const physical = "table" in object ? object.table : object.name;
@@ -5785,18 +5875,18 @@ function objectQuery(params) {
5785
5875
  }
5786
5876
  } else if (dialect.name === "mysql") {
5787
5877
  if (object.kind === "constraint" && object.constraintType === "exclusion")
5788
- throw unsupportedCapability34({ dialect: "mysql", feature: "exclusionConstraints" });
5878
+ throw unsupportedCapability35({ dialect: "mysql", feature: "exclusionConstraints" });
5789
5879
  const mysql = mysqlFactsText({ dialect, identity: object, namespace, name, collector });
5790
5880
  if (mysql === undefined)
5791
- throw unsupportedCapability34({ dialect: "mysql", feature: `inspect:${object.kind}` });
5881
+ throw unsupportedCapability35({ dialect: "mysql", feature: `inspect:${object.kind}` });
5792
5882
  text = mysql;
5793
5883
  } else {
5794
5884
  if (physical.namespace !== "main" && physical.namespace !== "temp")
5795
- throw unsupportedCapability34({ dialect: "sqlite", feature: "attachedSchemaInspection" });
5885
+ throw unsupportedCapability35({ dialect: "sqlite", feature: "attachedSchemaInspection" });
5796
5886
  const catalog = `${dialect.quoteIdent({ name: physical.namespace })}.sqlite_schema`;
5797
5887
  const kind = object.kind === "table" || object.kind === "view" ? object.kind : object.kind === "trigger" ? "trigger" : object.kind === "index" ? "index" : "table";
5798
5888
  if (["function", "sequence", "postgresExtension", "enum"].includes(object.kind) || object.kind === "constraint" && object.constraintType === "exclusion")
5799
- throw unsupportedCapability34({ dialect: "sqlite", feature: `inspect:${object.kind}` });
5889
+ throw unsupportedCapability35({ dialect: "sqlite", feature: `inspect:${object.kind}` });
5800
5890
  const sqliteParams = new ParamCollector(dialect);
5801
5891
  if (object.kind === "column" || object.kind === "columnDefault" || object.kind === "generatedColumn") {
5802
5892
  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 +5961,7 @@ function mysqlBatches(params) {
5871
5961
  function renderMysqlObjectBatch(params) {
5872
5962
  const { dialect } = params;
5873
5963
  if (dialect.name !== "mysql")
5874
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5964
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5875
5965
  const position = dialect.quoteIdent({ name: "vibe_position" });
5876
5966
  const groups = new Map;
5877
5967
  for (const [index, identity] of params.identities.entries()) {
@@ -5891,7 +5981,7 @@ function renderMysqlObjectBatch(params) {
5891
5981
  function renderMysqlObjectMembersBatch(params) {
5892
5982
  const { dialect } = params;
5893
5983
  if (dialect.name !== "mysql")
5894
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5984
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5895
5985
  const position = dialect.quoteIdent({ name: "vibe_position" });
5896
5986
  const ordinal = dialect.quoteIdent({ name: "vibe_ordinal" });
5897
5987
  const groups = new Map;
@@ -5911,7 +6001,7 @@ function renderMigrationDependencies(params) {
5911
6001
  }
5912
6002
  function dependencyQuery(params) {
5913
6003
  if (params.dialect.name !== "postgres")
5914
- throw unsupportedCapability34({ dialect: params.dialect.name, feature: "catalogDependencies" });
6004
+ throw unsupportedCapability35({ dialect: params.dialect.name, feature: "catalogDependencies" });
5915
6005
  const object = params.identity;
5916
6006
  const collector = params.collector;
5917
6007
  if (object.kind === "namespace") {
@@ -5968,7 +6058,7 @@ function inspectionReferences(params) {
5968
6058
  function renderMigrationObjectBatch(params) {
5969
6059
  const { dialect } = params;
5970
6060
  if (dialect.name !== "postgres")
5971
- throw unsupportedCapability34({ dialect: dialect.name, feature: "batchedMigrationInspection" });
6061
+ throw unsupportedCapability35({ dialect: dialect.name, feature: "batchedMigrationInspection" });
5972
6062
  const groups = new Map;
5973
6063
  for (const [position, identity] of params.identities.entries()) {
5974
6064
  const kind = identity.kind === "constraint" ? `${identity.kind}:${identity.constraintType}` : identity.kind === "generatedColumn" ? "columnDefault" : identity.kind;
@@ -5981,7 +6071,7 @@ function renderMigrationObjectBatch(params) {
5981
6071
  const facts = objectQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
5982
6072
  const dependencies = dependencyQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
5983
6073
  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 } });
6074
+ throw internalError5({ message: "Batched catalog queries bind their identity in different orders", meta: { kind: identity.kind } });
5985
6075
  const columns = facts.values.map((value, index) => `${dialect.quoteIdent({ name: `p${index + 1}` })} ${Array.isArray(value) ? "text[]" : "text"}`);
5986
6076
  const requests = group.map(({ identity, position }) => ({ position, ...Object.fromEntries(renderMigrationObjectQuery({ dialect, identity }).values.map((value, index) => [`p${index + 1}`, value])) }));
5987
6077
  const collector = new ParamCollector(dialect);
@@ -6001,16 +6091,16 @@ function renderMigrationNamespace(params) {
6001
6091
  }
6002
6092
  function renderCheckProbe(params) {
6003
6093
  if (params.dialect.name !== "postgres")
6004
- throw unsupportedCapability34({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
6094
+ throw unsupportedCapability35({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
6005
6095
  const q = (name) => params.dialect.quoteIdent({ name });
6006
6096
  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
6097
  }
6008
6098
  // packages/sql/src/dialects/postgres-migration.ts
6009
- import { VibeError as VibeError23, unsupportedCapability as unsupportedCapability35 } from "@vibeorm/schema";
6099
+ import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability36 } from "@vibeorm/schema";
6010
6100
  var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
6011
6101
  function requirePostgres2(params) {
6012
6102
  if (params.dialect.name !== "postgres") {
6013
- throw unsupportedCapability35({ dialect: params.dialect.name, feature: params.feature });
6103
+ throw unsupportedCapability36({ dialect: params.dialect.name, feature: params.feature });
6014
6104
  }
6015
6105
  }
6016
6106
  function renderMigrationSessionRead(params) {
@@ -6025,7 +6115,7 @@ function renderMigrationSessionSet(params) {
6025
6115
  requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
6026
6116
  const { milliseconds, setting } = params;
6027
6117
  if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
6028
- throw new VibeError23({
6118
+ throw new VibeError24({
6029
6119
  code: "VIBE_MIGRATION",
6030
6120
  message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
6031
6121
  meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
@@ -6196,6 +6286,7 @@ export {
6196
6286
  isSqlParam,
6197
6287
  isSqlQuery,
6198
6288
  isStepColumnRef,
6289
+ isTimeZoneName,
6199
6290
  jsonListParameter,
6200
6291
  jsonParamCell,
6201
6292
  keyListBindsUntypedRows,
@@ -6222,6 +6313,7 @@ export {
6222
6313
  renderColumnStateQuery,
6223
6314
  renderConstraintOwnedIndexDrop,
6224
6315
  renderConstraintStateQuery,
6316
+ renderDateTruncation,
6225
6317
  renderDiagnostic,
6226
6318
  renderExclusionCatalog,
6227
6319
  renderExclusionDdl,
@@ -6322,4 +6414,4 @@ export {
6322
6414
  toPgArrayLiteral
6323
6415
  };
6324
6416
 
6325
- //# debugId=F22851A5DD4820AE64756E2164756E21
6417
+ //# debugId=7A09C0BE2E07A46664756E2164756E21