@crvouga/mockingbird-service-postgres 3.6.0 → 3.8.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
@@ -2800,6 +2800,12 @@ var invalidName = () => {
2800
2800
  throw pgError("syntax", "invalid name syntax", "42602");
2801
2801
  };
2802
2802
  function parseNamespaceInput(text) {
2803
+ const parsed = parseRelationInput(text);
2804
+ if ("oid" in parsed) return parsed;
2805
+ if (parsed.parts.length !== 1) invalidName();
2806
+ return { name: parsed.parts[0] };
2807
+ }
2808
+ function parseRelationInput(text) {
2803
2809
  if (text === "-") return { oid: 0 };
2804
2810
  if (/^\d+$/.test(text)) return { oid: parseOidText(text) };
2805
2811
  const names = [];
@@ -2835,8 +2841,8 @@ function parseNamespaceInput(text) {
2835
2841
  skipSpace();
2836
2842
  if (at === text.length) invalidName();
2837
2843
  }
2838
- if (names.length !== 1) invalidName();
2839
- return { name: names[0] };
2844
+ if (names.length === 0 || names.length > 3) invalidName();
2845
+ return { parts: names };
2840
2846
  }
2841
2847
  function namespaceText(oid, name) {
2842
2848
  if (oid === 0) return "-";
@@ -2909,6 +2915,7 @@ var TYPE_ALIASES = {
2909
2915
  regtype: "regtype",
2910
2916
  regproc: "regproc",
2911
2917
  regnamespace: "regnamespace",
2918
+ xid: "xid",
2912
2919
  oid: "oid",
2913
2920
  void: "void",
2914
2921
  record: "record",
@@ -2985,6 +2992,7 @@ var TYPE_OIDS = {
2985
2992
  regproc: 24,
2986
2993
  text: 25,
2987
2994
  oid: 26,
2995
+ xid: 28,
2988
2996
  json: 114,
2989
2997
  float4: 700,
2990
2998
  float8: 701,
@@ -3303,6 +3311,7 @@ function recordText(rec, ctx) {
3303
3311
  return `(${parts.join(",")})`;
3304
3312
  }
3305
3313
  function datumText(t, v, ctx) {
3314
+ if (t === "regclass") return v === 0 ? "-" : ctx.relationName?.(v) ?? String(v);
3306
3315
  if (t === "regnamespace") return namespaceText(v, ctx.namespaceName?.(v));
3307
3316
  if (v === null) throw pgError("internal", "datumText called with null");
3308
3317
  if (isArrayType(t)) return arrayText(v, ctx);
@@ -3313,6 +3322,7 @@ function datumText(t, v, ctx) {
3313
3322
  case "int2":
3314
3323
  case "int4":
3315
3324
  case "oid":
3325
+ case "xid":
3316
3326
  return String(v);
3317
3327
  case "int8":
3318
3328
  return String(v);
@@ -3377,6 +3387,12 @@ function datumText(t, v, ctx) {
3377
3387
  }
3378
3388
  var UTC_INPUT = { zoneOffsetForNaive: () => 0 };
3379
3389
  function datumFromText(t, text, ctx) {
3390
+ if (t === "regclass") {
3391
+ const parsed = parseRelationInput(text);
3392
+ if ("oid" in parsed) return parsed.oid;
3393
+ if (!ctx.relationOid) throw unsupported("regclass input without a catalog");
3394
+ return ctx.relationOid(parsed.parts);
3395
+ }
3380
3396
  if (t === "regnamespace") {
3381
3397
  const parsed = parseNamespaceInput(text);
3382
3398
  if ("oid" in parsed) return parsed.oid;
@@ -3398,6 +3414,7 @@ function datumFromText(t, text, ctx) {
3398
3414
  case "int8":
3399
3415
  return parseIntegerText(text, t);
3400
3416
  case "oid":
3417
+ case "xid":
3401
3418
  return parseOidText(text);
3402
3419
  case "float4":
3403
3420
  return Math.fround(parseFloatText(text, "float4"));
@@ -3554,6 +3571,8 @@ var TableData = class _TableData {
3554
3571
  schema;
3555
3572
  columns;
3556
3573
  rows;
3574
+ rowXmins = /* @__PURE__ */ new WeakMap();
3575
+ slabXmins = null;
3557
3576
  /** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
3558
3577
  slab = null;
3559
3578
  constraints;
@@ -3596,6 +3615,11 @@ var TableData = class _TableData {
3596
3615
  materializeSlab() {
3597
3616
  if (!this.slab) return;
3598
3617
  this.rows = this.slab.materialize();
3618
+ if (this.slabXmins)
3619
+ this.rows.forEach((row, index) => {
3620
+ this.rowXmins.set(row, this.slabXmins[index] ?? 2);
3621
+ });
3622
+ this.slabXmins = null;
3599
3623
  this.slab = null;
3600
3624
  this.indexStores = null;
3601
3625
  }
@@ -3629,6 +3653,10 @@ var TableData = class _TableData {
3629
3653
  } else {
3630
3654
  t.rows = this.rows.map((r) => r.slice());
3631
3655
  }
3656
+ t.rows.forEach((row, index) => {
3657
+ const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.rows[index]);
3658
+ if (xmin !== void 0) t.rowXmins.set(row, xmin);
3659
+ });
3632
3660
  t.unlogged = this.unlogged;
3633
3661
  t.constraints = this.constraints.map((c) => ({ ...c }));
3634
3662
  t.triggers = this.triggers.map((tr) => ({ ...tr }));
@@ -3714,6 +3742,14 @@ var DatabaseState = class _DatabaseState {
3714
3742
  clock;
3715
3743
  changes = 0;
3716
3744
  oidCounter = 16384;
3745
+ /** Local monotonic xid allocator; 2 denotes a row from a legacy snapshot without metadata. */
3746
+ xidCounter = 3;
3747
+ transactionXid = null;
3748
+ allocateXid() {
3749
+ if (this.xidCounter > 4294967295)
3750
+ throw pgError("program_limit_exceeded", "transaction ID space exhausted; xid wraparound is not modeled", "54000");
3751
+ return this.xidCounter++;
3752
+ }
3717
3753
  inTransaction = false;
3718
3754
  /** Transaction-only events: included in rollback clones, never in exported snapshots. */
3719
3755
  pendingTriggers = [];
@@ -3927,6 +3963,8 @@ var DatabaseState = class _DatabaseState {
3927
3963
  s.sequenceCurrval = new Map(this.sequenceCurrval);
3928
3964
  s.tableStats = new Map(this.tableStats);
3929
3965
  s.oidCounter = this.oidCounter;
3966
+ s.xidCounter = this.xidCounter;
3967
+ s.transactionXid = this.transactionXid;
3930
3968
  for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
3931
3969
  return s;
3932
3970
  }
@@ -3946,6 +3984,8 @@ var DatabaseState = class _DatabaseState {
3946
3984
  s.sequenceCurrval = new Map(this.sequenceCurrval);
3947
3985
  s.tableStats = new Map(this.tableStats);
3948
3986
  s.oidCounter = this.oidCounter;
3987
+ s.xidCounter = this.xidCounter;
3988
+ s.transactionXid = this.transactionXid;
3949
3989
  for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
3950
3990
  return s;
3951
3991
  }
@@ -4021,6 +4061,8 @@ var DatabaseState = class _DatabaseState {
4021
4061
  this.sequenceCurrval = other.sequenceCurrval;
4022
4062
  this.tableStats = other.tableStats;
4023
4063
  this.oidCounter = other.oidCounter;
4064
+ this.xidCounter = Math.max(this.xidCounter, other.xidCounter);
4065
+ this.transactionXid = other.transactionXid;
4024
4066
  }
4025
4067
  /** @internal Snapshot codec access to the oid allocator. */
4026
4068
  snapshotOidCounter() {
@@ -4097,6 +4139,11 @@ function zoneOffsetForNaive(zone, naiveMicros) {
4097
4139
  // src/expressions/context.ts
4098
4140
  var EngineCtx = class {
4099
4141
  state;
4142
+ writeXid = null;
4143
+ rowVersionXid() {
4144
+ if (this.state.inTransaction) return this.state.transactionXid ??= this.state.allocateXid();
4145
+ return this.writeXid ??= this.state.allocateXid();
4146
+ }
4100
4147
  /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
4101
4148
  txNow;
4102
4149
  /** statement timestamp */
@@ -4111,6 +4158,23 @@ var EngineCtx = class {
4111
4158
  timezone() {
4112
4159
  return this.state.getSetting("timezone") ?? "UTC";
4113
4160
  }
4161
+ relationOid(parts) {
4162
+ const target = this.state.resolveRelationSchema(parts);
4163
+ const schema = target ? this.state.schemas.get(target.schema) : void 0;
4164
+ const relation = target ? schema?.sequences.get(target.name) ?? schema?.tables.get(target.name) ?? schema?.views.get(target.name) : void 0;
4165
+ if (!relation) throw pgError("undefined_table", `relation "${parts.join(".")}" does not exist`, "42P01");
4166
+ return relation.oid;
4167
+ }
4168
+ relationName(oid) {
4169
+ for (const schema of this.state.schemas.values()) {
4170
+ for (const relation of [...schema.tables.values(), ...schema.sequences.values(), ...schema.views.values()]) {
4171
+ if (relation.oid !== oid) continue;
4172
+ const visible = this.state.resolveRelationSchema([relation.name])?.schema === schema.name;
4173
+ return (visible ? [relation.name] : [schema.name, relation.name]).map(quoteIdentifier).join(".");
4174
+ }
4175
+ }
4176
+ return null;
4177
+ }
4114
4178
  namespaceOid(name) {
4115
4179
  const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
4116
4180
  if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
@@ -4411,7 +4475,7 @@ function castTo(env, value, target, opts = {}) {
4411
4475
  return tv(target, makeArray(toElem, items, arr.dims.slice(), arr.lbs.slice()));
4412
4476
  }
4413
4477
  if (from === "oid" && target === "regnamespace") return tv(target, v);
4414
- if (from === "regnamespace" && (target === "oid" || target === "int4" || target === "int8")) {
4478
+ if ((from === "regnamespace" || from === "regclass") && (target === "oid" || target === "int4" || target === "int8")) {
4415
4479
  return tv(target, target === "int8" ? BigInt(v) : v);
4416
4480
  }
4417
4481
  if ((from === "int4" || from === "int8" || from === "int2") && (target === "oid" || target === "regnamespace")) {
@@ -5112,6 +5176,8 @@ function datumCompare(t, a, b, ctx = DEFAULT_COMPARE_CTX) {
5112
5176
  case "int2":
5113
5177
  case "int4":
5114
5178
  case "oid":
5179
+ case "regclass":
5180
+ case "xid":
5115
5181
  case "regnamespace":
5116
5182
  case "date":
5117
5183
  return expectNumber(a, t) - expectNumber(b, t);
@@ -5231,6 +5297,8 @@ function datumKey(t, v) {
5231
5297
  case "int2":
5232
5298
  case "int4":
5233
5299
  case "oid":
5300
+ case "regclass":
5301
+ case "xid":
5234
5302
  case "date":
5235
5303
  return `i${v}`;
5236
5304
  case "float4":
@@ -8136,7 +8204,7 @@ function databaseCatalogContext(state) {
8136
8204
  var PG_VERSION_TEXT = "PostgreSQL 18.3 (postgres-mem) on TypeScript, in-memory engine";
8137
8205
  function findSequenceForCall(ctx, name) {
8138
8206
  const parts = parseQualifiedName(name);
8139
- const seq = ctx.state.findSequence(parts);
8207
+ const seq = /^\d+$/.test(name) ? [...ctx.state.schemas.values()].flatMap((schema) => [...schema.sequences.values()]).find((sequence) => sequence.oid === Number(name)) : ctx.state.findSequence(parts);
8140
8208
  if (!seq) {
8141
8209
  throw pgError("undefined_table", `relation "${name}" does not exist`, "42P01");
8142
8210
  }
@@ -8360,14 +8428,16 @@ function getMiscFunctions() {
8360
8428
  "pg_get_serial_sequence",
8361
8429
  strict("text", (ctx, args) => {
8362
8430
  const tableName2 = argText(ctx, args[0]);
8363
- const column = argText(ctx, args[1]).toLowerCase();
8431
+ const column = argText(ctx, args[1]);
8364
8432
  const table = ctx.state.findTable(parseQualifiedName(tableName2));
8365
8433
  if (!table) throw pgError("undefined_table", `relation "${tableName2}" does not exist`, "42P01");
8366
8434
  const col = table.columns.find((c) => c.name === column);
8367
8435
  if (!col)
8368
8436
  throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
8369
- if (!col.identity) return tv("text", null);
8370
- return tv("text", col.identity.sequence.split(".").map(quoteIdent).join("."));
8437
+ const seq = [...ctx.state.schemas.get(table.schema)?.sequences.values() ?? []].find(
8438
+ (candidate) => candidate.ownedBy?.table === table.name && candidate.ownedBy.column === column
8439
+ );
8440
+ return tv("text", seq ? [seq.schema, seq.name].map(quoteIdent).join(".") : null);
8371
8441
  })
8372
8442
  );
8373
8443
  m.set("format_type", (ctx, args) => {
@@ -9885,7 +9955,7 @@ function evalTrgmWordSimilar(ctx, left, right) {
9885
9955
  const score = wordSimilarity(a.v, b.v);
9886
9956
  return tv("bool", score >= WORD_SIMILARITY_THRESHOLD);
9887
9957
  }
9888
- function assertGinTrgmIndex(state, table, stmt) {
9958
+ function assertGinTrgmIndex(state, table, stmt, expressionType) {
9889
9959
  const trgmCols = stmt.columns.filter((col2) => isGinTrgmOpclass(col2.opclass));
9890
9960
  if (trgmCols.length === 0) return;
9891
9961
  if (trgmCols.length !== stmt.columns.length || stmt.columns.length !== 1) {
@@ -9905,11 +9975,7 @@ function assertGinTrgmIndex(state, table, stmt) {
9905
9975
  if (stmt.unique) {
9906
9976
  throw pgError("feature_not_supported", `access method "gin" does not support unique indexes`, "0A000");
9907
9977
  }
9908
- if (col.expr.type !== "colref" || col.expr.parts.length !== 1) {
9909
- throw unsupported("expression gin_trgm_ops");
9910
- }
9911
- const index = table.columnIndex(col.expr.parts[0]);
9912
- const typeId = table.columns[index].type.id;
9978
+ const typeId = col.expr.type === "colref" && col.expr.parts.length === 1 ? table.columns[table.columnIndex(col.expr.parts[0])].type.id : expressionType(col.expr);
9913
9979
  if (!GIN_TRGM_TYPES.has(typeId)) {
9914
9980
  throw pgError(
9915
9981
  "datatype_mismatch",
@@ -13311,7 +13377,7 @@ var RowScope = class {
13311
13377
  let count2 = 0;
13312
13378
  for (let i = 0; i < this.columns.length; i++) {
13313
13379
  const c = this.columns[i];
13314
- if (c.name === name2 && !c.hidden) {
13380
+ if (c.name === name2 && (!c.hidden || c.system)) {
13315
13381
  count2++;
13316
13382
  if (count2 > 1) {
13317
13383
  throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
@@ -13349,7 +13415,7 @@ var RowScope = class {
13349
13415
  const names = [];
13350
13416
  for (let i = 0; i < this.columns.length; i++) {
13351
13417
  const c = this.columns[i];
13352
- if (c.table === rangeVar) {
13418
+ if (c.table === rangeVar && !c.system) {
13353
13419
  types.push(c.type);
13354
13420
  values.push(this.row[i] ?? null);
13355
13421
  names.push(c.name);
@@ -13547,6 +13613,17 @@ function tableScope(table, row) {
13547
13613
  const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
13548
13614
  return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
13549
13615
  }
13616
+ function expressionTypeOf(env, table, expr) {
13617
+ const scope = makeEvalScope(
13618
+ env,
13619
+ tableScope(
13620
+ table,
13621
+ table.columns.map(() => null)
13622
+ )
13623
+ );
13624
+ const v = evalExpr(env.ctx, scope, expr);
13625
+ return v.t === "unknown" ? "text" : v.t;
13626
+ }
13550
13627
  function checkNotNull(env, table, row) {
13551
13628
  void env;
13552
13629
  for (let i = 0; i < table.columns.length; i++) {
@@ -13792,7 +13869,7 @@ function tryIndexedFromItem(env, item, where) {
13792
13869
  if (!table) return null;
13793
13870
  const alias = item.alias ?? item.name[item.name.length - 1];
13794
13871
  table.materializeSlab();
13795
- return tryIndexedTableRows(env, table, alias, where);
13872
+ return tryIndexedTableRows(env, table, alias, where)?.map((row) => [...row, table.rowXmins.get(row) ?? 2]) ?? null;
13796
13873
  }
13797
13874
  function joinKeyFromRow(row, colIdxs, srcTypes, unifiedTypes, ctx) {
13798
13875
  const parts = [];
@@ -16282,6 +16359,7 @@ var Parser = class {
16282
16359
  let sqlBody = null;
16283
16360
  let volatility = null;
16284
16361
  let strict2 = false;
16362
+ const settings = {};
16285
16363
  for (; ; ) {
16286
16364
  if (this.eatKw("language")) {
16287
16365
  language = this.peek().type === "string" ? this.next().value : this.ident(true);
@@ -16339,8 +16417,23 @@ var Parser = class {
16339
16417
  continue;
16340
16418
  }
16341
16419
  if (this.eatKw("set")) {
16342
- this.ident(true);
16343
- if (this.eatOp("=") || this.eatKw("to")) this.next();
16420
+ const key = this.ident(true).toLowerCase();
16421
+ if (this.eatKw("from")) {
16422
+ this.expectKw("current");
16423
+ settings[key] = "__from_current__";
16424
+ } else {
16425
+ if (!this.eatOp("=")) this.expectKw("to");
16426
+ const values = [];
16427
+ do {
16428
+ const value = this.next();
16429
+ if (!["string", "ident", "quoted_ident", "number"].includes(value.type))
16430
+ this.errorAt(value, "expected setting value");
16431
+ values.push(
16432
+ key === "search_path" && /[^a-z0-9_]/.test(value.value) ? `"${value.value.replaceAll('"', '""')}"` : value.value
16433
+ );
16434
+ } while (this.eatPunct(","));
16435
+ settings[key] = values.join(", ");
16436
+ }
16344
16437
  continue;
16345
16438
  }
16346
16439
  if (this.eatKw("window")) throw unsupported("window functions via CREATE FUNCTION");
@@ -16420,7 +16513,8 @@ var Parser = class {
16420
16513
  body,
16421
16514
  sqlBody,
16422
16515
  volatility,
16423
- strict: strict2
16516
+ strict: strict2,
16517
+ settings
16424
16518
  };
16425
16519
  }
16426
16520
  parseCreateTrigger(orReplace, constraint) {
@@ -16452,7 +16546,6 @@ var Parser = class {
16452
16546
  } while (this.eatKw("or"));
16453
16547
  this.expectKw("on");
16454
16548
  const table = this.qualifiedName();
16455
- if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
16456
16549
  let deferrable = false;
16457
16550
  let initiallyDeferred = false;
16458
16551
  if (this.eatKw("not")) this.expectKw("deferrable");
@@ -16465,6 +16558,23 @@ var Parser = class {
16465
16558
  this.errorAt(this.peek(), "only constraint triggers can be deferred");
16466
16559
  if (initiallyDeferred && !deferrable)
16467
16560
  this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
16561
+ const transitions = [];
16562
+ if (!constraint && this.eatKw("referencing")) {
16563
+ do {
16564
+ const isNew = this.eatKw("new");
16565
+ if (!isNew) this.expectKw("old");
16566
+ if (this.eatKw("row")) {
16567
+ throw pgError(
16568
+ "feature_not_supported",
16569
+ "ROW variable naming in the REFERENCING clause is not supported",
16570
+ "0A000"
16571
+ );
16572
+ }
16573
+ this.expectKw("table");
16574
+ this.eatKw("as");
16575
+ transitions.push({ isNew, name: this.ident() });
16576
+ } while (this.atKw("new") || this.atKw("old"));
16577
+ }
16468
16578
  let forEachRow = false;
16469
16579
  if (this.eatKw("for")) {
16470
16580
  this.eatKw("each");
@@ -16502,7 +16612,8 @@ var Parser = class {
16502
16612
  forEachRow,
16503
16613
  when,
16504
16614
  funcName,
16505
- funcArgs
16615
+ funcArgs,
16616
+ ...transitions.length > 0 ? { transitions } : {}
16506
16617
  };
16507
16618
  }
16508
16619
  // --- ALTER --------------------------------------------------------------------
@@ -16529,8 +16640,8 @@ var Parser = class {
16529
16640
  type: "alter_sequence",
16530
16641
  name,
16531
16642
  ifExists,
16532
- options: { ownedBy: void 0 },
16533
- ...{ renameTo: to }
16643
+ options: {},
16644
+ renameTo: to
16534
16645
  };
16535
16646
  }
16536
16647
  if (this.atKw("set") && (this.atKw("logged", 1) || this.atKw("unlogged", 1))) {
@@ -19106,7 +19217,20 @@ function runFunctionBody(env, fn, args) {
19106
19217
  }
19107
19218
  return last;
19108
19219
  }
19220
+ function withFunctionSettings(env, fn, run) {
19221
+ const state = env.ctx.state;
19222
+ const saved = new Map(state.localSettings);
19223
+ for (const [name, value] of Object.entries(fn.settings ?? {})) state.localSettings.set(name, value);
19224
+ try {
19225
+ return run();
19226
+ } finally {
19227
+ state.localSettings = saved;
19228
+ }
19229
+ }
19109
19230
  function callSqlFunctionScalar(env, fn, args) {
19231
+ return withFunctionSettings(env, fn, () => callSqlFunctionScalarBody(env, fn, args));
19232
+ }
19233
+ function callSqlFunctionScalarBody(env, fn, args) {
19110
19234
  const retT = fn.returns ?? "text";
19111
19235
  if (fn.language === "plpgsql") {
19112
19236
  return callPlpgsqlScalar(env, fn, args);
@@ -19136,6 +19260,9 @@ function callSqlFunctionScalar(env, fn, args) {
19136
19260
  return castTo(env.ctx, tv(rawT, raw), retT, {});
19137
19261
  }
19138
19262
  function callSqlFunctionSet(env, fn, args) {
19263
+ return withFunctionSettings(env, fn, () => callSqlFunctionSetBody(env, fn, args));
19264
+ }
19265
+ function callSqlFunctionSetBody(env, fn, args) {
19139
19266
  if (fn.language === "plpgsql") {
19140
19267
  return callPlpgsqlSet(env, fn, args);
19141
19268
  }
@@ -19290,15 +19417,17 @@ function executeRecursiveCte(env, cte, query) {
19290
19417
  return rel3;
19291
19418
  }
19292
19419
  function renameWithAliases(rel3, label, colAliases, what) {
19293
- if (colAliases && colAliases.length > rel3.columns.length) {
19420
+ const visibleWidth = rel3.columns.filter((column) => !column.system).length;
19421
+ if (colAliases && colAliases.length > visibleWidth) {
19294
19422
  throw pgError(
19295
19423
  "invalid_column_reference",
19296
- `table "${what}" has ${rel3.columns.length} columns available but ${colAliases.length} columns specified`,
19424
+ `table "${what}" has ${visibleWidth} columns available but ${colAliases.length} columns specified`,
19297
19425
  "42P10"
19298
19426
  );
19299
19427
  }
19300
19428
  const columns = rel3.columns.map((c, i) => ({
19301
- name: colAliases?.[i] ?? c.name,
19429
+ ...c,
19430
+ name: c.system ? c.name : colAliases?.[i] ?? c.name,
19302
19431
  type: c.type === UNKNOWN ? "text" : c.type,
19303
19432
  table: label
19304
19433
  }));
@@ -19319,8 +19448,11 @@ function materializeItem(env, item, scope) {
19319
19448
  if (table) {
19320
19449
  table.materializeSlab();
19321
19450
  const rel3 = {
19322
- columns: table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
19323
- rows: table.rows
19451
+ columns: [
19452
+ ...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
19453
+ { name: "xmin", type: "xid", table: label, hidden: true, system: true }
19454
+ ],
19455
+ rows: table.rows.map((row) => [...row, table.rowXmins.get(row) ?? 2])
19324
19456
  };
19325
19457
  return { rel: renameWithAliases(rel3, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
19326
19458
  }
@@ -19950,7 +20082,7 @@ function resolveColIdx(columns, parts) {
19950
20082
  let found = -1;
19951
20083
  for (let i = 0; i < columns.length; i++) {
19952
20084
  const c = columns[i];
19953
- if (!c.hidden && c.name === name2) {
20085
+ if ((!c.hidden || c.system) && c.name === name2) {
19954
20086
  if (found !== -1) return null;
19955
20087
  found = i;
19956
20088
  }
@@ -20534,7 +20666,7 @@ function expandTargets(core, columns) {
20534
20666
  let any = false;
20535
20667
  for (let i = 0; i < columns.length; i++) {
20536
20668
  const c = columns[i];
20537
- if (c.table === label) {
20669
+ if (c.table === label && !c.system) {
20538
20670
  any = true;
20539
20671
  items.push({ name: c.name, kind: "col", colIdx: i, expr: null, hidden: false });
20540
20672
  }
@@ -21027,6 +21159,29 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
21027
21159
  }
21028
21160
  return { row };
21029
21161
  }
21162
+ function transitionRelation(table, name, rows) {
21163
+ return {
21164
+ columns: table.columns.map((c) => ({ name: c.name, type: c.type.id, table: name })),
21165
+ rows
21166
+ };
21167
+ }
21168
+ function fireStatementTriggers(env, table, timing, event, transitions) {
21169
+ const fired = table.triggers.filter((t) => !t.forEachRow && t.timing === timing && matches(t, event)).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
21170
+ for (const t of fired) {
21171
+ if (!executor) throw unsupported(`trigger "${t.name}" execution`);
21172
+ const ctes = /* @__PURE__ */ new Map();
21173
+ if (t.oldTable !== void 0)
21174
+ ctes.set(t.oldTable, transitionRelation(table, t.oldTable, transitions?.oldRows ?? []));
21175
+ if (t.newTable !== void 0)
21176
+ ctes.set(t.newTable, transitionRelation(table, t.newTable, transitions?.newRows ?? []));
21177
+ const changes = env.ctx.state.changes;
21178
+ try {
21179
+ executor({ ctx: env.ctx, params: null, ctes, outer: null }, table, t, event, null, null);
21180
+ } finally {
21181
+ env.ctx.state.changes = changes;
21182
+ }
21183
+ }
21184
+ }
21030
21185
  var keyOf = (schema, name) => JSON.stringify([schema, name]);
21031
21186
  function flushConstraintTriggers(state, all = false) {
21032
21187
  const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
@@ -21139,6 +21294,7 @@ var TransactionManager = class {
21139
21294
  this.base = null;
21140
21295
  this.savepoints = [];
21141
21296
  this.state.inTransaction = false;
21297
+ this.state.transactionXid = null;
21142
21298
  this.state.localSettings.clear();
21143
21299
  this.state.pendingTriggers = [];
21144
21300
  this.state.constraintModes.clear();
@@ -21151,6 +21307,7 @@ var TransactionManager = class {
21151
21307
  this.base = null;
21152
21308
  this.savepoints = [];
21153
21309
  this.state.inTransaction = false;
21310
+ this.state.transactionXid = null;
21154
21311
  this.state.localSettings.clear();
21155
21312
  this.state.pendingTriggers = [];
21156
21313
  this.state.constraintModes.clear();
@@ -21185,6 +21342,7 @@ var TransactionManager = class {
21185
21342
  this.base = null;
21186
21343
  this.savepoints = [];
21187
21344
  this.state.inTransaction = false;
21345
+ this.state.transactionXid = null;
21188
21346
  this.freezeDepth = 0;
21189
21347
  }
21190
21348
  };
@@ -21388,6 +21546,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
21388
21546
  const updated = existing.slice();
21389
21547
  applyUpdateSets(env, table, sets, scope, updated);
21390
21548
  computeGeneratedColumns(env, table, updated);
21549
+ table.rowXmins.set(updated, env.ctx.rowVersionXid());
21391
21550
  rows[existingIdx] = updated;
21392
21551
  indexUpdateRow(env, table, existingIdx, existing, updated);
21393
21552
  checkNotNull(env, table, updated);
@@ -21406,7 +21565,13 @@ function executeInsert(env0, stmt) {
21406
21565
  const sourceRows = insertSourceRows(env, stmt, colIdxs);
21407
21566
  const insertedRows = [];
21408
21567
  let insertedCount = 0;
21568
+ const addedRows = [];
21569
+ const conflictOldRows = [];
21570
+ const conflictNewRows = [];
21571
+ const updatesOnConflict = stmt.onConflict !== null && stmt.onConflict.action !== "nothing";
21409
21572
  return withStatementRollback(env, () => {
21573
+ fireStatementTriggers(env, table, "before", "insert");
21574
+ if (updatesOnConflict) fireStatementTriggers(env, table, "before", "update");
21410
21575
  for (const srcRow of sourceRows) {
21411
21576
  const row = table.columns.map(() => null);
21412
21577
  const provided = /* @__PURE__ */ new Set();
@@ -21452,6 +21617,7 @@ function executeInsert(env0, stmt) {
21452
21617
  }
21453
21618
  if (conflictIdx !== null && conflictWhereOk) {
21454
21619
  if (stmt.onConflict.action === "nothing") continue;
21620
+ const replaced = rows[conflictIdx];
21455
21621
  const did = applyOnConflictUpdate(
21456
21622
  env,
21457
21623
  table,
@@ -21464,6 +21630,8 @@ function executeInsert(env0, stmt) {
21464
21630
  if (did) {
21465
21631
  insertedCount++;
21466
21632
  insertedRows.push(rows[conflictIdx]);
21633
+ conflictOldRows.push(replaced);
21634
+ conflictNewRows.push(rows[conflictIdx]);
21467
21635
  fireRowTriggers(env, table, "after", "update", null, rows[conflictIdx]);
21468
21636
  }
21469
21637
  continue;
@@ -21473,12 +21641,18 @@ function executeInsert(env0, stmt) {
21473
21641
  checkChecks(env, table, newRow);
21474
21642
  checkUnique(env, table, newRow, rows.length);
21475
21643
  checkForeignKeys(env, table, newRow);
21644
+ table.rowXmins.set(newRow, env.ctx.rowVersionXid());
21476
21645
  rows.push(newRow);
21477
21646
  indexInsertRow(env, table, rows.length - 1, newRow);
21478
21647
  insertedCount++;
21479
21648
  insertedRows.push(newRow);
21649
+ addedRows.push(newRow);
21480
21650
  fireRowTriggers(env, table, "after", "insert", null, newRow);
21481
21651
  }
21652
+ fireStatementTriggers(env, table, "after", "insert", { oldRows: [], newRows: addedRows });
21653
+ if (updatesOnConflict) {
21654
+ fireStatementTriggers(env, table, "after", "update", { oldRows: conflictOldRows, newRows: conflictNewRows });
21655
+ }
21482
21656
  env.ctx.state.changes = insertedCount;
21483
21657
  if (stmt.returning) {
21484
21658
  const res = evalReturning(env, table, label, stmt.returning, insertedRows, "INSERT");
@@ -21559,10 +21733,12 @@ function executeUpdate(env0, stmt) {
21559
21733
  const env = applyWith(env0, stmt.with);
21560
21734
  const table = requireTargetTable(env, stmt.table, "update");
21561
21735
  const label = stmt.alias ?? table.name;
21736
+ fireStatementTriggers(env, table, "before", "update");
21562
21737
  const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
21563
21738
  const rows = table.mutableRows();
21564
21739
  const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
21565
21740
  const updatedRows = [];
21741
+ const replacedRows = [];
21566
21742
  let updateCount = 0;
21567
21743
  for (let ri = 0; ri < rows.length; ri++) {
21568
21744
  const oldRow = rows[ri];
@@ -21590,6 +21766,7 @@ function executeUpdate(env0, stmt) {
21590
21766
  if (fired.row === null) continue;
21591
21767
  const finalRow = fired.row;
21592
21768
  computeGeneratedColumns(env, table, finalRow);
21769
+ table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
21593
21770
  rows[ri] = finalRow;
21594
21771
  try {
21595
21772
  checkNotNull(env, table, finalRow);
@@ -21604,8 +21781,10 @@ function executeUpdate(env0, stmt) {
21604
21781
  handleReferencedUpdate(env, table, [oldRow], [finalRow]);
21605
21782
  updateCount++;
21606
21783
  updatedRows.push(finalRow);
21784
+ replacedRows.push(oldRow);
21607
21785
  fireRowTriggers(env, table, "after", "update", oldRow, finalRow);
21608
21786
  }
21787
+ fireStatementTriggers(env, table, "after", "update", { oldRows: replacedRows, newRows: updatedRows });
21609
21788
  env.ctx.state.changes = updateCount;
21610
21789
  if (stmt.returning) {
21611
21790
  const res = evalReturning(env, table, label, stmt.returning, updatedRows, "UPDATE");
@@ -21617,6 +21796,7 @@ function executeDelete(env0, stmt) {
21617
21796
  const env = applyWith(env0, stmt.with);
21618
21797
  const table = requireTargetTable(env, stmt.table, "delete from");
21619
21798
  const label = stmt.alias ?? table.name;
21799
+ fireStatementTriggers(env, table, "before", "delete");
21620
21800
  const rows = table.mutableRows();
21621
21801
  const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
21622
21802
  const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
@@ -21654,6 +21834,7 @@ function executeDelete(env0, stmt) {
21654
21834
  fireRowTriggers(env, table, "after", "delete", row, null);
21655
21835
  }
21656
21836
  if (deletedRows.length > 0) rebuildTableIndexes(env, table);
21837
+ fireStatementTriggers(env, table, "after", "delete", { oldRows: deletedRows, newRows: [] });
21657
21838
  env.ctx.state.changes = deletedRows.length;
21658
21839
  if (stmt.returning) {
21659
21840
  const res = evalReturning(env, table, label, stmt.returning, deletedRows, "DELETE");
@@ -21709,6 +21890,7 @@ function handleReferencedDelete(env, table, row, depth) {
21709
21890
  for (const c of con.columns) {
21710
21891
  newRow[child.columnIndex(c)] = null;
21711
21892
  }
21893
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21712
21894
  childRows[i] = newRow;
21713
21895
  checkNotNull(env, child, newRow);
21714
21896
  checkChecks(env, child, newRow);
@@ -21722,6 +21904,7 @@ function handleReferencedDelete(env, table, row, depth) {
21722
21904
  const ci = child.columnIndex(c);
21723
21905
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
21724
21906
  }
21907
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21725
21908
  childRows[i] = newRow;
21726
21909
  checkNotNull(env, child, newRow);
21727
21910
  checkChecks(env, child, newRow);
@@ -21759,6 +21942,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21759
21942
  const refIdx = table.columnIndex(con.refColumns[c]);
21760
21943
  newRow[localIdx] = newRows[r][refIdx] ?? null;
21761
21944
  }
21945
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21762
21946
  childRows[i] = newRow;
21763
21947
  checkChecks(env, child, newRow);
21764
21948
  }
@@ -21768,6 +21952,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21768
21952
  for (const i of idxs) {
21769
21953
  const newRow = childRows[i].slice();
21770
21954
  for (const c of con.columns) newRow[child.columnIndex(c)] = null;
21955
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21771
21956
  childRows[i] = newRow;
21772
21957
  checkNotNull(env, child, newRow);
21773
21958
  }
@@ -21780,6 +21965,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21780
21965
  const ci = child.columnIndex(c);
21781
21966
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
21782
21967
  }
21968
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21783
21969
  childRows[i] = newRow;
21784
21970
  checkForeignKeys(env, child, newRow);
21785
21971
  }
@@ -23791,7 +23977,7 @@ function pgTrigger(ctx) {
23791
23977
  let oid = 4e4;
23792
23978
  for (const t of allTables(ctx.state)) {
23793
23979
  for (const tr of t.triggers) {
23794
- rows.push([oid++, tr.name, t.oid, !tr.forEachRow]);
23980
+ rows.push([oid++, tr.name, t.oid, !tr.forEachRow, tr.oldTable ?? null, tr.newTable ?? null]);
23795
23981
  }
23796
23982
  }
23797
23983
  return rel2(
@@ -23799,7 +23985,9 @@ function pgTrigger(ctx) {
23799
23985
  ["oid", "oid"],
23800
23986
  ["tgname", "name"],
23801
23987
  ["tgrelid", "oid"],
23802
- ["tgisinternal", "bool"]
23988
+ ["tgisinternal", "bool"],
23989
+ ["tgoldtable", "name"],
23990
+ ["tgnewtable", "name"]
23803
23991
  ],
23804
23992
  rows,
23805
23993
  "pg_trigger"
@@ -24824,13 +25012,63 @@ function executeCreateSequence(env, stmt) {
24824
25012
  schema.sequences.set(name, seq);
24825
25013
  return commandResult("CREATE SEQUENCE", 0);
24826
25014
  }
25015
+ function renameSequenceReferences(env, seq, newName) {
25016
+ const state = env.ctx.state;
25017
+ const matches2 = (value) => {
25018
+ try {
25019
+ const input = parseRelationInput(value);
25020
+ if ("oid" in input) return input.oid === seq.oid;
25021
+ return state.findSequence(input.parts)?.oid === seq.oid;
25022
+ } catch {
25023
+ return false;
25024
+ }
25025
+ };
25026
+ const rewrite = (value) => {
25027
+ if (Array.isArray(value)) return value.map(rewrite);
25028
+ if (value === null || typeof value !== "object") return value;
25029
+ const node = value;
25030
+ const updated = Object.fromEntries(Object.entries(node).map(([key, child]) => [key, rewrite(child)]));
25031
+ const rewriteLiteral = (literal2) => {
25032
+ if (literal2 && typeof literal2 === "object" && "type" in literal2 && literal2.type === "string_lit" && "value" in literal2 && typeof literal2.value === "string" && matches2(literal2.value)) {
25033
+ return { ...literal2, value: [seq.schema, newName].map(quoteIdent).join(".") };
25034
+ }
25035
+ return literal2;
25036
+ };
25037
+ if (node.type === "func" && Array.isArray(node.name) && ["nextval", "currval", "setval"].includes(String(node.name.at(-1))) && Array.isArray(updated.args))
25038
+ updated.args[0] = rewriteLiteral(updated.args[0]);
25039
+ if (node.type === "cast" && node.target && typeof node.target === "object" && "parts" in node.target && Array.isArray(node.target.parts) && node.target.parts.at(-1) === "regclass")
25040
+ updated.expr = rewriteLiteral(updated.expr);
25041
+ return updated;
25042
+ };
25043
+ for (const schema of state.schemas.values()) {
25044
+ for (const table of [...schema.tables.values()]) {
25045
+ const writable = state.ensureWritableTable(table);
25046
+ for (const column of writable.columns) {
25047
+ if (column.defaultExpr) column.defaultExpr = rewrite(column.defaultExpr);
25048
+ if (column.identity?.sequence === `${seq.schema}.${seq.name}`)
25049
+ column.identity = { ...column.identity, sequence: `${seq.schema}.${newName}` };
25050
+ }
25051
+ }
25052
+ }
25053
+ }
24827
25054
  function executeAlterSequence(env, stmt) {
24828
25055
  const state = env.ctx.state;
24829
- const seq = state.findSequence(stmt.name);
24830
- if (!seq) {
25056
+ const foundSequence = state.findSequence(stmt.name);
25057
+ if (!foundSequence) {
24831
25058
  if (stmt.ifExists) return commandResult("ALTER SEQUENCE", 0);
24832
25059
  throw pgError("undefined_table", `relation "${stmt.name.join(".")}" does not exist`, "42P01");
24833
25060
  }
25061
+ const seq = state.ensureWritableSequence(foundSequence);
25062
+ if (stmt.renameTo) {
25063
+ const schema = state.getSchema(seq.schema);
25064
+ if (schema.hasRelation(stmt.renameTo))
25065
+ throw pgError("duplicate_table", `relation "${stmt.renameTo}" already exists`, "42P07");
25066
+ renameSequenceReferences(env, seq, stmt.renameTo);
25067
+ schema.sequences.delete(seq.name);
25068
+ seq.name = stmt.renameTo;
25069
+ schema.sequences.set(seq.name, seq);
25070
+ return commandResult("ALTER SEQUENCE", 0);
25071
+ }
24834
25072
  const o = stmt.options;
24835
25073
  if (o.logged !== void 0) {
24836
25074
  setSequenceLogged(state, seq, o.logged);
@@ -24888,7 +25126,7 @@ function buildColumn(env, schema, tableName2, def, out, taken) {
24888
25126
  col.defaultExpr = {
24889
25127
  type: "func",
24890
25128
  name: ["nextval"],
24891
- args: [{ type: "string_lit", value: `${schema.name}.${seqName}` }]
25129
+ args: [{ type: "string_lit", value: [schema.name, seqName].map(quoteIdent).join(".") }]
24892
25130
  };
24893
25131
  }
24894
25132
  for (const c of def.constraints) {
@@ -25141,7 +25379,11 @@ function executeCreateTableAs(env, stmt) {
25141
25379
  const table = new TableData(schema.name, name, columns, state.nextOid(), stmt.temp);
25142
25380
  table.unlogged = stmt.unlogged === true;
25143
25381
  if (stmt.withData) {
25144
- for (const row of rel3.rows) table.mutableRows().push(row.slice());
25382
+ for (const row of rel3.rows) {
25383
+ const stored = row.slice();
25384
+ table.rowXmins.set(stored, env.ctx.rowVersionXid());
25385
+ table.mutableRows().push(stored);
25386
+ }
25145
25387
  }
25146
25388
  schema.tables.set(name, table);
25147
25389
  const count = stmt.withData ? table.rowCount() : 0;
@@ -25169,7 +25411,7 @@ function executeCreateIndex(env, stmt) {
25169
25411
  }
25170
25412
  }
25171
25413
  }
25172
- assertGinTrgmIndex(state, table, stmt);
25414
+ assertGinTrgmIndex(state, table, stmt, (expr) => expressionTypeOf(env, table, expr));
25173
25415
  const method = stmt.using ?? "btree";
25174
25416
  if (!ACCESS_METHODS.has(method) || method === "heap") {
25175
25417
  throw pgError("undefined_object", `access method "${method}" does not exist`, "42704");
@@ -25410,6 +25652,12 @@ function executeCreateFunction(env, stmt) {
25410
25652
  language: lang,
25411
25653
  body,
25412
25654
  rawBody: stmt.body,
25655
+ settings: Object.fromEntries(
25656
+ Object.entries(stmt.settings ?? {}).map(([key, value]) => [
25657
+ key,
25658
+ value === "__from_current__" ? state.getSetting(key) ?? "" : value
25659
+ ])
25660
+ ),
25413
25661
  strict: stmt.strict,
25414
25662
  oid: state.nextOid()
25415
25663
  };
@@ -25418,6 +25666,50 @@ function executeCreateFunction(env, stmt) {
25418
25666
  schema.functions.set(name, existing);
25419
25667
  return commandResult("CREATE FUNCTION", 0);
25420
25668
  }
25669
+ function validateTransitionTables(stmt) {
25670
+ const transitions = stmt.transitions ?? [];
25671
+ if (transitions.length === 0) return {};
25672
+ const invalid = (message) => pgError("invalid_table_definition", message, "42P17");
25673
+ const unsupportedClause = (message) => pgError("feature_not_supported", message, "0A000");
25674
+ if (stmt.timing !== "after") throw invalid("transition table name can only be specified for an AFTER trigger");
25675
+ if (stmt.events.some((e) => e.event === "truncate")) {
25676
+ throw unsupportedClause("TRUNCATE triggers with transition tables are not supported");
25677
+ }
25678
+ if (stmt.events.some((e) => e.columns !== null)) {
25679
+ throw unsupportedClause("transition tables cannot be specified for triggers with column lists");
25680
+ }
25681
+ if (stmt.events.length > 1) {
25682
+ throw unsupportedClause("transition tables cannot be specified for triggers with more than one event");
25683
+ }
25684
+ const event = stmt.events[0].event;
25685
+ let oldTable;
25686
+ let newTable;
25687
+ for (const transition of transitions) {
25688
+ if (transition.isNew) {
25689
+ if (event !== "insert" && event !== "update") {
25690
+ throw invalid("NEW TABLE can only be specified for an INSERT or UPDATE trigger");
25691
+ }
25692
+ if (newTable !== void 0) throw invalid("NEW TABLE cannot be specified multiple times");
25693
+ newTable = transition.name;
25694
+ } else {
25695
+ if (event !== "delete" && event !== "update") {
25696
+ throw invalid("OLD TABLE can only be specified for a DELETE or UPDATE trigger");
25697
+ }
25698
+ if (oldTable !== void 0) throw invalid("OLD TABLE cannot be specified multiple times");
25699
+ oldTable = transition.name;
25700
+ }
25701
+ }
25702
+ if (oldTable !== void 0 && oldTable === newTable) {
25703
+ throw invalid("OLD TABLE name and NEW TABLE name cannot be the same");
25704
+ }
25705
+ if (stmt.forEachRow) {
25706
+ throw pgError("unsupported", "transition tables on FOR EACH ROW triggers are not supported", "0A000");
25707
+ }
25708
+ return {
25709
+ ...oldTable !== void 0 ? { oldTable } : {},
25710
+ ...newTable !== void 0 ? { newTable } : {}
25711
+ };
25712
+ }
25421
25713
  function executeCreateTrigger(env, stmt) {
25422
25714
  if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
25423
25715
  throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
@@ -25427,6 +25719,7 @@ function executeCreateTrigger(env, stmt) {
25427
25719
  throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
25428
25720
  }
25429
25721
  const table = state.ensureWritableTable(found);
25722
+ const transitionTables = validateTransitionTables(stmt);
25430
25723
  const fnSchema = stmt.funcName.length >= 2 ? stmt.funcName[stmt.funcName.length - 2] : null;
25431
25724
  const fnName = stmt.funcName[stmt.funcName.length - 1];
25432
25725
  const fns = state.findFunctions(stmt.funcName);
@@ -25445,6 +25738,7 @@ function executeCreateTrigger(env, stmt) {
25445
25738
  timing: stmt.timing,
25446
25739
  events: stmt.events,
25447
25740
  forEachRow: stmt.forEachRow,
25741
+ ...transitionTables,
25448
25742
  when: stmt.when,
25449
25743
  funcSchema: fnSchema ?? fns[0].schema,
25450
25744
  funcName: fnName,
@@ -25594,6 +25888,7 @@ function executeAlterTable(env, stmt) {
25594
25888
  }
25595
25889
  const next = row.slice();
25596
25890
  next[idx] = nv;
25891
+ table.rowXmins.set(next, table.rowXmins.get(rows[i]) ?? 2);
25597
25892
  rows[i] = next;
25598
25893
  }
25599
25894
  col.type = resolved.column;
@@ -25695,6 +25990,11 @@ function executeAlterTable(env, stmt) {
25695
25990
  );
25696
25991
  }
25697
25992
  table.columns[idx].name = action.to;
25993
+ for (const seq of [...schema.sequences.values()]) {
25994
+ if (seq.ownedBy?.table === table.name && seq.ownedBy.column === action.from) {
25995
+ state.ensureWritableSequence(seq).ownedBy = { table: table.name, column: action.to };
25996
+ }
25997
+ }
25698
25998
  for (const con of table.constraints) {
25699
25999
  if (con.kind === "primary_key" || con.kind === "unique" || con.kind === "foreign_key") {
25700
26000
  con.columns = con.columns.map((c) => c === action.from ? action.to : c);
@@ -25731,6 +26031,11 @@ function executeAlterTable(env, stmt) {
25731
26031
  throw pgError("duplicate_table", `relation "${action.to}" already exists`, "42P07");
25732
26032
  }
25733
26033
  const oldName = table.name;
26034
+ for (const seq of [...schema.sequences.values()]) {
26035
+ if (seq.ownedBy?.table === oldName) {
26036
+ state.ensureWritableSequence(seq).ownedBy = { ...seq.ownedBy, table: action.to };
26037
+ }
26038
+ }
25734
26039
  schema.tables.delete(table.name);
25735
26040
  table.name = action.to;
25736
26041
  schema.tables.set(action.to, table);
@@ -27320,6 +27625,7 @@ function encodeDatabaseState(state, runtime) {
27320
27625
  writeBjv(
27321
27626
  w,
27322
27627
  {
27628
+ xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
27323
27629
  name: table.name,
27324
27630
  schema: table.schema,
27325
27631
  columns: table.columns,
@@ -27417,8 +27723,14 @@ function decodeInner(snapshot, prng, clock) {
27417
27723
  table.constraints = meta.constraints;
27418
27724
  table.triggers = meta.triggers;
27419
27725
  table.unlogged = meta.unlogged === true;
27726
+ table.slabXmins = meta.xmins ?? null;
27727
+ state.xidCounter = Math.max(
27728
+ state.xidCounter,
27729
+ (meta.xmins ?? []).reduce((max, xid) => Math.max(max, xid + 1), 3)
27730
+ );
27420
27731
  if (meta.stats) state.tableStats.set(table.oid, meta.stats);
27421
27732
  const rowCount = readVarintU32(r);
27733
+ if (meta.xmins && meta.xmins.length !== rowCount) throw snapshotError();
27422
27734
  const slabColumns = [];
27423
27735
  for (let c = 0; c < meta.columns.length; c++)
27424
27736
  slabColumns.push(readPackedColumn(r, rowCount, intern, meta.columns[c].type.id));
@@ -27862,6 +28174,8 @@ function validateTableMetaBjv(value) {
27862
28174
  if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
27863
28175
  if (!Array.isArray(value.columns)) throw snapshotError();
27864
28176
  for (const col of value.columns) validateColumnMeta(col);
28177
+ if (value.xmins !== void 0 && (!Array.isArray(value.xmins) || value.xmins.some((xid) => !Number.isInteger(xid) || xid < 2 || xid > 4294967295)))
28178
+ throw snapshotError();
27865
28179
  }
27866
28180
  function validateViewMetaBjv(value) {
27867
28181
  if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
@@ -27955,6 +28269,7 @@ function forceSchemaIntern(pool, state, schemas) {
27955
28269
  pool.forceId(table.name);
27956
28270
  pool.forceId(table.schema);
27957
28271
  forceBjvStrings(pool, {
28272
+ xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
27958
28273
  name: table.name,
27959
28274
  schema: table.schema,
27960
28275
  columns: table.columns,
@@ -28032,10 +28347,188 @@ function forceBjvStrings(pool, value) {
28032
28347
  }
28033
28348
  }
28034
28349
  registerCodec({ encodeDatabaseState, decodeDatabaseState });
28350
+
28351
+ // src/api/schema-report.ts
28352
+ var bounded = (value, fallback2, max) => {
28353
+ if (value === void 0) return fallback2;
28354
+ if (!Number.isSafeInteger(value) || value < 1 || value > max)
28355
+ throw new RangeError(`limit must be an integer between 1 and ${max}`);
28356
+ return value;
28357
+ };
28358
+ function analyzeSchema(sql, options = {}) {
28359
+ const inputLimit = bounded(options.maxInputBytes, 1024 * 1024, 4 * 1024 * 1024);
28360
+ const statementLimit = bounded(options.maxStatements, 1e3, 1e4);
28361
+ const diagnosticLimit = bounded(options.maxDiagnostics, 100, 1e3);
28362
+ const report = {
28363
+ engine: "mockingbird-postgres",
28364
+ version: true ? "3.8.0" : "source-tree",
28365
+ statements: 0,
28366
+ accepted: 0,
28367
+ compatible: null,
28368
+ truncated: false,
28369
+ diagnostics: []
28370
+ };
28371
+ const add = (diagnostic) => {
28372
+ if (report.diagnostics.length >= diagnosticLimit) {
28373
+ report.truncated = true;
28374
+ return false;
28375
+ }
28376
+ report.diagnostics.push({
28377
+ ...diagnostic,
28378
+ ...diagnostic.object ? { object: diagnostic.object.slice(0, 200) } : {}
28379
+ });
28380
+ if (diagnostic.category !== "unknown") report.compatible = false;
28381
+ return true;
28382
+ };
28383
+ const location = (offset) => {
28384
+ const prefix = sql.slice(0, offset);
28385
+ return { offset, line: prefix.split("\n").length, column: offset - prefix.lastIndexOf("\n") };
28386
+ };
28387
+ if (sql.length > inputLimit || new TextEncoder().encode(sql).length > inputLimit) {
28388
+ add({
28389
+ statement: 0,
28390
+ ...location(0),
28391
+ sqlstate: null,
28392
+ category: "limit",
28393
+ message: `Input exceeds ${inputLimit} bytes`
28394
+ });
28395
+ report.truncated = true;
28396
+ return report;
28397
+ }
28398
+ let tokens;
28399
+ try {
28400
+ tokens = tokenize(sql);
28401
+ } catch (error) {
28402
+ add({
28403
+ statement: 1,
28404
+ ...location(0),
28405
+ sqlstate: isPostgresError(error) ? error.code : null,
28406
+ category: "syntax",
28407
+ message: error instanceof Error ? error.message.slice(0, 500) : "Cannot tokenize input"
28408
+ });
28409
+ return report;
28410
+ }
28411
+ const db = new Database();
28412
+ const failed = /* @__PURE__ */ new Map();
28413
+ let start = 0;
28414
+ let tokenStart = 0;
28415
+ let atomicDepth = 0;
28416
+ const run = (end, tokenEnd) => {
28417
+ const statementTokens = tokens.slice(tokenStart, tokenEnd);
28418
+ tokenStart = tokenEnd + 1;
28419
+ const offset = statementTokens[0]?.pos;
28420
+ const statementSql = sql.slice(start, end);
28421
+ start = end + 1;
28422
+ if (offset === void 0) return;
28423
+ if (report.statements >= statementLimit) {
28424
+ report.truncated = true;
28425
+ add({
28426
+ statement: report.statements + 1,
28427
+ ...location(offset),
28428
+ sqlstate: null,
28429
+ category: "limit",
28430
+ message: `Statement limit ${statementLimit} reached`
28431
+ });
28432
+ return;
28433
+ }
28434
+ report.statements++;
28435
+ let object;
28436
+ const candidate = statementTokens.findIndex(
28437
+ (token) => ["table", "view", "function", "sequence", "schema", "type", "index"].includes(token.value)
28438
+ );
28439
+ if (candidate >= 0) {
28440
+ const name = statementTokens[candidate + 1];
28441
+ if (name?.type === "ident" || name?.type === "quoted_ident") object = name.value;
28442
+ }
28443
+ try {
28444
+ const ast = parse(statementSql);
28445
+ const statement = ast[0];
28446
+ if (statement && "name" in statement && Array.isArray(statement.name)) object = statement.name.join(".");
28447
+ if (statement && !/^(create_|alter_|comment|grant|revoke|set|reset)/.test(statement.type)) {
28448
+ add({
28449
+ statement: report.statements,
28450
+ ...location(offset),
28451
+ object,
28452
+ sqlstate: null,
28453
+ category: "unknown",
28454
+ message: "Only schema statements are analyzed; data, queries and dynamic commands are unknown"
28455
+ });
28456
+ return;
28457
+ }
28458
+ const references = statementTokens.flatMap((token, index) => {
28459
+ if (!["from", "join", "references"].includes(token.value)) return [];
28460
+ const parts = [];
28461
+ for (let at = index + 1; at < statementTokens.length; at += 2) {
28462
+ const part = statementTokens[at];
28463
+ if (!part || part.type !== "ident" && part.type !== "quoted_ident") break;
28464
+ parts.push(part.value);
28465
+ if (statementTokens[at + 1]?.value !== ".") break;
28466
+ }
28467
+ return parts.length ? [parts.join(".")] : [];
28468
+ });
28469
+ const missing = [...failed].find(
28470
+ ([name]) => references.some(
28471
+ (reference) => reference === name || !reference.includes(".") && reference === name.split(".").at(-1) || !name.includes(".") && name === reference.split(".").at(-1)
28472
+ )
28473
+ );
28474
+ if (missing) {
28475
+ add({
28476
+ statement: report.statements,
28477
+ ...location(offset),
28478
+ object,
28479
+ sqlstate: "42P01",
28480
+ category: "prerequisite",
28481
+ message: `Prerequisite ${missing[0]} was not created`,
28482
+ prerequisiteStatement: missing[1]
28483
+ });
28484
+ if (object) failed.set(object, report.statements);
28485
+ return;
28486
+ }
28487
+ db.exec(statementSql);
28488
+ report.accepted++;
28489
+ add({
28490
+ statement: report.statements,
28491
+ ...location(offset),
28492
+ object,
28493
+ sqlstate: null,
28494
+ category: "unknown",
28495
+ message: "Accepted by the ephemeral engine; semantics, function bodies and dynamic SQL require oracle verification"
28496
+ });
28497
+ } catch (error) {
28498
+ const code = isPostgresError(error) ? error.code : null;
28499
+ const message = error instanceof Error ? error.message.slice(0, 500) : "Schema analysis failed";
28500
+ const prerequisite = code?.startsWith("42") ? [...failed].find(([name]) => message.includes(`"${name}"`) || message.includes(`"${name.split(".").at(-1)}"`)) : void 0;
28501
+ add({
28502
+ statement: report.statements,
28503
+ ...location(offset),
28504
+ object,
28505
+ sqlstate: code,
28506
+ category: prerequisite ? "prerequisite" : code === "0A000" ? "unsupported" : code?.startsWith("426") ? "syntax" : "execution",
28507
+ message,
28508
+ ...prerequisite ? { prerequisiteStatement: prerequisite[1] } : {}
28509
+ });
28510
+ if (object) failed.set(object, report.statements);
28511
+ }
28512
+ };
28513
+ try {
28514
+ for (let index = 0; index < tokens.length; index++) {
28515
+ const token = tokens[index];
28516
+ if (token.value === "begin" && tokens[index + 1]?.value === "atomic") atomicDepth++;
28517
+ if (token.value === "end" && atomicDepth > 0) atomicDepth--;
28518
+ if (token.type === "punct" && token.value === ";" && atomicDepth === 0 || token.type === "eof")
28519
+ run(token.pos, index);
28520
+ if (report.truncated) break;
28521
+ }
28522
+ } finally {
28523
+ db.close();
28524
+ }
28525
+ return report;
28526
+ }
28035
28527
  export {
28036
28528
  Database,
28037
28529
  PostgresError,
28038
28530
  Snapshot,
28039
- Statement
28531
+ Statement,
28532
+ analyzeSchema
28040
28533
  };
28041
28534
  //# sourceMappingURL=index.js.map