@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/server.js
CHANGED
|
@@ -250,6 +250,12 @@ function pgError(category, message2, sqlState) {
|
|
|
250
250
|
function unsupported(what) {
|
|
251
251
|
return new PostgresError("unsupported", `${what} is not supported`);
|
|
252
252
|
}
|
|
253
|
+
function isPostgresError(err) {
|
|
254
|
+
if (err instanceof PostgresError) return true;
|
|
255
|
+
if (!(err instanceof Error) || err.name !== "PostgresError") return false;
|
|
256
|
+
const sqlState = err.sqlState;
|
|
257
|
+
return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
|
|
258
|
+
}
|
|
253
259
|
|
|
254
260
|
// src/runtime/prng.ts
|
|
255
261
|
var Prng = class _Prng {
|
|
@@ -370,6 +376,9 @@ var PG_CATALOG_RELATIONS = /* @__PURE__ */ new Set([
|
|
|
370
376
|
"pg_enum",
|
|
371
377
|
"pg_constraint",
|
|
372
378
|
"pg_index",
|
|
379
|
+
"pg_description",
|
|
380
|
+
"pg_stat_user_tables",
|
|
381
|
+
"pg_stat_user_indexes",
|
|
373
382
|
"pg_sequence",
|
|
374
383
|
"pg_sequences",
|
|
375
384
|
"pg_tables",
|
|
@@ -2647,6 +2656,7 @@ var TYPE_ALIASES = {
|
|
|
2647
2656
|
regclass: "regclass",
|
|
2648
2657
|
regtype: "regtype",
|
|
2649
2658
|
regproc: "regproc",
|
|
2659
|
+
regnamespace: "regnamespace",
|
|
2650
2660
|
oid: "oid",
|
|
2651
2661
|
void: "void",
|
|
2652
2662
|
record: "record",
|
|
@@ -2740,6 +2750,7 @@ var TYPE_OIDS = {
|
|
|
2740
2750
|
numeric: 1700,
|
|
2741
2751
|
regclass: 2205,
|
|
2742
2752
|
regtype: 2206,
|
|
2753
|
+
regnamespace: 4089,
|
|
2743
2754
|
record: 2249,
|
|
2744
2755
|
uuid: 2950,
|
|
2745
2756
|
jsonb: 3802,
|
|
@@ -3000,6 +3011,7 @@ function arrayElemNeedsQuote(s) {
|
|
|
3000
3011
|
function quoteArrayElem(s) {
|
|
3001
3012
|
return `"${s.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3002
3013
|
}
|
|
3014
|
+
var UTC_OUTPUT = { zoneOffsetAt: () => 0 };
|
|
3003
3015
|
function arrayText(arr, ctx) {
|
|
3004
3016
|
if (arr.dims.length === 0) return "{}";
|
|
3005
3017
|
const needsLb = arr.lbs.some((l) => l !== 1);
|
|
@@ -3040,6 +3052,7 @@ function recordText(rec, ctx) {
|
|
|
3040
3052
|
return `(${parts.join(",")})`;
|
|
3041
3053
|
}
|
|
3042
3054
|
function datumText(t, v, ctx) {
|
|
3055
|
+
if (t === "regnamespace") return ctx.namespaceName?.(v) ?? String(v);
|
|
3043
3056
|
if (v === null) throw pgError("internal", "datumText called with null");
|
|
3044
3057
|
if (isArrayType(t)) return arrayText(v, ctx);
|
|
3045
3058
|
if (isEnumType(t)) return v;
|
|
@@ -3113,6 +3126,11 @@ function datumText(t, v, ctx) {
|
|
|
3113
3126
|
}
|
|
3114
3127
|
var UTC_INPUT = { zoneOffsetForNaive: () => 0 };
|
|
3115
3128
|
function datumFromText(t, text, ctx) {
|
|
3129
|
+
if (t === "regnamespace") {
|
|
3130
|
+
if (/^\s*\d+\s*$/.test(text)) return datumFromText("oid", text, ctx);
|
|
3131
|
+
if (!ctx.namespaceOid) throw unsupported("regnamespace input without a catalog");
|
|
3132
|
+
return ctx.namespaceOid(text);
|
|
3133
|
+
}
|
|
3116
3134
|
if (isArrayType(t)) return parseArrayText(t, text, ctx);
|
|
3117
3135
|
if (isEnumType(t)) {
|
|
3118
3136
|
if (ctx.enumHasLabel && !ctx.enumHasLabel(t, text)) {
|
|
@@ -3969,7 +3987,11 @@ function castTo(env, value, target, opts = {}) {
|
|
|
3969
3987
|
);
|
|
3970
3988
|
return tv(target, makeArray(toElem, items, arr.dims.slice(), arr.lbs.slice()));
|
|
3971
3989
|
}
|
|
3972
|
-
if (
|
|
3990
|
+
if (from === "oid" && target === "regnamespace") return tv(target, v);
|
|
3991
|
+
if (from === "regnamespace" && (target === "oid" || target === "int4" || target === "int8")) {
|
|
3992
|
+
return tv(target, target === "int8" ? BigInt(v) : v);
|
|
3993
|
+
}
|
|
3994
|
+
if ((from === "int4" || from === "int8" || from === "int2") && (target === "oid" || target === "regclass" || target === "regtype" || target === "regproc" || target === "regnamespace")) {
|
|
3973
3995
|
return tv(target, Number(v));
|
|
3974
3996
|
}
|
|
3975
3997
|
if (from === "oid" && (target === "int4" || target === "int8")) {
|
|
@@ -4917,6 +4939,7 @@ function datumCompare(t, a, b, ctx = DEFAULT_COMPARE_CTX) {
|
|
|
4917
4939
|
case "int2":
|
|
4918
4940
|
case "int4":
|
|
4919
4941
|
case "oid":
|
|
4942
|
+
case "regnamespace":
|
|
4920
4943
|
case "date":
|
|
4921
4944
|
return expectNumber(a, t) - expectNumber(b, t);
|
|
4922
4945
|
case "float4":
|
|
@@ -7072,10 +7095,99 @@ function exactDegTan(x) {
|
|
|
7072
7095
|
}
|
|
7073
7096
|
|
|
7074
7097
|
// src/sql/deparse.ts
|
|
7098
|
+
function deparseSelect(stmt) {
|
|
7099
|
+
return deparseBody(stmt.body);
|
|
7100
|
+
}
|
|
7101
|
+
function deparseBody(body) {
|
|
7102
|
+
switch (body.type) {
|
|
7103
|
+
case "select_core":
|
|
7104
|
+
return deparseSelectCore(body);
|
|
7105
|
+
case "setop":
|
|
7106
|
+
return `${deparseBody(body.left)} ${body.op.toUpperCase()}${body.all ? " ALL" : ""} ${deparseBody(body.right)}`;
|
|
7107
|
+
case "values":
|
|
7108
|
+
return `VALUES ${body.rows.map((r) => `(${r.map(deparseExpr).join(", ")})`).join(", ")}`;
|
|
7109
|
+
}
|
|
7110
|
+
}
|
|
7111
|
+
function deparseSelectCore(body) {
|
|
7112
|
+
const targets = body.targets.length === 0 ? "*" : body.targets.map((t) => {
|
|
7113
|
+
const expr = deparseExpr(t.expr);
|
|
7114
|
+
return t.alias ? `${expr} AS ${quoteIdent(t.alias)}` : expr;
|
|
7115
|
+
}).join(", ");
|
|
7116
|
+
let sql = `SELECT ${targets}`;
|
|
7117
|
+
if (body.from.length > 0) {
|
|
7118
|
+
sql += ` FROM ${body.from.map(deparseFrom).join(", ")}`;
|
|
7119
|
+
}
|
|
7120
|
+
if (body.where) sql += ` WHERE ${deparseExpr(body.where)}`;
|
|
7121
|
+
if (body.groupBy && body.groupBy.length > 0) {
|
|
7122
|
+
sql += ` GROUP BY ${body.groupBy.map((g) => g.kind === "expr" ? deparseExpr(g.expr) : "?").join(", ")}`;
|
|
7123
|
+
}
|
|
7124
|
+
if (body.having) sql += ` HAVING ${deparseExpr(body.having)}`;
|
|
7125
|
+
return sql;
|
|
7126
|
+
}
|
|
7127
|
+
function deparseFrom(item) {
|
|
7128
|
+
if (item.type === "from_table") {
|
|
7129
|
+
const name = item.name.join(".");
|
|
7130
|
+
return item.alias ? `${name} ${quoteIdent(item.alias)}` : name;
|
|
7131
|
+
}
|
|
7132
|
+
return "(subquery)";
|
|
7133
|
+
}
|
|
7075
7134
|
function quoteIdent(name) {
|
|
7076
7135
|
if (/^[a-z_][a-z0-9_$]*$/.test(name)) return name;
|
|
7077
7136
|
return `"${name.replaceAll('"', '""')}"`;
|
|
7078
7137
|
}
|
|
7138
|
+
function deparseExpr(e) {
|
|
7139
|
+
switch (e.type) {
|
|
7140
|
+
case "null_lit":
|
|
7141
|
+
return "NULL";
|
|
7142
|
+
case "string_lit":
|
|
7143
|
+
return `'${e.value.replaceAll("'", "''")}'`;
|
|
7144
|
+
case "number_lit":
|
|
7145
|
+
return e.raw;
|
|
7146
|
+
case "bool_lit":
|
|
7147
|
+
return e.value ? "true" : "false";
|
|
7148
|
+
case "colref":
|
|
7149
|
+
return e.parts.join(".");
|
|
7150
|
+
case "star":
|
|
7151
|
+
return e.table ? `${e.table.join(".")}.*` : "*";
|
|
7152
|
+
case "binop":
|
|
7153
|
+
return `(${deparseExpr(e.left)} ${e.op} ${deparseExpr(e.right)})`;
|
|
7154
|
+
case "unop":
|
|
7155
|
+
return `${e.op}${deparseExpr(e.operand)}`;
|
|
7156
|
+
case "cast":
|
|
7157
|
+
return `CAST(${deparseExpr(e.expr)} AS ${e.target.parts.join(".")})`;
|
|
7158
|
+
case "func":
|
|
7159
|
+
return `${e.name.join(".")}(${e.args.map(deparseExpr).join(", ")})`;
|
|
7160
|
+
case "case": {
|
|
7161
|
+
let out = "CASE";
|
|
7162
|
+
if (e.operand) out += ` ${deparseExpr(e.operand)}`;
|
|
7163
|
+
for (const arm of e.whens) {
|
|
7164
|
+
out += ` WHEN ${deparseExpr(arm.when)} THEN ${deparseExpr(arm.then)}`;
|
|
7165
|
+
}
|
|
7166
|
+
if (e.elseExpr) out += ` ELSE ${deparseExpr(e.elseExpr)}`;
|
|
7167
|
+
return `${out} END`;
|
|
7168
|
+
}
|
|
7169
|
+
case "subquery_expr":
|
|
7170
|
+
return `(${deparseSelect(e.query)})`;
|
|
7171
|
+
default:
|
|
7172
|
+
return "?";
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
// src/sql/deparse-index.ts
|
|
7177
|
+
function deparseIndexColumn(column) {
|
|
7178
|
+
let result = column.column === null ? deparseExpr(column.expr) : quoteIdent(column.column);
|
|
7179
|
+
if (column.dir === "desc") result += " DESC";
|
|
7180
|
+
const defaultNulls = column.dir === "desc" ? "first" : "last";
|
|
7181
|
+
if (column.nulls !== defaultNulls) result += ` NULLS ${column.nulls.toUpperCase()}`;
|
|
7182
|
+
return result;
|
|
7183
|
+
}
|
|
7184
|
+
function deparseIndex(index) {
|
|
7185
|
+
const columns = index.columns.map(deparseIndexColumn).join(", ");
|
|
7186
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
7187
|
+
const nulls = index.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
7188
|
+
const predicate = index.where ? ` WHERE ${deparseExpr(index.where)}` : "";
|
|
7189
|
+
return `CREATE ${unique}INDEX ${quoteIdent(index.name)} ON ${quoteIdent(index.schema)}.${quoteIdent(index.table)} USING btree (${columns})${nulls}${predicate}`;
|
|
7190
|
+
}
|
|
7079
7191
|
|
|
7080
7192
|
// src/runtime/database-context.ts
|
|
7081
7193
|
var contexts = /* @__PURE__ */ new WeakMap();
|
|
@@ -7169,6 +7281,22 @@ function sequenceNextval(ctx, seq) {
|
|
|
7169
7281
|
}
|
|
7170
7282
|
function getMiscFunctions() {
|
|
7171
7283
|
const m = /* @__PURE__ */ new Map();
|
|
7284
|
+
m.set(
|
|
7285
|
+
"pg_get_indexdef",
|
|
7286
|
+
strict("text", (ctx, args) => {
|
|
7287
|
+
const oid = argInt(ctx, args[0]);
|
|
7288
|
+
const column = args[1] ? argInt(ctx, args[1]) : 0;
|
|
7289
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
7290
|
+
for (const index of schema.indexes.values()) {
|
|
7291
|
+
if (index.oid !== oid) continue;
|
|
7292
|
+
if (column === 0) return tv("text", deparseIndex(index));
|
|
7293
|
+
const part = index.columns[column - 1];
|
|
7294
|
+
return tv("text", part ? deparseIndexColumn(part) : "");
|
|
7295
|
+
}
|
|
7296
|
+
}
|
|
7297
|
+
return tv("text", null);
|
|
7298
|
+
})
|
|
7299
|
+
);
|
|
7172
7300
|
m.set("version", () => tv("text", PG_VERSION_TEXT));
|
|
7173
7301
|
m.set("current_database", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
|
|
7174
7302
|
m.set("current_catalog", (ctx) => tv("name", databaseCatalogContext(ctx.state).name));
|
|
@@ -12389,6 +12517,9 @@ function tryIndexedTableRows(env, table, alias, where) {
|
|
|
12389
12517
|
const key = uniqueKeyOf(env, table, spec, probeRow);
|
|
12390
12518
|
if (key === null) return [];
|
|
12391
12519
|
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
12520
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
12521
|
+
const index = schema?.indexes.get(spec.name);
|
|
12522
|
+
if (schema && index) schema.indexes.set(spec.name, { ...index, scans: (index.scans ?? 0n) + 1n });
|
|
12392
12523
|
return hits.map((i) => table.rowAt(i));
|
|
12393
12524
|
}
|
|
12394
12525
|
function tryIndexedFromItem(env, item, where) {
|
|
@@ -15151,8 +15282,8 @@ var Parser = class {
|
|
|
15151
15282
|
this.eatKw("column");
|
|
15152
15283
|
const column = this.ident();
|
|
15153
15284
|
let alterType = this.eatKw("type");
|
|
15154
|
-
if (!alterType && this.
|
|
15155
|
-
this.pos +=
|
|
15285
|
+
if (!alterType && this.atKw("set") && this.atKw("data", 1)) {
|
|
15286
|
+
this.pos += 2;
|
|
15156
15287
|
this.expectKw("type");
|
|
15157
15288
|
alterType = true;
|
|
15158
15289
|
}
|
|
@@ -19504,7 +19635,7 @@ function uniqueSpecsFor(env, table) {
|
|
|
19504
19635
|
const schema = env.ctx.state.schemas.get(table.schema);
|
|
19505
19636
|
if (schema) {
|
|
19506
19637
|
for (const idx of schema.indexes.values()) {
|
|
19507
|
-
if (idx.table !== table.name || !idx.unique || idx.isConstraint) continue;
|
|
19638
|
+
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
19508
19639
|
specs.push({
|
|
19509
19640
|
name: idx.name,
|
|
19510
19641
|
keys: idx.columns.map((c) => ({
|
|
@@ -20655,6 +20786,25 @@ var EngineCtx = class {
|
|
|
20655
20786
|
timezone() {
|
|
20656
20787
|
return this.state.getSetting("timezone") ?? "UTC";
|
|
20657
20788
|
}
|
|
20789
|
+
namespaceOid(input) {
|
|
20790
|
+
const trimmed = input.trim();
|
|
20791
|
+
const name = trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1).replaceAll('""', '"') : trimmed.toLowerCase();
|
|
20792
|
+
if (name === "pg_catalog") return 11;
|
|
20793
|
+
if (name === "information_schema") return 13212;
|
|
20794
|
+
const schema = this.state.schemas.get(name);
|
|
20795
|
+
if (!schema) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
20796
|
+
return schema.oid;
|
|
20797
|
+
}
|
|
20798
|
+
namespaceName(oid) {
|
|
20799
|
+
if (oid === 11) return "pg_catalog";
|
|
20800
|
+
if (oid === 13212) return "information_schema";
|
|
20801
|
+
for (const schema of this.state.schemas.values()) {
|
|
20802
|
+
if (schema.oid === oid) {
|
|
20803
|
+
return /^[a-z_][a-z0-9_$]*$/.test(schema.name) ? schema.name : `"${schema.name.replaceAll('"', '""')}"`;
|
|
20804
|
+
}
|
|
20805
|
+
}
|
|
20806
|
+
return null;
|
|
20807
|
+
}
|
|
20658
20808
|
zoneOffsetAt(utcMicros) {
|
|
20659
20809
|
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
20660
20810
|
}
|
|
@@ -20925,7 +21075,7 @@ function pgClass(ctx) {
|
|
|
20925
21075
|
for (const s of schemaData.sequences.values()) push(s.oid, s.name, s.schema, "S", 3, 1, false, true, s.temp);
|
|
20926
21076
|
for (const i of schemaData.indexes.values()) {
|
|
20927
21077
|
push(
|
|
20928
|
-
state.schemas.get(i.schema)?.tables.get(i.table)?.oid ?? 0,
|
|
21078
|
+
i.oid ?? state.schemas.get(i.schema)?.tables.get(i.table)?.oid ?? 0,
|
|
20929
21079
|
i.name,
|
|
20930
21080
|
i.schema,
|
|
20931
21081
|
"i",
|
|
@@ -21162,7 +21312,15 @@ function pgIndex(ctx) {
|
|
|
21162
21312
|
for (const i of schema.indexes.values()) {
|
|
21163
21313
|
const table = schema.tables.get(i.table);
|
|
21164
21314
|
const isPrimary = i.isConstraint && table?.constraints.some((c) => c.kind === "primary_key" && c.name === i.name) === true;
|
|
21165
|
-
rows.push([
|
|
21315
|
+
rows.push([
|
|
21316
|
+
i.oid ?? table?.oid ?? 0,
|
|
21317
|
+
table?.oid ?? 0,
|
|
21318
|
+
i.columns.length,
|
|
21319
|
+
i.unique,
|
|
21320
|
+
isPrimary,
|
|
21321
|
+
i.where !== null,
|
|
21322
|
+
i.valid !== false
|
|
21323
|
+
]);
|
|
21166
21324
|
}
|
|
21167
21325
|
}
|
|
21168
21326
|
return rel(
|
|
@@ -21172,12 +21330,73 @@ function pgIndex(ctx) {
|
|
|
21172
21330
|
["indnatts", "int2"],
|
|
21173
21331
|
["indisunique", "bool"],
|
|
21174
21332
|
["indisprimary", "bool"],
|
|
21175
|
-
["indpred", "bool"]
|
|
21333
|
+
["indpred", "bool"],
|
|
21334
|
+
["indisvalid", "bool"]
|
|
21176
21335
|
],
|
|
21177
21336
|
rows,
|
|
21178
21337
|
"pg_index"
|
|
21179
21338
|
);
|
|
21180
21339
|
}
|
|
21340
|
+
function pgStatUserTables(ctx) {
|
|
21341
|
+
return rel(
|
|
21342
|
+
[
|
|
21343
|
+
["relid", "oid"],
|
|
21344
|
+
["schemaname", "name"],
|
|
21345
|
+
["relname", "name"],
|
|
21346
|
+
["n_live_tup", "int8"]
|
|
21347
|
+
],
|
|
21348
|
+
[...allTables(ctx.state)].map((table) => [table.oid, table.schema, table.name, BigInt(table.rowCount())]),
|
|
21349
|
+
"pg_stat_user_tables"
|
|
21350
|
+
);
|
|
21351
|
+
}
|
|
21352
|
+
function pgDescription(ctx) {
|
|
21353
|
+
const rows = [];
|
|
21354
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
21355
|
+
for (const index of schema.indexes.values()) {
|
|
21356
|
+
if (index.comment) rows.push([index.oid ?? 0, 1259, 0, index.comment]);
|
|
21357
|
+
}
|
|
21358
|
+
}
|
|
21359
|
+
return rel(
|
|
21360
|
+
[
|
|
21361
|
+
["objoid", "oid"],
|
|
21362
|
+
["classoid", "oid"],
|
|
21363
|
+
["objsubid", "int4"],
|
|
21364
|
+
["description", "text"]
|
|
21365
|
+
],
|
|
21366
|
+
rows,
|
|
21367
|
+
"pg_description"
|
|
21368
|
+
);
|
|
21369
|
+
}
|
|
21370
|
+
function pgStatUserIndexes(ctx) {
|
|
21371
|
+
const rows = [];
|
|
21372
|
+
for (const schema of ctx.state.schemas.values()) {
|
|
21373
|
+
if (schema.name === "pg_catalog" || schema.name === "information_schema" || schema.name.startsWith("pg_toast"))
|
|
21374
|
+
continue;
|
|
21375
|
+
for (const index of schema.indexes.values()) {
|
|
21376
|
+
const table = schema.tables.get(index.table);
|
|
21377
|
+
rows.push([
|
|
21378
|
+
table?.oid ?? 0,
|
|
21379
|
+
index.oid ?? table?.oid ?? 0,
|
|
21380
|
+
schema.name,
|
|
21381
|
+
index.table,
|
|
21382
|
+
index.name,
|
|
21383
|
+
index.scans ?? 0n
|
|
21384
|
+
]);
|
|
21385
|
+
}
|
|
21386
|
+
}
|
|
21387
|
+
return rel(
|
|
21388
|
+
[
|
|
21389
|
+
["relid", "oid"],
|
|
21390
|
+
["indexrelid", "oid"],
|
|
21391
|
+
["schemaname", "name"],
|
|
21392
|
+
["relname", "name"],
|
|
21393
|
+
["indexrelname", "name"],
|
|
21394
|
+
["idx_scan", "int8"]
|
|
21395
|
+
],
|
|
21396
|
+
rows,
|
|
21397
|
+
"pg_stat_user_indexes"
|
|
21398
|
+
);
|
|
21399
|
+
}
|
|
21181
21400
|
function pgSequence(ctx) {
|
|
21182
21401
|
const rows = [];
|
|
21183
21402
|
for (const s of allSequences(ctx.state)) {
|
|
@@ -21292,7 +21511,7 @@ function pgIndexesView(ctx) {
|
|
|
21292
21511
|
const rows = [];
|
|
21293
21512
|
for (const schema of ctx.state.schemas.values()) {
|
|
21294
21513
|
for (const i of schema.indexes.values()) {
|
|
21295
|
-
rows.push([i.schema, i.table, i.name, null,
|
|
21514
|
+
rows.push([i.schema, i.table, i.name, null, deparseIndex(i)]);
|
|
21296
21515
|
}
|
|
21297
21516
|
}
|
|
21298
21517
|
return rel(
|
|
@@ -21613,6 +21832,12 @@ function buildCatalogRelation(ctx, schema, name) {
|
|
|
21613
21832
|
return pgConstraint(ctx);
|
|
21614
21833
|
case "pg_index":
|
|
21615
21834
|
return pgIndex(ctx);
|
|
21835
|
+
case "pg_description":
|
|
21836
|
+
return pgDescription(ctx);
|
|
21837
|
+
case "pg_stat_user_tables":
|
|
21838
|
+
return pgStatUserTables(ctx);
|
|
21839
|
+
case "pg_stat_user_indexes":
|
|
21840
|
+
return pgStatUserIndexes(ctx);
|
|
21616
21841
|
case "pg_sequence":
|
|
21617
21842
|
return pgSequence(ctx);
|
|
21618
21843
|
case "pg_sequences":
|
|
@@ -22196,6 +22421,7 @@ function executeCreateIndex(env, stmt) {
|
|
|
22196
22421
|
}
|
|
22197
22422
|
assertGinTrgmIndex(state, table, stmt);
|
|
22198
22423
|
schema.indexes.set(name, {
|
|
22424
|
+
oid: state.nextOid(),
|
|
22199
22425
|
name,
|
|
22200
22426
|
schema: schema.name,
|
|
22201
22427
|
table: table.name,
|
|
@@ -22208,7 +22434,8 @@ function executeCreateIndex(env, stmt) {
|
|
|
22208
22434
|
})),
|
|
22209
22435
|
where: stmt.where,
|
|
22210
22436
|
nullsNotDistinct: stmt.nullsNotDistinct,
|
|
22211
|
-
isConstraint: false
|
|
22437
|
+
isConstraint: false,
|
|
22438
|
+
valid: true
|
|
22212
22439
|
});
|
|
22213
22440
|
if (stmt.unique) {
|
|
22214
22441
|
try {
|
|
@@ -22216,7 +22443,9 @@ function executeCreateIndex(env, stmt) {
|
|
|
22216
22443
|
checkUnique(env, table, table.rowAt(i), i);
|
|
22217
22444
|
}
|
|
22218
22445
|
} catch (err) {
|
|
22219
|
-
schema.indexes.
|
|
22446
|
+
if (stmt.concurrently) schema.indexes.get(name).valid = false;
|
|
22447
|
+
else schema.indexes.delete(name);
|
|
22448
|
+
table.indexStores = null;
|
|
22220
22449
|
throw err;
|
|
22221
22450
|
}
|
|
22222
22451
|
}
|
|
@@ -23336,6 +23565,9 @@ var Statement = class _Statement {
|
|
|
23336
23565
|
const typed = params.map((p, i) => bindValueToTyped(p, i));
|
|
23337
23566
|
let last = null;
|
|
23338
23567
|
for (const stmt of this.statements) {
|
|
23568
|
+
if (stmt.type === "create_index" && stmt.concurrently && this.database.transactions.inTransaction) {
|
|
23569
|
+
throw pgError("misuse", "CREATE INDEX CONCURRENTLY cannot run inside a transaction block", "25001");
|
|
23570
|
+
}
|
|
23339
23571
|
const env = {
|
|
23340
23572
|
ctx: new EngineCtx(this.database.state),
|
|
23341
23573
|
params: typed,
|
|
@@ -24362,9 +24594,48 @@ var RENAME_DATABASE = new RegExp(`^alter\\s+database\\s+${IDENT}\\s+rename\\s+to
|
|
|
24362
24594
|
var identifier = (quoted, plain) => quoted === void 0 ? plain.toLowerCase() : quoted.replaceAll('""', '"');
|
|
24363
24595
|
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;
|
|
24364
24596
|
var LIMIT = /\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$/i;
|
|
24365
|
-
var
|
|
24366
|
-
var
|
|
24367
|
-
var
|
|
24597
|
+
var SQL_NAME = String.raw`(?:"[^"]+"|[A-Za-z_][\w$]*)`;
|
|
24598
|
+
var SQL_RELATION = String.raw`(?:"[^"]+"|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|[A-Za-z_][\w$]*))?`;
|
|
24599
|
+
var FROM_TABLE = new RegExp(String.raw`\bfrom\s+(${SQL_RELATION})`, "i");
|
|
24600
|
+
var UPDATE_TARGET = new RegExp(
|
|
24601
|
+
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]*)?$`,
|
|
24602
|
+
"i"
|
|
24603
|
+
);
|
|
24604
|
+
var DELETE_TARGET = new RegExp(
|
|
24605
|
+
String.raw`^delete\s+from\s+(${SQL_RELATION})(?:\s+(?:as\s+)?(${SQL_NAME}))?(?:\s+where\s+([\s\S]*?))?(?:\s+returning\s+[\s\S]*)?$`,
|
|
24606
|
+
"i"
|
|
24607
|
+
);
|
|
24608
|
+
var lockedMutationQuery = (sql) => {
|
|
24609
|
+
const match = UPDATE_TARGET.exec(sql) ?? DELETE_TARGET.exec(sql);
|
|
24610
|
+
const relation = match?.[1];
|
|
24611
|
+
if (!relation) return null;
|
|
24612
|
+
const alias = match?.[2];
|
|
24613
|
+
const where = match?.[3];
|
|
24614
|
+
const from = alias ? `${relation} AS ${alias}` : relation;
|
|
24615
|
+
return `SELECT * FROM ${from}${where ? ` WHERE ${where}` : ""}`;
|
|
24616
|
+
};
|
|
24617
|
+
var lockLiteral = (expr, params) => {
|
|
24618
|
+
if (!expr) return void 0;
|
|
24619
|
+
switch (expr.type) {
|
|
24620
|
+
case "null_lit":
|
|
24621
|
+
return null;
|
|
24622
|
+
case "string_lit":
|
|
24623
|
+
return expr.value;
|
|
24624
|
+
case "bool_lit":
|
|
24625
|
+
return expr.value ? "t" : "f";
|
|
24626
|
+
case "number_lit":
|
|
24627
|
+
return expr.raw.replace(/^\+/, "");
|
|
24628
|
+
case "param": {
|
|
24629
|
+
const value = params[expr.index - 1];
|
|
24630
|
+
if (value === void 0) return void 0;
|
|
24631
|
+
const typed = bindValueToTyped(value, expr.index - 1);
|
|
24632
|
+
if (typed.v === null) return null;
|
|
24633
|
+
return datumText(typed.t, typed.v, UTC_OUTPUT);
|
|
24634
|
+
}
|
|
24635
|
+
default:
|
|
24636
|
+
return void 0;
|
|
24637
|
+
}
|
|
24638
|
+
};
|
|
24368
24639
|
var TYPLEN = {
|
|
24369
24640
|
bool: 1,
|
|
24370
24641
|
int2: 2,
|
|
@@ -24383,7 +24654,7 @@ var TEXT_TYPES2 = /* @__PURE__ */ new Set(["text", "varchar", "bpchar", "name",
|
|
|
24383
24654
|
var PG_EPOCH_MS = 9466848e5;
|
|
24384
24655
|
var pgError2 = (category, message2, code) => new PostgresError(category, message2, code);
|
|
24385
24656
|
var errorFields = (error) => {
|
|
24386
|
-
if (error
|
|
24657
|
+
if (isPostgresError(error)) {
|
|
24387
24658
|
const message2 = error.message;
|
|
24388
24659
|
const fields = { code: error.sqlState, message: message2 };
|
|
24389
24660
|
const constraint = /constraint "([^"]+)"/.exec(message2)?.[1];
|
|
@@ -24779,8 +25050,8 @@ var Connection = class {
|
|
|
24779
25050
|
return outcome;
|
|
24780
25051
|
} catch (error) {
|
|
24781
25052
|
if (this.inTransaction) this.aborted = true;
|
|
24782
|
-
if (error
|
|
24783
|
-
const status = error
|
|
25053
|
+
if (isPostgresError(error) && error.sqlState === "40P01") this.cluster.unlockAll(this, true);
|
|
25054
|
+
const status = isPostgresError(error) ? error.sqlState : "XX000";
|
|
24784
25055
|
this.options.onLog?.({ pid: this.pid, sql: text, durationMs: performance.now() - started, status });
|
|
24785
25056
|
throw error;
|
|
24786
25057
|
}
|
|
@@ -24883,7 +25154,11 @@ var Connection = class {
|
|
|
24883
25154
|
const result = this.cluster.as(this, () => this.runQuery(db, text, params));
|
|
24884
25155
|
return { empty: false, result, tag: commandTag(result) };
|
|
24885
25156
|
};
|
|
24886
|
-
const
|
|
25157
|
+
const concurrentIndex = /\bconcurrently\b/i.test(text) && parse(text).some((stmt) => stmt.type === "create_index" && stmt.concurrently);
|
|
25158
|
+
if (concurrentIndex && this.transaction !== null) {
|
|
25159
|
+
throw new PostgresError("misuse", "CREATE INDEX CONCURRENTLY cannot run inside a transaction block", "25001");
|
|
25160
|
+
}
|
|
25161
|
+
const outcome = concurrentIndex ? run() : db.transaction(run);
|
|
24887
25162
|
if (this.transaction && mutatesDatabase(text)) {
|
|
24888
25163
|
this.transaction.statements.push({ kind: "sql", sql: text, params: params.slice() });
|
|
24889
25164
|
}
|
|
@@ -24972,14 +25247,48 @@ var Connection = class {
|
|
|
24972
25247
|
}
|
|
24973
25248
|
});
|
|
24974
25249
|
}
|
|
25250
|
+
/**
|
|
25251
|
+
* Hold the primary key until the transaction ends. PostgreSQL's unique index makes a
|
|
25252
|
+
* second `INSERT` of the same key wait; `ON CONFLICT DO UPDATE` then sees the committed row.
|
|
25253
|
+
* Without this, each session's workspace inserts the key and commit replay raises 23505.
|
|
25254
|
+
*/
|
|
25255
|
+
lockInsertedPrimaryKey(db, text, params) {
|
|
25256
|
+
let statements;
|
|
25257
|
+
try {
|
|
25258
|
+
statements = parse(text);
|
|
25259
|
+
} catch {
|
|
25260
|
+
return;
|
|
25261
|
+
}
|
|
25262
|
+
const stmt = statements.length === 1 ? statements[0] : void 0;
|
|
25263
|
+
if (stmt?.type !== "insert" || stmt.source === "default_values" || stmt.source.body.type !== "values") return;
|
|
25264
|
+
if (!stmt.columns) return;
|
|
25265
|
+
const table = db.state.findTable(stmt.table);
|
|
25266
|
+
const primary = table?.constraints.find((constraint) => constraint.kind === "primary_key");
|
|
25267
|
+
if (!primary || primary.columns.length === 0) return;
|
|
25268
|
+
const indexes = primary.columns.map((column) => stmt.columns?.indexOf(column) ?? -1);
|
|
25269
|
+
if (indexes.some((index) => index < 0)) return;
|
|
25270
|
+
const identities = [];
|
|
25271
|
+
for (const row of stmt.source.body.rows) {
|
|
25272
|
+
const identity = [];
|
|
25273
|
+
for (const index of indexes) {
|
|
25274
|
+
const value = lockLiteral(row[index], params);
|
|
25275
|
+
if (value === void 0) return;
|
|
25276
|
+
identity.push(value);
|
|
25277
|
+
}
|
|
25278
|
+
identities.push(identity);
|
|
25279
|
+
}
|
|
25280
|
+
const relation = stmt.table.join(".");
|
|
25281
|
+
for (const identity of identities) {
|
|
25282
|
+
const key = `row:${this.databaseName}:${relation}:${JSON.stringify(identity)}`;
|
|
25283
|
+
if (!this.cluster.tryLock(this, key, true)) throw new LockWait(key);
|
|
25284
|
+
}
|
|
25285
|
+
}
|
|
24975
25286
|
runQuery(db, text, params) {
|
|
24976
25287
|
const lock = ROW_LOCK.exec(text);
|
|
24977
25288
|
if (!lock) {
|
|
24978
|
-
const
|
|
24979
|
-
if (
|
|
24980
|
-
|
|
24981
|
-
this.lockRows(db, lockQuery, params, "wait");
|
|
24982
|
-
}
|
|
25289
|
+
const lockQuery = lockedMutationQuery(text);
|
|
25290
|
+
if (lockQuery) this.lockRows(db, lockQuery, params, "wait");
|
|
25291
|
+
this.lockInsertedPrimaryKey(db, text, params);
|
|
24983
25292
|
return db.prepare(text).textResult(...params);
|
|
24984
25293
|
}
|
|
24985
25294
|
let query = text.slice(0, lock.index).trimEnd();
|