@crvouga/mockingbird-service-postgres 2.3.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 +27 -1
- package/dist/admin.js +235 -10
- package/dist/admin.js.map +3 -3
- package/dist/executor/comment.d.ts +3 -0
- package/dist/expressions/context.d.ts +2 -0
- package/dist/index.js +235 -10
- package/dist/index.js.map +3 -3
- package/dist/server.js +331 -22
- package/dist/server.js.map +3 -3
- package/dist/sql/deparse-index.d.ts +3 -0
- package/dist/sql/deparse.d.ts +2 -1
- package/dist/storage/database-state.d.ts +4 -0
- package/dist/types/value.d.ts +2 -0
- package/dist/unstable.js +232 -10
- package/dist/unstable.js.map +3 -3
- package/dist/wire/cli.js +331 -22
- package/dist/wire/cli.js.map +3 -3
- package/dist/wire/connection.d.ts +6 -0
- package/dist/wire/index.js +331 -22
- package/dist/wire/index.js.map +3 -3
- 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 {
|
|
@@ -1218,6 +1224,9 @@ var PG_CATALOG_RELATIONS = /* @__PURE__ */ new Set([
|
|
|
1218
1224
|
"pg_enum",
|
|
1219
1225
|
"pg_constraint",
|
|
1220
1226
|
"pg_index",
|
|
1227
|
+
"pg_description",
|
|
1228
|
+
"pg_stat_user_tables",
|
|
1229
|
+
"pg_stat_user_indexes",
|
|
1221
1230
|
"pg_sequence",
|
|
1222
1231
|
"pg_sequences",
|
|
1223
1232
|
"pg_tables",
|
|
@@ -3495,6 +3504,7 @@ var TYPE_ALIASES = {
|
|
|
3495
3504
|
regclass: "regclass",
|
|
3496
3505
|
regtype: "regtype",
|
|
3497
3506
|
regproc: "regproc",
|
|
3507
|
+
regnamespace: "regnamespace",
|
|
3498
3508
|
oid: "oid",
|
|
3499
3509
|
void: "void",
|
|
3500
3510
|
record: "record",
|
|
@@ -3588,6 +3598,7 @@ var TYPE_OIDS = {
|
|
|
3588
3598
|
numeric: 1700,
|
|
3589
3599
|
regclass: 2205,
|
|
3590
3600
|
regtype: 2206,
|
|
3601
|
+
regnamespace: 4089,
|
|
3591
3602
|
record: 2249,
|
|
3592
3603
|
uuid: 2950,
|
|
3593
3604
|
jsonb: 3802,
|
|
@@ -3848,6 +3859,7 @@ function arrayElemNeedsQuote(s) {
|
|
|
3848
3859
|
function quoteArrayElem(s) {
|
|
3849
3860
|
return `"${s.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3850
3861
|
}
|
|
3862
|
+
var UTC_OUTPUT = { zoneOffsetAt: () => 0 };
|
|
3851
3863
|
function arrayText(arr, ctx) {
|
|
3852
3864
|
if (arr.dims.length === 0) return "{}";
|
|
3853
3865
|
const needsLb = arr.lbs.some((l) => l !== 1);
|
|
@@ -3888,6 +3900,7 @@ function recordText(rec, ctx) {
|
|
|
3888
3900
|
return `(${parts.join(",")})`;
|
|
3889
3901
|
}
|
|
3890
3902
|
function datumText(t, v, ctx) {
|
|
3903
|
+
if (t === "regnamespace") return ctx.namespaceName?.(v) ?? String(v);
|
|
3891
3904
|
if (v === null) throw pgError("internal", "datumText called with null");
|
|
3892
3905
|
if (isArrayType(t)) return arrayText(v, ctx);
|
|
3893
3906
|
if (isEnumType(t)) return v;
|
|
@@ -3961,6 +3974,11 @@ function datumText(t, v, ctx) {
|
|
|
3961
3974
|
}
|
|
3962
3975
|
var UTC_INPUT = { zoneOffsetForNaive: () => 0 };
|
|
3963
3976
|
function datumFromText(t, text, ctx) {
|
|
3977
|
+
if (t === "regnamespace") {
|
|
3978
|
+
if (/^\s*\d+\s*$/.test(text)) return datumFromText("oid", text, ctx);
|
|
3979
|
+
if (!ctx.namespaceOid) throw unsupported("regnamespace input without a catalog");
|
|
3980
|
+
return ctx.namespaceOid(text);
|
|
3981
|
+
}
|
|
3964
3982
|
if (isArrayType(t)) return parseArrayText(t, text, ctx);
|
|
3965
3983
|
if (isEnumType(t)) {
|
|
3966
3984
|
if (ctx.enumHasLabel && !ctx.enumHasLabel(t, text)) {
|
|
@@ -4817,7 +4835,11 @@ function castTo(env, value, target, opts = {}) {
|
|
|
4817
4835
|
);
|
|
4818
4836
|
return tv(target, makeArray(toElem, items, arr.dims.slice(), arr.lbs.slice()));
|
|
4819
4837
|
}
|
|
4820
|
-
if (
|
|
4838
|
+
if (from === "oid" && target === "regnamespace") return tv(target, v);
|
|
4839
|
+
if (from === "regnamespace" && (target === "oid" || target === "int4" || target === "int8")) {
|
|
4840
|
+
return tv(target, target === "int8" ? BigInt(v) : v);
|
|
4841
|
+
}
|
|
4842
|
+
if ((from === "int4" || from === "int8" || from === "int2") && (target === "oid" || target === "regclass" || target === "regtype" || target === "regproc" || target === "regnamespace")) {
|
|
4821
4843
|
return tv(target, Number(v));
|
|
4822
4844
|
}
|
|
4823
4845
|
if (from === "oid" && (target === "int4" || target === "int8")) {
|
|
@@ -5765,6 +5787,7 @@ function datumCompare(t, a, b, ctx = DEFAULT_COMPARE_CTX) {
|
|
|
5765
5787
|
case "int2":
|
|
5766
5788
|
case "int4":
|
|
5767
5789
|
case "oid":
|
|
5790
|
+
case "regnamespace":
|
|
5768
5791
|
case "date":
|
|
5769
5792
|
return expectNumber(a, t) - expectNumber(b, t);
|
|
5770
5793
|
case "float4":
|
|
@@ -7920,10 +7943,99 @@ function exactDegTan(x) {
|
|
|
7920
7943
|
}
|
|
7921
7944
|
|
|
7922
7945
|
// src/sql/deparse.ts
|
|
7946
|
+
function deparseSelect(stmt) {
|
|
7947
|
+
return deparseBody(stmt.body);
|
|
7948
|
+
}
|
|
7949
|
+
function deparseBody(body) {
|
|
7950
|
+
switch (body.type) {
|
|
7951
|
+
case "select_core":
|
|
7952
|
+
return deparseSelectCore(body);
|
|
7953
|
+
case "setop":
|
|
7954
|
+
return `${deparseBody(body.left)} ${body.op.toUpperCase()}${body.all ? " ALL" : ""} ${deparseBody(body.right)}`;
|
|
7955
|
+
case "values":
|
|
7956
|
+
return `VALUES ${body.rows.map((r) => `(${r.map(deparseExpr).join(", ")})`).join(", ")}`;
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
function deparseSelectCore(body) {
|
|
7960
|
+
const targets = body.targets.length === 0 ? "*" : body.targets.map((t) => {
|
|
7961
|
+
const expr = deparseExpr(t.expr);
|
|
7962
|
+
return t.alias ? `${expr} AS ${quoteIdent(t.alias)}` : expr;
|
|
7963
|
+
}).join(", ");
|
|
7964
|
+
let sql = `SELECT ${targets}`;
|
|
7965
|
+
if (body.from.length > 0) {
|
|
7966
|
+
sql += ` FROM ${body.from.map(deparseFrom).join(", ")}`;
|
|
7967
|
+
}
|
|
7968
|
+
if (body.where) sql += ` WHERE ${deparseExpr(body.where)}`;
|
|
7969
|
+
if (body.groupBy && body.groupBy.length > 0) {
|
|
7970
|
+
sql += ` GROUP BY ${body.groupBy.map((g) => g.kind === "expr" ? deparseExpr(g.expr) : "?").join(", ")}`;
|
|
7971
|
+
}
|
|
7972
|
+
if (body.having) sql += ` HAVING ${deparseExpr(body.having)}`;
|
|
7973
|
+
return sql;
|
|
7974
|
+
}
|
|
7975
|
+
function deparseFrom(item) {
|
|
7976
|
+
if (item.type === "from_table") {
|
|
7977
|
+
const name = item.name.join(".");
|
|
7978
|
+
return item.alias ? `${name} ${quoteIdent(item.alias)}` : name;
|
|
7979
|
+
}
|
|
7980
|
+
return "(subquery)";
|
|
7981
|
+
}
|
|
7923
7982
|
function quoteIdent(name) {
|
|
7924
7983
|
if (/^[a-z_][a-z0-9_$]*$/.test(name)) return name;
|
|
7925
7984
|
return `"${name.replaceAll('"', '""')}"`;
|
|
7926
7985
|
}
|
|
7986
|
+
function deparseExpr(e) {
|
|
7987
|
+
switch (e.type) {
|
|
7988
|
+
case "null_lit":
|
|
7989
|
+
return "NULL";
|
|
7990
|
+
case "string_lit":
|
|
7991
|
+
return `'${e.value.replaceAll("'", "''")}'`;
|
|
7992
|
+
case "number_lit":
|
|
7993
|
+
return e.raw;
|
|
7994
|
+
case "bool_lit":
|
|
7995
|
+
return e.value ? "true" : "false";
|
|
7996
|
+
case "colref":
|
|
7997
|
+
return e.parts.join(".");
|
|
7998
|
+
case "star":
|
|
7999
|
+
return e.table ? `${e.table.join(".")}.*` : "*";
|
|
8000
|
+
case "binop":
|
|
8001
|
+
return `(${deparseExpr(e.left)} ${e.op} ${deparseExpr(e.right)})`;
|
|
8002
|
+
case "unop":
|
|
8003
|
+
return `${e.op}${deparseExpr(e.operand)}`;
|
|
8004
|
+
case "cast":
|
|
8005
|
+
return `CAST(${deparseExpr(e.expr)} AS ${e.target.parts.join(".")})`;
|
|
8006
|
+
case "func":
|
|
8007
|
+
return `${e.name.join(".")}(${e.args.map(deparseExpr).join(", ")})`;
|
|
8008
|
+
case "case": {
|
|
8009
|
+
let out = "CASE";
|
|
8010
|
+
if (e.operand) out += ` ${deparseExpr(e.operand)}`;
|
|
8011
|
+
for (const arm of e.whens) {
|
|
8012
|
+
out += ` WHEN ${deparseExpr(arm.when)} THEN ${deparseExpr(arm.then)}`;
|
|
8013
|
+
}
|
|
8014
|
+
if (e.elseExpr) out += ` ELSE ${deparseExpr(e.elseExpr)}`;
|
|
8015
|
+
return `${out} END`;
|
|
8016
|
+
}
|
|
8017
|
+
case "subquery_expr":
|
|
8018
|
+
return `(${deparseSelect(e.query)})`;
|
|
8019
|
+
default:
|
|
8020
|
+
return "?";
|
|
8021
|
+
}
|
|
8022
|
+
}
|
|
8023
|
+
|
|
8024
|
+
// src/sql/deparse-index.ts
|
|
8025
|
+
function deparseIndexColumn(column) {
|
|
8026
|
+
let result = column.column === null ? deparseExpr(column.expr) : quoteIdent(column.column);
|
|
8027
|
+
if (column.dir === "desc") result += " DESC";
|
|
8028
|
+
const defaultNulls = column.dir === "desc" ? "first" : "last";
|
|
8029
|
+
if (column.nulls !== defaultNulls) result += ` NULLS ${column.nulls.toUpperCase()}`;
|
|
8030
|
+
return result;
|
|
8031
|
+
}
|
|
8032
|
+
function deparseIndex(index) {
|
|
8033
|
+
const columns = index.columns.map(deparseIndexColumn).join(", ");
|
|
8034
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
8035
|
+
const nulls = index.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
8036
|
+
const predicate = index.where ? ` WHERE ${deparseExpr(index.where)}` : "";
|
|
8037
|
+
return `CREATE ${unique}INDEX ${quoteIdent(index.name)} ON ${quoteIdent(index.schema)}.${quoteIdent(index.table)} USING btree (${columns})${nulls}${predicate}`;
|
|
8038
|
+
}
|
|
7927
8039
|
|
|
7928
8040
|
// src/runtime/database-context.ts
|
|
7929
8041
|
var contexts = /* @__PURE__ */ new WeakMap();
|
|
@@ -8017,6 +8129,22 @@ function sequenceNextval(ctx, seq) {
|
|
|
8017
8129
|
}
|
|
8018
8130
|
function getMiscFunctions() {
|
|
8019
8131
|
const m = /* @__PURE__ */ new Map();
|
|
8132
|
+
m.set(
|
|
8133
|
+
"pg_get_indexdef",
|
|
8134
|
+
strict("text", (ctx, args2) => {
|
|
8135
|
+
const oid = argInt(ctx, args2[0]);
|
|
8136
|
+
const column = args2[1] ? argInt(ctx, args2[1]) : 0;
|
|
8137
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
8138
|
+
for (const index of schema.indexes.values()) {
|
|
8139
|
+
if (index.oid !== oid) continue;
|
|
8140
|
+
if (column === 0) return tv("text", deparseIndex(index));
|
|
8141
|
+
const part = index.columns[column - 1];
|
|
8142
|
+
return tv("text", part ? deparseIndexColumn(part) : "");
|
|
8143
|
+
}
|
|
8144
|
+
}
|
|
8145
|
+
return tv("text", null);
|
|
8146
|
+
})
|
|
8147
|
+
);
|
|
8020
8148
|
m.set("version", () => tv("text", PG_VERSION_TEXT));
|
|
8021
8149
|
m.set("current_database", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
|
|
8022
8150
|
m.set("current_catalog", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
|
|
@@ -13237,6 +13365,9 @@ function tryIndexedTableRows(env, table, alias, where) {
|
|
|
13237
13365
|
const key = uniqueKeyOf(env, table, spec, probeRow);
|
|
13238
13366
|
if (key === null) return [];
|
|
13239
13367
|
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
13368
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
13369
|
+
const index = schema?.indexes.get(spec.name);
|
|
13370
|
+
if (schema && index) schema.indexes.set(spec.name, { ...index, scans: (index.scans ?? 0n) + 1n });
|
|
13240
13371
|
return hits.map((i) => table.rowAt(i));
|
|
13241
13372
|
}
|
|
13242
13373
|
function tryIndexedFromItem(env, item, where) {
|
|
@@ -15999,8 +16130,8 @@ var Parser = class {
|
|
|
15999
16130
|
this.eatKw("column");
|
|
16000
16131
|
const column = this.ident();
|
|
16001
16132
|
let alterType = this.eatKw("type");
|
|
16002
|
-
if (!alterType && this.
|
|
16003
|
-
this.pos +=
|
|
16133
|
+
if (!alterType && this.atKw("set") && this.atKw("data", 1)) {
|
|
16134
|
+
this.pos += 2;
|
|
16004
16135
|
this.expectKw("type");
|
|
16005
16136
|
alterType = true;
|
|
16006
16137
|
}
|
|
@@ -20352,7 +20483,7 @@ function uniqueSpecsFor(env, table) {
|
|
|
20352
20483
|
const schema = env.ctx.state.schemas.get(table.schema);
|
|
20353
20484
|
if (schema) {
|
|
20354
20485
|
for (const idx of schema.indexes.values()) {
|
|
20355
|
-
if (idx.table !== table.name || !idx.unique || idx.isConstraint) continue;
|
|
20486
|
+
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
20356
20487
|
specs.push({
|
|
20357
20488
|
name: idx.name,
|
|
20358
20489
|
keys: idx.columns.map((c) => ({
|
|
@@ -21503,6 +21634,25 @@ var EngineCtx = class {
|
|
|
21503
21634
|
timezone() {
|
|
21504
21635
|
return this.state.getSetting("timezone") ?? "UTC";
|
|
21505
21636
|
}
|
|
21637
|
+
namespaceOid(input) {
|
|
21638
|
+
const trimmed = input.trim();
|
|
21639
|
+
const name = trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1).replaceAll('""', '"') : trimmed.toLowerCase();
|
|
21640
|
+
if (name === "pg_catalog") return 11;
|
|
21641
|
+
if (name === "information_schema") return 13212;
|
|
21642
|
+
const schema = this.state.schemas.get(name);
|
|
21643
|
+
if (!schema) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
21644
|
+
return schema.oid;
|
|
21645
|
+
}
|
|
21646
|
+
namespaceName(oid) {
|
|
21647
|
+
if (oid === 11) return "pg_catalog";
|
|
21648
|
+
if (oid === 13212) return "information_schema";
|
|
21649
|
+
for (const schema of this.state.schemas.values()) {
|
|
21650
|
+
if (schema.oid === oid) {
|
|
21651
|
+
return /^[a-z_][a-z0-9_$]*$/.test(schema.name) ? schema.name : `"${schema.name.replaceAll('"', '""')}"`;
|
|
21652
|
+
}
|
|
21653
|
+
}
|
|
21654
|
+
return null;
|
|
21655
|
+
}
|
|
21506
21656
|
zoneOffsetAt(utcMicros) {
|
|
21507
21657
|
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
21508
21658
|
}
|
|
@@ -21773,7 +21923,7 @@ function pgClass(ctx) {
|
|
|
21773
21923
|
for (const s of schemaData.sequences.values()) push(s.oid, s.name, s.schema, "S", 3, 1, false, true, s.temp);
|
|
21774
21924
|
for (const i of schemaData.indexes.values()) {
|
|
21775
21925
|
push(
|
|
21776
|
-
state.schemas.get(i.schema)?.tables.get(i.table)?.oid ?? 0,
|
|
21926
|
+
i.oid ?? state.schemas.get(i.schema)?.tables.get(i.table)?.oid ?? 0,
|
|
21777
21927
|
i.name,
|
|
21778
21928
|
i.schema,
|
|
21779
21929
|
"i",
|
|
@@ -22010,7 +22160,15 @@ function pgIndex(ctx) {
|
|
|
22010
22160
|
for (const i of schema.indexes.values()) {
|
|
22011
22161
|
const table = schema.tables.get(i.table);
|
|
22012
22162
|
const isPrimary = i.isConstraint && table?.constraints.some((c) => c.kind === "primary_key" && c.name === i.name) === true;
|
|
22013
|
-
rows.push([
|
|
22163
|
+
rows.push([
|
|
22164
|
+
i.oid ?? table?.oid ?? 0,
|
|
22165
|
+
table?.oid ?? 0,
|
|
22166
|
+
i.columns.length,
|
|
22167
|
+
i.unique,
|
|
22168
|
+
isPrimary,
|
|
22169
|
+
i.where !== null,
|
|
22170
|
+
i.valid !== false
|
|
22171
|
+
]);
|
|
22014
22172
|
}
|
|
22015
22173
|
}
|
|
22016
22174
|
return rel(
|
|
@@ -22020,12 +22178,73 @@ function pgIndex(ctx) {
|
|
|
22020
22178
|
["indnatts", "int2"],
|
|
22021
22179
|
["indisunique", "bool"],
|
|
22022
22180
|
["indisprimary", "bool"],
|
|
22023
|
-
["indpred", "bool"]
|
|
22181
|
+
["indpred", "bool"],
|
|
22182
|
+
["indisvalid", "bool"]
|
|
22024
22183
|
],
|
|
22025
22184
|
rows,
|
|
22026
22185
|
"pg_index"
|
|
22027
22186
|
);
|
|
22028
22187
|
}
|
|
22188
|
+
function pgStatUserTables(ctx) {
|
|
22189
|
+
return rel(
|
|
22190
|
+
[
|
|
22191
|
+
["relid", "oid"],
|
|
22192
|
+
["schemaname", "name"],
|
|
22193
|
+
["relname", "name"],
|
|
22194
|
+
["n_live_tup", "int8"]
|
|
22195
|
+
],
|
|
22196
|
+
[...allTables(ctx.state)].map((table) => [table.oid, table.schema, table.name, BigInt(table.rowCount())]),
|
|
22197
|
+
"pg_stat_user_tables"
|
|
22198
|
+
);
|
|
22199
|
+
}
|
|
22200
|
+
function pgDescription(ctx) {
|
|
22201
|
+
const rows = [];
|
|
22202
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
22203
|
+
for (const index of schema.indexes.values()) {
|
|
22204
|
+
if (index.comment) rows.push([index.oid ?? 0, 1259, 0, index.comment]);
|
|
22205
|
+
}
|
|
22206
|
+
}
|
|
22207
|
+
return rel(
|
|
22208
|
+
[
|
|
22209
|
+
["objoid", "oid"],
|
|
22210
|
+
["classoid", "oid"],
|
|
22211
|
+
["objsubid", "int4"],
|
|
22212
|
+
["description", "text"]
|
|
22213
|
+
],
|
|
22214
|
+
rows,
|
|
22215
|
+
"pg_description"
|
|
22216
|
+
);
|
|
22217
|
+
}
|
|
22218
|
+
function pgStatUserIndexes(ctx) {
|
|
22219
|
+
const rows = [];
|
|
22220
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
22221
|
+
if (schema.name === "pg_catalog" || schema.name === "information_schema" || schema.name.startsWith("pg_toast"))
|
|
22222
|
+
continue;
|
|
22223
|
+
for (const index of schema.indexes.values()) {
|
|
22224
|
+
const table = schema.tables.get(index.table);
|
|
22225
|
+
rows.push([
|
|
22226
|
+
table?.oid ?? 0,
|
|
22227
|
+
index.oid ?? table?.oid ?? 0,
|
|
22228
|
+
schema.name,
|
|
22229
|
+
index.table,
|
|
22230
|
+
index.name,
|
|
22231
|
+
index.scans ?? 0n
|
|
22232
|
+
]);
|
|
22233
|
+
}
|
|
22234
|
+
}
|
|
22235
|
+
return rel(
|
|
22236
|
+
[
|
|
22237
|
+
["relid", "oid"],
|
|
22238
|
+
["indexrelid", "oid"],
|
|
22239
|
+
["schemaname", "name"],
|
|
22240
|
+
["relname", "name"],
|
|
22241
|
+
["indexrelname", "name"],
|
|
22242
|
+
["idx_scan", "int8"]
|
|
22243
|
+
],
|
|
22244
|
+
rows,
|
|
22245
|
+
"pg_stat_user_indexes"
|
|
22246
|
+
);
|
|
22247
|
+
}
|
|
22029
22248
|
function pgSequence(ctx) {
|
|
22030
22249
|
const rows = [];
|
|
22031
22250
|
for (const s of allSequences(ctx.state)) {
|
|
@@ -22140,7 +22359,7 @@ function pgIndexesView(ctx) {
|
|
|
22140
22359
|
const rows = [];
|
|
22141
22360
|
for (const schema of ctx.state.schemas.values()) {
|
|
22142
22361
|
for (const i of schema.indexes.values()) {
|
|
22143
|
-
rows.push([i.schema, i.table, i.name, null,
|
|
22362
|
+
rows.push([i.schema, i.table, i.name, null, deparseIndex(i)]);
|
|
22144
22363
|
}
|
|
22145
22364
|
}
|
|
22146
22365
|
return rel(
|
|
@@ -22461,6 +22680,12 @@ function buildCatalogRelation(ctx, schema, name) {
|
|
|
22461
22680
|
return pgConstraint(ctx);
|
|
22462
22681
|
case "pg_index":
|
|
22463
22682
|
return pgIndex(ctx);
|
|
22683
|
+
case "pg_description":
|
|
22684
|
+
return pgDescription(ctx);
|
|
22685
|
+
case "pg_stat_user_tables":
|
|
22686
|
+
return pgStatUserTables(ctx);
|
|
22687
|
+
case "pg_stat_user_indexes":
|
|
22688
|
+
return pgStatUserIndexes(ctx);
|
|
22464
22689
|
case "pg_sequence":
|
|
22465
22690
|
return pgSequence(ctx);
|
|
22466
22691
|
case "pg_sequences":
|
|
@@ -23044,6 +23269,7 @@ function executeCreateIndex(env, stmt) {
|
|
|
23044
23269
|
}
|
|
23045
23270
|
assertGinTrgmIndex(state, table, stmt);
|
|
23046
23271
|
schema.indexes.set(name, {
|
|
23272
|
+
oid: state.nextOid(),
|
|
23047
23273
|
name,
|
|
23048
23274
|
schema: schema.name,
|
|
23049
23275
|
table: table.name,
|
|
@@ -23056,7 +23282,8 @@ function executeCreateIndex(env, stmt) {
|
|
|
23056
23282
|
})),
|
|
23057
23283
|
where: stmt.where,
|
|
23058
23284
|
nullsNotDistinct: stmt.nullsNotDistinct,
|
|
23059
|
-
isConstraint: false
|
|
23285
|
+
isConstraint: false,
|
|
23286
|
+
valid: true
|
|
23060
23287
|
});
|
|
23061
23288
|
if (stmt.unique) {
|
|
23062
23289
|
try {
|
|
@@ -23064,7 +23291,9 @@ function executeCreateIndex(env, stmt) {
|
|
|
23064
23291
|
checkUnique(env, table, table.rowAt(i), i);
|
|
23065
23292
|
}
|
|
23066
23293
|
} catch (err) {
|
|
23067
|
-
schema.indexes.
|
|
23294
|
+
if (stmt.concurrently) schema.indexes.get(name).valid = false;
|
|
23295
|
+
else schema.indexes.delete(name);
|
|
23296
|
+
table.indexStores = null;
|
|
23068
23297
|
throw err;
|
|
23069
23298
|
}
|
|
23070
23299
|
}
|
|
@@ -24184,6 +24413,9 @@ var Statement = class _Statement {
|
|
|
24184
24413
|
const typed = params.map((p, i) => bindValueToTyped(p, i));
|
|
24185
24414
|
let last = null;
|
|
24186
24415
|
for (const stmt of this.statements) {
|
|
24416
|
+
if (stmt.type === "create_index" && stmt.concurrently && this.database.transactions.inTransaction) {
|
|
24417
|
+
throw pgError("misuse", "CREATE INDEX CONCURRENTLY cannot run inside a transaction block", "25001");
|
|
24418
|
+
}
|
|
24187
24419
|
const env = {
|
|
24188
24420
|
ctx: new EngineCtx(this.database.state),
|
|
24189
24421
|
params: typed,
|
|
@@ -25210,9 +25442,48 @@ var RENAME_DATABASE = new RegExp(`^alter\\s+database\\s+${IDENT}\\s+rename\\s+to
|
|
|
25210
25442
|
var identifier = (quoted, plain) => quoted === void 0 ? plain.toLowerCase() : quoted.replaceAll('""', '"');
|
|
25211
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;
|
|
25212
25444
|
var LIMIT = /\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$/i;
|
|
25213
|
-
var
|
|
25214
|
-
var
|
|
25215
|
-
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
|
+
};
|
|
25216
25487
|
var TYPLEN = {
|
|
25217
25488
|
bool: 1,
|
|
25218
25489
|
int2: 2,
|
|
@@ -25231,7 +25502,7 @@ var TEXT_TYPES2 = /* @__PURE__ */ new Set(["text", "varchar", "bpchar", "name",
|
|
|
25231
25502
|
var PG_EPOCH_MS = 9466848e5;
|
|
25232
25503
|
var pgError2 = (category, message2, code) => new PostgresError(category, message2, code);
|
|
25233
25504
|
var errorFields = (error) => {
|
|
25234
|
-
if (error
|
|
25505
|
+
if (isPostgresError(error)) {
|
|
25235
25506
|
const message2 = error.message;
|
|
25236
25507
|
const fields = { code: error.sqlState, message: message2 };
|
|
25237
25508
|
const constraint = /constraint "([^"]+)"/.exec(message2)?.[1];
|
|
@@ -25627,8 +25898,8 @@ var Connection = class {
|
|
|
25627
25898
|
return outcome;
|
|
25628
25899
|
} catch (error) {
|
|
25629
25900
|
if (this.inTransaction) this.aborted = true;
|
|
25630
|
-
if (error
|
|
25631
|
-
const status = error
|
|
25901
|
+
if (isPostgresError(error) && error.sqlState === "40P01") this.cluster.unlockAll(this, true);
|
|
25902
|
+
const status = isPostgresError(error) ? error.sqlState : "XX000";
|
|
25632
25903
|
this.options.onLog?.({ pid: this.pid, sql: text, durationMs: performance.now() - started, status });
|
|
25633
25904
|
throw error;
|
|
25634
25905
|
}
|
|
@@ -25731,7 +26002,11 @@ var Connection = class {
|
|
|
25731
26002
|
const result = this.cluster.as(this, () => this.runQuery(db, text, params));
|
|
25732
26003
|
return { empty: false, result, tag: commandTag(result) };
|
|
25733
26004
|
};
|
|
25734
|
-
const
|
|
26005
|
+
const concurrentIndex = /\bconcurrently\b/i.test(text) && parse(text).some((stmt) => stmt.type === "create_index" && stmt.concurrently);
|
|
26006
|
+
if (concurrentIndex && this.transaction !== null) {
|
|
26007
|
+
throw new PostgresError("misuse", "CREATE INDEX CONCURRENTLY cannot run inside a transaction block", "25001");
|
|
26008
|
+
}
|
|
26009
|
+
const outcome = concurrentIndex ? run() : db.transaction(run);
|
|
25735
26010
|
if (this.transaction && mutatesDatabase(text)) {
|
|
25736
26011
|
this.transaction.statements.push({ kind: "sql", sql: text, params: params.slice() });
|
|
25737
26012
|
}
|
|
@@ -25820,14 +26095,48 @@ var Connection = class {
|
|
|
25820
26095
|
}
|
|
25821
26096
|
});
|
|
25822
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
|
+
}
|
|
25823
26134
|
runQuery(db, text, params) {
|
|
25824
26135
|
const lock = ROW_LOCK.exec(text);
|
|
25825
26136
|
if (!lock) {
|
|
25826
|
-
const
|
|
25827
|
-
if (
|
|
25828
|
-
|
|
25829
|
-
this.lockRows(db, lockQuery, params, "wait");
|
|
25830
|
-
}
|
|
26137
|
+
const lockQuery = lockedMutationQuery(text);
|
|
26138
|
+
if (lockQuery) this.lockRows(db, lockQuery, params, "wait");
|
|
26139
|
+
this.lockInsertedPrimaryKey(db, text, params);
|
|
25831
26140
|
return db.prepare(text).textResult(...params);
|
|
25832
26141
|
}
|
|
25833
26142
|
let query = text.slice(0, lock.index).trimEnd();
|