@vibeorm/sql 5.10.0 → 5.11.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
@@ -3605,6 +3605,9 @@ function renderSqliteJsonEquality(params) {
3605
3605
  return `(SELECT CASE WHEN ${left} IS NULL THEN NULL ELSE NOT EXISTS (` + `WITH RECURSIVE ${tree} AS (` + `SELECT 1 AS ${side}, ${id}, ${parentId}, ${key}, ${type}, ${atom} FROM json_tree(CASE WHEN json_valid(${left}) THEN ${left} ELSE 'null' END) ` + `UNION ALL SELECT -1 AS ${side}, ${id}, ${parentId}, ${key}, ${type}, ${atom} FROM json_tree(${right})` + `), ${paths} (${side}, ${id}, ${path}, ${type}, ${atom}) AS (` + `SELECT ${side}, ${id}, json_array(), ${type}, ${atom} FROM ${tree} WHERE ${parentId} IS NULL ` + `UNION ALL SELECT ${child}.${side}, ${child}.${id}, json_insert(${parent}.${path}, '$[#]', ${child}.${key}), ${child}.${type}, ${child}.${atom} ` + `FROM ${tree} AS ${child} JOIN ${paths} AS ${parent} ON ${child}.${side} = ${parent}.${side} AND ${child}.${parentId} = ${parent}.${id}` + `) SELECT 1 FROM ${paths} GROUP BY ${path}, CASE WHEN ${type} IN ('integer', 'real') THEN 'number' ELSE ${type} END, ${atom} HAVING SUM(${side}) <> 0` + `) END FROM (SELECT ${params.left} AS ${q("left")}, ${params.right} AS ${q("right")}) AS ${values})`;
3606
3606
  }
3607
3607
 
3608
+ // packages/sql/src/dialects/mysql-json-keys.ts
3609
+ import { internalError as internalError2 } from "@vibeorm/schema";
3610
+
3608
3611
  // packages/sql/src/json-transport.ts
3609
3612
  var JSON_TRANSPORT = Object.freeze({ chunkSize: 100 });
3610
3613
  function projectionOutputName(params) {
@@ -3612,8 +3615,176 @@ function projectionOutputName(params) {
3612
3615
  return column.alias ?? column.name;
3613
3616
  }
3614
3617
 
3618
+ // packages/sql/src/key-list.ts
3619
+ import { unsupportedCapability as unsupportedCapability16 } from "@vibeorm/schema";
3620
+ var SCALAR_ELEMENT_TYPES = Object.freeze({
3621
+ String: "text",
3622
+ Boolean: "bool",
3623
+ Int: "int4",
3624
+ BigInt: "int8",
3625
+ Float: "float8",
3626
+ Decimal: "numeric",
3627
+ DateTime: "timestamptz",
3628
+ Json: null,
3629
+ Bytes: "bytea"
3630
+ });
3631
+ var NATIVE_ELEMENT_TYPES = Object.freeze({
3632
+ text: "text",
3633
+ varchar: "varchar",
3634
+ charactervarying: "varchar",
3635
+ char: "bpchar",
3636
+ character: "bpchar",
3637
+ bpchar: "bpchar",
3638
+ uuid: "uuid",
3639
+ citext: "citext",
3640
+ xml: "xml",
3641
+ inet: "inet",
3642
+ money: "money",
3643
+ bit: '"bit"',
3644
+ varbit: "varbit",
3645
+ bitvarying: "varbit",
3646
+ smallint: "int2",
3647
+ int2: "int2",
3648
+ integer: "int4",
3649
+ int: "int4",
3650
+ int4: "int4",
3651
+ bigint: "int8",
3652
+ int8: "int8",
3653
+ oid: "oid",
3654
+ real: "float4",
3655
+ float4: "float4",
3656
+ doubleprecision: "float8",
3657
+ double: "float8",
3658
+ float8: "float8",
3659
+ decimal: "numeric",
3660
+ numeric: "numeric",
3661
+ boolean: "bool",
3662
+ bool: "bool",
3663
+ timestamp: "timestamp",
3664
+ timestampwithouttimezone: "timestamp",
3665
+ timestamptz: "timestamptz",
3666
+ timestampwithtimezone: "timestamptz",
3667
+ date: "date",
3668
+ time: "time",
3669
+ timewithouttimezone: "time",
3670
+ timetz: "timetz",
3671
+ timewithtimezone: "timetz",
3672
+ bytea: "bytea"
3673
+ });
3674
+ function postgresKeyListElementTypeOf(params) {
3675
+ const { dialect, member } = params;
3676
+ if (member.type === "Enum") {
3677
+ if (member.enumType === undefined)
3678
+ return "text";
3679
+ const name = dialect.quoteIdent({ name: member.enumType.name });
3680
+ return member.enumType.namespace === undefined ? name : `${dialect.quoteIdent({ name: member.enumType.namespace })}.${name}`;
3681
+ }
3682
+ const native = member.nativeType === undefined ? undefined : NATIVE_ELEMENT_TYPES[member.nativeType.toLowerCase().replace(/\([^)]*\)/g, "").replace(/\s+/g, "")];
3683
+ const type = member.nativeType === undefined ? SCALAR_ELEMENT_TYPES[member.type] : native;
3684
+ return type === null ? undefined : type;
3685
+ }
3686
+ function postgresKeyListElementType(params) {
3687
+ const { dialect, member } = params;
3688
+ const type = postgresKeyListElementTypeOf({ dialect, member });
3689
+ if (type === undefined) {
3690
+ throw unsupportedCapability16({
3691
+ dialect: dialect.name,
3692
+ feature: `the key-list filter (IN) on a ${member.type}${member.nativeType === undefined ? "" : ` @db.${member.nativeType}`} member`,
3693
+ hint: "IN binds every member as a typed array; filter this field with equality inside OR instead"
3694
+ });
3695
+ }
3696
+ return type;
3697
+ }
3698
+ function keyListBindsUntypedRows(params) {
3699
+ return params.members.some((member) => member.type === "DateTime" && member.nativeType === undefined);
3700
+ }
3701
+ var MYSQL_JSON_TEXT_KEY_TYPES = new Set(["varchar", "char", "character", "text", "tinytext", "mediumtext", "longtext"]);
3702
+ var MYSQL_JSON_INTEGER_KEY_TYPES = new Set(["int", "integer", "bigint", "smallint", "tinyint", "mediumint"]);
3703
+ function mysqlJsonKeyTypeOf(params) {
3704
+ const { member } = params;
3705
+ const native = member.nativeType?.toLowerCase().replace(/\([^)]*\)/g, "").replace(/\s+/g, "");
3706
+ if (member.type === "String")
3707
+ return native === undefined || MYSQL_JSON_TEXT_KEY_TYPES.has(native) ? "text" : undefined;
3708
+ if (member.type !== "Int" && member.type !== "BigInt")
3709
+ return;
3710
+ if (native === undefined)
3711
+ return "signed";
3712
+ const unsigned = native.startsWith("unsigned") || native.endsWith("unsigned");
3713
+ const base = native.replace(/^unsigned|unsigned$/g, "");
3714
+ if (!MYSQL_JSON_INTEGER_KEY_TYPES.has(base))
3715
+ return;
3716
+ return unsigned ? "unsigned" : "signed";
3717
+ }
3718
+ function jsonKeyTokenCast(params) {
3719
+ const { dialect, member } = params;
3720
+ return !(dialect.lateralJson?.keysFromJson === "jsonTable" && mysqlJsonKeyTypeOf({ member }) === "text");
3721
+ }
3722
+ function jsonKeysTyped(params) {
3723
+ const { dialect, members } = params;
3724
+ if (dialect.lateralJson?.keysFromJson === "jsonTable")
3725
+ return members.every((member) => mysqlJsonKeyTypeOf({ member }) !== undefined);
3726
+ return !keyListBindsUntypedRows({ members }) && members.every((member) => postgresKeyListElementTypeOf({ dialect, member }) !== undefined);
3727
+ }
3728
+ function postgresBulkInsertArrayCastOf(params) {
3729
+ const { dialect, member } = params;
3730
+ if (member.type === "Json")
3731
+ return member.nativeType?.trim().toLowerCase() === "json" ? "text::json" : "text::jsonb";
3732
+ return postgresKeyListElementTypeOf({ dialect, member });
3733
+ }
3734
+ function postgresBulkInsertArrayCast(params) {
3735
+ const { dialect, member } = params;
3736
+ const cast = postgresBulkInsertArrayCastOf({ dialect, member });
3737
+ if (cast === undefined) {
3738
+ throw unsupportedCapability16({
3739
+ dialect: dialect.name,
3740
+ feature: `a bulk insert binding a ${member.type}${member.nativeType === undefined ? "" : ` @db.${member.nativeType}`} column as a typed array`,
3741
+ hint: "the query builder keeps VALUES for this column type"
3742
+ });
3743
+ }
3744
+ return cast;
3745
+ }
3746
+
3747
+ // packages/sql/src/dialects/mysql-json-keys.ts
3748
+ var SOURCE_ALIAS = "__vibe_s";
3749
+ var ROWS_ALIAS = "__vibe_e";
3750
+ var SET_ALIAS = "__vibe_k";
3751
+ function slotPath(params) {
3752
+ const { slot } = params;
3753
+ if (!Number.isSafeInteger(slot.index) || !Number.isSafeInteger(slot.width) || slot.index < 0 || slot.index >= slot.width) {
3754
+ throw internalError2({ message: "a JSON slot must be an index inside its row" });
3755
+ }
3756
+ const size = JSON_TRANSPORT.chunkSize;
3757
+ return slot.width <= size ? `[${slot.index}]` : `[${Math.floor(slot.index / size)}][${slot.index % size}]`;
3758
+ }
3759
+ function renderMysqlJsonKeys(params) {
3760
+ const { dialect, statement, keys } = params;
3761
+ const quote = (name) => dialect.quoteIdent({ name });
3762
+ const source = quote(SOURCE_ALIAS);
3763
+ const rows = quote(ROWS_ALIAS);
3764
+ const set = quote(SET_ALIAS);
3765
+ const rowPath = `$${keys.steps.map((step) => step.kind === "elements" ? "[*]" : slotPath({ slot: step.slot })).join("")}`;
3766
+ const columns = keys.keys.map((key) => `${quote(key.token)} JSON PATH '$${slotPath({ slot: key.slot })}'`);
3767
+ const typed = keys.keys.flatMap((key) => {
3768
+ const type = mysqlJsonKeyTypeOf({ member: key.member });
3769
+ if (type === undefined) {
3770
+ throw internalError2({ message: "fromJsonKeys cannot type this key from its JSON text on mysql: only string and integer keys are admitted" });
3771
+ }
3772
+ const token = `${set}.${quote(key.token)}`;
3773
+ const text = `CASE WHEN JSON_TYPE(${token}) = 'NULL' THEN NULL ELSE JSON_UNQUOTE(${token}) END`;
3774
+ const value = type === "text" ? text : `CAST(${text} AS ${type === "signed" ? "SIGNED" : "UNSIGNED"})`;
3775
+ return [`${text} AS ${quote(key.token)}`, `${value} AS ${quote(key.alias)}`];
3776
+ });
3777
+ const distinct = keys.keys.map((key) => `${rows}.${quote(key.token)}`);
3778
+ const from = `${quote(keys.source.table)} AS ${source} JOIN JSON_TABLE(${source}.${quote(keys.source.name)}, '${rowPath}' COLUMNS (${columns.join(", ")})) AS ${rows}`;
3779
+ return `(SELECT ${typed.join(", ")} FROM (SELECT DISTINCT ${distinct.join(", ")} FROM ${from}) AS ${set}) AS ${quote(statement.table.name)}`;
3780
+ }
3781
+ function mysqlPinnedCommonTableSuffix(params) {
3782
+ const { select } = params.table;
3783
+ return params.table.materialized !== true || select.limit !== undefined || select.offset !== undefined ? "" : " LIMIT 18446744073709551615";
3784
+ }
3785
+
3615
3786
  // packages/sql/src/dialects/diagnostics.ts
3616
- import { assertSqlNamespace as assertSqlNamespace2, unsupportedCapability as unsupportedCapability16 } from "@vibeorm/schema";
3787
+ import { assertSqlNamespace as assertSqlNamespace2, unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3617
3788
  var QUERIES = Object.freeze({
3618
3789
  postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
3619
3790
  postgresServerVersion: { dialect: "postgres", text: `SELECT pg_catalog.current_setting('server_version_num') AS "serverVersionNum"` },
@@ -3622,7 +3793,7 @@ var QUERIES = Object.freeze({
3622
3793
  function renderDiagnostic(params) {
3623
3794
  const query = QUERIES[params.query];
3624
3795
  if (params.dialect.name !== query.dialect) {
3625
- throw unsupportedCapability16({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3796
+ throw unsupportedCapability17({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
3626
3797
  }
3627
3798
  return { text: query.text, values: [], returning: "none" };
3628
3799
  }
@@ -3633,7 +3804,7 @@ var CAPTURE_READINESS_SQL = `SELECT pg_catalog.current_setting('server_version_n
3633
3804
  function renderCaptureReadiness(params) {
3634
3805
  const { dialect, tables } = params;
3635
3806
  if (dialect.name !== "postgres") {
3636
- throw unsupportedCapability16({ dialect: dialect.name, feature: "postgresCaptureReadiness", hint: "row capture reads RETURNING old/new, PostgreSQL 18 or newer" });
3807
+ throw unsupportedCapability17({ dialect: dialect.name, feature: "postgresCaptureReadiness", hint: "row capture reads RETURNING old/new, PostgreSQL 18 or newer" });
3637
3808
  }
3638
3809
  const entries = tables.map((entry, index) => {
3639
3810
  const { name, namespace } = entry.table;
@@ -3647,7 +3818,7 @@ function renderCaptureReadiness(params) {
3647
3818
  }
3648
3819
 
3649
3820
  // packages/sql/src/dialects/postgres-diagnostics.ts
3650
- import { VibeError as VibeError16, unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3821
+ import { VibeError as VibeError16, unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
3651
3822
  function renderPostgresExplain(params) {
3652
3823
  const { dialect, query, options } = params;
3653
3824
  requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
@@ -3682,7 +3853,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
3682
3853
  });
3683
3854
  function requirePostgres(params) {
3684
3855
  if (params.dialect.name !== "postgres") {
3685
- throw unsupportedCapability17({
3856
+ throw unsupportedCapability18({
3686
3857
  dialect: params.dialect.name,
3687
3858
  feature: params.feature,
3688
3859
  hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
@@ -3729,10 +3900,10 @@ function requireArg(params) {
3729
3900
  }
3730
3901
 
3731
3902
  // packages/sql/src/dialects/postgres-rls-readiness.ts
3732
- import { unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
3903
+ import { unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
3733
3904
 
3734
3905
  // packages/sql/src/dialects/postgres-rls.ts
3735
- import { RlsContextError, VibeError as VibeError17, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability18, validateRlsContextIR } from "@vibeorm/schema";
3906
+ import { RlsContextError, VibeError as VibeError17, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability19, validateRlsContextIR } from "@vibeorm/schema";
3736
3907
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
3737
3908
  var COLUMN_TYPES = {
3738
3909
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -3745,7 +3916,7 @@ var COLUMN_TYPES = {
3745
3916
  };
3746
3917
  function requireRls(params) {
3747
3918
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
3748
- throw unsupportedCapability18({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3919
+ throw unsupportedCapability19({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
3749
3920
  }
3750
3921
  }
3751
3922
  function identifier2(params) {
@@ -3756,7 +3927,7 @@ function identifier2(params) {
3756
3927
  }
3757
3928
  function tableRef(params) {
3758
3929
  if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
3759
- throw unsupportedCapability18({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
3930
+ throw unsupportedCapability19({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
3760
3931
  }
3761
3932
  return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
3762
3933
  }
@@ -3797,7 +3968,7 @@ function renderRlsColumnType(params) {
3797
3968
  if (native !== undefined)
3798
3969
  return native;
3799
3970
  }
3800
- throw unsupportedCapability18({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3971
+ throw unsupportedCapability19({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
3801
3972
  }
3802
3973
  function protectedModel(params) {
3803
3974
  requireRls(params);
@@ -4037,7 +4208,7 @@ ORDER BY c.relname, p.polname`,
4037
4208
  });
4038
4209
  function requireRls2(params) {
4039
4210
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
4040
- throw unsupportedCapability19({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
4211
+ throw unsupportedCapability20({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
4041
4212
  }
4042
4213
  }
4043
4214
  function renderRlsReadinessQuery(params) {
@@ -4069,7 +4240,7 @@ function renderRlsPolicyPath(params) {
4069
4240
  }
4070
4241
 
4071
4242
  // packages/sql/src/render.ts
4072
- import { assertSqlNamespace as assertSqlNamespace3, internalError as internalError4, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
4243
+ import { assertSqlNamespace as assertSqlNamespace3, internalError as internalError5, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
4073
4244
 
4074
4245
  // packages/sql/src/like.ts
4075
4246
  function escapeLikePattern(params) {
@@ -4100,7 +4271,7 @@ function strictMatchCollationPrefilter(params) {
4100
4271
  }
4101
4272
 
4102
4273
  // packages/sql/src/locks.ts
4103
- import { internalError as internalError2, unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
4274
+ import { internalError as internalError3, unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
4104
4275
  var ROW_LOCK_PROSE = Object.freeze({
4105
4276
  strengths: Object.freeze({
4106
4277
  update: "FOR UPDATE",
@@ -4120,7 +4291,7 @@ function renderRowLockClause(params) {
4120
4291
  if (syntax === null) {
4121
4292
  if (lock.strength === "update" && lock.wait === "wait")
4122
4293
  return null;
4123
- throw unsupportedCapability20({
4294
+ throw unsupportedCapability21({
4124
4295
  dialect: dialect.name,
4125
4296
  feature: `a ${describeRowLock({ lock })} locking read`,
4126
4297
  hint: `${dialect.name} has no locking-read clause at all`
@@ -4128,7 +4299,7 @@ function renderRowLockClause(params) {
4128
4299
  }
4129
4300
  const strengthText = syntax.strengths[lock.strength];
4130
4301
  if (strengthText === undefined) {
4131
- throw unsupportedCapability20({
4302
+ throw unsupportedCapability21({
4132
4303
  dialect: dialect.name,
4133
4304
  feature: `the ${describeRowLock({ lock: { ...lock, wait: "wait" } })} row-lock strength`,
4134
4305
  hint: `${dialect.name} can express: ${Object.keys(syntax.strengths).join(", ")}`
@@ -4136,7 +4307,7 @@ function renderRowLockClause(params) {
4136
4307
  }
4137
4308
  const waitText = syntax.wait[lock.wait];
4138
4309
  if (waitText === undefined) {
4139
- throw unsupportedCapability20({
4310
+ throw unsupportedCapability21({
4140
4311
  dialect: dialect.name,
4141
4312
  feature: `the "${lock.wait}" lock wait policy`,
4142
4313
  hint: `${dialect.name} can express: ${Object.keys(syntax.wait).join(", ")}`
@@ -4144,7 +4315,7 @@ function renderRowLockClause(params) {
4144
4315
  }
4145
4316
  if (lock.of !== undefined) {
4146
4317
  if (lock.of.length === 0)
4147
- throw internalError2({ message: "a row lock's `of` list cannot be empty" });
4318
+ throw internalError3({ message: "a row lock's `of` list cannot be empty" });
4148
4319
  const tables = lock.of.map((name) => dialect.quoteIdent({ name })).join(", ");
4149
4320
  return `${strengthText} OF ${tables}${waitText}`;
4150
4321
  }
@@ -4162,7 +4333,7 @@ function renderAdvisoryLock(params) {
4162
4333
  const { dialect, statement, collector } = params;
4163
4334
  const functions = dialect.advisoryLockFunctions;
4164
4335
  if (functions === null) {
4165
- throw unsupportedCapability20({
4336
+ throw unsupportedCapability21({
4166
4337
  dialect: dialect.name,
4167
4338
  feature: "advisory locks",
4168
4339
  hint: dialect.name === "mysql" ? "mysql's GET_LOCK is SESSION-scoped \u2014 it survives a rollback, so it cannot be released with the transaction" : `${dialect.name} has no advisory-lock functions`
@@ -4183,11 +4354,11 @@ function renderAdvisoryLocks(params) {
4183
4354
  const { dialect, keys, mode } = params;
4184
4355
  const functions = dialect.advisoryLockFunctions;
4185
4356
  if (functions === null)
4186
- throw unsupportedCapability20({ dialect: dialect.name, feature: "advisory locks" });
4357
+ throw unsupportedCapability21({ dialect: dialect.name, feature: "advisory locks" });
4187
4358
  if (mode !== "exclusive" && mode !== "shared")
4188
- throw internalError2({ message: "advisory lock mode must be exclusive or shared" });
4359
+ throw internalError3({ message: "advisory lock mode must be exclusive or shared" });
4189
4360
  if (keys.some((key) => typeof key !== "bigint" || key < ADVISORY_KEY_BOUNDS.single.min || key > ADVISORY_KEY_BOUNDS.single.max)) {
4190
- throw internalError2({ message: "advisory lock keys must be signed 64-bit integers" });
4361
+ throw internalError3({ message: "advisory lock keys must be signed 64-bit integers" });
4191
4362
  }
4192
4363
  const collector = new ParamCollector(dialect);
4193
4364
  const binding = collector.add(JSON.stringify(keys.map((key) => key.toString())));
@@ -4204,7 +4375,7 @@ function renderAdvisoryLocks(params) {
4204
4375
  function assertInt32(params) {
4205
4376
  const { value, part } = params;
4206
4377
  if (!Number.isInteger(value) || value < ADVISORY_KEY_BOUNDS.pair.min || value > ADVISORY_KEY_BOUNDS.pair.max) {
4207
- throw internalError2({
4378
+ throw internalError3({
4208
4379
  message: `advisory lock ${part} must be a 32-bit integer, got ${String(value)}`,
4209
4380
  meta: { part, value }
4210
4381
  });
@@ -4213,7 +4384,7 @@ function assertInt32(params) {
4213
4384
  }
4214
4385
 
4215
4386
  // packages/sql/src/date-truncation.ts
4216
- import { DATE_TRUNCATION_UNITS, VibeError as VibeError18, internalError as internalError3, unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
4387
+ import { DATE_TRUNCATION_UNITS, VibeError as VibeError18, internalError as internalError4, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
4217
4388
  var TIME_ZONE_NAME = /^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/;
4218
4389
  function isTimeZoneName(params) {
4219
4390
  return typeof params.value === "string" && TIME_ZONE_NAME.test(params.value);
@@ -4221,14 +4392,14 @@ function isTimeZoneName(params) {
4221
4392
  function renderDateTruncation(params) {
4222
4393
  const { dialect, reference, truncation } = params;
4223
4394
  if (!dialect.capabilities.dateTruncation) {
4224
- throw unsupportedCapability21({
4395
+ throw unsupportedCapability22({
4225
4396
  dialect: dialect.name,
4226
4397
  feature: "a truncated groupBy key ({ field, truncate, timeZone })",
4227
4398
  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"
4228
4399
  });
4229
4400
  }
4230
4401
  if (!DATE_TRUNCATION_UNITS.includes(truncation.unit)) {
4231
- throw internalError3({ message: `unknown date truncation unit ${JSON.stringify(truncation.unit)}` });
4402
+ throw internalError4({ message: `unknown date truncation unit ${JSON.stringify(truncation.unit)}` });
4232
4403
  }
4233
4404
  if (!isTimeZoneName({ value: truncation.timeZone })) {
4234
4405
  throw new VibeError18({
@@ -4245,110 +4416,8 @@ function renderDateTruncation(params) {
4245
4416
  case "timestamp":
4246
4417
  return `(date_trunc(${unit}, ${reference} AT TIME ZONE 'UTC', ${zone}) AT TIME ZONE 'UTC')`;
4247
4418
  default:
4248
- throw internalError3({ message: `unknown date truncation storage ${JSON.stringify(truncation.storage)}` });
4249
- }
4250
- }
4251
-
4252
- // packages/sql/src/key-list.ts
4253
- import { unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
4254
- var SCALAR_ELEMENT_TYPES = Object.freeze({
4255
- String: "text",
4256
- Boolean: "bool",
4257
- Int: "int4",
4258
- BigInt: "int8",
4259
- Float: "float8",
4260
- Decimal: "numeric",
4261
- DateTime: "timestamptz",
4262
- Json: null,
4263
- Bytes: "bytea"
4264
- });
4265
- var NATIVE_ELEMENT_TYPES = Object.freeze({
4266
- text: "text",
4267
- varchar: "varchar",
4268
- charactervarying: "varchar",
4269
- char: "bpchar",
4270
- character: "bpchar",
4271
- bpchar: "bpchar",
4272
- uuid: "uuid",
4273
- citext: "citext",
4274
- xml: "xml",
4275
- inet: "inet",
4276
- money: "money",
4277
- bit: '"bit"',
4278
- varbit: "varbit",
4279
- bitvarying: "varbit",
4280
- smallint: "int2",
4281
- int2: "int2",
4282
- integer: "int4",
4283
- int: "int4",
4284
- int4: "int4",
4285
- bigint: "int8",
4286
- int8: "int8",
4287
- oid: "oid",
4288
- real: "float4",
4289
- float4: "float4",
4290
- doubleprecision: "float8",
4291
- double: "float8",
4292
- float8: "float8",
4293
- decimal: "numeric",
4294
- numeric: "numeric",
4295
- boolean: "bool",
4296
- bool: "bool",
4297
- timestamp: "timestamp",
4298
- timestampwithouttimezone: "timestamp",
4299
- timestamptz: "timestamptz",
4300
- timestampwithtimezone: "timestamptz",
4301
- date: "date",
4302
- time: "time",
4303
- timewithouttimezone: "time",
4304
- timetz: "timetz",
4305
- timewithtimezone: "timetz",
4306
- bytea: "bytea"
4307
- });
4308
- function postgresKeyListElementTypeOf(params) {
4309
- const { dialect, member } = params;
4310
- if (member.type === "Enum") {
4311
- if (member.enumType === undefined)
4312
- return "text";
4313
- const name = dialect.quoteIdent({ name: member.enumType.name });
4314
- return member.enumType.namespace === undefined ? name : `${dialect.quoteIdent({ name: member.enumType.namespace })}.${name}`;
4419
+ throw internalError4({ message: `unknown date truncation storage ${JSON.stringify(truncation.storage)}` });
4315
4420
  }
4316
- const native = member.nativeType === undefined ? undefined : NATIVE_ELEMENT_TYPES[member.nativeType.toLowerCase().replace(/\([^)]*\)/g, "").replace(/\s+/g, "")];
4317
- const type = member.nativeType === undefined ? SCALAR_ELEMENT_TYPES[member.type] : native;
4318
- return type === null ? undefined : type;
4319
- }
4320
- function postgresKeyListElementType(params) {
4321
- const { dialect, member } = params;
4322
- const type = postgresKeyListElementTypeOf({ dialect, member });
4323
- if (type === undefined) {
4324
- throw unsupportedCapability22({
4325
- dialect: dialect.name,
4326
- feature: `the key-list filter (IN) on a ${member.type}${member.nativeType === undefined ? "" : ` @db.${member.nativeType}`} member`,
4327
- hint: "IN binds every member as a typed array; filter this field with equality inside OR instead"
4328
- });
4329
- }
4330
- return type;
4331
- }
4332
- function keyListBindsUntypedRows(params) {
4333
- return params.members.some((member) => member.type === "DateTime" && member.nativeType === undefined);
4334
- }
4335
- function postgresBulkInsertArrayCastOf(params) {
4336
- const { dialect, member } = params;
4337
- if (member.type === "Json")
4338
- return member.nativeType?.trim().toLowerCase() === "json" ? "text::json" : "text::jsonb";
4339
- return postgresKeyListElementTypeOf({ dialect, member });
4340
- }
4341
- function postgresBulkInsertArrayCast(params) {
4342
- const { dialect, member } = params;
4343
- const cast = postgresBulkInsertArrayCastOf({ dialect, member });
4344
- if (cast === undefined) {
4345
- throw unsupportedCapability22({
4346
- dialect: dialect.name,
4347
- feature: `a bulk insert binding a ${member.type}${member.nativeType === undefined ? "" : ` @db.${member.nativeType}`} column as a typed array`,
4348
- hint: "the query builder keeps VALUES for this column type"
4349
- });
4350
- }
4351
- return cast;
4352
4421
  }
4353
4422
 
4354
4423
  // packages/sql/src/render.ts
@@ -4371,7 +4440,7 @@ function renderStatement(params) {
4371
4440
  return renderPostgresWorkload({ dialect, ...statement });
4372
4441
  }
4373
4442
  case "select":
4374
- return renderSelect({ dialect, statement, collector });
4443
+ return renderSelect({ dialect, statement, collector, outermost: true });
4375
4444
  case "advisoryLocks":
4376
4445
  return renderAdvisoryLocks({ dialect, keys: statement.keys, mode: statement.mode });
4377
4446
  case "advisoryLock":
@@ -4411,7 +4480,7 @@ function renderStatement(params) {
4411
4480
  function quoteCol(dialect, column) {
4412
4481
  if (column.coalesce !== undefined) {
4413
4482
  if (column.coalesce.length < 2 || column.coalesce.some((member) => member.coalesce !== undefined)) {
4414
- throw internalError4({ message: "ColRef.coalesce needs two or more plain member references" });
4483
+ throw internalError5({ message: "ColRef.coalesce needs two or more plain member references" });
4415
4484
  }
4416
4485
  return `COALESCE(${column.coalesce.map((member) => quoteCol(dialect, member)).join(", ")})`;
4417
4486
  }
@@ -4425,13 +4494,13 @@ function quoteCol(dialect, column) {
4425
4494
  return dialect.renderEnumOrdinal({ column: ref });
4426
4495
  if (column.castReal === true) {
4427
4496
  if (dialect.name !== "sqlite") {
4428
- throw internalError4({ message: "ColRef.castReal is a sqlite Decimal construct" });
4497
+ throw internalError5({ message: "ColRef.castReal is a sqlite Decimal construct" });
4429
4498
  }
4430
4499
  return `CAST(${ref} AS REAL)`;
4431
4500
  }
4432
4501
  if (column.castJsonb !== undefined) {
4433
4502
  if (dialect.name !== "postgres") {
4434
- throw internalError4({ message: "ColRef.castJsonb is a postgres json construct" });
4503
+ throw internalError5({ message: "ColRef.castJsonb is a postgres json construct" });
4435
4504
  }
4436
4505
  return `${ref}::${column.castJsonb}`;
4437
4506
  }
@@ -4450,7 +4519,7 @@ function jsonCellCast(dialect, cell) {
4450
4519
  if (cell.storage !== "json" || dialect.jsonParamCast === "")
4451
4520
  return dialect.jsonParamCast;
4452
4521
  if (dialect.name !== "postgres") {
4453
- throw internalError4({ message: "JsonParamCell storage json is a postgres construct" });
4522
+ throw internalError5({ message: "JsonParamCell storage json is a postgres construct" });
4454
4523
  }
4455
4524
  return "::text::json";
4456
4525
  }
@@ -4474,7 +4543,7 @@ function quoteProjection(dialect, column) {
4474
4543
  if (column.transform === "encodeBase64" && dialect.name === "mysql")
4475
4544
  return `TO_BASE64(${ref}) AS ${alias}`;
4476
4545
  if (dialect.name !== "postgres") {
4477
- throw internalError4({ message: `${column.transform} is a postgres construct` });
4546
+ throw internalError5({ message: `${column.transform} is a postgres construct` });
4478
4547
  }
4479
4548
  if (column.transform === "encodeArrayBase64")
4480
4549
  return `encode(array_send(${ref}), 'base64') AS ${alias}`;
@@ -4498,7 +4567,7 @@ function renderTable(dialect, table) {
4498
4567
  }
4499
4568
  function assertCount(value, what) {
4500
4569
  if (!Number.isSafeInteger(value) || value < 0) {
4501
- throw internalError4({ message: `${what} must be a non-negative integer, got ${value}` });
4570
+ throw internalError5({ message: `${what} must be a non-negative integer, got ${value}` });
4502
4571
  }
4503
4572
  }
4504
4573
  var COMPARISON_SQL2 = {
@@ -4554,14 +4623,14 @@ function renderGroupKey(params) {
4554
4623
  const { dialect, reference, key } = params;
4555
4624
  if (key.kind === "prefix") {
4556
4625
  if (!Number.isSafeInteger(key.length) || key.length < 1 || key.length > MAX_GROUP_KEY_PREFIX) {
4557
- throw internalError4({ message: `a groupBy prefix length must be an integer from 1 to ${MAX_GROUP_KEY_PREFIX}, got ${String(key.length)}` });
4626
+ throw internalError5({ message: `a groupBy prefix length must be an integer from 1 to ${MAX_GROUP_KEY_PREFIX}, got ${String(key.length)}` });
4558
4627
  }
4559
4628
  if (dialect.name === "sqlite")
4560
4629
  return `substr(${reference}, 1, ${key.length})`;
4561
4630
  return `${dialect.name === "mysql" ? "LEFT" : "left"}(${reference}, ${key.length})`;
4562
4631
  }
4563
4632
  if (key.path.length === 0 || key.path.some((step) => typeof step !== "string" || step.length === 0 || step.includes("\x00"))) {
4564
- throw internalError4({ message: "a groupBy JSON path needs one or more non-empty object keys without NUL characters" });
4633
+ throw internalError5({ message: "a groupBy JSON path needs one or more non-empty object keys without NUL characters" });
4565
4634
  }
4566
4635
  switch (dialect.jsonPathMode) {
4567
4636
  case "operators": {
@@ -4600,11 +4669,11 @@ function renderGroupingSets(params) {
4600
4669
  function groupingSetIndexes(params) {
4601
4670
  const { keys, grouping } = params;
4602
4671
  if (grouping.sets.length === 0 || keys.length === 0 || keys.length > 31) {
4603
- throw internalError4({ message: "grouping sets need one or more sets over 1 to 31 groupBy keys" });
4672
+ throw internalError5({ message: "grouping sets need one or more sets over 1 to 31 groupBy keys" });
4604
4673
  }
4605
4674
  for (const set of grouping.sets) {
4606
4675
  if (set.some((index) => !Number.isSafeInteger(index) || index < 0 || index >= keys.length)) {
4607
- throw internalError4({ message: "a grouping set names a groupBy key that does not exist" });
4676
+ throw internalError5({ message: "a grouping set names a groupBy key that does not exist" });
4608
4677
  }
4609
4678
  }
4610
4679
  return grouping.sets;
@@ -4650,7 +4719,7 @@ function renderExpr(params) {
4650
4719
  case "cmp": {
4651
4720
  const op = COMPARISON_SQL2[expr.op];
4652
4721
  if (op === undefined) {
4653
- throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4722
+ throw internalError5({ message: `unknown comparison operator "${expr.op}"` });
4654
4723
  }
4655
4724
  if (isDbNowMarker(expr.value)) {
4656
4725
  return `${quoteCol(dialect, expr.column)} ${op} ${dialect.renderDbNow({ offsetSeconds: expr.value.offsetSeconds, collector })}`;
@@ -4661,7 +4730,7 @@ function renderExpr(params) {
4661
4730
  case "cmpCol": {
4662
4731
  const op = COMPARISON_SQL2[expr.op];
4663
4732
  if (op === undefined) {
4664
- throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4733
+ throw internalError5({ message: `unknown comparison operator "${expr.op}"` });
4665
4734
  }
4666
4735
  return `${quoteCol(dialect, expr.left)} ${op} ${quoteCol(dialect, expr.right)}`;
4667
4736
  }
@@ -4685,11 +4754,11 @@ function renderExpr(params) {
4685
4754
  }
4686
4755
  case "tupleIn": {
4687
4756
  if (expr.columns.length === 0) {
4688
- throw internalError4({ message: "tupleIn needs at least one column" });
4757
+ throw internalError5({ message: "tupleIn needs at least one column" });
4689
4758
  }
4690
4759
  for (const row of expr.rows) {
4691
4760
  if (row.length !== expr.columns.length) {
4692
- throw internalError4({
4761
+ throw internalError5({
4693
4762
  message: `tupleIn row has ${row.length} values for ${expr.columns.length} columns`
4694
4763
  });
4695
4764
  }
@@ -4715,7 +4784,7 @@ function renderExpr(params) {
4715
4784
  case "rowCompare": {
4716
4785
  const op = COMPARISON_SQL2[expr.op];
4717
4786
  if (op === undefined || expr.columns.length === 0 || expr.values.length !== expr.columns.length) {
4718
- throw internalError4({ message: `rowCompare needs a range operator and one value per column (${expr.columns.length} columns, ${expr.values.length} values)` });
4787
+ throw internalError5({ message: `rowCompare needs a range operator and one value per column (${expr.columns.length} columns, ${expr.values.length} values)` });
4719
4788
  }
4720
4789
  const columns = expr.columns.map((column) => quoteCol(dialect, column));
4721
4790
  const placeholders = expr.values.map((value) => collector.add(value));
@@ -4723,7 +4792,7 @@ function renderExpr(params) {
4723
4792
  }
4724
4793
  case "inSelect": {
4725
4794
  if (expr.columns.length === 0)
4726
- throw internalError4({ message: "inSelect needs at least one column" });
4795
+ throw internalError5({ message: "inSelect needs at least one column" });
4727
4796
  if (dialect.name === "mysql" && (expr.select.limit !== undefined || expr.select.offset !== undefined)) {
4728
4797
  throw unsupportedCapability23({
4729
4798
  dialect: dialect.name,
@@ -4733,7 +4802,7 @@ function renderExpr(params) {
4733
4802
  }
4734
4803
  const columns = expr.columns.map((column) => quoteCol(dialect, column));
4735
4804
  if (expr.single === true && columns.length !== 1)
4736
- throw internalError4({ message: "a single-row inSelect compares exactly one column" });
4805
+ throw internalError5({ message: "a single-row inSelect compares exactly one column" });
4737
4806
  const tested = columns.length === 1 ? columns[0] : `(${columns.join(", ")})`;
4738
4807
  const sub = renderSelect({ dialect, statement: expr.select, collector });
4739
4808
  return expr.single === true ? `${tested} = (${sub.text})` : `${tested} IN (${sub.text})`;
@@ -4798,7 +4867,7 @@ function renderExpr(params) {
4798
4867
  case "aggCmp": {
4799
4868
  const op = COMPARISON_SQL2[expr.op];
4800
4869
  if (op === undefined) {
4801
- throw internalError4({ message: `unknown comparison operator "${expr.op}"` });
4870
+ throw internalError5({ message: `unknown comparison operator "${expr.op}"` });
4802
4871
  }
4803
4872
  return `${renderAggRef(dialect, expr.agg, collector)} ${op} ${collector.add(expr.value)}`;
4804
4873
  }
@@ -4867,13 +4936,13 @@ function renderKeyList(params) {
4867
4936
  const { dialect, expr, collector } = params;
4868
4937
  const arity = expr.columns.length;
4869
4938
  if (arity === 0)
4870
- throw internalError4({ message: "keyList needs at least one column" });
4939
+ throw internalError5({ message: "keyList needs at least one column" });
4871
4940
  if (expr.members.length !== arity) {
4872
- throw internalError4({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4941
+ throw internalError5({ message: `keyList has ${expr.members.length} members for ${arity} columns` });
4873
4942
  }
4874
4943
  for (const row of expr.rows) {
4875
4944
  if (row.length !== arity)
4876
- throw internalError4({ message: `keyList row has ${row.length} values for ${arity} columns` });
4945
+ throw internalError5({ message: `keyList row has ${row.length} values for ${arity} columns` });
4877
4946
  }
4878
4947
  if (expr.rows.length === 0)
4879
4948
  return "1 = 0";
@@ -4905,22 +4974,22 @@ function renderKeyPage(params) {
4905
4974
  const { select } = page;
4906
4975
  const arity = page.keyColumns.length;
4907
4976
  if (arity === 0 || page.members.length !== arity) {
4908
- throw internalError4({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4977
+ throw internalError5({ message: `keyPage has ${page.members.length} members for ${arity} key columns` });
4909
4978
  }
4910
4979
  if (page.rows.length === 0)
4911
- throw internalError4({ message: "keyPage needs at least one key" });
4980
+ throw internalError5({ message: "keyPage needs at least one key" });
4912
4981
  for (const row of page.rows) {
4913
4982
  if (row.length !== arity)
4914
- throw internalError4({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4983
+ throw internalError5({ message: `keyPage row has ${row.length} values for ${arity} key columns` });
4915
4984
  }
4916
4985
  if (page.identity.length === 0)
4917
- throw internalError4({ message: "keyPage needs the child's identity columns" });
4986
+ throw internalError5({ message: "keyPage needs the child's identity columns" });
4918
4987
  const order = select.orderBy ?? [];
4919
4988
  if (order.length === 0 || order.some((term) => !("column" in term))) {
4920
- throw internalError4({ message: "keyPage needs a non-empty order of plain column terms" });
4989
+ throw internalError5({ message: "keyPage needs a non-empty order of plain column terms" });
4921
4990
  }
4922
4991
  if (select.table.alias !== undefined || Object.keys(select).some((member) => !KEY_PAGE_SELECT_MEMBERS.has(member))) {
4923
- throw internalError4({ message: "keyPage select carries a member the per-key page cannot render" });
4992
+ throw internalError5({ message: "keyPage select carries a member the per-key page cannot render" });
4924
4993
  }
4925
4994
  if (dialect.keyPageStrategy === "window") {
4926
4995
  throw unsupportedCapability23({
@@ -4979,19 +5048,19 @@ function renderKeyPage(params) {
4979
5048
  function renderKeyRows(params) {
4980
5049
  const { dialect, statement, keys, collector } = params;
4981
5050
  if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.fromUnion !== undefined || statement.table.alias !== undefined) {
4982
- throw internalError4({ message: "fromKeys uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion and table.alias must be unset" });
5051
+ throw internalError5({ message: "fromKeys uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion and table.alias must be unset" });
4983
5052
  }
4984
5053
  if (statement.rowLock !== undefined)
4985
- throw internalError4({ message: "a locking read cannot read from key rows" });
5054
+ throw internalError5({ message: "a locking read cannot read from key rows" });
4986
5055
  const arity = keys.columns.length;
4987
5056
  if (arity === 0 || keys.members.length !== arity) {
4988
- throw internalError4({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
5057
+ throw internalError5({ message: `fromKeys has ${keys.members.length} members for ${arity} columns` });
4989
5058
  }
4990
5059
  if (keys.rows.length === 0)
4991
- throw internalError4({ message: "fromKeys needs at least one key row" });
5060
+ throw internalError5({ message: "fromKeys needs at least one key row" });
4992
5061
  for (const row of keys.rows) {
4993
5062
  if (row.length !== arity)
4994
- throw internalError4({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
5063
+ throw internalError5({ message: `fromKeys row has ${row.length} values for ${arity} columns` });
4995
5064
  }
4996
5065
  if (dialect.keyListStrategy !== "unnest" || keyListBindsUntypedRows({ members: keys.members })) {
4997
5066
  throw unsupportedCapability23({
@@ -5005,25 +5074,145 @@ function renderKeyRows(params) {
5005
5074
  const names = keys.columns.map((name) => dialect.quoteIdent({ name }));
5006
5075
  return `unnest(${arrays.join(", ")}) AS ${dialect.quoteIdent({ name: statement.table.name })} (${names.join(", ")})`;
5007
5076
  }
5077
+ function renderOuterRow(params) {
5078
+ const { dialect, statement, row } = params;
5079
+ if (!dialect.capabilities.carrierRows) {
5080
+ throw unsupportedCapability23({
5081
+ dialect: dialect.name,
5082
+ feature: "carrier rows",
5083
+ hint: "lists below a shared to-one nest on small levels and load by key otherwise on this dialect"
5084
+ });
5085
+ }
5086
+ if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.fromUnion !== undefined || statement.table.alias !== undefined || statement.table.namespace !== undefined || statement.rowLock !== undefined) {
5087
+ throw internalError5({ message: "fromRow uses table.name as the derived-table alias \u2014 fromSelect, keyPage, fromUnion, table.alias, a namespace and a row lock must be unset" });
5088
+ }
5089
+ const names = row.map((entry) => entry.alias);
5090
+ if (row.length === 0 || new Set(names).size !== names.length || row.some((entry) => entry.column.table === undefined)) {
5091
+ throw internalError5({ message: "fromRow needs one or more table-qualified outer columns under distinct names" });
5092
+ }
5093
+ const cells = row.map((entry) => `${quoteCol(dialect, entry.column)} AS ${dialect.quoteIdent({ name: entry.alias })}`);
5094
+ return `(SELECT ${cells.join(", ")}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
5095
+ }
5096
+ function refuseSharedListDictionary(params) {
5097
+ if (params.dialect.capabilities.sharedListDictionary)
5098
+ return;
5099
+ throw unsupportedCapability23({
5100
+ dialect: params.dialect.name,
5101
+ feature: "a dictionary of shared lists",
5102
+ hint: "lists below a to-one under a list nest on small levels and load by key otherwise on this dialect"
5103
+ });
5104
+ }
5105
+ function renderCommonTables(params) {
5106
+ const { dialect, tables, collector } = params;
5107
+ refuseSharedListDictionary({ dialect });
5108
+ const names = tables.map((table) => table.name);
5109
+ if (tables.length === 0 || new Set(names).size !== names.length || tables.some((table) => table.select.with !== undefined)) {
5110
+ throw internalError5({ message: "common tables need distinct names and selects without common tables of their own" });
5111
+ }
5112
+ const pinned = lateralJsonOf({ dialect }).pinnedCommonTable;
5113
+ const rendered = tables.map((table) => {
5114
+ const select = renderSelect({ dialect, statement: table.select, collector }).text;
5115
+ if (pinned === "limit")
5116
+ return `${dialect.quoteIdent({ name: table.name })} AS (${select}${mysqlPinnedCommonTableSuffix({ table })})`;
5117
+ return `${dialect.quoteIdent({ name: table.name })} AS ${table.materialized === true ? "MATERIALIZED " : ""}(${select})`;
5118
+ });
5119
+ return `WITH ${rendered.join(", ")} `;
5120
+ }
5121
+ var JSON_KEYS_SOURCE_ALIAS = "__vibe_s";
5122
+ var JSON_KEYS_SET_ALIAS = "__vibe_k";
5123
+ function renderJsonSlot(params) {
5124
+ const { row, slot } = params;
5125
+ if (!Number.isSafeInteger(slot.index) || !Number.isSafeInteger(slot.width) || slot.index < 0 || slot.index >= slot.width) {
5126
+ throw internalError5({ message: "a JSON slot must be an index inside its row" });
5127
+ }
5128
+ const last = params.text ? "->>" : "->";
5129
+ const size = JSON_TRANSPORT.chunkSize;
5130
+ if (slot.width <= size)
5131
+ return `${row} ${last} ${slot.index}`;
5132
+ return `${row} -> ${Math.floor(slot.index / size)} ${last} ${slot.index % size}`;
5133
+ }
5134
+ function renderJsonKeys(params) {
5135
+ const { dialect, statement, keys } = params;
5136
+ refuseSharedListDictionary({ dialect });
5137
+ if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.fromUnion !== undefined || statement.fromKeys !== undefined || statement.fromRow !== undefined || statement.table.alias !== undefined || statement.table.namespace !== undefined || statement.rowLock !== undefined) {
5138
+ throw internalError5({ message: "fromJsonKeys uses table.name as the derived-table alias \u2014 every other FROM form, table.alias, a namespace and a row lock must be unset" });
5139
+ }
5140
+ const outputs = keys.keys.flatMap((key) => [key.token, key.alias]);
5141
+ if (keys.keys.length === 0 || keys.source.table === undefined || new Set(outputs).size !== outputs.length) {
5142
+ throw internalError5({ message: "fromJsonKeys needs a table-qualified source and one or more keys under distinct names" });
5143
+ }
5144
+ if (lateralJsonOf({ dialect }).keysFromJson === "jsonTable")
5145
+ return renderMysqlJsonKeys({ dialect, statement, keys });
5146
+ if (keyListBindsUntypedRows({ members: keys.keys.map((key) => key.member) })) {
5147
+ throw internalError5({ message: "fromJsonKeys cannot type a DateTime key without a declared native type" });
5148
+ }
5149
+ const source = dialect.quoteIdent({ name: JSON_KEYS_SOURCE_ALIAS });
5150
+ const set = dialect.quoteIdent({ name: JSON_KEYS_SET_ALIAS });
5151
+ const column = dialect.quoteIdent({ name: "value" });
5152
+ let value = `${source}.${dialect.quoteIdent({ name: keys.source.name })}`;
5153
+ let from = `${dialect.quoteIdent({ name: keys.source.table })} AS ${source}`;
5154
+ let lists = 0;
5155
+ for (const step of keys.steps) {
5156
+ if (step.kind === "slot") {
5157
+ value = renderJsonSlot({ row: value, slot: step.slot, text: false });
5158
+ continue;
5159
+ }
5160
+ const all = dialect.quoteIdent({ name: `__vibe_d${lists}` });
5161
+ const element = dialect.quoteIdent({ name: `__vibe_e${lists++}` });
5162
+ from = `json_array_elements((SELECT json_agg(${value}) FROM ${from} WHERE ${value} IS NOT NULL)) AS ${all}(${column}) ` + `CROSS JOIN LATERAL json_array_elements(${all}.${column}) AS ${element}(${column})`;
5163
+ value = `${element}.${column}`;
5164
+ }
5165
+ const tokens = keys.keys.map((key) => `${renderJsonSlot({ row: value, slot: key.slot, text: true })} AS ${dialect.quoteIdent({ name: key.token })}`);
5166
+ const typed = keys.keys.flatMap((key) => {
5167
+ const token = `${set}.${dialect.quoteIdent({ name: key.token })}`;
5168
+ return [token, `CAST(${token} AS ${postgresKeyListElementType({ dialect, member: key.member })}) AS ${dialect.quoteIdent({ name: key.alias })}`];
5169
+ });
5170
+ return `(SELECT ${typed.join(", ")} FROM (SELECT DISTINCT ${tokens.join(", ")} FROM ${from}) AS ${set}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
5171
+ }
5172
+ var TABLE_ARRAYS_ALIAS = "__vibe_a";
5173
+ function renderTableArrays(params) {
5174
+ const { dialect, statement, rows } = params;
5175
+ if (!dialect.capabilities.carrierRows || dialect.lateralJson?.carriedLists !== "firstRow") {
5176
+ throw unsupportedCapability23({
5177
+ dialect: dialect.name,
5178
+ feature: "the rows of a common table read back through arrays (fromTableArrays)",
5179
+ hint: "carrier rows return their lists on the row that carries, or load by key, on this dialect"
5180
+ });
5181
+ }
5182
+ if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.fromUnion !== undefined || statement.fromKeys !== undefined || statement.fromRow !== undefined || statement.fromJsonKeys !== undefined || statement.table.alias !== undefined || statement.table.namespace !== undefined || statement.rowLock !== undefined) {
5183
+ throw internalError5({ message: "fromTableArrays uses table.name as the row alias \u2014 every other FROM form, table.alias, a namespace and a row lock must be unset" });
5184
+ }
5185
+ const names = rows.columns.map((entry) => entry.alias);
5186
+ if (rows.columns.length === 0 || new Set(names).size !== names.length) {
5187
+ throw internalError5({ message: "fromTableArrays needs one or more columns under distinct names" });
5188
+ }
5189
+ const quote = (name) => dialect.quoteIdent({ name });
5190
+ const source = quote(rows.source);
5191
+ const arrays = quote(TABLE_ARRAYS_ALIAS);
5192
+ const order = `${source}.${quote(rows.orderBy)}`;
5193
+ const cells = rows.columns.map((entry, index) => `array_agg(${source}.${quote(entry.name)} ORDER BY ${order}) AS ${quote(`__vibe_a${index}`)}`);
5194
+ const read = rows.columns.map((_, index) => `${arrays}.${quote(`__vibe_a${index}`)}`);
5195
+ return `(SELECT ${cells.join(", ")} FROM ${source}) AS ${arrays} CROSS JOIN LATERAL unnest(${read.join(", ")}) AS ${quote(statement.table.name)}(${names.map(quote).join(", ")})`;
5196
+ }
5008
5197
  var UNION_BRANCH_MEMBERS = new Set(["kind", "table", "columns", "where", "orderBy", "limit"]);
5009
5198
  function renderUnionPage(params) {
5010
5199
  const { dialect, statement, collector } = params;
5011
5200
  const branches = statement.fromUnion ?? [];
5012
5201
  if (branches.length < 2)
5013
- throw internalError4({ message: "fromUnion needs at least two branches" });
5202
+ throw internalError5({ message: "fromUnion needs at least two branches" });
5014
5203
  if (statement.fromSelect !== undefined || statement.keyPage !== undefined || statement.table.alias !== undefined) {
5015
- throw internalError4({ message: "fromUnion uses table.name as the derived-table alias \u2014 fromSelect, keyPage and table.alias must be unset" });
5204
+ throw internalError5({ message: "fromUnion uses table.name as the derived-table alias \u2014 fromSelect, keyPage and table.alias must be unset" });
5016
5205
  }
5017
5206
  if (statement.rowLock !== undefined)
5018
- throw internalError4({ message: "a locking read cannot read from a union page" });
5207
+ throw internalError5({ message: "a locking read cannot read from a union page" });
5019
5208
  const outputNames = (branch) => branch.columns.map((column) => column.alias ?? column.name).join("\x00");
5020
5209
  const names = outputNames(branches[0]);
5021
5210
  return branches.map((branch, index) => {
5022
5211
  if (branch.columns.length === 0 || Object.keys(branch).some((member) => !UNION_BRANCH_MEMBERS.has(member))) {
5023
- throw internalError4({ message: "a fromUnion branch must be a plain SELECT <columns> FROM <table> [WHERE] [ORDER BY] [LIMIT]" });
5212
+ throw internalError5({ message: "a fromUnion branch must be a plain SELECT <columns> FROM <table> [WHERE] [ORDER BY] [LIMIT]" });
5024
5213
  }
5025
5214
  if (outputNames(branch) !== names)
5026
- throw internalError4({ message: "fromUnion branches must project the same output names in the same order" });
5215
+ throw internalError5({ message: "fromUnion branches must project the same output names in the same order" });
5027
5216
  const inner = renderSelect({ dialect, statement: branch, collector });
5028
5217
  return `SELECT * FROM (${inner.text}) AS ${dialect.quoteIdent({ name: `__vibe_u${index}` })}`;
5029
5218
  }).join(" UNION ALL ");
@@ -5031,7 +5220,7 @@ function renderUnionPage(params) {
5031
5220
  function jsonStringPattern(params) {
5032
5221
  const { expr } = params;
5033
5222
  if (typeof expr.value !== "string") {
5034
- throw internalError4({ message: `json ${expr.op} expects a string operand` });
5223
+ throw internalError5({ message: `json ${expr.op} expects a string operand` });
5035
5224
  }
5036
5225
  if (expr.op === "string_contains")
5037
5226
  return containsPattern({ raw: expr.value });
@@ -5152,7 +5341,7 @@ function renderJsonFilter(params) {
5152
5341
  return tests.length === 0 ? guard : `(${[guard, ...tests].join(" AND ")})`;
5153
5342
  }
5154
5343
  default:
5155
- throw internalError4({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
5344
+ throw internalError5({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
5156
5345
  }
5157
5346
  }
5158
5347
  const column = quoteCol(dialect, expr.column);
@@ -5211,7 +5400,7 @@ function renderSelectExpr(params) {
5211
5400
  if (expr.kind === "one")
5212
5401
  return "1";
5213
5402
  if (expr.kind === "exists")
5214
- throw internalError4({ message: "an exists projection renders as its whole statement" });
5403
+ throw internalError5({ message: "an exists projection renders as its whole statement" });
5215
5404
  const alias = expr.alias === undefined ? "" : ` AS ${dialect.quoteIdent({ name: expr.alias })}`;
5216
5405
  return `COUNT(*)${alias}`;
5217
5406
  }
@@ -5221,16 +5410,16 @@ function renderPositionalRow(params) {
5221
5410
  const position = join.select.rowNumber;
5222
5411
  const positioned = position !== undefined && json.order === "positionSlot" && join.agg === "jsonArray" && position.partitionBy.length === 0 && position.orderBy.length > 0;
5223
5412
  if (join.select.columns.length === 0 || join.select.selectExpr !== undefined || (join.select.aggregates?.length ?? 0) > 0 || position !== undefined && !positioned) {
5224
- throw internalError4({ message: "positional lateral rows require an explicit column-only projection" });
5413
+ throw internalError5({ message: "positional lateral rows require an explicit column-only projection" });
5225
5414
  }
5226
5415
  const projected = join.select.columns.map((column) => projectionOutputName({ column }));
5227
5416
  const hidden = join.hidden ?? [];
5228
5417
  if (hidden.some((name) => !projected.includes(name)) || hidden.length >= projected.length) {
5229
- throw internalError4({ message: "a hidden lateral column must be one of the projected columns, and one column must remain" });
5418
+ throw internalError5({ message: "a hidden lateral column must be one of the projected columns, and one column must remain" });
5230
5419
  }
5231
5420
  const names = [...projected.filter((name) => !hidden.includes(name)), ...positioned ? [position.alias] : []];
5232
5421
  if (new Set(names).size !== names.length) {
5233
- throw internalError4({ message: "positional lateral projection has duplicate output names" });
5422
+ throw internalError5({ message: "positional lateral projection has duplicate output names" });
5234
5423
  }
5235
5424
  const values = names.map((name) => `${subAlias}.${dialect.quoteIdent({ name })}`);
5236
5425
  const chunks = [];
@@ -5238,30 +5427,34 @@ function renderPositionalRow(params) {
5238
5427
  chunks.push(`${json.rowArray}(${values.slice(index, index + JSON_TRANSPORT.chunkSize).join(", ")})`);
5239
5428
  }
5240
5429
  if (chunks.length > JSON_TRANSPORT.chunkSize) {
5241
- throw internalError4({ message: "positional lateral projection exceeds the transport width limit" });
5430
+ throw internalError5({ message: "positional lateral projection exceeds the transport width limit" });
5242
5431
  }
5243
5432
  return chunks.length === 1 ? chunks[0] : `${json.rowArray}(${chunks.join(", ")})`;
5244
5433
  }
5245
5434
  function lateralJsonOf(params) {
5246
5435
  const json = params.dialect.lateralJson;
5247
5436
  if (json === null)
5248
- throw internalError4({ message: "lateral JSON rendering on a dialect without LATERAL" });
5437
+ throw internalError5({ message: "lateral JSON rendering on a dialect without LATERAL" });
5249
5438
  return json;
5250
5439
  }
5251
5440
  function renderLateralJoin(params) {
5441
+ const { dialect, join } = params;
5442
+ return `LEFT JOIN LATERAL (${renderLateralSubquery(params)}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
5443
+ }
5444
+ function renderLateralSubquery(params) {
5252
5445
  const { dialect, join, collector } = params;
5253
5446
  const inner = renderSelect({ dialect, statement: join.select, collector });
5254
5447
  if (join.agg === "count") {
5255
5448
  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) {
5256
- throw internalError4({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
5449
+ throw internalError5({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
5257
5450
  }
5258
- return `LEFT JOIN LATERAL (${inner.text}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
5451
+ return inner.text;
5259
5452
  }
5260
5453
  if (join.agg === "aggregate") {
5261
5454
  if (join.rowFormat === "positional" || join.select.columns.length !== 0 || join.select.selectExpr !== undefined || (join.select.aggregates?.length ?? 0) === 0 || join.select.groupBy !== undefined || join.select.having !== undefined || join.select.groupingSets !== undefined || join.select.limit !== undefined || join.select.offset !== undefined || join.select.distinctOn !== undefined || join.select.distinct !== undefined || join.select.rowLock !== undefined) {
5262
- throw internalError4({ message: "aggregate lateral must return one ungrouped row of aggregates" });
5455
+ throw internalError5({ message: "aggregate lateral must return one ungrouped row of aggregates" });
5263
5456
  }
5264
- return `LEFT JOIN LATERAL (${inner.text}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
5457
+ return inner.text;
5265
5458
  }
5266
5459
  const columnAlias = dialect.quoteIdent({ name: join.columnAlias });
5267
5460
  const subAlias = dialect.quoteIdent({ name: "__sub" });
@@ -5290,14 +5483,30 @@ function renderLateralJoin(params) {
5290
5483
  }
5291
5484
  const rowJson = join.rowFormat === "positional" ? renderPositionalRow({ dialect, join, subAlias }) : `to_json(${subAlias}.*)`;
5292
5485
  const projection = join.agg === "jsonArray" ? `COALESCE(${json.arrayAgg}(${rowJson}${aggOrder}), ${json.emptyArray}) AS ${columnAlias}` : `${rowJson} AS ${columnAlias}`;
5293
- const alias = dialect.quoteIdent({ name: join.alias });
5294
- return `LEFT JOIN LATERAL (SELECT ${projection} FROM (${inner.text}) AS ${subAlias}) AS ${alias} ON true`;
5486
+ const exposed = join.expose ?? [];
5487
+ if (exposed.length > 0) {
5488
+ const projected = join.select.columns.map((column) => projectionOutputName({ column }));
5489
+ if (join.agg !== "jsonObject" || exposed.some((name) => !projected.includes(name) || name === join.columnAlias) || new Set(exposed).size !== exposed.length) {
5490
+ throw internalError5({ message: "a lateral exposes distinct projected columns of a one-row subselect, beside its JSON column" });
5491
+ }
5492
+ }
5493
+ const exposure = exposed.map((name) => `, ${subAlias}.${dialect.quoteIdent({ name })}`).join("");
5494
+ return `SELECT ${projection}${exposure} FROM (${inner.text}) AS ${subAlias}`;
5495
+ }
5496
+ function renderFirstRowList(params) {
5497
+ const { dialect, entry, collector } = params;
5498
+ refuseSharedListDictionary({ dialect });
5499
+ if (entry.list.agg !== "jsonArray" || entry.list.expose !== undefined || entry.ordinal.table === undefined) {
5500
+ throw internalError5({ message: "a first-row list is a JSON list gated by a table-qualified ordinal of the statement's row" });
5501
+ }
5502
+ const list = renderLateralSubquery({ dialect, join: entry.list, collector });
5503
+ return `CASE WHEN ${quoteCol(dialect, entry.ordinal)} = 1 THEN (${list}) END AS ${dialect.quoteIdent({ name: entry.list.columnAlias })}`;
5295
5504
  }
5296
5505
  function renderCountSubquery(params) {
5297
5506
  const { dialect, subquery, collector } = params;
5298
5507
  const select = subquery.select;
5299
5508
  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) {
5300
- throw internalError4({ message: "count subquery must be one ungrouped COUNT(*)" });
5509
+ throw internalError5({ message: "count subquery must be one ungrouped COUNT(*)" });
5301
5510
  }
5302
5511
  const inner = renderSelect({ dialect, statement: select, collector });
5303
5512
  return `(${inner.text}) AS ${dialect.quoteIdent({ name: subquery.alias })}`;
@@ -5362,7 +5571,7 @@ function renderCaptureReturning(params) {
5362
5571
  const alias = dialect.quoteIdent({ name: column.alias });
5363
5572
  if (column.capture === "cascade") {
5364
5573
  if (params.collector === undefined)
5365
- throw internalError4({ message: `capture cascade column "${column.alias}" rendered without a parameter collector` });
5574
+ throw internalError5({ message: `capture cascade column "${column.alias}" rendered without a parameter collector` });
5366
5575
  const child = dialect.quoteIdent({ name: CAPTURE_CHILD_ALIAS });
5367
5576
  const match = column.cascade.on.map((pair) => `${child}.${dialect.quoteIdent({ name: pair.child })} = ${rows.old}.${dialect.quoteIdent({ name: pair.parent })}`).join(" AND ");
5368
5577
  return `${renderCascadeImages({ dialect, cascade: column.cascade, match, collector: params.collector })} AS ${alias}`;
@@ -5382,7 +5591,7 @@ function renderCaptureCell(params) {
5382
5591
  if (cell.hop === undefined)
5383
5592
  return textProjection({ dialect, expression: `${row}.${name}` });
5384
5593
  if (cell.hop.on.length === 0)
5385
- throw internalError4({ message: `capture hop to "${cell.hop.table.name}" needs at least one key column` });
5594
+ throw internalError5({ message: `capture hop to "${cell.hop.table.name}" needs at least one key column` });
5386
5595
  const related = dialect.quoteIdent({ name: CAPTURE_HOP_ALIAS });
5387
5596
  const match = cell.hop.on.map((pair) => `${related}.${dialect.quoteIdent({ name: pair.target })} = ${row}.${dialect.quoteIdent({ name: pair.source })}`).join(" AND ");
5388
5597
  return `(SELECT ${textProjection({ dialect, expression: `${related}.${name}` })} FROM ${renderTable(dialect, cell.hop.table)} AS ${related} WHERE ${match})`;
@@ -5390,9 +5599,9 @@ function renderCaptureCell(params) {
5390
5599
  function renderCascadeImages(params) {
5391
5600
  const { dialect, cascade, match, collector } = params;
5392
5601
  if (cascade.cells.length === 0 || cascade.on.length === 0)
5393
- throw internalError4({ message: `capture cascade into "${cascade.table.name}" needs at least one cell and one key column` });
5602
+ throw internalError5({ message: `capture cascade into "${cascade.table.name}" needs at least one cell and one key column` });
5394
5603
  if (!Number.isSafeInteger(cascade.limit) || cascade.limit < 1)
5395
- throw internalError4({ message: `capture cascade into "${cascade.table.name}" needs a positive integer limit` });
5604
+ throw internalError5({ message: `capture cascade into "${cascade.table.name}" needs a positive integer limit` });
5396
5605
  const child = dialect.quoteIdent({ name: CAPTURE_CHILD_ALIAS });
5397
5606
  const item = dialect.quoteIdent({ name: "__vibe_i" });
5398
5607
  const cells = cascade.cells.map((cell) => renderCaptureCell({ dialect, row: child, cell })).join(", ");
@@ -5425,20 +5634,20 @@ function renderKeyTableJoin(params) {
5425
5634
  });
5426
5635
  }
5427
5636
  if (join.columns.length === 0 || join.columns.length !== join.members.length) {
5428
- throw internalError4({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
5637
+ throw internalError5({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
5429
5638
  }
5430
5639
  if (join.rows.length === 0)
5431
- throw internalError4({ message: "key table needs at least one key row" });
5640
+ throw internalError5({ message: "key table needs at least one key row" });
5432
5641
  for (const member of join.members) {
5433
5642
  if (member.kind === "decimal" && !(Number.isInteger(member.scale) && member.scale >= 0 && member.scale <= 30)) {
5434
- throw internalError4({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
5643
+ throw internalError5({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
5435
5644
  }
5436
5645
  }
5437
5646
  const alias = dialect.quoteIdent({ name: join.alias });
5438
5647
  const memberNames = join.members.map((_, index) => dialect.quoteIdent({ name: `__vibe_k${index}` }));
5439
5648
  const rows = join.rows.map((row, ordinal) => {
5440
5649
  if (row.length !== join.members.length) {
5441
- throw internalError4({ message: `key table row has ${row.length} values for ${join.members.length} members` });
5650
+ throw internalError5({ message: `key table row has ${row.length} values for ${join.members.length} members` });
5442
5651
  }
5443
5652
  const cells = row.map((value, index) => {
5444
5653
  const placeholder = collector.add(value);
@@ -5468,10 +5677,10 @@ function renderKeyTableJoin(params) {
5468
5677
  function renderLeftJoin(params) {
5469
5678
  const { dialect, join, collector } = params;
5470
5679
  if (join.on.length === 0) {
5471
- throw internalError4({ message: "left join needs at least one ON pair" });
5680
+ throw internalError5({ message: "left join needs at least one ON pair" });
5472
5681
  }
5473
5682
  if (join.select.columns.length === 0 || join.select.columns.some((column) => column.alias === undefined)) {
5474
- throw internalError4({ message: "a left join's derived select must alias every projected column" });
5683
+ throw internalError5({ message: "a left join's derived select must alias every projected column" });
5475
5684
  }
5476
5685
  const inner = renderSelect({ dialect, statement: join.select, collector });
5477
5686
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
@@ -5481,19 +5690,28 @@ function renderExistsSelect(params) {
5481
5690
  const { dialect, statement, alias, collector } = params;
5482
5691
  const extra = statement.columns.length > 0 || (statement.aggregates?.length ?? 0) > 0 || statement.countSubqueries !== undefined || statement.scalarSubqueries !== undefined || statement.rowNumber !== undefined || statement.groupBy !== undefined || statement.groupingSets !== undefined || statement.having !== undefined || statement.orderBy !== undefined || statement.limit !== undefined || statement.offset !== undefined || statement.rowLock !== undefined || statement.distinct !== undefined || statement.distinctOn !== undefined || statement.lateralJoins !== undefined || statement.innerJoins !== undefined || statement.leftJoins !== undefined || statement.keyTable !== undefined;
5483
5692
  if (extra)
5484
- throw internalError4({ message: "an exists select takes only a FROM and a WHERE" });
5693
+ throw internalError5({ message: "an exists select takes only a FROM and a WHERE" });
5485
5694
  const inner = renderSelect({ dialect, statement: { ...statement, selectExpr: { kind: "one" } }, collector });
5486
5695
  return { ...inner, text: `SELECT EXISTS (${inner.text}) AS ${dialect.quoteIdent({ name: alias })}` };
5487
5696
  }
5488
5697
  function renderSelect(params) {
5489
5698
  const { dialect, statement, collector } = params;
5699
+ if (statement.with !== undefined) {
5700
+ if (params.outermost !== true)
5701
+ throw internalError5({ message: "common tables render on the outermost select only" });
5702
+ const { with: tables, ...main } = statement;
5703
+ const prefix = renderCommonTables({ dialect, tables, collector });
5704
+ const names = new Set(tables.map((table) => table.name));
5705
+ const rendered = renderSelect({ dialect, statement: main, collector, ...main.allColumns === true && names.has(main.table.name) ? { overCommonTable: true } : {} });
5706
+ return { ...rendered, text: `${prefix}${rendered.text}` };
5707
+ }
5490
5708
  if (statement.selectExpr?.kind === "exists")
5491
5709
  return renderExistsSelect({ dialect, statement, alias: statement.selectExpr.alias, collector });
5492
5710
  if (statement.selectExpr !== undefined && statement.columns.length > 0 && statement.selectExpr.kind !== "countAll") {
5493
- throw internalError4({ message: "only countAll can combine with a column list" });
5711
+ throw internalError5({ message: "only countAll can combine with a column list" });
5494
5712
  }
5495
5713
  if (statement.selectExpr !== undefined && statement.aggregates !== undefined && statement.aggregates.length > 0) {
5496
- throw internalError4({ message: "selectExpr and aggregates cannot combine" });
5714
+ throw internalError5({ message: "selectExpr and aggregates cannot combine" });
5497
5715
  }
5498
5716
  const columnList = statement.columns.map((column) => quoteProjection(dialect, column));
5499
5717
  if (statement.selectExpr !== undefined) {
@@ -5523,15 +5741,45 @@ function renderSelect(params) {
5523
5741
  if (statement.rowNumber !== undefined) {
5524
5742
  const rowNumber = statement.rowNumber;
5525
5743
  if (rowNumber.partitionBy.length === 0 && rowNumber.orderBy.length === 0) {
5526
- throw internalError4({ message: "rowNumber needs a partition column or an order" });
5744
+ throw internalError5({ message: "rowNumber needs a partition column or an order" });
5527
5745
  }
5528
5746
  const partition = rowNumber.partitionBy.length === 0 ? "" : `PARTITION BY ${rowNumber.partitionBy.map((column) => quoteCol(dialect, column)).join(", ")}`;
5529
5747
  const order = rowNumber.orderBy.length === 0 ? "" : `${partition === "" ? "" : " "}ORDER BY ${rowNumber.orderBy.map((term) => renderOrderTerm({ dialect, term, collector })).join(", ")}`;
5530
5748
  columnList.push(`ROW_NUMBER() OVER (${partition}${order}) AS ${dialect.quoteIdent({ name: rowNumber.alias })}`);
5531
5749
  }
5750
+ if (statement.windows !== undefined) {
5751
+ for (const window of statement.windows) {
5752
+ const alias = dialect.quoteIdent({ name: window.alias });
5753
+ if (window.kind === "ordinal") {
5754
+ const order = window.orderBy.length === 0 ? "" : `ORDER BY ${window.orderBy.map((term) => renderOrderTerm({ dialect, term, collector })).join(", ")}`;
5755
+ columnList.push(`ROW_NUMBER() OVER (${order}) AS ${alias}`);
5756
+ continue;
5757
+ }
5758
+ if (window.partitionBy.length === 0)
5759
+ throw internalError5({ message: "a firstOrdinal window needs a partition column" });
5760
+ columnList.push(`MIN(${quoteCol(dialect, window.ordinal)}) OVER (PARTITION BY ${window.partitionBy.map((column) => quoteCol(dialect, column)).join(", ")}) AS ${alias}`);
5761
+ }
5762
+ }
5763
+ if (statement.firstRowLists !== undefined) {
5764
+ for (const entry of statement.firstRowLists)
5765
+ columnList.push(renderFirstRowList({ dialect, entry, collector }));
5766
+ }
5767
+ if (statement.allColumns === true) {
5768
+ if (params.overCommonTable === true) {
5769
+ if (statement.fromSelect !== undefined || statement.selectExpr !== undefined || statement.table.namespace !== undefined) {
5770
+ throw internalError5({ message: "allColumns over a common table reads that table alone" });
5771
+ }
5772
+ columnList.unshift(`${dialect.quoteIdent({ name: statement.table.alias ?? statement.table.name })}.*`);
5773
+ } else {
5774
+ if (statement.fromSelect === undefined || statement.columns.length > 0 || statement.selectExpr !== undefined) {
5775
+ throw internalError5({ message: "allColumns re-exposes a derived table: it needs fromSelect and no column list" });
5776
+ }
5777
+ columnList.unshift(`${dialect.quoteIdent({ name: statement.table.name })}.*`);
5778
+ }
5779
+ }
5532
5780
  let distinctPrefix = statement.distinct === true ? "DISTINCT " : "";
5533
5781
  if (statement.distinct === true && statement.distinctOn !== undefined) {
5534
- throw internalError4({ message: "distinct and distinctOn cannot combine" });
5782
+ throw internalError5({ message: "distinct and distinctOn cannot combine" });
5535
5783
  }
5536
5784
  if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
5537
5785
  if (!dialect.capabilities.distinctOn) {
@@ -5547,16 +5795,22 @@ function renderSelect(params) {
5547
5795
  let from;
5548
5796
  if (statement.fromKeys !== undefined) {
5549
5797
  from = renderKeyRows({ dialect, statement, keys: statement.fromKeys, collector });
5798
+ } else if (statement.fromRow !== undefined) {
5799
+ from = renderOuterRow({ dialect, statement, row: statement.fromRow });
5800
+ } else if (statement.fromJsonKeys !== undefined) {
5801
+ from = renderJsonKeys({ dialect, statement, keys: statement.fromJsonKeys });
5802
+ } else if (statement.fromTableArrays !== undefined) {
5803
+ from = renderTableArrays({ dialect, statement, rows: statement.fromTableArrays });
5550
5804
  } else if (statement.fromUnion !== undefined) {
5551
5805
  from = `(${renderUnionPage({ dialect, statement, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
5552
5806
  } else if (statement.keyPage !== undefined) {
5553
5807
  if (statement.fromSelect !== undefined || statement.table.alias !== undefined) {
5554
- throw internalError4({ message: "keyPage uses table.name as the derived-table alias \u2014 fromSelect and table.alias must be unset" });
5808
+ throw internalError5({ message: "keyPage uses table.name as the derived-table alias \u2014 fromSelect and table.alias must be unset" });
5555
5809
  }
5556
5810
  from = `(${renderKeyPage({ dialect, page: statement.keyPage, collector })}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
5557
5811
  } else if (statement.fromSelect !== undefined) {
5558
5812
  if (statement.table.alias !== undefined) {
5559
- throw internalError4({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
5813
+ throw internalError5({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
5560
5814
  }
5561
5815
  const inner = renderSelect({ dialect, statement: statement.fromSelect, collector });
5562
5816
  from = `(${inner.text}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
@@ -5567,7 +5821,7 @@ function renderSelect(params) {
5567
5821
  if (statement.innerJoins !== undefined && statement.innerJoins.length > 0) {
5568
5822
  for (const join of statement.innerJoins) {
5569
5823
  if (join.on.length === 0) {
5570
- throw internalError4({ message: "inner join needs at least one ON pair" });
5824
+ throw internalError5({ message: "inner join needs at least one ON pair" });
5571
5825
  }
5572
5826
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
5573
5827
  text += ` INNER JOIN ${renderTable(dialect, join.table)} ON ${on}`;
@@ -5578,7 +5832,7 @@ function renderSelect(params) {
5578
5832
  }
5579
5833
  if (statement.leftJoins !== undefined && statement.leftJoins.length > 0) {
5580
5834
  if (statement.rowLock !== undefined) {
5581
- throw internalError4({ message: "a locking read cannot carry a left join \u2014 postgres refuses FOR UPDATE on the nullable side of an outer join" });
5835
+ throw internalError5({ message: "a locking read cannot carry a left join \u2014 postgres refuses FOR UPDATE on the nullable side of an outer join" });
5582
5836
  }
5583
5837
  for (const join of statement.leftJoins) {
5584
5838
  text += ` ${renderLeftJoin({ dialect, join, collector })}`;
@@ -5602,7 +5856,7 @@ function renderSelect(params) {
5602
5856
  if (statement.groupBy !== undefined && statement.groupBy.length > 0) {
5603
5857
  text += ` GROUP BY ${statement.groupingSets === undefined ? groupKeys.join(", ") : renderGroupingSets({ dialect, keys: groupKeys, grouping: statement.groupingSets })}`;
5604
5858
  } else if (statement.groupingSets !== undefined) {
5605
- throw internalError4({ message: "grouping sets need groupBy columns" });
5859
+ throw internalError5({ message: "grouping sets need groupBy columns" });
5606
5860
  }
5607
5861
  if (statement.having !== undefined) {
5608
5862
  text += ` HAVING ${renderExpr({ dialect, expr: statement.having, collector })}`;
@@ -5635,10 +5889,10 @@ function renderSelect(params) {
5635
5889
  if (statement.rowLock !== undefined) {
5636
5890
  const target = statement.table.alias ?? statement.table.name;
5637
5891
  if (statement.rowLock.of !== undefined && statement.rowLock.of.some((name) => name !== target)) {
5638
- throw internalError4({ message: `a row lock's \`of\` list may name only the statement's own table "${target}"` });
5892
+ throw internalError5({ message: `a row lock's \`of\` list may name only the statement's own table "${target}"` });
5639
5893
  }
5640
5894
  if (statement.rowLock.of !== undefined && (statement.fromSelect !== undefined || statement.keyPage !== undefined)) {
5641
- throw internalError4({ message: "a row lock's `of` list cannot name a derived table" });
5895
+ throw internalError5({ message: "a row lock's `of` list cannot name a derived table" });
5642
5896
  }
5643
5897
  const clause = renderRowLockClause({ dialect, lock: statement.rowLock });
5644
5898
  if (clause !== null)
@@ -5654,7 +5908,7 @@ function unnestElement(params) {
5654
5908
  return null;
5655
5909
  if (isJsonParamCell(value)) {
5656
5910
  if (jsonStorage === undefined || (value.storage ?? "jsonb") !== jsonStorage) {
5657
- throw internalError4({ message: `unnest insert column "${column}" got a Json cell its declared type does not take` });
5911
+ throw internalError5({ message: `unnest insert column "${column}" got a Json cell its declared type does not take` });
5658
5912
  }
5659
5913
  return value.text;
5660
5914
  }
@@ -5670,7 +5924,7 @@ function unnestElement(params) {
5670
5924
  return value;
5671
5925
  }
5672
5926
  }
5673
- 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` });
5927
+ throw internalError5({ 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` });
5674
5928
  }
5675
5929
  function renderUnnestRows(params) {
5676
5930
  const { dialect, statement, members, collector } = params;
@@ -5682,12 +5936,12 @@ function renderUnnestRows(params) {
5682
5936
  });
5683
5937
  }
5684
5938
  if (statement.columns.length === 0)
5685
- throw internalError4({ message: "an unnest insert needs at least one column" });
5939
+ throw internalError5({ message: "an unnest insert needs at least one column" });
5686
5940
  if (members.length !== statement.columns.length) {
5687
- throw internalError4({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5941
+ throw internalError5({ message: `unnest insert has ${members.length} column types for ${statement.columns.length} columns` });
5688
5942
  }
5689
5943
  if (statement.onConflict !== undefined && !("doNothing" in statement.onConflict)) {
5690
- throw internalError4({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5944
+ throw internalError5({ message: "an unnest insert takes no DO UPDATE arm \u2014 upserts bind VALUES" });
5691
5945
  }
5692
5946
  const aliases = statement.columns.map((_column, index) => dialect.quoteIdent({ name: `c${index + 1}` }));
5693
5947
  const arrays = members.map((member, index) => {
@@ -5703,11 +5957,11 @@ function renderUnnestRows(params) {
5703
5957
  function renderInsert(params) {
5704
5958
  const { dialect, statement, collector } = params;
5705
5959
  if (statement.rows.length === 0) {
5706
- throw internalError4({ message: "insert requires at least one row" });
5960
+ throw internalError5({ message: "insert requires at least one row" });
5707
5961
  }
5708
5962
  const defaultValues = statement.columns.length === 0 && dialect.emptyInsert.form === "defaultValues";
5709
5963
  if (defaultValues && statement.rows.length !== 1) {
5710
- throw internalError4({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5964
+ throw internalError5({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
5711
5965
  }
5712
5966
  if (defaultValues && statement.onConflict !== undefined && !(dialect.emptyInsert.form === "defaultValues" && dialect.emptyInsert.conflictClause)) {
5713
5967
  throw unsupportedCapability23({
@@ -5718,7 +5972,7 @@ function renderInsert(params) {
5718
5972
  }
5719
5973
  for (const row of statement.rows) {
5720
5974
  if (row.length !== statement.columns.length) {
5721
- throw internalError4({
5975
+ throw internalError5({
5722
5976
  message: `insert row has ${row.length} values but ${statement.columns.length} columns`
5723
5977
  });
5724
5978
  }
@@ -5736,7 +5990,7 @@ function renderInsert(params) {
5736
5990
  if (!isSqlDefaultCell(value))
5737
5991
  return collector.add(value);
5738
5992
  if (!dialect.rowDefaultKeyword) {
5739
- throw internalError4({
5993
+ throw internalError5({
5740
5994
  message: `${dialect.name} cannot render a per-row DEFAULT cell \u2014 rows must be grouped by column set`
5741
5995
  });
5742
5996
  }
@@ -5776,20 +6030,20 @@ function renderInsert(params) {
5776
6030
  }
5777
6031
  const keepName = onConflict.keepColumn ?? statement.columns[0];
5778
6032
  if (keepName === undefined) {
5779
- throw internalError4({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
6033
+ throw internalError5({ message: "a skip-duplicates insert of an all-default row must name its keepColumn" });
5780
6034
  }
5781
6035
  const keep = dialect.quoteIdent({ name: keepName });
5782
6036
  const current = `${dialect.quoteIdent({ name: statement.table.name })}.${keep}`;
5783
6037
  const flag = onConflict.duplicateFlag;
5784
6038
  if (flag !== undefined && !/^\d{1,20}$/.test(flag)) {
5785
- throw internalError4({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
6039
+ throw internalError5({ message: `duplicateFlag must be a decimal literal, got ${JSON.stringify(flag)}` });
5786
6040
  }
5787
6041
  text += flag === undefined ? ` ON DUPLICATE KEY UPDATE ${keep} = ${current}` : ` ON DUPLICATE KEY UPDATE ${keep} = IF(LAST_INSERT_ID(${flag}), ${current}, ${current})`;
5788
6042
  }
5789
6043
  } else {
5790
6044
  const updates = Object.entries(onConflict.update);
5791
6045
  if (updates.length === 0) {
5792
- throw internalError4({ message: "upsert update must set at least one column" });
6046
+ throw internalError5({ message: "upsert update must set at least one column" });
5793
6047
  }
5794
6048
  const selfRef = dialect.quoteIdent({ name: statement.table.name });
5795
6049
  if (onConflict.skipUnchanged !== undefined && !dialect.capabilities.upsertSkipUnchanged) {
@@ -5801,7 +6055,7 @@ function renderInsert(params) {
5801
6055
  const assignments = dialect.upsertForm !== "onConflict" && onConflict.skipUnchanged !== undefined ? renderStampFirstAssignments({ dialect, columns: onConflict.skipUnchanged, update: onConflict.update, collector, selfRef, tableName: statement.table.name }) : renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true, tableName: statement.table.name });
5802
6056
  if (dialect.upsertForm === "onConflict") {
5803
6057
  if (onConflict.lastInsertIdColumn !== undefined) {
5804
- throw internalError4({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
6058
+ throw internalError5({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
5805
6059
  }
5806
6060
  if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
5807
6061
  throw unsupportedCapability23({
@@ -5811,7 +6065,7 @@ function renderInsert(params) {
5811
6065
  });
5812
6066
  }
5813
6067
  if (onConflict.constraint !== undefined && onConflict.conflictWhere !== undefined) {
5814
- throw internalError4({
6068
+ throw internalError5({
5815
6069
  message: "a named-constraint conflict target already identifies the index \u2014 it cannot carry a partial predicate too"
5816
6070
  });
5817
6071
  }
@@ -5856,7 +6110,7 @@ function renderInsert(params) {
5856
6110
  const trick = onConflict.lastInsertIdColumn === undefined ? "" : `, ${dialect.quoteIdent({ name: onConflict.lastInsertIdColumn })} = LAST_INSERT_ID(${dialect.quoteIdent({ name: onConflict.lastInsertIdColumn })})`;
5857
6111
  const flagged = onConflict.duplicateFlag;
5858
6112
  if (flagged !== undefined && (onConflict.lastInsertIdColumn !== undefined || !/^\d{1,20}$/.test(flagged.flag))) {
5859
- throw internalError4({ message: "an update arm's duplicate flag must be a decimal literal, and cannot share LAST_INSERT_ID with lastInsertIdColumn" });
6113
+ throw internalError5({ message: "an update arm's duplicate flag must be a decimal literal, and cannot share LAST_INSERT_ID with lastInsertIdColumn" });
5860
6114
  }
5861
6115
  const flag = flagged === undefined ? "" : `, ${dialect.quoteIdent({ name: flagged.column })} = IF(LAST_INSERT_ID(${flagged.flag}), ${selfRef}.${dialect.quoteIdent({ name: flagged.column })}, ${selfRef}.${dialect.quoteIdent({ name: flagged.column })})`;
5862
6116
  text += ` ON DUPLICATE KEY UPDATE ${assignments}${trick}${flag}`;
@@ -5870,12 +6124,12 @@ function renderUpdate(params) {
5870
6124
  const { dialect, statement, collector } = params;
5871
6125
  const assignments = Object.entries(statement.set);
5872
6126
  if (assignments.length === 0) {
5873
- throw internalError4({ message: "update must set at least one column" });
6127
+ throw internalError5({ message: "update must set at least one column" });
5874
6128
  }
5875
6129
  const source = statement.from;
5876
6130
  if (source !== undefined) {
5877
6131
  if (statement.alias === undefined || statement.alias === source.alias)
5878
- throw internalError4({ message: "UPDATE \u2026 FROM needs distinct target and source aliases" });
6132
+ throw internalError5({ message: "UPDATE \u2026 FROM needs distinct target and source aliases" });
5879
6133
  const target = dialect.quoteIdent({ name: statement.alias });
5880
6134
  const joined = dialect.quoteIdent({ name: source.alias });
5881
6135
  const assign = () => renderAssignments({ dialect, entries: assignments, collector, selfRef: target, source: joined, ...dialect.name === "mysql" ? { qualifyWrite: target } : {}, ...params.earlier === undefined ? {} : { steps: params.earlier } });
@@ -5915,12 +6169,12 @@ function renderAssignments(params) {
5915
6169
  function renderUnchangedGuard(params) {
5916
6170
  const { dialect, columns, update, collector, selfRef, tableName } = params;
5917
6171
  if (columns.length === 0)
5918
- throw internalError4({ message: "a skip-unchanged guard needs at least one compared column" });
6172
+ throw internalError5({ message: "a skip-unchanged guard needs at least one compared column" });
5919
6173
  const left = [];
5920
6174
  const right = [];
5921
6175
  for (const entry of columns) {
5922
6176
  if (!Object.hasOwn(update, entry.column)) {
5923
- throw internalError4({ message: `the skip-unchanged column ${entry.column} has no assignment in the update arm` });
6177
+ throw internalError5({ message: `the skip-unchanged column ${entry.column} has no assignment in the update arm` });
5924
6178
  }
5925
6179
  const assigned = update[entry.column];
5926
6180
  const value = renderAssignmentValue({ dialect, collector, selfRef, allowExcluded: true, tableName, name: entry.column, value: assigned });
@@ -5966,23 +6220,23 @@ function renderAssignmentValue(params) {
5966
6220
  const read = selfRef === undefined ? column : `${selfRef}.${column}`;
5967
6221
  if (isSourceColumnRef(value)) {
5968
6222
  if (params.source === undefined)
5969
- throw internalError4({ message: `an UPDATE set value reads the joined source's column ${value.column}, but the statement joins no source` });
6223
+ throw internalError5({ message: `an UPDATE set value reads the joined source's column ${value.column}, but the statement joins no source` });
5970
6224
  return `${params.source}.${dialect.quoteIdent({ name: value.column })}`;
5971
6225
  }
5972
6226
  if (isStepColumnRef(value)) {
5973
6227
  if (params.steps === undefined || !params.steps.has(value.step)) {
5974
- throw internalError4({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
6228
+ throw internalError5({ message: `an UPDATE set value reads step "${value.step}", which is not an earlier chain step` });
5975
6229
  }
5976
6230
  return `(SELECT ${dialect.quoteIdent({ name: value.column })} FROM ${dialect.quoteIdent({ name: value.step })})`;
5977
6231
  }
5978
6232
  if (isSqlFragment(value)) {
5979
6233
  if (params.allowExcluded !== true || selfRef === undefined)
5980
- throw internalError4({ message: "SQL assignment fragments require a conflict update" });
6234
+ throw internalError5({ message: "SQL assignment fragments require a conflict update" });
5981
6235
  return renderSqlFragment({ dialect, fragment: value, collector, conflict: { table: params.tableName } });
5982
6236
  }
5983
6237
  if (isExcludedRef(value)) {
5984
6238
  if (params.allowExcluded !== true) {
5985
- throw internalError4({
6239
+ throw internalError5({
5986
6240
  message: `an incoming-row reference (${value.column}) is only meaningful inside an upsert's update arm`
5987
6241
  });
5988
6242
  }
@@ -5990,7 +6244,7 @@ function renderAssignmentValue(params) {
5990
6244
  }
5991
6245
  if (isSqlKeepCell(value)) {
5992
6246
  if (params.allowExcluded !== true || selfRef === undefined)
5993
- throw internalError4({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
6247
+ throw internalError5({ message: "a keep-column cell is only meaningful inside an upsert's update arm" });
5994
6248
  return read;
5995
6249
  }
5996
6250
  if (isJsonPatchOperation(value)) {
@@ -6037,12 +6291,12 @@ function renderChain(params) {
6037
6291
  });
6038
6292
  }
6039
6293
  if (statement.steps.length === 0)
6040
- throw internalError4({ message: "a statement chain needs at least one step" });
6294
+ throw internalError5({ message: "a statement chain needs at least one step" });
6041
6295
  const defined = new Set;
6042
6296
  const steps = [];
6043
6297
  for (const step of statement.steps) {
6044
6298
  if (step.name === CHAIN_ROWS_ALIAS || step.name === CHAIN_ONE_ALIAS || defined.has(step.name)) {
6045
- throw internalError4({ message: `chain step name "${step.name}" is reserved or repeated` });
6299
+ throw internalError5({ message: `chain step name "${step.name}" is reserved or repeated` });
6046
6300
  }
6047
6301
  const body = renderChainStep({ dialect, statement: step.statement, collector, earlier: defined });
6048
6302
  steps.push(`${dialect.quoteIdent({ name: step.name })} AS (${body})`);
@@ -6055,7 +6309,7 @@ function renderChain(params) {
6055
6309
  }
6056
6310
  for (const alias of captureAliases({ captures })) {
6057
6311
  if (result.columns.includes(alias) || alias === result.verdict?.alias || alias === result.tally?.alias) {
6058
- throw internalError4({ message: `chain capture alias "${alias}" collides with a result column` });
6312
+ throw internalError5({ message: `chain capture alias "${alias}" collides with a result column` });
6059
6313
  }
6060
6314
  }
6061
6315
  if (result.rowsFrom.length === 0 && result.columns.length === 0 && result.tally !== undefined && result.verdict === undefined) {
@@ -6063,11 +6317,11 @@ function renderChain(params) {
6063
6317
  return { text: `WITH ${steps.join(", ")} SELECT ${[tally, ...renderChainCaptures({ dialect, captures, defined, collector })].join(", ")}`, values: collector.values, returning: "none" };
6064
6318
  }
6065
6319
  if (result.rowsFrom.length === 0 || result.columns.length === 0) {
6066
- throw internalError4({ message: "a chain result needs at least one source step and one column" });
6320
+ throw internalError5({ message: "a chain result needs at least one source step and one column" });
6067
6321
  }
6068
6322
  for (const name of result.rowsFrom) {
6069
6323
  if (!defined.has(name))
6070
- throw internalError4({ message: `chain result reads undefined step "${name}"` });
6324
+ throw internalError5({ message: `chain result reads undefined step "${name}"` });
6071
6325
  }
6072
6326
  const columnList = result.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
6073
6327
  const guarded = result.verdict !== undefined || result.tally !== undefined || result.veto !== undefined && result.veto.length > 0;
@@ -6078,7 +6332,7 @@ function renderChain(params) {
6078
6332
  const extra = [];
6079
6333
  for (const alias of [result.verdict?.alias, result.tally?.alias]) {
6080
6334
  if (alias !== undefined && result.columns.includes(alias)) {
6081
- throw internalError4({ message: `chain result alias "${alias}" collides with a result column` });
6335
+ throw internalError5({ message: `chain result alias "${alias}" collides with a result column` });
6082
6336
  }
6083
6337
  }
6084
6338
  const rows = dialect.quoteIdent({ name: CHAIN_ROWS_ALIAS });
@@ -6134,7 +6388,7 @@ function renderChainCaptures(params) {
6134
6388
  const out = [];
6135
6389
  for (const entry of captures) {
6136
6390
  if (!defined.has(entry.step))
6137
- throw internalError4({ message: `chain capture reads undefined step "${entry.step}"` });
6391
+ throw internalError5({ message: `chain capture reads undefined step "${entry.step}"` });
6138
6392
  const step = quote(entry.step);
6139
6393
  const marker = entry.inserted === undefined ? undefined : quote(entry.inserted);
6140
6394
  out.push(`(SELECT count(*) FROM ${step}) AS ${quote(`${entry.alias}_n`)}`);
@@ -6159,7 +6413,7 @@ function renderImageAggregates(params) {
6159
6413
  if (before.length === 0 && after.length === 0)
6160
6414
  return out;
6161
6415
  if (!Number.isSafeInteger(entry.limit) || entry.limit < 1)
6162
- throw internalError4({ message: `chain capture "${entry.alias}" needs a positive integer limit` });
6416
+ throw internalError5({ message: `chain capture "${entry.alias}" needs a positive integer limit` });
6163
6417
  const limit = collector.add(entry.limit);
6164
6418
  const item = quote("__vibe_i");
6165
6419
  const image = (columns, filter) => `(SELECT array_agg(${item})::text FROM (SELECT DISTINCT ARRAY[${columns.map(quote).join(", ")}] AS ${item} FROM ${step}${filter} LIMIT ${limit}) AS ${quote("__vibe_d")})`;
@@ -6179,21 +6433,21 @@ function renderSelectCell(params) {
6179
6433
  if (isDbNowMarker(value))
6180
6434
  return dialect.renderDbNow({ offsetSeconds: value.offsetSeconds, collector });
6181
6435
  if (isSqlDefaultCell(value)) {
6182
- throw internalError4({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
6436
+ throw internalError5({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
6183
6437
  }
6184
6438
  return collector.add(value);
6185
6439
  }
6186
6440
  function renderRowSource(params) {
6187
6441
  const { dialect, source, collector } = params;
6188
6442
  if (source.columns.length === 0 || source.rows.length === 0) {
6189
- throw internalError4({ message: "a row source needs at least one column and one row" });
6443
+ throw internalError5({ message: "a row source needs at least one column and one row" });
6190
6444
  }
6191
6445
  const typedBy = renderTable(dialect, source.typedBy);
6192
6446
  const columns = source.columns.map((name) => dialect.quoteIdent({ name }));
6193
6447
  const rows = [`(${columns.map((column) => `(SELECT ${column} FROM ${typedBy} WHERE FALSE)`).join(", ")})`];
6194
6448
  for (const row of source.rows) {
6195
6449
  if (row.length !== source.columns.length) {
6196
- throw internalError4({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
6450
+ throw internalError5({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
6197
6451
  }
6198
6452
  rows.push(`(${row.map((value) => renderSelectCell({ dialect, value, collector })).join(", ")})`);
6199
6453
  }
@@ -6205,12 +6459,12 @@ function renderTallyColumn(params) {
6205
6459
  const { dialect, result, defined, collector } = params;
6206
6460
  const tally = result.tally;
6207
6461
  if (tally === undefined)
6208
- throw internalError4({ message: "a chain veto needs a tally column to guard" });
6462
+ throw internalError5({ message: "a chain veto needs a tally column to guard" });
6209
6463
  if (tally.steps.length === 0)
6210
- throw internalError4({ message: "a chain tally needs at least one step" });
6464
+ throw internalError5({ message: "a chain tally needs at least one step" });
6211
6465
  const step = (name) => {
6212
6466
  if (!defined.has(name))
6213
- throw internalError4({ message: `chain tally or veto reads undefined step "${name}"` });
6467
+ throw internalError5({ message: `chain tally or veto reads undefined step "${name}"` });
6214
6468
  return dialect.quoteIdent({ name });
6215
6469
  };
6216
6470
  const count = (name) => `(SELECT count(*) FROM ${step(name)})`;
@@ -6218,20 +6472,20 @@ function renderTallyColumn(params) {
6218
6472
  const vetoes = result.veto ?? [];
6219
6473
  if (vetoes.length === 0) {
6220
6474
  if (result.vetoGate !== undefined)
6221
- throw internalError4({ message: "a chain veto gate needs a veto to gate" });
6475
+ throw internalError5({ message: "a chain veto gate needs a veto to gate" });
6222
6476
  return `${sum} AS ${dialect.quoteIdent({ name: tally.alias })}`;
6223
6477
  }
6224
6478
  const gate = result.vetoGate === undefined ? [] : [`WHEN NOT EXISTS (SELECT 1 FROM ${step(result.vetoGate)}) THEN ${sum}`];
6225
6479
  const arms = vetoes.map((veto) => {
6226
6480
  if (!CHAIN_VETO_TOKEN_PATTERN.test(veto.token))
6227
- throw internalError4({ message: `chain veto token "${veto.token}" is not a lower-case word` });
6481
+ throw internalError5({ message: `chain veto token "${veto.token}" is not a lower-case word` });
6228
6482
  if (typeof veto.nonce !== "string" || veto.nonce === "")
6229
- throw internalError4({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
6483
+ throw internalError5({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
6230
6484
  const minRows = veto.minRows ?? 1;
6231
6485
  if (!Number.isSafeInteger(minRows) || minRows < 1)
6232
- throw internalError4({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
6486
+ throw internalError5({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
6233
6487
  if (veto.exactRows !== undefined && (!Number.isSafeInteger(veto.exactRows) || veto.exactRows < 0 || veto.minRows !== undefined)) {
6234
- throw internalError4({ message: `chain veto "${veto.token}" needs a nonnegative safe integer exactRows without minRows` });
6488
+ throw internalError5({ message: `chain veto "${veto.token}" needs a nonnegative safe integer exactRows without minRows` });
6235
6489
  }
6236
6490
  const when = veto.exactRows !== undefined ? `WHEN ${count(veto.step)} <> ${veto.exactRows}` : minRows === 1 ? `WHEN NOT EXISTS (SELECT 1 FROM ${step(veto.step)})` : `WHEN ${count(veto.step)} < ${minRows}`;
6237
6491
  return `${when} THEN CAST('${CHAIN_VETO_PREFIX}${veto.token}:' || ${collector.add(veto.nonce)} || ':' || ${count(veto.step)} AS integer)`;
@@ -6241,25 +6495,25 @@ function renderTallyColumn(params) {
6241
6495
  function renderInsertSelect(params) {
6242
6496
  const { dialect, statement, collector } = params;
6243
6497
  if (statement.columns.length === 0)
6244
- throw internalError4({ message: "insert requires at least one column" });
6498
+ throw internalError5({ message: "insert requires at least one column" });
6245
6499
  if (statement.cells.length !== statement.columns.length) {
6246
- throw internalError4({
6500
+ throw internalError5({
6247
6501
  message: `insert row has ${statement.cells.length} values but ${statement.columns.length} columns`
6248
6502
  });
6249
6503
  }
6250
6504
  const from = statement.from ?? [];
6251
6505
  for (const name of from) {
6252
6506
  if (!params.earlier.has(name))
6253
- throw internalError4({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
6507
+ throw internalError5({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
6254
6508
  }
6255
6509
  const source = statement.source;
6256
6510
  if (source !== undefined && (params.earlier.has(source.alias) || from.includes(source.alias))) {
6257
- throw internalError4({ message: `row source alias "${source.alias}" collides with a chain step` });
6511
+ throw internalError5({ message: `row source alias "${source.alias}" collides with a chain step` });
6258
6512
  }
6259
6513
  const cells = statement.cells.map((value) => {
6260
6514
  if (isStepColumnRef(value)) {
6261
6515
  if (!from.includes(value.step) && value.step !== source?.alias) {
6262
- throw internalError4({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
6516
+ throw internalError5({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
6263
6517
  }
6264
6518
  return `${dialect.quoteIdent({ name: value.step })}.${dialect.quoteIdent({ name: value.column })}`;
6265
6519
  }
@@ -6278,7 +6532,7 @@ function renderInsertSelect(params) {
6278
6532
  const conflict = statement.conflictDoNothing;
6279
6533
  if (conflict !== undefined) {
6280
6534
  if (conflict.columns.length === 0)
6281
- throw internalError4({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
6535
+ throw internalError5({ message: "an INSERT \u2026 SELECT conflict clause needs at least one column" });
6282
6536
  if (dialect.doNothingForm !== "onConflictDoNothing") {
6283
6537
  throw unsupportedCapability23({
6284
6538
  dialect: dialect.name,
@@ -6538,7 +6792,7 @@ var postgresDialect = {
6538
6792
  keyListStrategy: "unnest",
6539
6793
  keysetSeekStrategy: "rowValue",
6540
6794
  keyPageStrategy: "lateral",
6541
- lateralJson: { rowArray: "json_build_array", arrayAgg: "json_agg", emptyArray: "'[]'::json", order: "inAggregate", timeOfDayAsText: false, requiresUtcDateTimeWire: false },
6795
+ lateralJson: { rowArray: "json_build_array", arrayAgg: "json_agg", emptyArray: "'[]'::json", order: "inAggregate", timeOfDayAsText: false, requiresUtcDateTimeWire: false, keysFromJson: "elements", pinnedCommonTable: "keyword", carriedLists: "firstRow", silentSizeLimit: null },
6542
6796
  relationStrategyDefault: "join",
6543
6797
  relationFilterStrategy: "eqSelect",
6544
6798
  deferredJoinPage: { minSkip: 1000, requiresCoveringIndex: true },
@@ -6636,7 +6890,7 @@ var mysqlDialect = {
6636
6890
  keyListStrategy: "rowValue",
6637
6891
  keysetSeekStrategy: "or",
6638
6892
  keyPageStrategy: "window",
6639
- lateralJson: { rowArray: "JSON_ARRAY", arrayAgg: "JSON_ARRAYAGG", emptyArray: "JSON_ARRAY()", order: "positionSlot", timeOfDayAsText: true, requiresUtcDateTimeWire: true },
6893
+ lateralJson: { rowArray: "JSON_ARRAY", arrayAgg: "JSON_ARRAYAGG", emptyArray: "JSON_ARRAY()", order: "positionSlot", timeOfDayAsText: true, requiresUtcDateTimeWire: true, keysFromJson: "jsonTable", pinnedCommonTable: "limit", carriedLists: "carrierRow", silentSizeLimit: "max_allowed_packet" },
6640
6894
  relationStrategyDefault: "join",
6641
6895
  relationFilterStrategy: "exists",
6642
6896
  deferredJoinPage: { minSkip: 1000, requiresCoveringIndex: false },
@@ -6903,7 +7157,7 @@ function renderGeneratedColumnClause({ dialect, generated }) {
6903
7157
  }
6904
7158
  }
6905
7159
  // packages/sql/src/row-rules.ts
6906
- import { VibeError as VibeError25, internalError as internalError5, normalizeRowRules, rowRuleGateField, rowRuleIdentities, rowRuleProtectedFields, rowRuleShapeIssue, rowRuleTriggerName, unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
7160
+ import { VibeError as VibeError25, internalError as internalError6, normalizeRowRules, rowRuleGateField, rowRuleIdentities, rowRuleProtectedFields, rowRuleShapeIssue, rowRuleTriggerName, unsupportedCapability as unsupportedCapability31 } from "@vibeorm/schema";
6907
7161
 
6908
7162
  // packages/sql/src/dialects/postgres-row-rules.ts
6909
7163
  import { VibeError as VibeError22, unsupportedCapability as unsupportedCapability28 } from "@vibeorm/schema";
@@ -7073,7 +7327,7 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
7073
7327
  const functionIdentity = objects.find((object) => object.role === "function")?.identity;
7074
7328
  const triggerIdentity = objects.find((object) => object.role === "trigger")?.identity;
7075
7329
  if (functionIdentity?.kind !== "function" || triggerIdentity?.kind !== "trigger")
7076
- throw internalError5({ message: "PostgreSQL row rules own one function and one trigger", meta: { model: model.name, rule: normalized.name } });
7330
+ throw internalError6({ message: "PostgreSQL row rules own one function and one trigger", meta: { model: model.name, rule: normalized.name } });
7077
7331
  const gate = rowRuleGateField({ rule: normalized });
7078
7332
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
7079
7333
  return renderPostgresRowRule({
@@ -7094,15 +7348,15 @@ function renderRowRule({ schema, model, rule, dialect, placement }) {
7094
7348
  const objects = rowRuleIdentities({ schema, model, rule: normalized, dialect: dialect.name });
7095
7349
  const triggers = objects.map(({ role, identity }) => {
7096
7350
  if (role !== "trigger" || identity.kind !== "trigger")
7097
- throw internalError5({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
7351
+ throw internalError6({ message: "Event row rules own only triggers", meta: { model: model.name, rule: normalized.name } });
7098
7352
  const event = ["update", "delete"].find((event) => identity.name === rowRuleTriggerName({ rule: normalized, event }));
7099
7353
  if (event === undefined)
7100
- throw internalError5({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
7354
+ throw internalError6({ message: "Row-rule identity has no declared event", meta: { model: model.name, rule: normalized.name, trigger: identity.name } });
7101
7355
  return { event, identity: { kind: "trigger", table: identity.table, name: identity.name } };
7102
7356
  });
7103
7357
  const table = triggers[0]?.identity.table;
7104
7358
  if (table === undefined)
7105
- throw internalError5({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
7359
+ throw internalError6({ message: "Event row rules require at least one trigger", meta: { model: model.name, rule: normalized.name } });
7106
7360
  const gate = rowRuleGateField({ rule: normalized });
7107
7361
  const gateField = gate === undefined ? undefined : model.fields.find((field) => field.name === gate);
7108
7362
  const context = {
@@ -7355,7 +7609,7 @@ function renderMigrationVisibility(params) {
7355
7609
  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" };
7356
7610
  }
7357
7611
  // packages/sql/src/dialects/migration-objects.ts
7358
- import { internalError as internalError6, unsupportedCapability as unsupportedCapability38 } from "@vibeorm/schema";
7612
+ import { internalError as internalError7, unsupportedCapability as unsupportedCapability38 } from "@vibeorm/schema";
7359
7613
  function renderMigrationObjectQuery(params) {
7360
7614
  return objectQuery({ ...params, collector: new ParamCollector(params.dialect) });
7361
7615
  }
@@ -7607,7 +7861,7 @@ function renderMigrationObjectBatch(params) {
7607
7861
  const facts = objectQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
7608
7862
  const dependencies = dependencyQuery({ dialect, identity, collector: inspectionReferences({ dialect }) });
7609
7863
  if (JSON.stringify(dependencies.values) !== JSON.stringify(facts.values))
7610
- throw internalError6({ message: "Batched catalog queries bind their identity in different orders", meta: { kind: identity.kind } });
7864
+ throw internalError7({ message: "Batched catalog queries bind their identity in different orders", meta: { kind: identity.kind } });
7611
7865
  const columns = facts.values.map((value, index) => `${dialect.quoteIdent({ name: `p${index + 1}` })} ${Array.isArray(value) ? "text[]" : "text"}`);
7612
7866
  const requests = group.map(({ identity, position }) => ({ position, ...Object.fromEntries(renderMigrationObjectQuery({ dialect, identity }).values.map((value, index) => [`p${index + 1}`, value])) }));
7613
7867
  const collector = new ParamCollector(dialect);
@@ -7849,6 +8103,8 @@ export {
7849
8103
  isSqlQuery,
7850
8104
  isStepColumnRef,
7851
8105
  isTimeZoneName,
8106
+ jsonKeyTokenCast,
8107
+ jsonKeysTyped,
7852
8108
  jsonListParameter,
7853
8109
  jsonParamCell,
7854
8110
  jsonPatchVetoOf,
@@ -7857,6 +8113,7 @@ export {
7857
8113
  mapConflictColumns,
7858
8114
  mysqlBoundTimestampText,
7859
8115
  mysqlDialect,
8116
+ mysqlJsonKeyTypeOf,
7860
8117
  nativeEnumArrayParameter,
7861
8118
  normalizeMysqlNativeType,
7862
8119
  normalizePostgresNativeType,
@@ -7995,4 +8252,4 @@ export {
7995
8252
  zonedCodec
7996
8253
  };
7997
8254
 
7998
- //# debugId=43389FE0EFAEA89964756E2164756E21
8255
+ //# debugId=07BC696B142BA5A864756E2164756E21