@crvouga/mockingbird-service-postgres 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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) => {
@@ -13311,7 +13381,7 @@ var RowScope = class {
13311
13381
  let count2 = 0;
13312
13382
  for (let i = 0; i < this.columns.length; i++) {
13313
13383
  const c = this.columns[i];
13314
- if (c.name === name2 && !c.hidden) {
13384
+ if (c.name === name2 && (!c.hidden || c.system)) {
13315
13385
  count2++;
13316
13386
  if (count2 > 1) {
13317
13387
  throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
@@ -13349,7 +13419,7 @@ var RowScope = class {
13349
13419
  const names = [];
13350
13420
  for (let i = 0; i < this.columns.length; i++) {
13351
13421
  const c = this.columns[i];
13352
- if (c.table === rangeVar) {
13422
+ if (c.table === rangeVar && !c.system) {
13353
13423
  types.push(c.type);
13354
13424
  values.push(this.row[i] ?? null);
13355
13425
  names.push(c.name);
@@ -13792,7 +13862,7 @@ function tryIndexedFromItem(env, item, where) {
13792
13862
  if (!table) return null;
13793
13863
  const alias = item.alias ?? item.name[item.name.length - 1];
13794
13864
  table.materializeSlab();
13795
- return tryIndexedTableRows(env, table, alias, where);
13865
+ return tryIndexedTableRows(env, table, alias, where)?.map((row) => [...row, table.rowXmins.get(row) ?? 2]) ?? null;
13796
13866
  }
13797
13867
  function joinKeyFromRow(row, colIdxs, srcTypes, unifiedTypes, ctx) {
13798
13868
  const parts = [];
@@ -16282,6 +16352,7 @@ var Parser = class {
16282
16352
  let sqlBody = null;
16283
16353
  let volatility = null;
16284
16354
  let strict2 = false;
16355
+ const settings = {};
16285
16356
  for (; ; ) {
16286
16357
  if (this.eatKw("language")) {
16287
16358
  language = this.peek().type === "string" ? this.next().value : this.ident(true);
@@ -16339,8 +16410,23 @@ var Parser = class {
16339
16410
  continue;
16340
16411
  }
16341
16412
  if (this.eatKw("set")) {
16342
- this.ident(true);
16343
- if (this.eatOp("=") || this.eatKw("to")) this.next();
16413
+ const key = this.ident(true).toLowerCase();
16414
+ if (this.eatKw("from")) {
16415
+ this.expectKw("current");
16416
+ settings[key] = "__from_current__";
16417
+ } else {
16418
+ if (!this.eatOp("=")) this.expectKw("to");
16419
+ const values = [];
16420
+ do {
16421
+ const value = this.next();
16422
+ if (!["string", "ident", "quoted_ident", "number"].includes(value.type))
16423
+ this.errorAt(value, "expected setting value");
16424
+ values.push(
16425
+ key === "search_path" && /[^a-z0-9_]/.test(value.value) ? `"${value.value.replaceAll('"', '""')}"` : value.value
16426
+ );
16427
+ } while (this.eatPunct(","));
16428
+ settings[key] = values.join(", ");
16429
+ }
16344
16430
  continue;
16345
16431
  }
16346
16432
  if (this.eatKw("window")) throw unsupported("window functions via CREATE FUNCTION");
@@ -16420,7 +16506,8 @@ var Parser = class {
16420
16506
  body,
16421
16507
  sqlBody,
16422
16508
  volatility,
16423
- strict: strict2
16509
+ strict: strict2,
16510
+ settings
16424
16511
  };
16425
16512
  }
16426
16513
  parseCreateTrigger(orReplace, constraint) {
@@ -16529,8 +16616,8 @@ var Parser = class {
16529
16616
  type: "alter_sequence",
16530
16617
  name,
16531
16618
  ifExists,
16532
- options: { ownedBy: void 0 },
16533
- ...{ renameTo: to }
16619
+ options: {},
16620
+ renameTo: to
16534
16621
  };
16535
16622
  }
16536
16623
  if (this.atKw("set") && (this.atKw("logged", 1) || this.atKw("unlogged", 1))) {
@@ -19106,7 +19193,20 @@ function runFunctionBody(env, fn, args) {
19106
19193
  }
19107
19194
  return last;
19108
19195
  }
19196
+ function withFunctionSettings(env, fn, run) {
19197
+ const state = env.ctx.state;
19198
+ const saved = new Map(state.localSettings);
19199
+ for (const [name, value] of Object.entries(fn.settings ?? {})) state.localSettings.set(name, value);
19200
+ try {
19201
+ return run();
19202
+ } finally {
19203
+ state.localSettings = saved;
19204
+ }
19205
+ }
19109
19206
  function callSqlFunctionScalar(env, fn, args) {
19207
+ return withFunctionSettings(env, fn, () => callSqlFunctionScalarBody(env, fn, args));
19208
+ }
19209
+ function callSqlFunctionScalarBody(env, fn, args) {
19110
19210
  const retT = fn.returns ?? "text";
19111
19211
  if (fn.language === "plpgsql") {
19112
19212
  return callPlpgsqlScalar(env, fn, args);
@@ -19136,6 +19236,9 @@ function callSqlFunctionScalar(env, fn, args) {
19136
19236
  return castTo(env.ctx, tv(rawT, raw), retT, {});
19137
19237
  }
19138
19238
  function callSqlFunctionSet(env, fn, args) {
19239
+ return withFunctionSettings(env, fn, () => callSqlFunctionSetBody(env, fn, args));
19240
+ }
19241
+ function callSqlFunctionSetBody(env, fn, args) {
19139
19242
  if (fn.language === "plpgsql") {
19140
19243
  return callPlpgsqlSet(env, fn, args);
19141
19244
  }
@@ -19290,15 +19393,17 @@ function executeRecursiveCte(env, cte, query) {
19290
19393
  return rel3;
19291
19394
  }
19292
19395
  function renameWithAliases(rel3, label, colAliases, what) {
19293
- if (colAliases && colAliases.length > rel3.columns.length) {
19396
+ const visibleWidth = rel3.columns.filter((column) => !column.system).length;
19397
+ if (colAliases && colAliases.length > visibleWidth) {
19294
19398
  throw pgError(
19295
19399
  "invalid_column_reference",
19296
- `table "${what}" has ${rel3.columns.length} columns available but ${colAliases.length} columns specified`,
19400
+ `table "${what}" has ${visibleWidth} columns available but ${colAliases.length} columns specified`,
19297
19401
  "42P10"
19298
19402
  );
19299
19403
  }
19300
19404
  const columns = rel3.columns.map((c, i) => ({
19301
- name: colAliases?.[i] ?? c.name,
19405
+ ...c,
19406
+ name: c.system ? c.name : colAliases?.[i] ?? c.name,
19302
19407
  type: c.type === UNKNOWN ? "text" : c.type,
19303
19408
  table: label
19304
19409
  }));
@@ -19319,8 +19424,11 @@ function materializeItem(env, item, scope) {
19319
19424
  if (table) {
19320
19425
  table.materializeSlab();
19321
19426
  const rel3 = {
19322
- columns: table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
19323
- rows: table.rows
19427
+ columns: [
19428
+ ...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
19429
+ { name: "xmin", type: "xid", table: label, hidden: true, system: true }
19430
+ ],
19431
+ rows: table.rows.map((row) => [...row, table.rowXmins.get(row) ?? 2])
19324
19432
  };
19325
19433
  return { rel: renameWithAliases(rel3, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
19326
19434
  }
@@ -19950,7 +20058,7 @@ function resolveColIdx(columns, parts) {
19950
20058
  let found = -1;
19951
20059
  for (let i = 0; i < columns.length; i++) {
19952
20060
  const c = columns[i];
19953
- if (!c.hidden && c.name === name2) {
20061
+ if ((!c.hidden || c.system) && c.name === name2) {
19954
20062
  if (found !== -1) return null;
19955
20063
  found = i;
19956
20064
  }
@@ -20534,7 +20642,7 @@ function expandTargets(core, columns) {
20534
20642
  let any = false;
20535
20643
  for (let i = 0; i < columns.length; i++) {
20536
20644
  const c = columns[i];
20537
- if (c.table === label) {
20645
+ if (c.table === label && !c.system) {
20538
20646
  any = true;
20539
20647
  items.push({ name: c.name, kind: "col", colIdx: i, expr: null, hidden: false });
20540
20648
  }
@@ -21139,6 +21247,7 @@ var TransactionManager = class {
21139
21247
  this.base = null;
21140
21248
  this.savepoints = [];
21141
21249
  this.state.inTransaction = false;
21250
+ this.state.transactionXid = null;
21142
21251
  this.state.localSettings.clear();
21143
21252
  this.state.pendingTriggers = [];
21144
21253
  this.state.constraintModes.clear();
@@ -21151,6 +21260,7 @@ var TransactionManager = class {
21151
21260
  this.base = null;
21152
21261
  this.savepoints = [];
21153
21262
  this.state.inTransaction = false;
21263
+ this.state.transactionXid = null;
21154
21264
  this.state.localSettings.clear();
21155
21265
  this.state.pendingTriggers = [];
21156
21266
  this.state.constraintModes.clear();
@@ -21185,6 +21295,7 @@ var TransactionManager = class {
21185
21295
  this.base = null;
21186
21296
  this.savepoints = [];
21187
21297
  this.state.inTransaction = false;
21298
+ this.state.transactionXid = null;
21188
21299
  this.freezeDepth = 0;
21189
21300
  }
21190
21301
  };
@@ -21388,6 +21499,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
21388
21499
  const updated = existing.slice();
21389
21500
  applyUpdateSets(env, table, sets, scope, updated);
21390
21501
  computeGeneratedColumns(env, table, updated);
21502
+ table.rowXmins.set(updated, env.ctx.rowVersionXid());
21391
21503
  rows[existingIdx] = updated;
21392
21504
  indexUpdateRow(env, table, existingIdx, existing, updated);
21393
21505
  checkNotNull(env, table, updated);
@@ -21473,6 +21585,7 @@ function executeInsert(env0, stmt) {
21473
21585
  checkChecks(env, table, newRow);
21474
21586
  checkUnique(env, table, newRow, rows.length);
21475
21587
  checkForeignKeys(env, table, newRow);
21588
+ table.rowXmins.set(newRow, env.ctx.rowVersionXid());
21476
21589
  rows.push(newRow);
21477
21590
  indexInsertRow(env, table, rows.length - 1, newRow);
21478
21591
  insertedCount++;
@@ -21590,6 +21703,7 @@ function executeUpdate(env0, stmt) {
21590
21703
  if (fired.row === null) continue;
21591
21704
  const finalRow = fired.row;
21592
21705
  computeGeneratedColumns(env, table, finalRow);
21706
+ table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
21593
21707
  rows[ri] = finalRow;
21594
21708
  try {
21595
21709
  checkNotNull(env, table, finalRow);
@@ -21709,6 +21823,7 @@ function handleReferencedDelete(env, table, row, depth) {
21709
21823
  for (const c of con.columns) {
21710
21824
  newRow[child.columnIndex(c)] = null;
21711
21825
  }
21826
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21712
21827
  childRows[i] = newRow;
21713
21828
  checkNotNull(env, child, newRow);
21714
21829
  checkChecks(env, child, newRow);
@@ -21722,6 +21837,7 @@ function handleReferencedDelete(env, table, row, depth) {
21722
21837
  const ci = child.columnIndex(c);
21723
21838
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
21724
21839
  }
21840
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21725
21841
  childRows[i] = newRow;
21726
21842
  checkNotNull(env, child, newRow);
21727
21843
  checkChecks(env, child, newRow);
@@ -21759,6 +21875,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21759
21875
  const refIdx = table.columnIndex(con.refColumns[c]);
21760
21876
  newRow[localIdx] = newRows[r][refIdx] ?? null;
21761
21877
  }
21878
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21762
21879
  childRows[i] = newRow;
21763
21880
  checkChecks(env, child, newRow);
21764
21881
  }
@@ -21768,6 +21885,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21768
21885
  for (const i of idxs) {
21769
21886
  const newRow = childRows[i].slice();
21770
21887
  for (const c of con.columns) newRow[child.columnIndex(c)] = null;
21888
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21771
21889
  childRows[i] = newRow;
21772
21890
  checkNotNull(env, child, newRow);
21773
21891
  }
@@ -21780,6 +21898,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
21780
21898
  const ci = child.columnIndex(c);
21781
21899
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
21782
21900
  }
21901
+ child.rowXmins.set(newRow, env.ctx.rowVersionXid());
21783
21902
  childRows[i] = newRow;
21784
21903
  checkForeignKeys(env, child, newRow);
21785
21904
  }
@@ -24824,13 +24943,63 @@ function executeCreateSequence(env, stmt) {
24824
24943
  schema.sequences.set(name, seq);
24825
24944
  return commandResult("CREATE SEQUENCE", 0);
24826
24945
  }
24946
+ function renameSequenceReferences(env, seq, newName) {
24947
+ const state = env.ctx.state;
24948
+ const matches2 = (value) => {
24949
+ try {
24950
+ const input = parseRelationInput(value);
24951
+ if ("oid" in input) return input.oid === seq.oid;
24952
+ return state.findSequence(input.parts)?.oid === seq.oid;
24953
+ } catch {
24954
+ return false;
24955
+ }
24956
+ };
24957
+ const rewrite = (value) => {
24958
+ if (Array.isArray(value)) return value.map(rewrite);
24959
+ if (value === null || typeof value !== "object") return value;
24960
+ const node = value;
24961
+ const updated = Object.fromEntries(Object.entries(node).map(([key, child]) => [key, rewrite(child)]));
24962
+ const rewriteLiteral = (literal2) => {
24963
+ if (literal2 && typeof literal2 === "object" && "type" in literal2 && literal2.type === "string_lit" && "value" in literal2 && typeof literal2.value === "string" && matches2(literal2.value)) {
24964
+ return { ...literal2, value: [seq.schema, newName].map(quoteIdent).join(".") };
24965
+ }
24966
+ return literal2;
24967
+ };
24968
+ if (node.type === "func" && Array.isArray(node.name) && ["nextval", "currval", "setval"].includes(String(node.name.at(-1))) && Array.isArray(updated.args))
24969
+ updated.args[0] = rewriteLiteral(updated.args[0]);
24970
+ 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")
24971
+ updated.expr = rewriteLiteral(updated.expr);
24972
+ return updated;
24973
+ };
24974
+ for (const schema of state.schemas.values()) {
24975
+ for (const table of [...schema.tables.values()]) {
24976
+ const writable = state.ensureWritableTable(table);
24977
+ for (const column of writable.columns) {
24978
+ if (column.defaultExpr) column.defaultExpr = rewrite(column.defaultExpr);
24979
+ if (column.identity?.sequence === `${seq.schema}.${seq.name}`)
24980
+ column.identity = { ...column.identity, sequence: `${seq.schema}.${newName}` };
24981
+ }
24982
+ }
24983
+ }
24984
+ }
24827
24985
  function executeAlterSequence(env, stmt) {
24828
24986
  const state = env.ctx.state;
24829
- const seq = state.findSequence(stmt.name);
24830
- if (!seq) {
24987
+ const foundSequence = state.findSequence(stmt.name);
24988
+ if (!foundSequence) {
24831
24989
  if (stmt.ifExists) return commandResult("ALTER SEQUENCE", 0);
24832
24990
  throw pgError("undefined_table", `relation "${stmt.name.join(".")}" does not exist`, "42P01");
24833
24991
  }
24992
+ const seq = state.ensureWritableSequence(foundSequence);
24993
+ if (stmt.renameTo) {
24994
+ const schema = state.getSchema(seq.schema);
24995
+ if (schema.hasRelation(stmt.renameTo))
24996
+ throw pgError("duplicate_table", `relation "${stmt.renameTo}" already exists`, "42P07");
24997
+ renameSequenceReferences(env, seq, stmt.renameTo);
24998
+ schema.sequences.delete(seq.name);
24999
+ seq.name = stmt.renameTo;
25000
+ schema.sequences.set(seq.name, seq);
25001
+ return commandResult("ALTER SEQUENCE", 0);
25002
+ }
24834
25003
  const o = stmt.options;
24835
25004
  if (o.logged !== void 0) {
24836
25005
  setSequenceLogged(state, seq, o.logged);
@@ -24888,7 +25057,7 @@ function buildColumn(env, schema, tableName2, def, out, taken) {
24888
25057
  col.defaultExpr = {
24889
25058
  type: "func",
24890
25059
  name: ["nextval"],
24891
- args: [{ type: "string_lit", value: `${schema.name}.${seqName}` }]
25060
+ args: [{ type: "string_lit", value: [schema.name, seqName].map(quoteIdent).join(".") }]
24892
25061
  };
24893
25062
  }
24894
25063
  for (const c of def.constraints) {
@@ -25141,7 +25310,11 @@ function executeCreateTableAs(env, stmt) {
25141
25310
  const table = new TableData(schema.name, name, columns, state.nextOid(), stmt.temp);
25142
25311
  table.unlogged = stmt.unlogged === true;
25143
25312
  if (stmt.withData) {
25144
- for (const row of rel3.rows) table.mutableRows().push(row.slice());
25313
+ for (const row of rel3.rows) {
25314
+ const stored = row.slice();
25315
+ table.rowXmins.set(stored, env.ctx.rowVersionXid());
25316
+ table.mutableRows().push(stored);
25317
+ }
25145
25318
  }
25146
25319
  schema.tables.set(name, table);
25147
25320
  const count = stmt.withData ? table.rowCount() : 0;
@@ -25410,6 +25583,12 @@ function executeCreateFunction(env, stmt) {
25410
25583
  language: lang,
25411
25584
  body,
25412
25585
  rawBody: stmt.body,
25586
+ settings: Object.fromEntries(
25587
+ Object.entries(stmt.settings ?? {}).map(([key, value]) => [
25588
+ key,
25589
+ value === "__from_current__" ? state.getSetting(key) ?? "" : value
25590
+ ])
25591
+ ),
25413
25592
  strict: stmt.strict,
25414
25593
  oid: state.nextOid()
25415
25594
  };
@@ -25594,6 +25773,7 @@ function executeAlterTable(env, stmt) {
25594
25773
  }
25595
25774
  const next = row.slice();
25596
25775
  next[idx] = nv;
25776
+ table.rowXmins.set(next, table.rowXmins.get(rows[i]) ?? 2);
25597
25777
  rows[i] = next;
25598
25778
  }
25599
25779
  col.type = resolved.column;
@@ -25695,6 +25875,11 @@ function executeAlterTable(env, stmt) {
25695
25875
  );
25696
25876
  }
25697
25877
  table.columns[idx].name = action.to;
25878
+ for (const seq of [...schema.sequences.values()]) {
25879
+ if (seq.ownedBy?.table === table.name && seq.ownedBy.column === action.from) {
25880
+ state.ensureWritableSequence(seq).ownedBy = { table: table.name, column: action.to };
25881
+ }
25882
+ }
25698
25883
  for (const con of table.constraints) {
25699
25884
  if (con.kind === "primary_key" || con.kind === "unique" || con.kind === "foreign_key") {
25700
25885
  con.columns = con.columns.map((c) => c === action.from ? action.to : c);
@@ -25731,6 +25916,11 @@ function executeAlterTable(env, stmt) {
25731
25916
  throw pgError("duplicate_table", `relation "${action.to}" already exists`, "42P07");
25732
25917
  }
25733
25918
  const oldName = table.name;
25919
+ for (const seq of [...schema.sequences.values()]) {
25920
+ if (seq.ownedBy?.table === oldName) {
25921
+ state.ensureWritableSequence(seq).ownedBy = { ...seq.ownedBy, table: action.to };
25922
+ }
25923
+ }
25734
25924
  schema.tables.delete(table.name);
25735
25925
  table.name = action.to;
25736
25926
  schema.tables.set(action.to, table);
@@ -27320,6 +27510,7 @@ function encodeDatabaseState(state, runtime) {
27320
27510
  writeBjv(
27321
27511
  w,
27322
27512
  {
27513
+ xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
27323
27514
  name: table.name,
27324
27515
  schema: table.schema,
27325
27516
  columns: table.columns,
@@ -27417,8 +27608,14 @@ function decodeInner(snapshot, prng, clock) {
27417
27608
  table.constraints = meta.constraints;
27418
27609
  table.triggers = meta.triggers;
27419
27610
  table.unlogged = meta.unlogged === true;
27611
+ table.slabXmins = meta.xmins ?? null;
27612
+ state.xidCounter = Math.max(
27613
+ state.xidCounter,
27614
+ (meta.xmins ?? []).reduce((max, xid) => Math.max(max, xid + 1), 3)
27615
+ );
27420
27616
  if (meta.stats) state.tableStats.set(table.oid, meta.stats);
27421
27617
  const rowCount = readVarintU32(r);
27618
+ if (meta.xmins && meta.xmins.length !== rowCount) throw snapshotError();
27422
27619
  const slabColumns = [];
27423
27620
  for (let c = 0; c < meta.columns.length; c++)
27424
27621
  slabColumns.push(readPackedColumn(r, rowCount, intern, meta.columns[c].type.id));
@@ -27862,6 +28059,8 @@ function validateTableMetaBjv(value) {
27862
28059
  if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
27863
28060
  if (!Array.isArray(value.columns)) throw snapshotError();
27864
28061
  for (const col of value.columns) validateColumnMeta(col);
28062
+ if (value.xmins !== void 0 && (!Array.isArray(value.xmins) || value.xmins.some((xid) => !Number.isInteger(xid) || xid < 2 || xid > 4294967295)))
28063
+ throw snapshotError();
27865
28064
  }
27866
28065
  function validateViewMetaBjv(value) {
27867
28066
  if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
@@ -27955,6 +28154,7 @@ function forceSchemaIntern(pool, state, schemas) {
27955
28154
  pool.forceId(table.name);
27956
28155
  pool.forceId(table.schema);
27957
28156
  forceBjvStrings(pool, {
28157
+ xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
27958
28158
  name: table.name,
27959
28159
  schema: table.schema,
27960
28160
  columns: table.columns,
@@ -28032,10 +28232,188 @@ function forceBjvStrings(pool, value) {
28032
28232
  }
28033
28233
  }
28034
28234
  registerCodec({ encodeDatabaseState, decodeDatabaseState });
28235
+
28236
+ // src/api/schema-report.ts
28237
+ var bounded = (value, fallback2, max) => {
28238
+ if (value === void 0) return fallback2;
28239
+ if (!Number.isSafeInteger(value) || value < 1 || value > max)
28240
+ throw new RangeError(`limit must be an integer between 1 and ${max}`);
28241
+ return value;
28242
+ };
28243
+ function analyzeSchema(sql, options = {}) {
28244
+ const inputLimit = bounded(options.maxInputBytes, 1024 * 1024, 4 * 1024 * 1024);
28245
+ const statementLimit = bounded(options.maxStatements, 1e3, 1e4);
28246
+ const diagnosticLimit = bounded(options.maxDiagnostics, 100, 1e3);
28247
+ const report = {
28248
+ engine: "mockingbird-postgres",
28249
+ version: true ? "3.7.0" : "source-tree",
28250
+ statements: 0,
28251
+ accepted: 0,
28252
+ compatible: null,
28253
+ truncated: false,
28254
+ diagnostics: []
28255
+ };
28256
+ const add = (diagnostic) => {
28257
+ if (report.diagnostics.length >= diagnosticLimit) {
28258
+ report.truncated = true;
28259
+ return false;
28260
+ }
28261
+ report.diagnostics.push({
28262
+ ...diagnostic,
28263
+ ...diagnostic.object ? { object: diagnostic.object.slice(0, 200) } : {}
28264
+ });
28265
+ if (diagnostic.category !== "unknown") report.compatible = false;
28266
+ return true;
28267
+ };
28268
+ const location = (offset) => {
28269
+ const prefix = sql.slice(0, offset);
28270
+ return { offset, line: prefix.split("\n").length, column: offset - prefix.lastIndexOf("\n") };
28271
+ };
28272
+ if (sql.length > inputLimit || new TextEncoder().encode(sql).length > inputLimit) {
28273
+ add({
28274
+ statement: 0,
28275
+ ...location(0),
28276
+ sqlstate: null,
28277
+ category: "limit",
28278
+ message: `Input exceeds ${inputLimit} bytes`
28279
+ });
28280
+ report.truncated = true;
28281
+ return report;
28282
+ }
28283
+ let tokens;
28284
+ try {
28285
+ tokens = tokenize(sql);
28286
+ } catch (error) {
28287
+ add({
28288
+ statement: 1,
28289
+ ...location(0),
28290
+ sqlstate: isPostgresError(error) ? error.code : null,
28291
+ category: "syntax",
28292
+ message: error instanceof Error ? error.message.slice(0, 500) : "Cannot tokenize input"
28293
+ });
28294
+ return report;
28295
+ }
28296
+ const db = new Database();
28297
+ const failed = /* @__PURE__ */ new Map();
28298
+ let start = 0;
28299
+ let tokenStart = 0;
28300
+ let atomicDepth = 0;
28301
+ const run = (end, tokenEnd) => {
28302
+ const statementTokens = tokens.slice(tokenStart, tokenEnd);
28303
+ tokenStart = tokenEnd + 1;
28304
+ const offset = statementTokens[0]?.pos;
28305
+ const statementSql = sql.slice(start, end);
28306
+ start = end + 1;
28307
+ if (offset === void 0) return;
28308
+ if (report.statements >= statementLimit) {
28309
+ report.truncated = true;
28310
+ add({
28311
+ statement: report.statements + 1,
28312
+ ...location(offset),
28313
+ sqlstate: null,
28314
+ category: "limit",
28315
+ message: `Statement limit ${statementLimit} reached`
28316
+ });
28317
+ return;
28318
+ }
28319
+ report.statements++;
28320
+ let object;
28321
+ const candidate = statementTokens.findIndex(
28322
+ (token) => ["table", "view", "function", "sequence", "schema", "type", "index"].includes(token.value)
28323
+ );
28324
+ if (candidate >= 0) {
28325
+ const name = statementTokens[candidate + 1];
28326
+ if (name?.type === "ident" || name?.type === "quoted_ident") object = name.value;
28327
+ }
28328
+ try {
28329
+ const ast = parse(statementSql);
28330
+ const statement = ast[0];
28331
+ if (statement && "name" in statement && Array.isArray(statement.name)) object = statement.name.join(".");
28332
+ if (statement && !/^(create_|alter_|comment|grant|revoke|set|reset)/.test(statement.type)) {
28333
+ add({
28334
+ statement: report.statements,
28335
+ ...location(offset),
28336
+ object,
28337
+ sqlstate: null,
28338
+ category: "unknown",
28339
+ message: "Only schema statements are analyzed; data, queries and dynamic commands are unknown"
28340
+ });
28341
+ return;
28342
+ }
28343
+ const references = statementTokens.flatMap((token, index) => {
28344
+ if (!["from", "join", "references"].includes(token.value)) return [];
28345
+ const parts = [];
28346
+ for (let at = index + 1; at < statementTokens.length; at += 2) {
28347
+ const part = statementTokens[at];
28348
+ if (!part || part.type !== "ident" && part.type !== "quoted_ident") break;
28349
+ parts.push(part.value);
28350
+ if (statementTokens[at + 1]?.value !== ".") break;
28351
+ }
28352
+ return parts.length ? [parts.join(".")] : [];
28353
+ });
28354
+ const missing = [...failed].find(
28355
+ ([name]) => references.some(
28356
+ (reference) => reference === name || !reference.includes(".") && reference === name.split(".").at(-1) || !name.includes(".") && name === reference.split(".").at(-1)
28357
+ )
28358
+ );
28359
+ if (missing) {
28360
+ add({
28361
+ statement: report.statements,
28362
+ ...location(offset),
28363
+ object,
28364
+ sqlstate: "42P01",
28365
+ category: "prerequisite",
28366
+ message: `Prerequisite ${missing[0]} was not created`,
28367
+ prerequisiteStatement: missing[1]
28368
+ });
28369
+ if (object) failed.set(object, report.statements);
28370
+ return;
28371
+ }
28372
+ db.exec(statementSql);
28373
+ report.accepted++;
28374
+ add({
28375
+ statement: report.statements,
28376
+ ...location(offset),
28377
+ object,
28378
+ sqlstate: null,
28379
+ category: "unknown",
28380
+ message: "Accepted by the ephemeral engine; semantics, function bodies and dynamic SQL require oracle verification"
28381
+ });
28382
+ } catch (error) {
28383
+ const code = isPostgresError(error) ? error.code : null;
28384
+ const message = error instanceof Error ? error.message.slice(0, 500) : "Schema analysis failed";
28385
+ const prerequisite = code?.startsWith("42") ? [...failed].find(([name]) => message.includes(`"${name}"`) || message.includes(`"${name.split(".").at(-1)}"`)) : void 0;
28386
+ add({
28387
+ statement: report.statements,
28388
+ ...location(offset),
28389
+ object,
28390
+ sqlstate: code,
28391
+ category: prerequisite ? "prerequisite" : code === "0A000" ? "unsupported" : code?.startsWith("426") ? "syntax" : "execution",
28392
+ message,
28393
+ ...prerequisite ? { prerequisiteStatement: prerequisite[1] } : {}
28394
+ });
28395
+ if (object) failed.set(object, report.statements);
28396
+ }
28397
+ };
28398
+ try {
28399
+ for (let index = 0; index < tokens.length; index++) {
28400
+ const token = tokens[index];
28401
+ if (token.value === "begin" && tokens[index + 1]?.value === "atomic") atomicDepth++;
28402
+ if (token.value === "end" && atomicDepth > 0) atomicDepth--;
28403
+ if (token.type === "punct" && token.value === ";" && atomicDepth === 0 || token.type === "eof")
28404
+ run(token.pos, index);
28405
+ if (report.truncated) break;
28406
+ }
28407
+ } finally {
28408
+ db.close();
28409
+ }
28410
+ return report;
28411
+ }
28035
28412
  export {
28036
28413
  Database,
28037
28414
  PostgresError,
28038
28415
  Snapshot,
28039
- Statement
28416
+ Statement,
28417
+ analyzeSchema
28040
28418
  };
28041
28419
  //# sourceMappingURL=index.js.map