@crvouga/mockingbird-service-postgres 3.6.0 → 3.7.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 +31 -0
- package/README.md +5 -0
- package/dist/admin.js +216 -26
- package/dist/admin.js.map +3 -3
- package/dist/api/schema-report.d.ts +32 -0
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/executor/relation.d.ts +1 -0
- package/dist/expressions/context.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +405 -27
- package/dist/index.js.map +4 -4
- package/dist/server.js +229 -27
- package/dist/server.js.map +3 -3
- package/dist/storage/database-state.d.ts +7 -0
- package/dist/types/regnamespace.d.ts +5 -0
- package/dist/types/value.d.ts +2 -0
- package/dist/unstable.js +388 -198
- package/dist/unstable.js.map +4 -4
- package/dist/wire/cli.js +457 -82
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/index.js +229 -27
- package/dist/wire/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2800,6 +2800,12 @@ var invalidName = () => {
|
|
|
2800
2800
|
throw pgError("syntax", "invalid name syntax", "42602");
|
|
2801
2801
|
};
|
|
2802
2802
|
function parseNamespaceInput(text) {
|
|
2803
|
+
const parsed = parseRelationInput(text);
|
|
2804
|
+
if ("oid" in parsed) return parsed;
|
|
2805
|
+
if (parsed.parts.length !== 1) invalidName();
|
|
2806
|
+
return { name: parsed.parts[0] };
|
|
2807
|
+
}
|
|
2808
|
+
function parseRelationInput(text) {
|
|
2803
2809
|
if (text === "-") return { oid: 0 };
|
|
2804
2810
|
if (/^\d+$/.test(text)) return { oid: parseOidText(text) };
|
|
2805
2811
|
const names = [];
|
|
@@ -2835,8 +2841,8 @@ function parseNamespaceInput(text) {
|
|
|
2835
2841
|
skipSpace();
|
|
2836
2842
|
if (at === text.length) invalidName();
|
|
2837
2843
|
}
|
|
2838
|
-
if (names.length
|
|
2839
|
-
return {
|
|
2844
|
+
if (names.length === 0 || names.length > 3) invalidName();
|
|
2845
|
+
return { parts: names };
|
|
2840
2846
|
}
|
|
2841
2847
|
function namespaceText(oid, name) {
|
|
2842
2848
|
if (oid === 0) return "-";
|
|
@@ -2909,6 +2915,7 @@ var TYPE_ALIASES = {
|
|
|
2909
2915
|
regtype: "regtype",
|
|
2910
2916
|
regproc: "regproc",
|
|
2911
2917
|
regnamespace: "regnamespace",
|
|
2918
|
+
xid: "xid",
|
|
2912
2919
|
oid: "oid",
|
|
2913
2920
|
void: "void",
|
|
2914
2921
|
record: "record",
|
|
@@ -2985,6 +2992,7 @@ var TYPE_OIDS = {
|
|
|
2985
2992
|
regproc: 24,
|
|
2986
2993
|
text: 25,
|
|
2987
2994
|
oid: 26,
|
|
2995
|
+
xid: 28,
|
|
2988
2996
|
json: 114,
|
|
2989
2997
|
float4: 700,
|
|
2990
2998
|
float8: 701,
|
|
@@ -3303,6 +3311,7 @@ function recordText(rec, ctx) {
|
|
|
3303
3311
|
return `(${parts.join(",")})`;
|
|
3304
3312
|
}
|
|
3305
3313
|
function datumText(t, v, ctx) {
|
|
3314
|
+
if (t === "regclass") return v === 0 ? "-" : ctx.relationName?.(v) ?? String(v);
|
|
3306
3315
|
if (t === "regnamespace") return namespaceText(v, ctx.namespaceName?.(v));
|
|
3307
3316
|
if (v === null) throw pgError("internal", "datumText called with null");
|
|
3308
3317
|
if (isArrayType(t)) return arrayText(v, ctx);
|
|
@@ -3313,6 +3322,7 @@ function datumText(t, v, ctx) {
|
|
|
3313
3322
|
case "int2":
|
|
3314
3323
|
case "int4":
|
|
3315
3324
|
case "oid":
|
|
3325
|
+
case "xid":
|
|
3316
3326
|
return String(v);
|
|
3317
3327
|
case "int8":
|
|
3318
3328
|
return String(v);
|
|
@@ -3377,6 +3387,12 @@ function datumText(t, v, ctx) {
|
|
|
3377
3387
|
}
|
|
3378
3388
|
var UTC_INPUT = { zoneOffsetForNaive: () => 0 };
|
|
3379
3389
|
function datumFromText(t, text, ctx) {
|
|
3390
|
+
if (t === "regclass") {
|
|
3391
|
+
const parsed = parseRelationInput(text);
|
|
3392
|
+
if ("oid" in parsed) return parsed.oid;
|
|
3393
|
+
if (!ctx.relationOid) throw unsupported("regclass input without a catalog");
|
|
3394
|
+
return ctx.relationOid(parsed.parts);
|
|
3395
|
+
}
|
|
3380
3396
|
if (t === "regnamespace") {
|
|
3381
3397
|
const parsed = parseNamespaceInput(text);
|
|
3382
3398
|
if ("oid" in parsed) return parsed.oid;
|
|
@@ -3398,6 +3414,7 @@ function datumFromText(t, text, ctx) {
|
|
|
3398
3414
|
case "int8":
|
|
3399
3415
|
return parseIntegerText(text, t);
|
|
3400
3416
|
case "oid":
|
|
3417
|
+
case "xid":
|
|
3401
3418
|
return parseOidText(text);
|
|
3402
3419
|
case "float4":
|
|
3403
3420
|
return Math.fround(parseFloatText(text, "float4"));
|
|
@@ -3554,6 +3571,8 @@ var TableData = class _TableData {
|
|
|
3554
3571
|
schema;
|
|
3555
3572
|
columns;
|
|
3556
3573
|
rows;
|
|
3574
|
+
rowXmins = /* @__PURE__ */ new WeakMap();
|
|
3575
|
+
slabXmins = null;
|
|
3557
3576
|
/** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
|
|
3558
3577
|
slab = null;
|
|
3559
3578
|
constraints;
|
|
@@ -3596,6 +3615,11 @@ var TableData = class _TableData {
|
|
|
3596
3615
|
materializeSlab() {
|
|
3597
3616
|
if (!this.slab) return;
|
|
3598
3617
|
this.rows = this.slab.materialize();
|
|
3618
|
+
if (this.slabXmins)
|
|
3619
|
+
this.rows.forEach((row, index) => {
|
|
3620
|
+
this.rowXmins.set(row, this.slabXmins[index] ?? 2);
|
|
3621
|
+
});
|
|
3622
|
+
this.slabXmins = null;
|
|
3599
3623
|
this.slab = null;
|
|
3600
3624
|
this.indexStores = null;
|
|
3601
3625
|
}
|
|
@@ -3629,6 +3653,10 @@ var TableData = class _TableData {
|
|
|
3629
3653
|
} else {
|
|
3630
3654
|
t.rows = this.rows.map((r) => r.slice());
|
|
3631
3655
|
}
|
|
3656
|
+
t.rows.forEach((row, index) => {
|
|
3657
|
+
const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.rows[index]);
|
|
3658
|
+
if (xmin !== void 0) t.rowXmins.set(row, xmin);
|
|
3659
|
+
});
|
|
3632
3660
|
t.unlogged = this.unlogged;
|
|
3633
3661
|
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
3634
3662
|
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
@@ -3714,6 +3742,14 @@ var DatabaseState = class _DatabaseState {
|
|
|
3714
3742
|
clock;
|
|
3715
3743
|
changes = 0;
|
|
3716
3744
|
oidCounter = 16384;
|
|
3745
|
+
/** Local monotonic xid allocator; 2 denotes a row from a legacy snapshot without metadata. */
|
|
3746
|
+
xidCounter = 3;
|
|
3747
|
+
transactionXid = null;
|
|
3748
|
+
allocateXid() {
|
|
3749
|
+
if (this.xidCounter > 4294967295)
|
|
3750
|
+
throw pgError("program_limit_exceeded", "transaction ID space exhausted; xid wraparound is not modeled", "54000");
|
|
3751
|
+
return this.xidCounter++;
|
|
3752
|
+
}
|
|
3717
3753
|
inTransaction = false;
|
|
3718
3754
|
/** Transaction-only events: included in rollback clones, never in exported snapshots. */
|
|
3719
3755
|
pendingTriggers = [];
|
|
@@ -3927,6 +3963,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3927
3963
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3928
3964
|
s.tableStats = new Map(this.tableStats);
|
|
3929
3965
|
s.oidCounter = this.oidCounter;
|
|
3966
|
+
s.xidCounter = this.xidCounter;
|
|
3967
|
+
s.transactionXid = this.transactionXid;
|
|
3930
3968
|
for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
|
|
3931
3969
|
return s;
|
|
3932
3970
|
}
|
|
@@ -3946,6 +3984,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3946
3984
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3947
3985
|
s.tableStats = new Map(this.tableStats);
|
|
3948
3986
|
s.oidCounter = this.oidCounter;
|
|
3987
|
+
s.xidCounter = this.xidCounter;
|
|
3988
|
+
s.transactionXid = this.transactionXid;
|
|
3949
3989
|
for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
|
|
3950
3990
|
return s;
|
|
3951
3991
|
}
|
|
@@ -4021,6 +4061,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
4021
4061
|
this.sequenceCurrval = other.sequenceCurrval;
|
|
4022
4062
|
this.tableStats = other.tableStats;
|
|
4023
4063
|
this.oidCounter = other.oidCounter;
|
|
4064
|
+
this.xidCounter = Math.max(this.xidCounter, other.xidCounter);
|
|
4065
|
+
this.transactionXid = other.transactionXid;
|
|
4024
4066
|
}
|
|
4025
4067
|
/** @internal Snapshot codec access to the oid allocator. */
|
|
4026
4068
|
snapshotOidCounter() {
|
|
@@ -4097,6 +4139,11 @@ function zoneOffsetForNaive(zone, naiveMicros) {
|
|
|
4097
4139
|
// src/expressions/context.ts
|
|
4098
4140
|
var EngineCtx = class {
|
|
4099
4141
|
state;
|
|
4142
|
+
writeXid = null;
|
|
4143
|
+
rowVersionXid() {
|
|
4144
|
+
if (this.state.inTransaction) return this.state.transactionXid ??= this.state.allocateXid();
|
|
4145
|
+
return this.writeXid ??= this.state.allocateXid();
|
|
4146
|
+
}
|
|
4100
4147
|
/** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
|
|
4101
4148
|
txNow;
|
|
4102
4149
|
/** statement timestamp */
|
|
@@ -4111,6 +4158,23 @@ var EngineCtx = class {
|
|
|
4111
4158
|
timezone() {
|
|
4112
4159
|
return this.state.getSetting("timezone") ?? "UTC";
|
|
4113
4160
|
}
|
|
4161
|
+
relationOid(parts) {
|
|
4162
|
+
const target = this.state.resolveRelationSchema(parts);
|
|
4163
|
+
const schema = target ? this.state.schemas.get(target.schema) : void 0;
|
|
4164
|
+
const relation = target ? schema?.sequences.get(target.name) ?? schema?.tables.get(target.name) ?? schema?.views.get(target.name) : void 0;
|
|
4165
|
+
if (!relation) throw pgError("undefined_table", `relation "${parts.join(".")}" does not exist`, "42P01");
|
|
4166
|
+
return relation.oid;
|
|
4167
|
+
}
|
|
4168
|
+
relationName(oid) {
|
|
4169
|
+
for (const schema of this.state.schemas.values()) {
|
|
4170
|
+
for (const relation of [...schema.tables.values(), ...schema.sequences.values(), ...schema.views.values()]) {
|
|
4171
|
+
if (relation.oid !== oid) continue;
|
|
4172
|
+
const visible = this.state.resolveRelationSchema([relation.name])?.schema === schema.name;
|
|
4173
|
+
return (visible ? [relation.name] : [schema.name, relation.name]).map(quoteIdentifier).join(".");
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
return null;
|
|
4177
|
+
}
|
|
4114
4178
|
namespaceOid(name) {
|
|
4115
4179
|
const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
|
|
4116
4180
|
if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
@@ -4411,7 +4475,7 @@ function castTo(env, value, target, opts = {}) {
|
|
|
4411
4475
|
return tv(target, makeArray(toElem, items, arr.dims.slice(), arr.lbs.slice()));
|
|
4412
4476
|
}
|
|
4413
4477
|
if (from === "oid" && target === "regnamespace") return tv(target, v);
|
|
4414
|
-
if (from === "regnamespace" && (target === "oid" || target === "int4" || target === "int8")) {
|
|
4478
|
+
if ((from === "regnamespace" || from === "regclass") && (target === "oid" || target === "int4" || target === "int8")) {
|
|
4415
4479
|
return tv(target, target === "int8" ? BigInt(v) : v);
|
|
4416
4480
|
}
|
|
4417
4481
|
if ((from === "int4" || from === "int8" || from === "int2") && (target === "oid" || target === "regnamespace")) {
|
|
@@ -5112,6 +5176,8 @@ function datumCompare(t, a, b, ctx = DEFAULT_COMPARE_CTX) {
|
|
|
5112
5176
|
case "int2":
|
|
5113
5177
|
case "int4":
|
|
5114
5178
|
case "oid":
|
|
5179
|
+
case "regclass":
|
|
5180
|
+
case "xid":
|
|
5115
5181
|
case "regnamespace":
|
|
5116
5182
|
case "date":
|
|
5117
5183
|
return expectNumber(a, t) - expectNumber(b, t);
|
|
@@ -5231,6 +5297,8 @@ function datumKey(t, v) {
|
|
|
5231
5297
|
case "int2":
|
|
5232
5298
|
case "int4":
|
|
5233
5299
|
case "oid":
|
|
5300
|
+
case "regclass":
|
|
5301
|
+
case "xid":
|
|
5234
5302
|
case "date":
|
|
5235
5303
|
return `i${v}`;
|
|
5236
5304
|
case "float4":
|
|
@@ -8136,7 +8204,7 @@ function databaseCatalogContext(state) {
|
|
|
8136
8204
|
var PG_VERSION_TEXT = "PostgreSQL 18.3 (postgres-mem) on TypeScript, in-memory engine";
|
|
8137
8205
|
function findSequenceForCall(ctx, name) {
|
|
8138
8206
|
const parts = parseQualifiedName(name);
|
|
8139
|
-
const seq = ctx.state.findSequence(parts);
|
|
8207
|
+
const seq = /^\d+$/.test(name) ? [...ctx.state.schemas.values()].flatMap((schema) => [...schema.sequences.values()]).find((sequence) => sequence.oid === Number(name)) : ctx.state.findSequence(parts);
|
|
8140
8208
|
if (!seq) {
|
|
8141
8209
|
throw pgError("undefined_table", `relation "${name}" does not exist`, "42P01");
|
|
8142
8210
|
}
|
|
@@ -8360,14 +8428,16 @@ function getMiscFunctions() {
|
|
|
8360
8428
|
"pg_get_serial_sequence",
|
|
8361
8429
|
strict("text", (ctx, args) => {
|
|
8362
8430
|
const tableName2 = argText(ctx, args[0]);
|
|
8363
|
-
const column = argText(ctx, args[1])
|
|
8431
|
+
const column = argText(ctx, args[1]);
|
|
8364
8432
|
const table = ctx.state.findTable(parseQualifiedName(tableName2));
|
|
8365
8433
|
if (!table) throw pgError("undefined_table", `relation "${tableName2}" does not exist`, "42P01");
|
|
8366
8434
|
const col = table.columns.find((c) => c.name === column);
|
|
8367
8435
|
if (!col)
|
|
8368
8436
|
throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
|
|
8369
|
-
|
|
8370
|
-
|
|
8437
|
+
const seq = [...ctx.state.schemas.get(table.schema)?.sequences.values() ?? []].find(
|
|
8438
|
+
(candidate) => candidate.ownedBy?.table === table.name && candidate.ownedBy.column === column
|
|
8439
|
+
);
|
|
8440
|
+
return tv("text", seq ? [seq.schema, seq.name].map(quoteIdent).join(".") : null);
|
|
8371
8441
|
})
|
|
8372
8442
|
);
|
|
8373
8443
|
m.set("format_type", (ctx, args) => {
|
|
@@ -13311,7 +13381,7 @@ var RowScope = class {
|
|
|
13311
13381
|
let count2 = 0;
|
|
13312
13382
|
for (let i = 0; i < this.columns.length; i++) {
|
|
13313
13383
|
const c = this.columns[i];
|
|
13314
|
-
if (c.name === name2 && !c.hidden) {
|
|
13384
|
+
if (c.name === name2 && (!c.hidden || c.system)) {
|
|
13315
13385
|
count2++;
|
|
13316
13386
|
if (count2 > 1) {
|
|
13317
13387
|
throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
|
|
@@ -13349,7 +13419,7 @@ var RowScope = class {
|
|
|
13349
13419
|
const names = [];
|
|
13350
13420
|
for (let i = 0; i < this.columns.length; i++) {
|
|
13351
13421
|
const c = this.columns[i];
|
|
13352
|
-
if (c.table === rangeVar) {
|
|
13422
|
+
if (c.table === rangeVar && !c.system) {
|
|
13353
13423
|
types.push(c.type);
|
|
13354
13424
|
values.push(this.row[i] ?? null);
|
|
13355
13425
|
names.push(c.name);
|
|
@@ -13792,7 +13862,7 @@ function tryIndexedFromItem(env, item, where) {
|
|
|
13792
13862
|
if (!table) return null;
|
|
13793
13863
|
const alias = item.alias ?? item.name[item.name.length - 1];
|
|
13794
13864
|
table.materializeSlab();
|
|
13795
|
-
return tryIndexedTableRows(env, table, alias, where);
|
|
13865
|
+
return tryIndexedTableRows(env, table, alias, where)?.map((row) => [...row, table.rowXmins.get(row) ?? 2]) ?? null;
|
|
13796
13866
|
}
|
|
13797
13867
|
function joinKeyFromRow(row, colIdxs, srcTypes, unifiedTypes, ctx) {
|
|
13798
13868
|
const parts = [];
|
|
@@ -16282,6 +16352,7 @@ var Parser = class {
|
|
|
16282
16352
|
let sqlBody = null;
|
|
16283
16353
|
let volatility = null;
|
|
16284
16354
|
let strict2 = false;
|
|
16355
|
+
const settings = {};
|
|
16285
16356
|
for (; ; ) {
|
|
16286
16357
|
if (this.eatKw("language")) {
|
|
16287
16358
|
language = this.peek().type === "string" ? this.next().value : this.ident(true);
|
|
@@ -16339,8 +16410,23 @@ var Parser = class {
|
|
|
16339
16410
|
continue;
|
|
16340
16411
|
}
|
|
16341
16412
|
if (this.eatKw("set")) {
|
|
16342
|
-
this.ident(true);
|
|
16343
|
-
if (this.
|
|
16413
|
+
const key = this.ident(true).toLowerCase();
|
|
16414
|
+
if (this.eatKw("from")) {
|
|
16415
|
+
this.expectKw("current");
|
|
16416
|
+
settings[key] = "__from_current__";
|
|
16417
|
+
} else {
|
|
16418
|
+
if (!this.eatOp("=")) this.expectKw("to");
|
|
16419
|
+
const values = [];
|
|
16420
|
+
do {
|
|
16421
|
+
const value = this.next();
|
|
16422
|
+
if (!["string", "ident", "quoted_ident", "number"].includes(value.type))
|
|
16423
|
+
this.errorAt(value, "expected setting value");
|
|
16424
|
+
values.push(
|
|
16425
|
+
key === "search_path" && /[^a-z0-9_]/.test(value.value) ? `"${value.value.replaceAll('"', '""')}"` : value.value
|
|
16426
|
+
);
|
|
16427
|
+
} while (this.eatPunct(","));
|
|
16428
|
+
settings[key] = values.join(", ");
|
|
16429
|
+
}
|
|
16344
16430
|
continue;
|
|
16345
16431
|
}
|
|
16346
16432
|
if (this.eatKw("window")) throw unsupported("window functions via CREATE FUNCTION");
|
|
@@ -16420,7 +16506,8 @@ var Parser = class {
|
|
|
16420
16506
|
body,
|
|
16421
16507
|
sqlBody,
|
|
16422
16508
|
volatility,
|
|
16423
|
-
strict: strict2
|
|
16509
|
+
strict: strict2,
|
|
16510
|
+
settings
|
|
16424
16511
|
};
|
|
16425
16512
|
}
|
|
16426
16513
|
parseCreateTrigger(orReplace, constraint) {
|
|
@@ -16529,8 +16616,8 @@ var Parser = class {
|
|
|
16529
16616
|
type: "alter_sequence",
|
|
16530
16617
|
name,
|
|
16531
16618
|
ifExists,
|
|
16532
|
-
options: {
|
|
16533
|
-
|
|
16619
|
+
options: {},
|
|
16620
|
+
renameTo: to
|
|
16534
16621
|
};
|
|
16535
16622
|
}
|
|
16536
16623
|
if (this.atKw("set") && (this.atKw("logged", 1) || this.atKw("unlogged", 1))) {
|
|
@@ -19106,7 +19193,20 @@ function runFunctionBody(env, fn, args) {
|
|
|
19106
19193
|
}
|
|
19107
19194
|
return last;
|
|
19108
19195
|
}
|
|
19196
|
+
function withFunctionSettings(env, fn, run) {
|
|
19197
|
+
const state = env.ctx.state;
|
|
19198
|
+
const saved = new Map(state.localSettings);
|
|
19199
|
+
for (const [name, value] of Object.entries(fn.settings ?? {})) state.localSettings.set(name, value);
|
|
19200
|
+
try {
|
|
19201
|
+
return run();
|
|
19202
|
+
} finally {
|
|
19203
|
+
state.localSettings = saved;
|
|
19204
|
+
}
|
|
19205
|
+
}
|
|
19109
19206
|
function callSqlFunctionScalar(env, fn, args) {
|
|
19207
|
+
return withFunctionSettings(env, fn, () => callSqlFunctionScalarBody(env, fn, args));
|
|
19208
|
+
}
|
|
19209
|
+
function callSqlFunctionScalarBody(env, fn, args) {
|
|
19110
19210
|
const retT = fn.returns ?? "text";
|
|
19111
19211
|
if (fn.language === "plpgsql") {
|
|
19112
19212
|
return callPlpgsqlScalar(env, fn, args);
|
|
@@ -19136,6 +19236,9 @@ function callSqlFunctionScalar(env, fn, args) {
|
|
|
19136
19236
|
return castTo(env.ctx, tv(rawT, raw), retT, {});
|
|
19137
19237
|
}
|
|
19138
19238
|
function callSqlFunctionSet(env, fn, args) {
|
|
19239
|
+
return withFunctionSettings(env, fn, () => callSqlFunctionSetBody(env, fn, args));
|
|
19240
|
+
}
|
|
19241
|
+
function callSqlFunctionSetBody(env, fn, args) {
|
|
19139
19242
|
if (fn.language === "plpgsql") {
|
|
19140
19243
|
return callPlpgsqlSet(env, fn, args);
|
|
19141
19244
|
}
|
|
@@ -19290,15 +19393,17 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19290
19393
|
return rel3;
|
|
19291
19394
|
}
|
|
19292
19395
|
function renameWithAliases(rel3, label, colAliases, what) {
|
|
19293
|
-
|
|
19396
|
+
const visibleWidth = rel3.columns.filter((column) => !column.system).length;
|
|
19397
|
+
if (colAliases && colAliases.length > visibleWidth) {
|
|
19294
19398
|
throw pgError(
|
|
19295
19399
|
"invalid_column_reference",
|
|
19296
|
-
`table "${what}" has ${
|
|
19400
|
+
`table "${what}" has ${visibleWidth} columns available but ${colAliases.length} columns specified`,
|
|
19297
19401
|
"42P10"
|
|
19298
19402
|
);
|
|
19299
19403
|
}
|
|
19300
19404
|
const columns = rel3.columns.map((c, i) => ({
|
|
19301
|
-
|
|
19405
|
+
...c,
|
|
19406
|
+
name: c.system ? c.name : colAliases?.[i] ?? c.name,
|
|
19302
19407
|
type: c.type === UNKNOWN ? "text" : c.type,
|
|
19303
19408
|
table: label
|
|
19304
19409
|
}));
|
|
@@ -19319,8 +19424,11 @@ function materializeItem(env, item, scope) {
|
|
|
19319
19424
|
if (table) {
|
|
19320
19425
|
table.materializeSlab();
|
|
19321
19426
|
const rel3 = {
|
|
19322
|
-
columns:
|
|
19323
|
-
|
|
19427
|
+
columns: [
|
|
19428
|
+
...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
|
|
19429
|
+
{ name: "xmin", type: "xid", table: label, hidden: true, system: true }
|
|
19430
|
+
],
|
|
19431
|
+
rows: table.rows.map((row) => [...row, table.rowXmins.get(row) ?? 2])
|
|
19324
19432
|
};
|
|
19325
19433
|
return { rel: renameWithAliases(rel3, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
|
|
19326
19434
|
}
|
|
@@ -19950,7 +20058,7 @@ function resolveColIdx(columns, parts) {
|
|
|
19950
20058
|
let found = -1;
|
|
19951
20059
|
for (let i = 0; i < columns.length; i++) {
|
|
19952
20060
|
const c = columns[i];
|
|
19953
|
-
if (!c.hidden && c.name === name2) {
|
|
20061
|
+
if ((!c.hidden || c.system) && c.name === name2) {
|
|
19954
20062
|
if (found !== -1) return null;
|
|
19955
20063
|
found = i;
|
|
19956
20064
|
}
|
|
@@ -20534,7 +20642,7 @@ function expandTargets(core, columns) {
|
|
|
20534
20642
|
let any = false;
|
|
20535
20643
|
for (let i = 0; i < columns.length; i++) {
|
|
20536
20644
|
const c = columns[i];
|
|
20537
|
-
if (c.table === label) {
|
|
20645
|
+
if (c.table === label && !c.system) {
|
|
20538
20646
|
any = true;
|
|
20539
20647
|
items.push({ name: c.name, kind: "col", colIdx: i, expr: null, hidden: false });
|
|
20540
20648
|
}
|
|
@@ -21139,6 +21247,7 @@ var TransactionManager = class {
|
|
|
21139
21247
|
this.base = null;
|
|
21140
21248
|
this.savepoints = [];
|
|
21141
21249
|
this.state.inTransaction = false;
|
|
21250
|
+
this.state.transactionXid = null;
|
|
21142
21251
|
this.state.localSettings.clear();
|
|
21143
21252
|
this.state.pendingTriggers = [];
|
|
21144
21253
|
this.state.constraintModes.clear();
|
|
@@ -21151,6 +21260,7 @@ var TransactionManager = class {
|
|
|
21151
21260
|
this.base = null;
|
|
21152
21261
|
this.savepoints = [];
|
|
21153
21262
|
this.state.inTransaction = false;
|
|
21263
|
+
this.state.transactionXid = null;
|
|
21154
21264
|
this.state.localSettings.clear();
|
|
21155
21265
|
this.state.pendingTriggers = [];
|
|
21156
21266
|
this.state.constraintModes.clear();
|
|
@@ -21185,6 +21295,7 @@ var TransactionManager = class {
|
|
|
21185
21295
|
this.base = null;
|
|
21186
21296
|
this.savepoints = [];
|
|
21187
21297
|
this.state.inTransaction = false;
|
|
21298
|
+
this.state.transactionXid = null;
|
|
21188
21299
|
this.freezeDepth = 0;
|
|
21189
21300
|
}
|
|
21190
21301
|
};
|
|
@@ -21388,6 +21499,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
21388
21499
|
const updated = existing.slice();
|
|
21389
21500
|
applyUpdateSets(env, table, sets, scope, updated);
|
|
21390
21501
|
computeGeneratedColumns(env, table, updated);
|
|
21502
|
+
table.rowXmins.set(updated, env.ctx.rowVersionXid());
|
|
21391
21503
|
rows[existingIdx] = updated;
|
|
21392
21504
|
indexUpdateRow(env, table, existingIdx, existing, updated);
|
|
21393
21505
|
checkNotNull(env, table, updated);
|
|
@@ -21473,6 +21585,7 @@ function executeInsert(env0, stmt) {
|
|
|
21473
21585
|
checkChecks(env, table, newRow);
|
|
21474
21586
|
checkUnique(env, table, newRow, rows.length);
|
|
21475
21587
|
checkForeignKeys(env, table, newRow);
|
|
21588
|
+
table.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21476
21589
|
rows.push(newRow);
|
|
21477
21590
|
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
21478
21591
|
insertedCount++;
|
|
@@ -21590,6 +21703,7 @@ function executeUpdate(env0, stmt) {
|
|
|
21590
21703
|
if (fired.row === null) continue;
|
|
21591
21704
|
const finalRow = fired.row;
|
|
21592
21705
|
computeGeneratedColumns(env, table, finalRow);
|
|
21706
|
+
table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
|
|
21593
21707
|
rows[ri] = finalRow;
|
|
21594
21708
|
try {
|
|
21595
21709
|
checkNotNull(env, table, finalRow);
|
|
@@ -21709,6 +21823,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
21709
21823
|
for (const c of con.columns) {
|
|
21710
21824
|
newRow[child.columnIndex(c)] = null;
|
|
21711
21825
|
}
|
|
21826
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21712
21827
|
childRows[i] = newRow;
|
|
21713
21828
|
checkNotNull(env, child, newRow);
|
|
21714
21829
|
checkChecks(env, child, newRow);
|
|
@@ -21722,6 +21837,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
21722
21837
|
const ci = child.columnIndex(c);
|
|
21723
21838
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
21724
21839
|
}
|
|
21840
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21725
21841
|
childRows[i] = newRow;
|
|
21726
21842
|
checkNotNull(env, child, newRow);
|
|
21727
21843
|
checkChecks(env, child, newRow);
|
|
@@ -21759,6 +21875,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21759
21875
|
const refIdx = table.columnIndex(con.refColumns[c]);
|
|
21760
21876
|
newRow[localIdx] = newRows[r][refIdx] ?? null;
|
|
21761
21877
|
}
|
|
21878
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21762
21879
|
childRows[i] = newRow;
|
|
21763
21880
|
checkChecks(env, child, newRow);
|
|
21764
21881
|
}
|
|
@@ -21768,6 +21885,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21768
21885
|
for (const i of idxs) {
|
|
21769
21886
|
const newRow = childRows[i].slice();
|
|
21770
21887
|
for (const c of con.columns) newRow[child.columnIndex(c)] = null;
|
|
21888
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21771
21889
|
childRows[i] = newRow;
|
|
21772
21890
|
checkNotNull(env, child, newRow);
|
|
21773
21891
|
}
|
|
@@ -21780,6 +21898,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21780
21898
|
const ci = child.columnIndex(c);
|
|
21781
21899
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
21782
21900
|
}
|
|
21901
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21783
21902
|
childRows[i] = newRow;
|
|
21784
21903
|
checkForeignKeys(env, child, newRow);
|
|
21785
21904
|
}
|
|
@@ -24824,13 +24943,63 @@ function executeCreateSequence(env, stmt) {
|
|
|
24824
24943
|
schema.sequences.set(name, seq);
|
|
24825
24944
|
return commandResult("CREATE SEQUENCE", 0);
|
|
24826
24945
|
}
|
|
24946
|
+
function renameSequenceReferences(env, seq, newName) {
|
|
24947
|
+
const state = env.ctx.state;
|
|
24948
|
+
const matches2 = (value) => {
|
|
24949
|
+
try {
|
|
24950
|
+
const input = parseRelationInput(value);
|
|
24951
|
+
if ("oid" in input) return input.oid === seq.oid;
|
|
24952
|
+
return state.findSequence(input.parts)?.oid === seq.oid;
|
|
24953
|
+
} catch {
|
|
24954
|
+
return false;
|
|
24955
|
+
}
|
|
24956
|
+
};
|
|
24957
|
+
const rewrite = (value) => {
|
|
24958
|
+
if (Array.isArray(value)) return value.map(rewrite);
|
|
24959
|
+
if (value === null || typeof value !== "object") return value;
|
|
24960
|
+
const node = value;
|
|
24961
|
+
const updated = Object.fromEntries(Object.entries(node).map(([key, child]) => [key, rewrite(child)]));
|
|
24962
|
+
const rewriteLiteral = (literal2) => {
|
|
24963
|
+
if (literal2 && typeof literal2 === "object" && "type" in literal2 && literal2.type === "string_lit" && "value" in literal2 && typeof literal2.value === "string" && matches2(literal2.value)) {
|
|
24964
|
+
return { ...literal2, value: [seq.schema, newName].map(quoteIdent).join(".") };
|
|
24965
|
+
}
|
|
24966
|
+
return literal2;
|
|
24967
|
+
};
|
|
24968
|
+
if (node.type === "func" && Array.isArray(node.name) && ["nextval", "currval", "setval"].includes(String(node.name.at(-1))) && Array.isArray(updated.args))
|
|
24969
|
+
updated.args[0] = rewriteLiteral(updated.args[0]);
|
|
24970
|
+
if (node.type === "cast" && node.target && typeof node.target === "object" && "parts" in node.target && Array.isArray(node.target.parts) && node.target.parts.at(-1) === "regclass")
|
|
24971
|
+
updated.expr = rewriteLiteral(updated.expr);
|
|
24972
|
+
return updated;
|
|
24973
|
+
};
|
|
24974
|
+
for (const schema of state.schemas.values()) {
|
|
24975
|
+
for (const table of [...schema.tables.values()]) {
|
|
24976
|
+
const writable = state.ensureWritableTable(table);
|
|
24977
|
+
for (const column of writable.columns) {
|
|
24978
|
+
if (column.defaultExpr) column.defaultExpr = rewrite(column.defaultExpr);
|
|
24979
|
+
if (column.identity?.sequence === `${seq.schema}.${seq.name}`)
|
|
24980
|
+
column.identity = { ...column.identity, sequence: `${seq.schema}.${newName}` };
|
|
24981
|
+
}
|
|
24982
|
+
}
|
|
24983
|
+
}
|
|
24984
|
+
}
|
|
24827
24985
|
function executeAlterSequence(env, stmt) {
|
|
24828
24986
|
const state = env.ctx.state;
|
|
24829
|
-
const
|
|
24830
|
-
if (!
|
|
24987
|
+
const foundSequence = state.findSequence(stmt.name);
|
|
24988
|
+
if (!foundSequence) {
|
|
24831
24989
|
if (stmt.ifExists) return commandResult("ALTER SEQUENCE", 0);
|
|
24832
24990
|
throw pgError("undefined_table", `relation "${stmt.name.join(".")}" does not exist`, "42P01");
|
|
24833
24991
|
}
|
|
24992
|
+
const seq = state.ensureWritableSequence(foundSequence);
|
|
24993
|
+
if (stmt.renameTo) {
|
|
24994
|
+
const schema = state.getSchema(seq.schema);
|
|
24995
|
+
if (schema.hasRelation(stmt.renameTo))
|
|
24996
|
+
throw pgError("duplicate_table", `relation "${stmt.renameTo}" already exists`, "42P07");
|
|
24997
|
+
renameSequenceReferences(env, seq, stmt.renameTo);
|
|
24998
|
+
schema.sequences.delete(seq.name);
|
|
24999
|
+
seq.name = stmt.renameTo;
|
|
25000
|
+
schema.sequences.set(seq.name, seq);
|
|
25001
|
+
return commandResult("ALTER SEQUENCE", 0);
|
|
25002
|
+
}
|
|
24834
25003
|
const o = stmt.options;
|
|
24835
25004
|
if (o.logged !== void 0) {
|
|
24836
25005
|
setSequenceLogged(state, seq, o.logged);
|
|
@@ -24888,7 +25057,7 @@ function buildColumn(env, schema, tableName2, def, out, taken) {
|
|
|
24888
25057
|
col.defaultExpr = {
|
|
24889
25058
|
type: "func",
|
|
24890
25059
|
name: ["nextval"],
|
|
24891
|
-
args: [{ type: "string_lit", value:
|
|
25060
|
+
args: [{ type: "string_lit", value: [schema.name, seqName].map(quoteIdent).join(".") }]
|
|
24892
25061
|
};
|
|
24893
25062
|
}
|
|
24894
25063
|
for (const c of def.constraints) {
|
|
@@ -25141,7 +25310,11 @@ function executeCreateTableAs(env, stmt) {
|
|
|
25141
25310
|
const table = new TableData(schema.name, name, columns, state.nextOid(), stmt.temp);
|
|
25142
25311
|
table.unlogged = stmt.unlogged === true;
|
|
25143
25312
|
if (stmt.withData) {
|
|
25144
|
-
for (const row of rel3.rows)
|
|
25313
|
+
for (const row of rel3.rows) {
|
|
25314
|
+
const stored = row.slice();
|
|
25315
|
+
table.rowXmins.set(stored, env.ctx.rowVersionXid());
|
|
25316
|
+
table.mutableRows().push(stored);
|
|
25317
|
+
}
|
|
25145
25318
|
}
|
|
25146
25319
|
schema.tables.set(name, table);
|
|
25147
25320
|
const count = stmt.withData ? table.rowCount() : 0;
|
|
@@ -25410,6 +25583,12 @@ function executeCreateFunction(env, stmt) {
|
|
|
25410
25583
|
language: lang,
|
|
25411
25584
|
body,
|
|
25412
25585
|
rawBody: stmt.body,
|
|
25586
|
+
settings: Object.fromEntries(
|
|
25587
|
+
Object.entries(stmt.settings ?? {}).map(([key, value]) => [
|
|
25588
|
+
key,
|
|
25589
|
+
value === "__from_current__" ? state.getSetting(key) ?? "" : value
|
|
25590
|
+
])
|
|
25591
|
+
),
|
|
25413
25592
|
strict: stmt.strict,
|
|
25414
25593
|
oid: state.nextOid()
|
|
25415
25594
|
};
|
|
@@ -25594,6 +25773,7 @@ function executeAlterTable(env, stmt) {
|
|
|
25594
25773
|
}
|
|
25595
25774
|
const next = row.slice();
|
|
25596
25775
|
next[idx] = nv;
|
|
25776
|
+
table.rowXmins.set(next, table.rowXmins.get(rows[i]) ?? 2);
|
|
25597
25777
|
rows[i] = next;
|
|
25598
25778
|
}
|
|
25599
25779
|
col.type = resolved.column;
|
|
@@ -25695,6 +25875,11 @@ function executeAlterTable(env, stmt) {
|
|
|
25695
25875
|
);
|
|
25696
25876
|
}
|
|
25697
25877
|
table.columns[idx].name = action.to;
|
|
25878
|
+
for (const seq of [...schema.sequences.values()]) {
|
|
25879
|
+
if (seq.ownedBy?.table === table.name && seq.ownedBy.column === action.from) {
|
|
25880
|
+
state.ensureWritableSequence(seq).ownedBy = { table: table.name, column: action.to };
|
|
25881
|
+
}
|
|
25882
|
+
}
|
|
25698
25883
|
for (const con of table.constraints) {
|
|
25699
25884
|
if (con.kind === "primary_key" || con.kind === "unique" || con.kind === "foreign_key") {
|
|
25700
25885
|
con.columns = con.columns.map((c) => c === action.from ? action.to : c);
|
|
@@ -25731,6 +25916,11 @@ function executeAlterTable(env, stmt) {
|
|
|
25731
25916
|
throw pgError("duplicate_table", `relation "${action.to}" already exists`, "42P07");
|
|
25732
25917
|
}
|
|
25733
25918
|
const oldName = table.name;
|
|
25919
|
+
for (const seq of [...schema.sequences.values()]) {
|
|
25920
|
+
if (seq.ownedBy?.table === oldName) {
|
|
25921
|
+
state.ensureWritableSequence(seq).ownedBy = { ...seq.ownedBy, table: action.to };
|
|
25922
|
+
}
|
|
25923
|
+
}
|
|
25734
25924
|
schema.tables.delete(table.name);
|
|
25735
25925
|
table.name = action.to;
|
|
25736
25926
|
schema.tables.set(action.to, table);
|
|
@@ -27320,6 +27510,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
27320
27510
|
writeBjv(
|
|
27321
27511
|
w,
|
|
27322
27512
|
{
|
|
27513
|
+
xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
27323
27514
|
name: table.name,
|
|
27324
27515
|
schema: table.schema,
|
|
27325
27516
|
columns: table.columns,
|
|
@@ -27417,8 +27608,14 @@ function decodeInner(snapshot, prng, clock) {
|
|
|
27417
27608
|
table.constraints = meta.constraints;
|
|
27418
27609
|
table.triggers = meta.triggers;
|
|
27419
27610
|
table.unlogged = meta.unlogged === true;
|
|
27611
|
+
table.slabXmins = meta.xmins ?? null;
|
|
27612
|
+
state.xidCounter = Math.max(
|
|
27613
|
+
state.xidCounter,
|
|
27614
|
+
(meta.xmins ?? []).reduce((max, xid) => Math.max(max, xid + 1), 3)
|
|
27615
|
+
);
|
|
27420
27616
|
if (meta.stats) state.tableStats.set(table.oid, meta.stats);
|
|
27421
27617
|
const rowCount = readVarintU32(r);
|
|
27618
|
+
if (meta.xmins && meta.xmins.length !== rowCount) throw snapshotError();
|
|
27422
27619
|
const slabColumns = [];
|
|
27423
27620
|
for (let c = 0; c < meta.columns.length; c++)
|
|
27424
27621
|
slabColumns.push(readPackedColumn(r, rowCount, intern, meta.columns[c].type.id));
|
|
@@ -27862,6 +28059,8 @@ function validateTableMetaBjv(value) {
|
|
|
27862
28059
|
if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
|
|
27863
28060
|
if (!Array.isArray(value.columns)) throw snapshotError();
|
|
27864
28061
|
for (const col of value.columns) validateColumnMeta(col);
|
|
28062
|
+
if (value.xmins !== void 0 && (!Array.isArray(value.xmins) || value.xmins.some((xid) => !Number.isInteger(xid) || xid < 2 || xid > 4294967295)))
|
|
28063
|
+
throw snapshotError();
|
|
27865
28064
|
}
|
|
27866
28065
|
function validateViewMetaBjv(value) {
|
|
27867
28066
|
if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
|
|
@@ -27955,6 +28154,7 @@ function forceSchemaIntern(pool, state, schemas) {
|
|
|
27955
28154
|
pool.forceId(table.name);
|
|
27956
28155
|
pool.forceId(table.schema);
|
|
27957
28156
|
forceBjvStrings(pool, {
|
|
28157
|
+
xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
27958
28158
|
name: table.name,
|
|
27959
28159
|
schema: table.schema,
|
|
27960
28160
|
columns: table.columns,
|
|
@@ -28032,10 +28232,188 @@ function forceBjvStrings(pool, value) {
|
|
|
28032
28232
|
}
|
|
28033
28233
|
}
|
|
28034
28234
|
registerCodec({ encodeDatabaseState, decodeDatabaseState });
|
|
28235
|
+
|
|
28236
|
+
// src/api/schema-report.ts
|
|
28237
|
+
var bounded = (value, fallback2, max) => {
|
|
28238
|
+
if (value === void 0) return fallback2;
|
|
28239
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > max)
|
|
28240
|
+
throw new RangeError(`limit must be an integer between 1 and ${max}`);
|
|
28241
|
+
return value;
|
|
28242
|
+
};
|
|
28243
|
+
function analyzeSchema(sql, options = {}) {
|
|
28244
|
+
const inputLimit = bounded(options.maxInputBytes, 1024 * 1024, 4 * 1024 * 1024);
|
|
28245
|
+
const statementLimit = bounded(options.maxStatements, 1e3, 1e4);
|
|
28246
|
+
const diagnosticLimit = bounded(options.maxDiagnostics, 100, 1e3);
|
|
28247
|
+
const report = {
|
|
28248
|
+
engine: "mockingbird-postgres",
|
|
28249
|
+
version: true ? "3.7.0" : "source-tree",
|
|
28250
|
+
statements: 0,
|
|
28251
|
+
accepted: 0,
|
|
28252
|
+
compatible: null,
|
|
28253
|
+
truncated: false,
|
|
28254
|
+
diagnostics: []
|
|
28255
|
+
};
|
|
28256
|
+
const add = (diagnostic) => {
|
|
28257
|
+
if (report.diagnostics.length >= diagnosticLimit) {
|
|
28258
|
+
report.truncated = true;
|
|
28259
|
+
return false;
|
|
28260
|
+
}
|
|
28261
|
+
report.diagnostics.push({
|
|
28262
|
+
...diagnostic,
|
|
28263
|
+
...diagnostic.object ? { object: diagnostic.object.slice(0, 200) } : {}
|
|
28264
|
+
});
|
|
28265
|
+
if (diagnostic.category !== "unknown") report.compatible = false;
|
|
28266
|
+
return true;
|
|
28267
|
+
};
|
|
28268
|
+
const location = (offset) => {
|
|
28269
|
+
const prefix = sql.slice(0, offset);
|
|
28270
|
+
return { offset, line: prefix.split("\n").length, column: offset - prefix.lastIndexOf("\n") };
|
|
28271
|
+
};
|
|
28272
|
+
if (sql.length > inputLimit || new TextEncoder().encode(sql).length > inputLimit) {
|
|
28273
|
+
add({
|
|
28274
|
+
statement: 0,
|
|
28275
|
+
...location(0),
|
|
28276
|
+
sqlstate: null,
|
|
28277
|
+
category: "limit",
|
|
28278
|
+
message: `Input exceeds ${inputLimit} bytes`
|
|
28279
|
+
});
|
|
28280
|
+
report.truncated = true;
|
|
28281
|
+
return report;
|
|
28282
|
+
}
|
|
28283
|
+
let tokens;
|
|
28284
|
+
try {
|
|
28285
|
+
tokens = tokenize(sql);
|
|
28286
|
+
} catch (error) {
|
|
28287
|
+
add({
|
|
28288
|
+
statement: 1,
|
|
28289
|
+
...location(0),
|
|
28290
|
+
sqlstate: isPostgresError(error) ? error.code : null,
|
|
28291
|
+
category: "syntax",
|
|
28292
|
+
message: error instanceof Error ? error.message.slice(0, 500) : "Cannot tokenize input"
|
|
28293
|
+
});
|
|
28294
|
+
return report;
|
|
28295
|
+
}
|
|
28296
|
+
const db = new Database();
|
|
28297
|
+
const failed = /* @__PURE__ */ new Map();
|
|
28298
|
+
let start = 0;
|
|
28299
|
+
let tokenStart = 0;
|
|
28300
|
+
let atomicDepth = 0;
|
|
28301
|
+
const run = (end, tokenEnd) => {
|
|
28302
|
+
const statementTokens = tokens.slice(tokenStart, tokenEnd);
|
|
28303
|
+
tokenStart = tokenEnd + 1;
|
|
28304
|
+
const offset = statementTokens[0]?.pos;
|
|
28305
|
+
const statementSql = sql.slice(start, end);
|
|
28306
|
+
start = end + 1;
|
|
28307
|
+
if (offset === void 0) return;
|
|
28308
|
+
if (report.statements >= statementLimit) {
|
|
28309
|
+
report.truncated = true;
|
|
28310
|
+
add({
|
|
28311
|
+
statement: report.statements + 1,
|
|
28312
|
+
...location(offset),
|
|
28313
|
+
sqlstate: null,
|
|
28314
|
+
category: "limit",
|
|
28315
|
+
message: `Statement limit ${statementLimit} reached`
|
|
28316
|
+
});
|
|
28317
|
+
return;
|
|
28318
|
+
}
|
|
28319
|
+
report.statements++;
|
|
28320
|
+
let object;
|
|
28321
|
+
const candidate = statementTokens.findIndex(
|
|
28322
|
+
(token) => ["table", "view", "function", "sequence", "schema", "type", "index"].includes(token.value)
|
|
28323
|
+
);
|
|
28324
|
+
if (candidate >= 0) {
|
|
28325
|
+
const name = statementTokens[candidate + 1];
|
|
28326
|
+
if (name?.type === "ident" || name?.type === "quoted_ident") object = name.value;
|
|
28327
|
+
}
|
|
28328
|
+
try {
|
|
28329
|
+
const ast = parse(statementSql);
|
|
28330
|
+
const statement = ast[0];
|
|
28331
|
+
if (statement && "name" in statement && Array.isArray(statement.name)) object = statement.name.join(".");
|
|
28332
|
+
if (statement && !/^(create_|alter_|comment|grant|revoke|set|reset)/.test(statement.type)) {
|
|
28333
|
+
add({
|
|
28334
|
+
statement: report.statements,
|
|
28335
|
+
...location(offset),
|
|
28336
|
+
object,
|
|
28337
|
+
sqlstate: null,
|
|
28338
|
+
category: "unknown",
|
|
28339
|
+
message: "Only schema statements are analyzed; data, queries and dynamic commands are unknown"
|
|
28340
|
+
});
|
|
28341
|
+
return;
|
|
28342
|
+
}
|
|
28343
|
+
const references = statementTokens.flatMap((token, index) => {
|
|
28344
|
+
if (!["from", "join", "references"].includes(token.value)) return [];
|
|
28345
|
+
const parts = [];
|
|
28346
|
+
for (let at = index + 1; at < statementTokens.length; at += 2) {
|
|
28347
|
+
const part = statementTokens[at];
|
|
28348
|
+
if (!part || part.type !== "ident" && part.type !== "quoted_ident") break;
|
|
28349
|
+
parts.push(part.value);
|
|
28350
|
+
if (statementTokens[at + 1]?.value !== ".") break;
|
|
28351
|
+
}
|
|
28352
|
+
return parts.length ? [parts.join(".")] : [];
|
|
28353
|
+
});
|
|
28354
|
+
const missing = [...failed].find(
|
|
28355
|
+
([name]) => references.some(
|
|
28356
|
+
(reference) => reference === name || !reference.includes(".") && reference === name.split(".").at(-1) || !name.includes(".") && name === reference.split(".").at(-1)
|
|
28357
|
+
)
|
|
28358
|
+
);
|
|
28359
|
+
if (missing) {
|
|
28360
|
+
add({
|
|
28361
|
+
statement: report.statements,
|
|
28362
|
+
...location(offset),
|
|
28363
|
+
object,
|
|
28364
|
+
sqlstate: "42P01",
|
|
28365
|
+
category: "prerequisite",
|
|
28366
|
+
message: `Prerequisite ${missing[0]} was not created`,
|
|
28367
|
+
prerequisiteStatement: missing[1]
|
|
28368
|
+
});
|
|
28369
|
+
if (object) failed.set(object, report.statements);
|
|
28370
|
+
return;
|
|
28371
|
+
}
|
|
28372
|
+
db.exec(statementSql);
|
|
28373
|
+
report.accepted++;
|
|
28374
|
+
add({
|
|
28375
|
+
statement: report.statements,
|
|
28376
|
+
...location(offset),
|
|
28377
|
+
object,
|
|
28378
|
+
sqlstate: null,
|
|
28379
|
+
category: "unknown",
|
|
28380
|
+
message: "Accepted by the ephemeral engine; semantics, function bodies and dynamic SQL require oracle verification"
|
|
28381
|
+
});
|
|
28382
|
+
} catch (error) {
|
|
28383
|
+
const code = isPostgresError(error) ? error.code : null;
|
|
28384
|
+
const message = error instanceof Error ? error.message.slice(0, 500) : "Schema analysis failed";
|
|
28385
|
+
const prerequisite = code?.startsWith("42") ? [...failed].find(([name]) => message.includes(`"${name}"`) || message.includes(`"${name.split(".").at(-1)}"`)) : void 0;
|
|
28386
|
+
add({
|
|
28387
|
+
statement: report.statements,
|
|
28388
|
+
...location(offset),
|
|
28389
|
+
object,
|
|
28390
|
+
sqlstate: code,
|
|
28391
|
+
category: prerequisite ? "prerequisite" : code === "0A000" ? "unsupported" : code?.startsWith("426") ? "syntax" : "execution",
|
|
28392
|
+
message,
|
|
28393
|
+
...prerequisite ? { prerequisiteStatement: prerequisite[1] } : {}
|
|
28394
|
+
});
|
|
28395
|
+
if (object) failed.set(object, report.statements);
|
|
28396
|
+
}
|
|
28397
|
+
};
|
|
28398
|
+
try {
|
|
28399
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
28400
|
+
const token = tokens[index];
|
|
28401
|
+
if (token.value === "begin" && tokens[index + 1]?.value === "atomic") atomicDepth++;
|
|
28402
|
+
if (token.value === "end" && atomicDepth > 0) atomicDepth--;
|
|
28403
|
+
if (token.type === "punct" && token.value === ";" && atomicDepth === 0 || token.type === "eof")
|
|
28404
|
+
run(token.pos, index);
|
|
28405
|
+
if (report.truncated) break;
|
|
28406
|
+
}
|
|
28407
|
+
} finally {
|
|
28408
|
+
db.close();
|
|
28409
|
+
}
|
|
28410
|
+
return report;
|
|
28411
|
+
}
|
|
28035
28412
|
export {
|
|
28036
28413
|
Database,
|
|
28037
28414
|
PostgresError,
|
|
28038
28415
|
Snapshot,
|
|
28039
|
-
Statement
|
|
28416
|
+
Statement,
|
|
28417
|
+
analyzeSchema
|
|
28040
28418
|
};
|
|
28041
28419
|
//# sourceMappingURL=index.js.map
|