@crvouga/mockingbird-service-postgres 3.7.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/CHANGELOG.md +7 -1
- package/COMPATIBILITY.md +2 -1
- package/dist/admin.js +243 -78
- package/dist/admin.js.map +3 -3
- package/dist/ast/nodes.d.ts +5 -0
- package/dist/constraints/enforce.d.ts +7 -1
- package/dist/executor/triggers.d.ts +13 -0
- package/dist/functions/pgtrgm.d.ts +4 -4
- package/dist/index.js +127 -12
- package/dist/index.js.map +2 -2
- package/dist/server.js +126 -11
- package/dist/server.js.map +3 -3
- package/dist/storage/database-state.d.ts +4 -0
- package/dist/unstable.js +126 -11
- package/dist/unstable.js.map +2 -2
- package/dist/wire/cli.js +236 -21
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/fleet.d.ts +18 -0
- package/dist/wire/index.js +126 -11
- package/dist/wire/index.js.map +3 -3
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -9965,7 +9965,7 @@ function evalTrgmWordSimilar(ctx, left, right) {
|
|
|
9965
9965
|
const score = wordSimilarity(a.v, b.v);
|
|
9966
9966
|
return tv("bool", score >= WORD_SIMILARITY_THRESHOLD);
|
|
9967
9967
|
}
|
|
9968
|
-
function assertGinTrgmIndex(state, table, stmt) {
|
|
9968
|
+
function assertGinTrgmIndex(state, table, stmt, expressionType) {
|
|
9969
9969
|
const trgmCols = stmt.columns.filter((col2) => isGinTrgmOpclass(col2.opclass));
|
|
9970
9970
|
if (trgmCols.length === 0) return;
|
|
9971
9971
|
if (trgmCols.length !== stmt.columns.length || stmt.columns.length !== 1) {
|
|
@@ -9985,11 +9985,7 @@ function assertGinTrgmIndex(state, table, stmt) {
|
|
|
9985
9985
|
if (stmt.unique) {
|
|
9986
9986
|
throw pgError("feature_not_supported", `access method "gin" does not support unique indexes`, "0A000");
|
|
9987
9987
|
}
|
|
9988
|
-
|
|
9989
|
-
throw unsupported("expression gin_trgm_ops");
|
|
9990
|
-
}
|
|
9991
|
-
const index = table.columnIndex(col.expr.parts[0]);
|
|
9992
|
-
const typeId = table.columns[index].type.id;
|
|
9988
|
+
const typeId = col.expr.type === "colref" && col.expr.parts.length === 1 ? table.columns[table.columnIndex(col.expr.parts[0])].type.id : expressionType(col.expr);
|
|
9993
9989
|
if (!GIN_TRGM_TYPES.has(typeId)) {
|
|
9994
9990
|
throw pgError(
|
|
9995
9991
|
"datatype_mismatch",
|
|
@@ -13627,6 +13623,17 @@ function tableScope(table, row) {
|
|
|
13627
13623
|
const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
|
|
13628
13624
|
return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
|
|
13629
13625
|
}
|
|
13626
|
+
function expressionTypeOf(env, table, expr) {
|
|
13627
|
+
const scope = makeEvalScope(
|
|
13628
|
+
env,
|
|
13629
|
+
tableScope(
|
|
13630
|
+
table,
|
|
13631
|
+
table.columns.map(() => null)
|
|
13632
|
+
)
|
|
13633
|
+
);
|
|
13634
|
+
const v = evalExpr(env.ctx, scope, expr);
|
|
13635
|
+
return v.t === "unknown" ? "text" : v.t;
|
|
13636
|
+
}
|
|
13630
13637
|
function checkNotNull(env, table, row) {
|
|
13631
13638
|
void env;
|
|
13632
13639
|
for (let i = 0; i < table.columns.length; i++) {
|
|
@@ -16549,7 +16556,6 @@ var Parser = class {
|
|
|
16549
16556
|
} while (this.eatKw("or"));
|
|
16550
16557
|
this.expectKw("on");
|
|
16551
16558
|
const table = this.qualifiedName();
|
|
16552
|
-
if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
|
|
16553
16559
|
let deferrable = false;
|
|
16554
16560
|
let initiallyDeferred = false;
|
|
16555
16561
|
if (this.eatKw("not")) this.expectKw("deferrable");
|
|
@@ -16562,6 +16568,23 @@ var Parser = class {
|
|
|
16562
16568
|
this.errorAt(this.peek(), "only constraint triggers can be deferred");
|
|
16563
16569
|
if (initiallyDeferred && !deferrable)
|
|
16564
16570
|
this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
|
|
16571
|
+
const transitions = [];
|
|
16572
|
+
if (!constraint && this.eatKw("referencing")) {
|
|
16573
|
+
do {
|
|
16574
|
+
const isNew = this.eatKw("new");
|
|
16575
|
+
if (!isNew) this.expectKw("old");
|
|
16576
|
+
if (this.eatKw("row")) {
|
|
16577
|
+
throw pgError(
|
|
16578
|
+
"feature_not_supported",
|
|
16579
|
+
"ROW variable naming in the REFERENCING clause is not supported",
|
|
16580
|
+
"0A000"
|
|
16581
|
+
);
|
|
16582
|
+
}
|
|
16583
|
+
this.expectKw("table");
|
|
16584
|
+
this.eatKw("as");
|
|
16585
|
+
transitions.push({ isNew, name: this.ident() });
|
|
16586
|
+
} while (this.atKw("new") || this.atKw("old"));
|
|
16587
|
+
}
|
|
16565
16588
|
let forEachRow = false;
|
|
16566
16589
|
if (this.eatKw("for")) {
|
|
16567
16590
|
this.eatKw("each");
|
|
@@ -16599,7 +16622,8 @@ var Parser = class {
|
|
|
16599
16622
|
forEachRow,
|
|
16600
16623
|
when,
|
|
16601
16624
|
funcName,
|
|
16602
|
-
funcArgs
|
|
16625
|
+
funcArgs,
|
|
16626
|
+
...transitions.length > 0 ? { transitions } : {}
|
|
16603
16627
|
};
|
|
16604
16628
|
}
|
|
16605
16629
|
// --- ALTER --------------------------------------------------------------------
|
|
@@ -21145,6 +21169,29 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
|
|
|
21145
21169
|
}
|
|
21146
21170
|
return { row };
|
|
21147
21171
|
}
|
|
21172
|
+
function transitionRelation(table, name, rows) {
|
|
21173
|
+
return {
|
|
21174
|
+
columns: table.columns.map((c) => ({ name: c.name, type: c.type.id, table: name })),
|
|
21175
|
+
rows
|
|
21176
|
+
};
|
|
21177
|
+
}
|
|
21178
|
+
function fireStatementTriggers(env, table, timing, event, transitions) {
|
|
21179
|
+
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);
|
|
21180
|
+
for (const t of fired) {
|
|
21181
|
+
if (!executor) throw unsupported(`trigger "${t.name}" execution`);
|
|
21182
|
+
const ctes = /* @__PURE__ */ new Map();
|
|
21183
|
+
if (t.oldTable !== void 0)
|
|
21184
|
+
ctes.set(t.oldTable, transitionRelation(table, t.oldTable, transitions?.oldRows ?? []));
|
|
21185
|
+
if (t.newTable !== void 0)
|
|
21186
|
+
ctes.set(t.newTable, transitionRelation(table, t.newTable, transitions?.newRows ?? []));
|
|
21187
|
+
const changes = env.ctx.state.changes;
|
|
21188
|
+
try {
|
|
21189
|
+
executor({ ctx: env.ctx, params: null, ctes, outer: null }, table, t, event, null, null);
|
|
21190
|
+
} finally {
|
|
21191
|
+
env.ctx.state.changes = changes;
|
|
21192
|
+
}
|
|
21193
|
+
}
|
|
21194
|
+
}
|
|
21148
21195
|
var keyOf = (schema, name) => JSON.stringify([schema, name]);
|
|
21149
21196
|
function flushConstraintTriggers(state, all = false) {
|
|
21150
21197
|
const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
|
|
@@ -21528,7 +21575,13 @@ function executeInsert(env0, stmt) {
|
|
|
21528
21575
|
const sourceRows = insertSourceRows(env, stmt, colIdxs);
|
|
21529
21576
|
const insertedRows = [];
|
|
21530
21577
|
let insertedCount = 0;
|
|
21578
|
+
const addedRows = [];
|
|
21579
|
+
const conflictOldRows = [];
|
|
21580
|
+
const conflictNewRows = [];
|
|
21581
|
+
const updatesOnConflict = stmt.onConflict !== null && stmt.onConflict.action !== "nothing";
|
|
21531
21582
|
return withStatementRollback(env, () => {
|
|
21583
|
+
fireStatementTriggers(env, table, "before", "insert");
|
|
21584
|
+
if (updatesOnConflict) fireStatementTriggers(env, table, "before", "update");
|
|
21532
21585
|
for (const srcRow of sourceRows) {
|
|
21533
21586
|
const row = table.columns.map(() => null);
|
|
21534
21587
|
const provided = /* @__PURE__ */ new Set();
|
|
@@ -21574,6 +21627,7 @@ function executeInsert(env0, stmt) {
|
|
|
21574
21627
|
}
|
|
21575
21628
|
if (conflictIdx !== null && conflictWhereOk) {
|
|
21576
21629
|
if (stmt.onConflict.action === "nothing") continue;
|
|
21630
|
+
const replaced = rows[conflictIdx];
|
|
21577
21631
|
const did = applyOnConflictUpdate(
|
|
21578
21632
|
env,
|
|
21579
21633
|
table,
|
|
@@ -21586,6 +21640,8 @@ function executeInsert(env0, stmt) {
|
|
|
21586
21640
|
if (did) {
|
|
21587
21641
|
insertedCount++;
|
|
21588
21642
|
insertedRows.push(rows[conflictIdx]);
|
|
21643
|
+
conflictOldRows.push(replaced);
|
|
21644
|
+
conflictNewRows.push(rows[conflictIdx]);
|
|
21589
21645
|
fireRowTriggers(env, table, "after", "update", null, rows[conflictIdx]);
|
|
21590
21646
|
}
|
|
21591
21647
|
continue;
|
|
@@ -21600,8 +21656,13 @@ function executeInsert(env0, stmt) {
|
|
|
21600
21656
|
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
21601
21657
|
insertedCount++;
|
|
21602
21658
|
insertedRows.push(newRow);
|
|
21659
|
+
addedRows.push(newRow);
|
|
21603
21660
|
fireRowTriggers(env, table, "after", "insert", null, newRow);
|
|
21604
21661
|
}
|
|
21662
|
+
fireStatementTriggers(env, table, "after", "insert", { oldRows: [], newRows: addedRows });
|
|
21663
|
+
if (updatesOnConflict) {
|
|
21664
|
+
fireStatementTriggers(env, table, "after", "update", { oldRows: conflictOldRows, newRows: conflictNewRows });
|
|
21665
|
+
}
|
|
21605
21666
|
env.ctx.state.changes = insertedCount;
|
|
21606
21667
|
if (stmt.returning) {
|
|
21607
21668
|
const res = evalReturning(env, table, label, stmt.returning, insertedRows, "INSERT");
|
|
@@ -21682,10 +21743,12 @@ function executeUpdate(env0, stmt) {
|
|
|
21682
21743
|
const env = applyWith(env0, stmt.with);
|
|
21683
21744
|
const table = requireTargetTable(env, stmt.table, "update");
|
|
21684
21745
|
const label = stmt.alias ?? table.name;
|
|
21746
|
+
fireStatementTriggers(env, table, "before", "update");
|
|
21685
21747
|
const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
|
|
21686
21748
|
const rows = table.mutableRows();
|
|
21687
21749
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
21688
21750
|
const updatedRows = [];
|
|
21751
|
+
const replacedRows = [];
|
|
21689
21752
|
let updateCount = 0;
|
|
21690
21753
|
for (let ri = 0; ri < rows.length; ri++) {
|
|
21691
21754
|
const oldRow = rows[ri];
|
|
@@ -21728,8 +21791,10 @@ function executeUpdate(env0, stmt) {
|
|
|
21728
21791
|
handleReferencedUpdate(env, table, [oldRow], [finalRow]);
|
|
21729
21792
|
updateCount++;
|
|
21730
21793
|
updatedRows.push(finalRow);
|
|
21794
|
+
replacedRows.push(oldRow);
|
|
21731
21795
|
fireRowTriggers(env, table, "after", "update", oldRow, finalRow);
|
|
21732
21796
|
}
|
|
21797
|
+
fireStatementTriggers(env, table, "after", "update", { oldRows: replacedRows, newRows: updatedRows });
|
|
21733
21798
|
env.ctx.state.changes = updateCount;
|
|
21734
21799
|
if (stmt.returning) {
|
|
21735
21800
|
const res = evalReturning(env, table, label, stmt.returning, updatedRows, "UPDATE");
|
|
@@ -21741,6 +21806,7 @@ function executeDelete(env0, stmt) {
|
|
|
21741
21806
|
const env = applyWith(env0, stmt.with);
|
|
21742
21807
|
const table = requireTargetTable(env, stmt.table, "delete from");
|
|
21743
21808
|
const label = stmt.alias ?? table.name;
|
|
21809
|
+
fireStatementTriggers(env, table, "before", "delete");
|
|
21744
21810
|
const rows = table.mutableRows();
|
|
21745
21811
|
const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
|
|
21746
21812
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
@@ -21778,6 +21844,7 @@ function executeDelete(env0, stmt) {
|
|
|
21778
21844
|
fireRowTriggers(env, table, "after", "delete", row, null);
|
|
21779
21845
|
}
|
|
21780
21846
|
if (deletedRows.length > 0) rebuildTableIndexes(env, table);
|
|
21847
|
+
fireStatementTriggers(env, table, "after", "delete", { oldRows: deletedRows, newRows: [] });
|
|
21781
21848
|
env.ctx.state.changes = deletedRows.length;
|
|
21782
21849
|
if (stmt.returning) {
|
|
21783
21850
|
const res = evalReturning(env, table, label, stmt.returning, deletedRows, "DELETE");
|
|
@@ -23920,7 +23987,7 @@ function pgTrigger(ctx) {
|
|
|
23920
23987
|
let oid = 4e4;
|
|
23921
23988
|
for (const t of allTables(ctx.state)) {
|
|
23922
23989
|
for (const tr of t.triggers) {
|
|
23923
|
-
rows.push([oid++, tr.name, t.oid, !tr.forEachRow]);
|
|
23990
|
+
rows.push([oid++, tr.name, t.oid, !tr.forEachRow, tr.oldTable ?? null, tr.newTable ?? null]);
|
|
23924
23991
|
}
|
|
23925
23992
|
}
|
|
23926
23993
|
return rel2(
|
|
@@ -23928,7 +23995,9 @@ function pgTrigger(ctx) {
|
|
|
23928
23995
|
["oid", "oid"],
|
|
23929
23996
|
["tgname", "name"],
|
|
23930
23997
|
["tgrelid", "oid"],
|
|
23931
|
-
["tgisinternal", "bool"]
|
|
23998
|
+
["tgisinternal", "bool"],
|
|
23999
|
+
["tgoldtable", "name"],
|
|
24000
|
+
["tgnewtable", "name"]
|
|
23932
24001
|
],
|
|
23933
24002
|
rows,
|
|
23934
24003
|
"pg_trigger"
|
|
@@ -25355,7 +25424,7 @@ function executeCreateIndex(env, stmt) {
|
|
|
25355
25424
|
}
|
|
25356
25425
|
}
|
|
25357
25426
|
}
|
|
25358
|
-
assertGinTrgmIndex(state, table, stmt);
|
|
25427
|
+
assertGinTrgmIndex(state, table, stmt, (expr) => expressionTypeOf(env, table, expr));
|
|
25359
25428
|
const method = stmt.using ?? "btree";
|
|
25360
25429
|
if (!ACCESS_METHODS.has(method) || method === "heap") {
|
|
25361
25430
|
throw pgError("undefined_object", `access method "${method}" does not exist`, "42704");
|
|
@@ -25610,6 +25679,50 @@ function executeCreateFunction(env, stmt) {
|
|
|
25610
25679
|
schema.functions.set(name, existing);
|
|
25611
25680
|
return commandResult("CREATE FUNCTION", 0);
|
|
25612
25681
|
}
|
|
25682
|
+
function validateTransitionTables(stmt) {
|
|
25683
|
+
const transitions = stmt.transitions ?? [];
|
|
25684
|
+
if (transitions.length === 0) return {};
|
|
25685
|
+
const invalid = (message2) => pgError("invalid_table_definition", message2, "42P17");
|
|
25686
|
+
const unsupportedClause = (message2) => pgError("feature_not_supported", message2, "0A000");
|
|
25687
|
+
if (stmt.timing !== "after") throw invalid("transition table name can only be specified for an AFTER trigger");
|
|
25688
|
+
if (stmt.events.some((e) => e.event === "truncate")) {
|
|
25689
|
+
throw unsupportedClause("TRUNCATE triggers with transition tables are not supported");
|
|
25690
|
+
}
|
|
25691
|
+
if (stmt.events.some((e) => e.columns !== null)) {
|
|
25692
|
+
throw unsupportedClause("transition tables cannot be specified for triggers with column lists");
|
|
25693
|
+
}
|
|
25694
|
+
if (stmt.events.length > 1) {
|
|
25695
|
+
throw unsupportedClause("transition tables cannot be specified for triggers with more than one event");
|
|
25696
|
+
}
|
|
25697
|
+
const event = stmt.events[0].event;
|
|
25698
|
+
let oldTable;
|
|
25699
|
+
let newTable;
|
|
25700
|
+
for (const transition of transitions) {
|
|
25701
|
+
if (transition.isNew) {
|
|
25702
|
+
if (event !== "insert" && event !== "update") {
|
|
25703
|
+
throw invalid("NEW TABLE can only be specified for an INSERT or UPDATE trigger");
|
|
25704
|
+
}
|
|
25705
|
+
if (newTable !== void 0) throw invalid("NEW TABLE cannot be specified multiple times");
|
|
25706
|
+
newTable = transition.name;
|
|
25707
|
+
} else {
|
|
25708
|
+
if (event !== "delete" && event !== "update") {
|
|
25709
|
+
throw invalid("OLD TABLE can only be specified for a DELETE or UPDATE trigger");
|
|
25710
|
+
}
|
|
25711
|
+
if (oldTable !== void 0) throw invalid("OLD TABLE cannot be specified multiple times");
|
|
25712
|
+
oldTable = transition.name;
|
|
25713
|
+
}
|
|
25714
|
+
}
|
|
25715
|
+
if (oldTable !== void 0 && oldTable === newTable) {
|
|
25716
|
+
throw invalid("OLD TABLE name and NEW TABLE name cannot be the same");
|
|
25717
|
+
}
|
|
25718
|
+
if (stmt.forEachRow) {
|
|
25719
|
+
throw pgError("unsupported", "transition tables on FOR EACH ROW triggers are not supported", "0A000");
|
|
25720
|
+
}
|
|
25721
|
+
return {
|
|
25722
|
+
...oldTable !== void 0 ? { oldTable } : {},
|
|
25723
|
+
...newTable !== void 0 ? { newTable } : {}
|
|
25724
|
+
};
|
|
25725
|
+
}
|
|
25613
25726
|
function executeCreateTrigger(env, stmt) {
|
|
25614
25727
|
if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
|
|
25615
25728
|
throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
|
|
@@ -25619,6 +25732,7 @@ function executeCreateTrigger(env, stmt) {
|
|
|
25619
25732
|
throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
|
|
25620
25733
|
}
|
|
25621
25734
|
const table = state.ensureWritableTable(found);
|
|
25735
|
+
const transitionTables = validateTransitionTables(stmt);
|
|
25622
25736
|
const fnSchema = stmt.funcName.length >= 2 ? stmt.funcName[stmt.funcName.length - 2] : null;
|
|
25623
25737
|
const fnName = stmt.funcName[stmt.funcName.length - 1];
|
|
25624
25738
|
const fns = state.findFunctions(stmt.funcName);
|
|
@@ -25637,6 +25751,7 @@ function executeCreateTrigger(env, stmt) {
|
|
|
25637
25751
|
timing: stmt.timing,
|
|
25638
25752
|
events: stmt.events,
|
|
25639
25753
|
forEachRow: stmt.forEachRow,
|
|
25754
|
+
...transitionTables,
|
|
25640
25755
|
when: stmt.when,
|
|
25641
25756
|
funcSchema: fnSchema ?? fns[0].schema,
|
|
25642
25757
|
funcName: fnName,
|