@crvouga/mockingbird-service-postgres 3.0.0 → 3.0.1
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 +6 -0
- package/dist/admin.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.js +91 -11
- package/dist/server.js.map +2 -2
- package/dist/unstable.js.map +1 -1
- package/dist/wire/cli.js +91 -11
- package/dist/wire/cli.js.map +2 -2
- package/dist/wire/connection.d.ts +6 -0
- package/dist/wire/index.js +91 -11
- package/dist/wire/index.js.map +2 -2
- package/package.json +1 -1
package/dist/wire/cli.js
CHANGED
|
@@ -1098,6 +1098,12 @@ function pgError(category, message2, sqlState) {
|
|
|
1098
1098
|
function unsupported(what) {
|
|
1099
1099
|
return new PostgresError("unsupported", `${what} is not supported`);
|
|
1100
1100
|
}
|
|
1101
|
+
function isPostgresError(err) {
|
|
1102
|
+
if (err instanceof PostgresError) return true;
|
|
1103
|
+
if (!(err instanceof Error) || err.name !== "PostgresError") return false;
|
|
1104
|
+
const sqlState = err.sqlState;
|
|
1105
|
+
return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
|
|
1106
|
+
}
|
|
1101
1107
|
|
|
1102
1108
|
// src/runtime/prng.ts
|
|
1103
1109
|
var Prng = class _Prng {
|
|
@@ -3853,6 +3859,7 @@ function arrayElemNeedsQuote(s) {
|
|
|
3853
3859
|
function quoteArrayElem(s) {
|
|
3854
3860
|
return `"${s.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3855
3861
|
}
|
|
3862
|
+
var UTC_OUTPUT = { zoneOffsetAt: () => 0 };
|
|
3856
3863
|
function arrayText(arr, ctx) {
|
|
3857
3864
|
if (arr.dims.length === 0) return "{}";
|
|
3858
3865
|
const needsLb = arr.lbs.some((l) => l !== 1);
|
|
@@ -25435,9 +25442,48 @@ var RENAME_DATABASE = new RegExp(`^alter\\s+database\\s+${IDENT}\\s+rename\\s+to
|
|
|
25435
25442
|
var identifier = (quoted, plain) => quoted === void 0 ? plain.toLowerCase() : quoted.replaceAll('""', '"');
|
|
25436
25443
|
var ROW_LOCK = /\s+for\s+(?:no\s+key\s+update|key\s+share|update|share)(?:\s+of\s+(?:(?:"[^"]+"|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|[A-Za-z_][\w$]*))?)(?:\s*,\s*(?:(?:"[^"]+"|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|[A-Za-z_][\w$]*))?))*)?(?:\s+(nowait|skip\s+locked))?\s*$/i;
|
|
25437
25444
|
var LIMIT = /\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$/i;
|
|
25438
|
-
var
|
|
25439
|
-
var
|
|
25440
|
-
var
|
|
25445
|
+
var SQL_NAME = String.raw`(?:"[^"]+"|[A-Za-z_][\w$]*)`;
|
|
25446
|
+
var SQL_RELATION = String.raw`(?:"[^"]+"|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|[A-Za-z_][\w$]*))?`;
|
|
25447
|
+
var FROM_TABLE = new RegExp(String.raw`\bfrom\s+(${SQL_RELATION})`, "i");
|
|
25448
|
+
var UPDATE_TARGET = new RegExp(
|
|
25449
|
+
String.raw`^update\s+(${SQL_RELATION})(?:\s+(?:as\s+)?(${SQL_NAME}))?\s+set\s+[\s\S]*?(?:\s+where\s+([\s\S]*?))?(?:\s+returning\s+[\s\S]*)?$`,
|
|
25450
|
+
"i"
|
|
25451
|
+
);
|
|
25452
|
+
var DELETE_TARGET = new RegExp(
|
|
25453
|
+
String.raw`^delete\s+from\s+(${SQL_RELATION})(?:\s+(?:as\s+)?(${SQL_NAME}))?(?:\s+where\s+([\s\S]*?))?(?:\s+returning\s+[\s\S]*)?$`,
|
|
25454
|
+
"i"
|
|
25455
|
+
);
|
|
25456
|
+
var lockedMutationQuery = (sql) => {
|
|
25457
|
+
const match = UPDATE_TARGET.exec(sql) ?? DELETE_TARGET.exec(sql);
|
|
25458
|
+
const relation = match?.[1];
|
|
25459
|
+
if (!relation) return null;
|
|
25460
|
+
const alias = match?.[2];
|
|
25461
|
+
const where = match?.[3];
|
|
25462
|
+
const from = alias ? `${relation} AS ${alias}` : relation;
|
|
25463
|
+
return `SELECT * FROM ${from}${where ? ` WHERE ${where}` : ""}`;
|
|
25464
|
+
};
|
|
25465
|
+
var lockLiteral = (expr, params) => {
|
|
25466
|
+
if (!expr) return void 0;
|
|
25467
|
+
switch (expr.type) {
|
|
25468
|
+
case "null_lit":
|
|
25469
|
+
return null;
|
|
25470
|
+
case "string_lit":
|
|
25471
|
+
return expr.value;
|
|
25472
|
+
case "bool_lit":
|
|
25473
|
+
return expr.value ? "t" : "f";
|
|
25474
|
+
case "number_lit":
|
|
25475
|
+
return expr.raw.replace(/^\+/, "");
|
|
25476
|
+
case "param": {
|
|
25477
|
+
const value = params[expr.index - 1];
|
|
25478
|
+
if (value === void 0) return void 0;
|
|
25479
|
+
const typed = bindValueToTyped(value, expr.index - 1);
|
|
25480
|
+
if (typed.v === null) return null;
|
|
25481
|
+
return datumText(typed.t, typed.v, UTC_OUTPUT);
|
|
25482
|
+
}
|
|
25483
|
+
default:
|
|
25484
|
+
return void 0;
|
|
25485
|
+
}
|
|
25486
|
+
};
|
|
25441
25487
|
var TYPLEN = {
|
|
25442
25488
|
bool: 1,
|
|
25443
25489
|
int2: 2,
|
|
@@ -25456,7 +25502,7 @@ var TEXT_TYPES2 = /* @__PURE__ */ new Set(["text", "varchar", "bpchar", "name",
|
|
|
25456
25502
|
var PG_EPOCH_MS = 9466848e5;
|
|
25457
25503
|
var pgError2 = (category, message2, code) => new PostgresError(category, message2, code);
|
|
25458
25504
|
var errorFields = (error) => {
|
|
25459
|
-
if (error
|
|
25505
|
+
if (isPostgresError(error)) {
|
|
25460
25506
|
const message2 = error.message;
|
|
25461
25507
|
const fields = { code: error.sqlState, message: message2 };
|
|
25462
25508
|
const constraint = /constraint "([^"]+)"/.exec(message2)?.[1];
|
|
@@ -25852,8 +25898,8 @@ var Connection = class {
|
|
|
25852
25898
|
return outcome;
|
|
25853
25899
|
} catch (error) {
|
|
25854
25900
|
if (this.inTransaction) this.aborted = true;
|
|
25855
|
-
if (error
|
|
25856
|
-
const status = error
|
|
25901
|
+
if (isPostgresError(error) && error.sqlState === "40P01") this.cluster.unlockAll(this, true);
|
|
25902
|
+
const status = isPostgresError(error) ? error.sqlState : "XX000";
|
|
25857
25903
|
this.options.onLog?.({ pid: this.pid, sql: text, durationMs: performance.now() - started, status });
|
|
25858
25904
|
throw error;
|
|
25859
25905
|
}
|
|
@@ -26049,14 +26095,48 @@ var Connection = class {
|
|
|
26049
26095
|
}
|
|
26050
26096
|
});
|
|
26051
26097
|
}
|
|
26098
|
+
/**
|
|
26099
|
+
* Hold the primary key until the transaction ends. PostgreSQL's unique index makes a
|
|
26100
|
+
* second `INSERT` of the same key wait; `ON CONFLICT DO UPDATE` then sees the committed row.
|
|
26101
|
+
* Without this, each session's workspace inserts the key and commit replay raises 23505.
|
|
26102
|
+
*/
|
|
26103
|
+
lockInsertedPrimaryKey(db, text, params) {
|
|
26104
|
+
let statements;
|
|
26105
|
+
try {
|
|
26106
|
+
statements = parse(text);
|
|
26107
|
+
} catch {
|
|
26108
|
+
return;
|
|
26109
|
+
}
|
|
26110
|
+
const stmt = statements.length === 1 ? statements[0] : void 0;
|
|
26111
|
+
if (stmt?.type !== "insert" || stmt.source === "default_values" || stmt.source.body.type !== "values") return;
|
|
26112
|
+
if (!stmt.columns) return;
|
|
26113
|
+
const table = db.state.findTable(stmt.table);
|
|
26114
|
+
const primary = table?.constraints.find((constraint) => constraint.kind === "primary_key");
|
|
26115
|
+
if (!primary || primary.columns.length === 0) return;
|
|
26116
|
+
const indexes = primary.columns.map((column) => stmt.columns?.indexOf(column) ?? -1);
|
|
26117
|
+
if (indexes.some((index) => index < 0)) return;
|
|
26118
|
+
const identities = [];
|
|
26119
|
+
for (const row of stmt.source.body.rows) {
|
|
26120
|
+
const identity = [];
|
|
26121
|
+
for (const index of indexes) {
|
|
26122
|
+
const value = lockLiteral(row[index], params);
|
|
26123
|
+
if (value === void 0) return;
|
|
26124
|
+
identity.push(value);
|
|
26125
|
+
}
|
|
26126
|
+
identities.push(identity);
|
|
26127
|
+
}
|
|
26128
|
+
const relation = stmt.table.join(".");
|
|
26129
|
+
for (const identity of identities) {
|
|
26130
|
+
const key = `row:${this.databaseName}:${relation}:${JSON.stringify(identity)}`;
|
|
26131
|
+
if (!this.cluster.tryLock(this, key, true)) throw new LockWait(key);
|
|
26132
|
+
}
|
|
26133
|
+
}
|
|
26052
26134
|
runQuery(db, text, params) {
|
|
26053
26135
|
const lock = ROW_LOCK.exec(text);
|
|
26054
26136
|
if (!lock) {
|
|
26055
|
-
const
|
|
26056
|
-
if (
|
|
26057
|
-
|
|
26058
|
-
this.lockRows(db, lockQuery, params, "wait");
|
|
26059
|
-
}
|
|
26137
|
+
const lockQuery = lockedMutationQuery(text);
|
|
26138
|
+
if (lockQuery) this.lockRows(db, lockQuery, params, "wait");
|
|
26139
|
+
this.lockInsertedPrimaryKey(db, text, params);
|
|
26060
26140
|
return db.prepare(text).textResult(...params);
|
|
26061
26141
|
}
|
|
26062
26142
|
let query = text.slice(0, lock.index).trimEnd();
|