@crvouga/mockingbird-service-postgres 1.5.1 → 1.6.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.
@@ -520,6 +520,7 @@ export interface CreateViewStmt {
520
520
  readonly withData: boolean;
521
521
  }
522
522
  export interface SequenceOptions {
523
+ sequenceName?: string[];
523
524
  increment?: bigint;
524
525
  minValue?: bigint | "no";
525
526
  maxValue?: bigint | "no";
package/dist/index.js CHANGED
@@ -3460,7 +3460,13 @@ var DatabaseState = class _DatabaseState {
3460
3460
  this.settings.set("lc_numeric", "C");
3461
3461
  this.settings.set("lc_time", "C");
3462
3462
  this.settings.set("default_transaction_isolation", "read committed");
3463
+ this.settings.set("lock_timeout", "0");
3464
+ this.settings.set("idle_in_transaction_session_timeout", "0");
3465
+ this.settings.set("transaction_timeout", "0");
3463
3466
  this.settings.set("statement_timeout", "0");
3467
+ this.settings.set("check_function_bodies", "on");
3468
+ this.settings.set("xmloption", "content");
3469
+ this.settings.set("row_security", "on");
3464
3470
  this.settings.set("array_nulls", "on");
3465
3471
  this.settings.set("backslash_quote", "safe_encoding");
3466
3472
  }
@@ -7062,6 +7068,19 @@ function exactDegTan(x) {
7062
7068
  return Math.tan(x * Math.PI / 180);
7063
7069
  }
7064
7070
 
7071
+ // src/sql/deparse.ts
7072
+ function quoteIdent(name) {
7073
+ if (/^[a-z_][a-z0-9_$]*$/.test(name)) return name;
7074
+ return `"${name.replaceAll('"', '""')}"`;
7075
+ }
7076
+
7077
+ // src/runtime/database-context.ts
7078
+ var contexts = /* @__PURE__ */ new WeakMap();
7079
+ var fallback = { name: "postgres", names: () => ["postgres"] };
7080
+ function databaseCatalogContext(state) {
7081
+ return contexts.get(state) ?? fallback;
7082
+ }
7083
+
7065
7084
  // src/functions/misc-fns.ts
7066
7085
  var PG_VERSION_TEXT = "PostgreSQL 18.3 (postgres-mem) on TypeScript, in-memory engine";
7067
7086
  function findSequenceForCall(ctx, name) {
@@ -7145,8 +7164,8 @@ function sequenceNextval(ctx, seq) {
7145
7164
  function getMiscFunctions() {
7146
7165
  const m = /* @__PURE__ */ new Map();
7147
7166
  m.set("version", () => tv("text", PG_VERSION_TEXT));
7148
- m.set("current_database", () => tv("name", "postgres"));
7149
- m.set("current_catalog", () => tv("name", "postgres"));
7167
+ m.set("current_database", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
7168
+ m.set("current_catalog", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
7150
7169
  m.set("current_schema", (ctx) => {
7151
7170
  const path = ctx.state.effectiveSearchPath();
7152
7171
  const first = path.find((s) => ctx.state.schemas.has(s));
@@ -7268,7 +7287,7 @@ function getMiscFunctions() {
7268
7287
  if (!col.identity) return tv("text", null);
7269
7288
  return tv(
7270
7289
  "text",
7271
- col.identity.sequence.split(".").map((p) => p).join(".")
7290
+ col.identity.sequence.split(".").map(quoteIdent).join(".")
7272
7291
  );
7273
7292
  })
7274
7293
  );
@@ -14518,14 +14537,19 @@ var Parser = class {
14518
14537
  parseIdentityOptions() {
14519
14538
  const options = {};
14520
14539
  if (this.eatPunct("(")) {
14521
- Object.assign(options, this.parseSequenceOptions());
14540
+ Object.assign(options, this.parseSequenceOptions(true));
14522
14541
  this.expectPunct(")");
14523
14542
  }
14524
14543
  return options;
14525
14544
  }
14526
- parseSequenceOptions() {
14545
+ parseSequenceOptions(identity = false) {
14527
14546
  const options = {};
14528
14547
  for (; ; ) {
14548
+ if (identity && this.eatKw("sequence")) {
14549
+ this.expectKw("name");
14550
+ options.sequenceName = this.qualifiedName();
14551
+ continue;
14552
+ }
14529
14553
  if (this.eatKw("increment")) {
14530
14554
  this.eatKw("by");
14531
14555
  options.increment = this.parseSignedBigInt();
@@ -20527,6 +20551,27 @@ function parseCsvLine(line, delim, quote) {
20527
20551
  }
20528
20552
  return fields;
20529
20553
  }
20554
+ function splitCsvRecords(data, quote) {
20555
+ const records = [];
20556
+ let start = 0;
20557
+ let quoted = false;
20558
+ for (let i = 0; i < data.length; i++) {
20559
+ const char = data[i];
20560
+ if (char === quote) {
20561
+ if (quoted && data[i + 1] === quote) i++;
20562
+ else quoted = !quoted;
20563
+ continue;
20564
+ }
20565
+ if (char === "\n" && !quoted) {
20566
+ const end = i > start && data[i - 1] === "\r" ? i - 1 : i;
20567
+ records.push(data.slice(start, end));
20568
+ start = i + 1;
20569
+ }
20570
+ }
20571
+ if (quoted) throw pgError("invalid_text_representation", "unterminated CSV quoted field", "22P04");
20572
+ if (start < data.length) records.push(data.slice(start));
20573
+ return records;
20574
+ }
20530
20575
  function executeCopyFromData(env, stmt, data) {
20531
20576
  const format = String(stmt.options.format ?? "text").toLowerCase();
20532
20577
  if (format !== "text" && format !== "csv") throw unsupported(`COPY format ${format}`);
@@ -20537,7 +20582,7 @@ function executeCopyFromData(env, stmt, data) {
20537
20582
  const nullStr = String(stmt.options.null ?? (format === "csv" ? "" : "\\N"));
20538
20583
  const quote = String(stmt.options.quote ?? '"');
20539
20584
  const header = stmt.options.header === true || String(stmt.options.header ?? "").toLowerCase() === "true";
20540
- let lines = data.split(/\r?\n/);
20585
+ let lines = format === "csv" ? splitCsvRecords(data, quote) : data.split(/\r?\n/);
20541
20586
  const endMarker = lines.indexOf("\\.");
20542
20587
  if (endMarker !== -1) lines = lines.slice(0, endMarker);
20543
20588
  if (lines.length > 0 && lines[lines.length - 1] === "") lines = lines.slice(0, -1);
@@ -21271,7 +21316,8 @@ function pgSettings(ctx) {
21271
21316
  "pg_settings"
21272
21317
  );
21273
21318
  }
21274
- function pgDatabase() {
21319
+ function pgDatabase(ctx) {
21320
+ const names = databaseCatalogContext(ctx.state).names();
21275
21321
  return rel(
21276
21322
  [
21277
21323
  ["oid", "oid"],
@@ -21280,7 +21326,7 @@ function pgDatabase() {
21280
21326
  ["datistemplate", "bool"],
21281
21327
  ["datallowconn", "bool"]
21282
21328
  ],
21283
- [[5, "postgres", 6, false, true]],
21329
+ names.map((name, index) => [5 + index, name, 6, false, true]),
21284
21330
  "pg_database"
21285
21331
  );
21286
21332
  }
@@ -21315,13 +21361,14 @@ function pgTrigger(ctx) {
21315
21361
  "pg_trigger"
21316
21362
  );
21317
21363
  }
21318
- var CATALOG_NAME = "postgres";
21364
+ var catalogName = (state) => databaseCatalogContext(state).name;
21319
21365
  function infoSchemata(state) {
21366
+ const catalog = catalogName(state);
21320
21367
  const rows = [
21321
- [CATALOG_NAME, "pg_catalog", OWNER],
21322
- [CATALOG_NAME, "information_schema", OWNER]
21368
+ [catalog, "pg_catalog", OWNER],
21369
+ [catalog, "information_schema", OWNER]
21323
21370
  ];
21324
- for (const s of state.schemas.values()) rows.push([CATALOG_NAME, s.name, OWNER]);
21371
+ for (const s of state.schemas.values()) rows.push([catalog, s.name, OWNER]);
21325
21372
  return rel(
21326
21373
  [
21327
21374
  ["catalog_name", "name"],
@@ -21333,13 +21380,14 @@ function infoSchemata(state) {
21333
21380
  );
21334
21381
  }
21335
21382
  function infoTables(ctx) {
21383
+ const catalog = catalogName(ctx.state);
21336
21384
  const rows = [];
21337
21385
  for (const schema of ctx.state.schemas.values()) {
21338
21386
  for (const t of schema.tables.values()) {
21339
- rows.push([CATALOG_NAME, t.schema, t.name, t.temp ? "LOCAL TEMPORARY" : "BASE TABLE", "YES"]);
21387
+ rows.push([catalog, t.schema, t.name, t.temp ? "LOCAL TEMPORARY" : "BASE TABLE", "YES"]);
21340
21388
  }
21341
21389
  for (const v of schema.views.values()) {
21342
- if (!v.materialized) rows.push([CATALOG_NAME, v.schema, v.name, "VIEW", "NO"]);
21390
+ if (!v.materialized) rows.push([catalog, v.schema, v.name, "VIEW", "NO"]);
21343
21391
  }
21344
21392
  }
21345
21393
  return rel(
@@ -21355,6 +21403,7 @@ function infoTables(ctx) {
21355
21403
  );
21356
21404
  }
21357
21405
  function infoColumns(ctx) {
21406
+ const catalog = catalogName(ctx.state);
21358
21407
  const rows = [];
21359
21408
  for (const t of allTables(ctx.state)) {
21360
21409
  t.columns.forEach((c, i) => {
@@ -21365,7 +21414,7 @@ function infoColumns(ctx) {
21365
21414
  const dataType = isArrayType(id) ? "ARRAY" : isEnumType(id) ? "USER-DEFINED" : typeDisplayName(id);
21366
21415
  const udt = isArrayType(id) ? `_${arrayUdt(id)}` : isEnumType(id) ? id.slice(5).split(".").pop() : id;
21367
21416
  rows.push([
21368
- CATALOG_NAME,
21417
+ catalog,
21369
21418
  t.schema,
21370
21419
  t.name,
21371
21420
  c.name,
@@ -21410,10 +21459,11 @@ function arrayUdt(t) {
21410
21459
  return isEnumType(elem) ? elem.slice(5).split(".").pop() : elem;
21411
21460
  }
21412
21461
  function infoViews(ctx) {
21462
+ const catalog = catalogName(ctx.state);
21413
21463
  const rows = [];
21414
21464
  for (const schema of ctx.state.schemas.values()) {
21415
21465
  for (const v of schema.views.values()) {
21416
- if (!v.materialized) rows.push([CATALOG_NAME, v.schema, v.name, null]);
21466
+ if (!v.materialized) rows.push([catalog, v.schema, v.name, null]);
21417
21467
  }
21418
21468
  }
21419
21469
  return rel(
@@ -21428,10 +21478,11 @@ function infoViews(ctx) {
21428
21478
  );
21429
21479
  }
21430
21480
  function infoSequences(ctx) {
21481
+ const catalog = catalogName(ctx.state);
21431
21482
  const rows = [];
21432
21483
  for (const s of allSequences(ctx.state)) {
21433
21484
  rows.push([
21434
- CATALOG_NAME,
21485
+ catalog,
21435
21486
  s.schema,
21436
21487
  s.name,
21437
21488
  typeDisplayName(s.dataType),
@@ -21459,11 +21510,12 @@ function infoSequences(ctx) {
21459
21510
  );
21460
21511
  }
21461
21512
  function infoTableConstraints(ctx) {
21513
+ const catalog = catalogName(ctx.state);
21462
21514
  const rows = [];
21463
21515
  for (const t of allTables(ctx.state)) {
21464
21516
  for (const c of t.constraints) {
21465
21517
  const type = c.kind === "primary_key" ? "PRIMARY KEY" : c.kind === "unique" ? "UNIQUE" : c.kind === "check" ? "CHECK" : "FOREIGN KEY";
21466
- rows.push([CATALOG_NAME, t.schema, c.name, CATALOG_NAME, t.schema, t.name, type]);
21518
+ rows.push([catalog, t.schema, c.name, catalog, t.schema, t.name, type]);
21467
21519
  }
21468
21520
  }
21469
21521
  return rel(
@@ -21481,12 +21533,13 @@ function infoTableConstraints(ctx) {
21481
21533
  );
21482
21534
  }
21483
21535
  function infoKeyColumnUsage(ctx) {
21536
+ const catalog = catalogName(ctx.state);
21484
21537
  const rows = [];
21485
21538
  for (const t of allTables(ctx.state)) {
21486
21539
  for (const c of t.constraints) {
21487
21540
  if (c.kind !== "primary_key" && c.kind !== "unique" && c.kind !== "foreign_key") continue;
21488
21541
  c.columns.forEach((col, i) => {
21489
- rows.push([CATALOG_NAME, t.schema, c.name, CATALOG_NAME, t.schema, t.name, col, i + 1]);
21542
+ rows.push([catalog, t.schema, c.name, catalog, t.schema, t.name, col, i + 1]);
21490
21543
  });
21491
21544
  }
21492
21545
  }
@@ -21506,12 +21559,13 @@ function infoKeyColumnUsage(ctx) {
21506
21559
  );
21507
21560
  }
21508
21561
  function infoRoutines(ctx) {
21562
+ const catalog = catalogName(ctx.state);
21509
21563
  const rows = [];
21510
21564
  for (const schema of ctx.state.schemas.values()) {
21511
21565
  for (const fns of schema.functions.values()) {
21512
21566
  for (const f of fns) {
21513
21567
  rows.push([
21514
- CATALOG_NAME,
21568
+ catalog,
21515
21569
  f.schema,
21516
21570
  f.name,
21517
21571
  "FUNCTION",
@@ -21568,7 +21622,7 @@ function buildCatalogRelation(ctx, schema, name) {
21568
21622
  case "pg_settings":
21569
21623
  return pgSettings(ctx);
21570
21624
  case "pg_database":
21571
- return pgDatabase();
21625
+ return pgDatabase(ctx);
21572
21626
  case "pg_roles":
21573
21627
  case "pg_user":
21574
21628
  return pgRoles();
@@ -21749,6 +21803,11 @@ var MAX_IDENT = 63;
21749
21803
  function truncIdent(s) {
21750
21804
  return s.length > MAX_IDENT ? s.slice(0, MAX_IDENT) : s;
21751
21805
  }
21806
+ function identitySequenceTarget(env, tableSchema, defaultName, options) {
21807
+ const parts = options.sequenceName;
21808
+ if (!parts || parts.length === 0) return { schema: tableSchema, name: defaultName };
21809
+ return targetSchema(env, parts);
21810
+ }
21752
21811
  var SEQ_LIMITS = {
21753
21812
  int2: { min: -32768n, max: 32767n },
21754
21813
  int4: { min: -2147483648n, max: 2147483647n },
@@ -21925,8 +21984,8 @@ function buildColumn(env, schema, tableName, def, out, taken) {
21925
21984
  break;
21926
21985
  }
21927
21986
  case "generated_identity": {
21928
- const seqName = truncIdent(`${tableName}_${def.name}_seq`);
21929
- const seq = buildSequence(env, schema, seqName, c.options, false);
21987
+ const target = identitySequenceTarget(env, schema, truncIdent(`${tableName}_${def.name}_seq`), c.options);
21988
+ const seq = buildSequence(env, target.schema, target.name, c.options, false);
21930
21989
  if (!c.options.as) {
21931
21990
  seq.dataType = col.type.id === "int2" || col.type.id === "int4" || col.type.id === "int8" ? col.type.id : "int8";
21932
21991
  const limits = SEQ_LIMITS[seq.dataType];
@@ -21935,7 +21994,7 @@ function buildColumn(env, schema, tableName, def, out, taken) {
21935
21994
  }
21936
21995
  seq.ownedBy = { table: tableName, column: def.name };
21937
21996
  out.sequences.push(seq);
21938
- col.identity = { always: c.always, sequence: `${schema.name}.${seqName}` };
21997
+ col.identity = { always: c.always, sequence: `${target.schema.name}.${target.name}` };
21939
21998
  col.notNull = true;
21940
21999
  break;
21941
22000
  }
@@ -22071,10 +22130,11 @@ function executeCreateTable(env, stmt) {
22071
22130
  throw pgError("invalid_table_definition", `multiple primary keys for table "${name}" are not allowed`, "42P16");
22072
22131
  }
22073
22132
  for (const seq of built.sequences) {
22074
- if (schema.hasRelation(seq.name)) {
22133
+ const seqSchema = state.getSchema(seq.schema);
22134
+ if (seqSchema.hasRelation(seq.name)) {
22075
22135
  throw pgError("duplicate_table", `relation "${seq.name}" already exists`, "42P07");
22076
22136
  }
22077
- schema.sequences.set(seq.name, seq);
22137
+ seqSchema.sequences.set(seq.name, seq);
22078
22138
  }
22079
22139
  const table = new TableData(schema.name, name, built.columns, state.nextOid(), stmt.temp);
22080
22140
  table.constraints = built.constraints;
@@ -22426,7 +22486,13 @@ function executeAlterTable(env, stmt) {
22426
22486
  const built = { columns: [], constraints: [], sequences: [] };
22427
22487
  const taken = constraintNames(table);
22428
22488
  const col = buildColumn(env, schema, table.name, action.column, built, taken);
22429
- for (const seq of built.sequences) schema.sequences.set(seq.name, seq);
22489
+ for (const seq of built.sequences) {
22490
+ const seqSchema = state.getSchema(seq.schema);
22491
+ if (seqSchema.hasRelation(seq.name)) {
22492
+ throw pgError("duplicate_table", `relation "${seq.name}" already exists`, "42P07");
22493
+ }
22494
+ seqSchema.sequences.set(seq.name, seq);
22495
+ }
22430
22496
  table.columns.push(col);
22431
22497
  table.constraints.push(...built.constraints);
22432
22498
  for (let i = 0; i < rows.length; i++) {
@@ -22746,12 +22812,32 @@ function executeAlterTable(env, stmt) {
22746
22812
  "42P16"
22747
22813
  );
22748
22814
  }
22749
- const seqName = truncIdent(`${table.name}_${action.column}_seq`);
22750
- const seq = buildSequence(env, schema, seqName, action.options, false);
22815
+ if (!col.notNull) {
22816
+ throw pgError(
22817
+ "object_not_in_prerequisite_state",
22818
+ `column "${action.column}" of relation "${table.name}" must be declared NOT NULL before identity can be added`,
22819
+ "55000"
22820
+ );
22821
+ }
22822
+ const target = identitySequenceTarget(
22823
+ env,
22824
+ schema,
22825
+ truncIdent(`${table.name}_${action.column}_seq`),
22826
+ action.options
22827
+ );
22828
+ if (target.schema.hasRelation(target.name)) {
22829
+ throw pgError("duplicate_table", `relation "${target.name}" already exists`, "42P07");
22830
+ }
22831
+ const seq = buildSequence(env, target.schema, target.name, action.options, false);
22832
+ if (!action.options.as) {
22833
+ seq.dataType = col.type.id === "int2" || col.type.id === "int4" || col.type.id === "int8" ? col.type.id : "int8";
22834
+ const limits = SEQ_LIMITS[seq.dataType];
22835
+ if (action.options.maxValue === void 0) seq.maxValue = seq.increment > 0n ? limits.max : -1n;
22836
+ if (action.options.minValue === void 0) seq.minValue = seq.increment > 0n ? 1n : limits.min;
22837
+ }
22751
22838
  seq.ownedBy = { table: table.name, column: action.column };
22752
- schema.sequences.set(seqName, seq);
22753
- col.identity = { always: true, sequence: `${schema.name}.${seqName}` };
22754
- col.notNull = true;
22839
+ target.schema.sequences.set(target.name, seq);
22840
+ col.identity = { always: action.always, sequence: `${target.schema.name}.${target.name}` };
22755
22841
  break;
22756
22842
  }
22757
22843
  case "drop_identity": {