@crvouga/mockingbird-service-postgres 3.5.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 +12 -0
- package/COMPATIBILITY.md +34 -0
- package/README.md +5 -0
- package/compat/sections/trg.ts +2 -0
- package/dist/admin.js +1114 -735
- package/dist/admin.js.map +4 -4
- package/dist/api/schema-report.d.ts +32 -0
- package/dist/ast/nodes.d.ts +11 -1
- package/dist/executor/relation.d.ts +1 -0
- package/dist/executor/triggers-scope.d.ts +4 -0
- package/dist/executor/triggers.d.ts +4 -0
- package/dist/expressions/context.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1266 -699
- package/dist/index.js.map +4 -4
- package/dist/server.js +1144 -736
- package/dist/server.js.map +4 -4
- package/dist/storage/database-state.d.ts +20 -0
- package/dist/storage/guc.d.ts +2 -0
- package/dist/storage/indexable-relation.d.ts +3 -0
- package/dist/types/regnamespace.d.ts +5 -0
- package/dist/types/value.d.ts +2 -0
- package/dist/unstable.js +17231 -16852
- package/dist/unstable.js.map +4 -4
- package/dist/wire/cli.js +1330 -749
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/index.js +1144 -736
- package/dist/wire/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -254,6 +254,13 @@ function isPostgresError(err) {
|
|
|
254
254
|
return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
// src/storage/guc.ts
|
|
258
|
+
function validateStorageSetting(name, value) {
|
|
259
|
+
if (name === "default_table_access_method" && value !== "heap" || name === "default_tablespace" && value !== "" && value !== "pg_default") {
|
|
260
|
+
throw pgError("invalid_parameter_value", `invalid value for parameter "${name}": "${value}"`, "22023");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
257
264
|
// src/runtime/prng.ts
|
|
258
265
|
var Prng = class _Prng {
|
|
259
266
|
state;
|
|
@@ -2793,6 +2800,12 @@ var invalidName = () => {
|
|
|
2793
2800
|
throw pgError("syntax", "invalid name syntax", "42602");
|
|
2794
2801
|
};
|
|
2795
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) {
|
|
2796
2809
|
if (text === "-") return { oid: 0 };
|
|
2797
2810
|
if (/^\d+$/.test(text)) return { oid: parseOidText(text) };
|
|
2798
2811
|
const names = [];
|
|
@@ -2828,8 +2841,8 @@ function parseNamespaceInput(text) {
|
|
|
2828
2841
|
skipSpace();
|
|
2829
2842
|
if (at === text.length) invalidName();
|
|
2830
2843
|
}
|
|
2831
|
-
if (names.length
|
|
2832
|
-
return {
|
|
2844
|
+
if (names.length === 0 || names.length > 3) invalidName();
|
|
2845
|
+
return { parts: names };
|
|
2833
2846
|
}
|
|
2834
2847
|
function namespaceText(oid, name) {
|
|
2835
2848
|
if (oid === 0) return "-";
|
|
@@ -2902,6 +2915,7 @@ var TYPE_ALIASES = {
|
|
|
2902
2915
|
regtype: "regtype",
|
|
2903
2916
|
regproc: "regproc",
|
|
2904
2917
|
regnamespace: "regnamespace",
|
|
2918
|
+
xid: "xid",
|
|
2905
2919
|
oid: "oid",
|
|
2906
2920
|
void: "void",
|
|
2907
2921
|
record: "record",
|
|
@@ -2978,6 +2992,7 @@ var TYPE_OIDS = {
|
|
|
2978
2992
|
regproc: 24,
|
|
2979
2993
|
text: 25,
|
|
2980
2994
|
oid: 26,
|
|
2995
|
+
xid: 28,
|
|
2981
2996
|
json: 114,
|
|
2982
2997
|
float4: 700,
|
|
2983
2998
|
float8: 701,
|
|
@@ -3296,6 +3311,7 @@ function recordText(rec, ctx) {
|
|
|
3296
3311
|
return `(${parts.join(",")})`;
|
|
3297
3312
|
}
|
|
3298
3313
|
function datumText(t, v, ctx) {
|
|
3314
|
+
if (t === "regclass") return v === 0 ? "-" : ctx.relationName?.(v) ?? String(v);
|
|
3299
3315
|
if (t === "regnamespace") return namespaceText(v, ctx.namespaceName?.(v));
|
|
3300
3316
|
if (v === null) throw pgError("internal", "datumText called with null");
|
|
3301
3317
|
if (isArrayType(t)) return arrayText(v, ctx);
|
|
@@ -3306,6 +3322,7 @@ function datumText(t, v, ctx) {
|
|
|
3306
3322
|
case "int2":
|
|
3307
3323
|
case "int4":
|
|
3308
3324
|
case "oid":
|
|
3325
|
+
case "xid":
|
|
3309
3326
|
return String(v);
|
|
3310
3327
|
case "int8":
|
|
3311
3328
|
return String(v);
|
|
@@ -3370,6 +3387,12 @@ function datumText(t, v, ctx) {
|
|
|
3370
3387
|
}
|
|
3371
3388
|
var UTC_INPUT = { zoneOffsetForNaive: () => 0 };
|
|
3372
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
|
+
}
|
|
3373
3396
|
if (t === "regnamespace") {
|
|
3374
3397
|
const parsed = parseNamespaceInput(text);
|
|
3375
3398
|
if ("oid" in parsed) return parsed.oid;
|
|
@@ -3391,6 +3414,7 @@ function datumFromText(t, text, ctx) {
|
|
|
3391
3414
|
case "int8":
|
|
3392
3415
|
return parseIntegerText(text, t);
|
|
3393
3416
|
case "oid":
|
|
3417
|
+
case "xid":
|
|
3394
3418
|
return parseOidText(text);
|
|
3395
3419
|
case "float4":
|
|
3396
3420
|
return Math.fround(parseFloatText(text, "float4"));
|
|
@@ -3547,6 +3571,8 @@ var TableData = class _TableData {
|
|
|
3547
3571
|
schema;
|
|
3548
3572
|
columns;
|
|
3549
3573
|
rows;
|
|
3574
|
+
rowXmins = /* @__PURE__ */ new WeakMap();
|
|
3575
|
+
slabXmins = null;
|
|
3550
3576
|
/** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
|
|
3551
3577
|
slab = null;
|
|
3552
3578
|
constraints;
|
|
@@ -3589,6 +3615,11 @@ var TableData = class _TableData {
|
|
|
3589
3615
|
materializeSlab() {
|
|
3590
3616
|
if (!this.slab) return;
|
|
3591
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;
|
|
3592
3623
|
this.slab = null;
|
|
3593
3624
|
this.indexStores = null;
|
|
3594
3625
|
}
|
|
@@ -3622,6 +3653,10 @@ var TableData = class _TableData {
|
|
|
3622
3653
|
} else {
|
|
3623
3654
|
t.rows = this.rows.map((r) => r.slice());
|
|
3624
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
|
+
});
|
|
3625
3660
|
t.unlogged = this.unlogged;
|
|
3626
3661
|
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
3627
3662
|
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
@@ -3707,7 +3742,18 @@ var DatabaseState = class _DatabaseState {
|
|
|
3707
3742
|
clock;
|
|
3708
3743
|
changes = 0;
|
|
3709
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
|
+
}
|
|
3710
3753
|
inTransaction = false;
|
|
3754
|
+
/** Transaction-only events: included in rollback clones, never in exported snapshots. */
|
|
3755
|
+
pendingTriggers = [];
|
|
3756
|
+
constraintModes = /* @__PURE__ */ new Map();
|
|
3711
3757
|
/** most recent sequence touched by nextval/setval, for lastval() */
|
|
3712
3758
|
lastSequence = null;
|
|
3713
3759
|
/** Session currval per sequence; absent entry means currval is undefined. */
|
|
@@ -3750,6 +3796,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3750
3796
|
this.settings.set("row_security", "on");
|
|
3751
3797
|
this.settings.set("array_nulls", "on");
|
|
3752
3798
|
this.settings.set("backslash_quote", "safe_encoding");
|
|
3799
|
+
this.settings.set("default_tablespace", "");
|
|
3800
|
+
this.settings.set("default_table_access_method", "heap");
|
|
3753
3801
|
}
|
|
3754
3802
|
nextOid() {
|
|
3755
3803
|
return this.oidCounter++;
|
|
@@ -3909,10 +3957,14 @@ var DatabaseState = class _DatabaseState {
|
|
|
3909
3957
|
s.prepared = new Map(this.prepared);
|
|
3910
3958
|
s.changes = this.changes;
|
|
3911
3959
|
s.inTransaction = this.inTransaction;
|
|
3960
|
+
s.pendingTriggers = this.pendingTriggers.slice();
|
|
3961
|
+
s.constraintModes = new Map(this.constraintModes);
|
|
3912
3962
|
s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
|
|
3913
3963
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3914
3964
|
s.tableStats = new Map(this.tableStats);
|
|
3915
3965
|
s.oidCounter = this.oidCounter;
|
|
3966
|
+
s.xidCounter = this.xidCounter;
|
|
3967
|
+
s.transactionXid = this.transactionXid;
|
|
3916
3968
|
for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
|
|
3917
3969
|
return s;
|
|
3918
3970
|
}
|
|
@@ -3926,10 +3978,14 @@ var DatabaseState = class _DatabaseState {
|
|
|
3926
3978
|
s.prepared = new Map(this.prepared);
|
|
3927
3979
|
s.changes = this.changes;
|
|
3928
3980
|
s.inTransaction = this.inTransaction;
|
|
3981
|
+
s.pendingTriggers = this.pendingTriggers.slice();
|
|
3982
|
+
s.constraintModes = new Map(this.constraintModes);
|
|
3929
3983
|
s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
|
|
3930
3984
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3931
3985
|
s.tableStats = new Map(this.tableStats);
|
|
3932
3986
|
s.oidCounter = this.oidCounter;
|
|
3987
|
+
s.xidCounter = this.xidCounter;
|
|
3988
|
+
s.transactionXid = this.transactionXid;
|
|
3933
3989
|
for (const [name, overloads] of this.systemFunctions) s.systemFunctions.set(name, overloads.slice());
|
|
3934
3990
|
return s;
|
|
3935
3991
|
}
|
|
@@ -3998,11 +4054,15 @@ var DatabaseState = class _DatabaseState {
|
|
|
3998
4054
|
this.settings = other.settings;
|
|
3999
4055
|
this.localSettings = other.localSettings;
|
|
4000
4056
|
this.prepared = other.prepared;
|
|
4057
|
+
this.pendingTriggers = other.pendingTriggers.slice();
|
|
4058
|
+
this.constraintModes = new Map(other.constraintModes);
|
|
4001
4059
|
this.changes = other.changes;
|
|
4002
4060
|
this.lastSequence = other.lastSequence;
|
|
4003
4061
|
this.sequenceCurrval = other.sequenceCurrval;
|
|
4004
4062
|
this.tableStats = other.tableStats;
|
|
4005
4063
|
this.oidCounter = other.oidCounter;
|
|
4064
|
+
this.xidCounter = Math.max(this.xidCounter, other.xidCounter);
|
|
4065
|
+
this.transactionXid = other.transactionXid;
|
|
4006
4066
|
}
|
|
4007
4067
|
/** @internal Snapshot codec access to the oid allocator. */
|
|
4008
4068
|
snapshotOidCounter() {
|
|
@@ -4014,91 +4074,212 @@ var DatabaseState = class _DatabaseState {
|
|
|
4014
4074
|
}
|
|
4015
4075
|
};
|
|
4016
4076
|
|
|
4017
|
-
// src/
|
|
4018
|
-
var
|
|
4019
|
-
|
|
4020
|
-
|
|
4077
|
+
// src/types/timezone.ts
|
|
4078
|
+
var intlCache = /* @__PURE__ */ new Map();
|
|
4079
|
+
function intlFor(zone) {
|
|
4080
|
+
const cached = intlCache.get(zone);
|
|
4081
|
+
if (cached) return cached;
|
|
4082
|
+
try {
|
|
4083
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
4084
|
+
timeZone: zone,
|
|
4085
|
+
timeZoneName: "longOffset",
|
|
4086
|
+
year: "numeric"
|
|
4087
|
+
});
|
|
4088
|
+
intlCache.set(zone, fmt);
|
|
4089
|
+
return fmt;
|
|
4090
|
+
} catch {
|
|
4091
|
+
return null;
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
function parseFixedOffset(zone) {
|
|
4095
|
+
const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
|
|
4096
|
+
if (!m) return null;
|
|
4097
|
+
const sign = m[1] === "-" ? -1 : 1;
|
|
4098
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
|
|
4099
|
+
}
|
|
4100
|
+
function parsePosixOffset(zone) {
|
|
4101
|
+
const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
|
|
4102
|
+
if (!m) return null;
|
|
4103
|
+
const sign = m[1] === "-" ? 1 : -1;
|
|
4104
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
|
|
4105
|
+
}
|
|
4106
|
+
function pgMicrosToUnixMs(utcMicros) {
|
|
4107
|
+
return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
|
|
4108
|
+
}
|
|
4109
|
+
function zoneOffsetAtUtc(zone, utcMicros) {
|
|
4110
|
+
const z = zone.trim();
|
|
4111
|
+
const lower = z.toLowerCase();
|
|
4112
|
+
if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
|
|
4113
|
+
const posix = parsePosixOffset(z);
|
|
4114
|
+
if (posix !== null) return posix;
|
|
4115
|
+
const fixed = parseFixedOffset(z);
|
|
4116
|
+
if (fixed !== null) return fixed;
|
|
4117
|
+
const fmt = intlFor(z);
|
|
4118
|
+
if (fmt) {
|
|
4119
|
+
const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
|
|
4120
|
+
const parts = fmt.formatToParts(new Date(ms));
|
|
4121
|
+
const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
|
|
4122
|
+
const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
|
|
4123
|
+
if (!m) return 0;
|
|
4124
|
+
const sign = m[1] === "-" ? -1 : 1;
|
|
4125
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
|
|
4021
4126
|
}
|
|
4127
|
+
throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
|
|
4128
|
+
}
|
|
4129
|
+
function clampDateMs(ms) {
|
|
4130
|
+
const MAX = 864e13;
|
|
4131
|
+
return Math.max(-MAX, Math.min(MAX, ms));
|
|
4132
|
+
}
|
|
4133
|
+
function zoneOffsetForNaive(zone, naiveMicros) {
|
|
4134
|
+
const first = zoneOffsetAtUtc(zone, naiveMicros);
|
|
4135
|
+
const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
|
|
4136
|
+
return second;
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
// src/expressions/context.ts
|
|
4140
|
+
var EngineCtx = class {
|
|
4022
4141
|
state;
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
return this.base !== null;
|
|
4142
|
+
writeXid = null;
|
|
4143
|
+
rowVersionXid() {
|
|
4144
|
+
if (this.state.inTransaction) return this.state.transactionXid ??= this.state.allocateXid();
|
|
4145
|
+
return this.writeXid ??= this.state.allocateXid();
|
|
4028
4146
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4147
|
+
/** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
|
|
4148
|
+
txNow;
|
|
4149
|
+
/** statement timestamp */
|
|
4150
|
+
stmtNow;
|
|
4151
|
+
constructor(state, txNow) {
|
|
4152
|
+
this.state = state;
|
|
4153
|
+
const nowMs = state.clock().getTime();
|
|
4154
|
+
const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
|
|
4155
|
+
this.txNow = txNow ?? nowMicros;
|
|
4156
|
+
this.stmtNow = nowMicros;
|
|
4033
4157
|
}
|
|
4034
|
-
|
|
4035
|
-
this.state.
|
|
4036
|
-
this.state.prng.setState(snap.prngState);
|
|
4158
|
+
timezone() {
|
|
4159
|
+
return this.state.getSetting("timezone") ?? "UTC";
|
|
4037
4160
|
}
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
}
|
|
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;
|
|
4043
4167
|
}
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
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
|
+
}
|
|
4047
4175
|
}
|
|
4048
|
-
|
|
4049
|
-
this.state.inTransaction = true;
|
|
4050
|
-
assert(this.freezeDepth === 1, "BEGIN freeze depth");
|
|
4176
|
+
return null;
|
|
4051
4177
|
}
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
this.state.localSettings.clear();
|
|
4057
|
-
while (this.freezeDepth > 0) this.thawOnce();
|
|
4178
|
+
namespaceOid(name) {
|
|
4179
|
+
const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
|
|
4180
|
+
if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
4181
|
+
return oid;
|
|
4058
4182
|
}
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4183
|
+
namespaceName(oid) {
|
|
4184
|
+
const builtin = builtinNamespaceName(oid);
|
|
4185
|
+
if (builtin !== void 0) return builtin;
|
|
4186
|
+
for (const schema of this.state.schemas.values()) {
|
|
4187
|
+
if (schema.oid === oid) return schema.name;
|
|
4062
4188
|
}
|
|
4063
|
-
|
|
4064
|
-
this.savepoints = [];
|
|
4065
|
-
this.state.inTransaction = false;
|
|
4066
|
-
this.state.localSettings.clear();
|
|
4067
|
-
while (this.freezeDepth > 0) this.thawOnce();
|
|
4189
|
+
return null;
|
|
4068
4190
|
}
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
|
|
4072
|
-
}
|
|
4073
|
-
this.savepoints.push({ name, snap: this.takeSnapshot() });
|
|
4191
|
+
zoneOffsetAt(utcMicros) {
|
|
4192
|
+
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
4074
4193
|
}
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
this.savepoints.splice(idx);
|
|
4194
|
+
zoneOffsetForNaive(naiveMicros) {
|
|
4195
|
+
return zoneOffsetForNaive(this.timezone(), naiveMicros);
|
|
4078
4196
|
}
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
const
|
|
4082
|
-
|
|
4083
|
-
this.savepoints.splice(idx + 1);
|
|
4197
|
+
enumLabels(enumType) {
|
|
4198
|
+
if (!isEnumType(enumType)) return null;
|
|
4199
|
+
const e = this.state.findEnumByKey(enumType.slice(5));
|
|
4200
|
+
return e?.labels ?? null;
|
|
4084
4201
|
}
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
}
|
|
4089
|
-
for (let i = this.savepoints.length - 1; i >= 0; i--) {
|
|
4090
|
-
if (this.savepoints[i].name === name) return i;
|
|
4091
|
-
}
|
|
4092
|
-
throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
|
|
4202
|
+
enumHasLabel(enumType, label) {
|
|
4203
|
+
const labels = this.enumLabels(enumType);
|
|
4204
|
+
return labels === null ? true : labels.includes(label);
|
|
4093
4205
|
}
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4206
|
+
enumOrder(enumType, label) {
|
|
4207
|
+
const labels = this.enumLabels(enumType);
|
|
4208
|
+
if (!labels) return 0;
|
|
4209
|
+
const idx = labels.indexOf(label);
|
|
4210
|
+
return idx === -1 ? labels.length : idx;
|
|
4211
|
+
}
|
|
4212
|
+
requireEnum(enumType) {
|
|
4213
|
+
const labels = this.enumLabels(enumType);
|
|
4214
|
+
if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
|
|
4215
|
+
return labels;
|
|
4099
4216
|
}
|
|
4100
4217
|
};
|
|
4101
4218
|
|
|
4219
|
+
// src/api/bind.ts
|
|
4220
|
+
var INT4_MIN2 = -2147483648;
|
|
4221
|
+
var INT4_MAX2 = 2147483647;
|
|
4222
|
+
function bindValueToTyped(value, index) {
|
|
4223
|
+
if (value === null || value === void 0) return tv(UNKNOWN, null);
|
|
4224
|
+
switch (typeof value) {
|
|
4225
|
+
case "boolean":
|
|
4226
|
+
return tv("bool", value);
|
|
4227
|
+
case "number":
|
|
4228
|
+
if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
|
|
4229
|
+
return tv("int4", value);
|
|
4230
|
+
}
|
|
4231
|
+
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
4232
|
+
return tv("int8", BigInt(value));
|
|
4233
|
+
}
|
|
4234
|
+
return tv("float8", value);
|
|
4235
|
+
case "bigint":
|
|
4236
|
+
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
4237
|
+
throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
|
|
4238
|
+
}
|
|
4239
|
+
return tv("int8", value);
|
|
4240
|
+
case "string":
|
|
4241
|
+
return tv(UNKNOWN, value);
|
|
4242
|
+
case "object": {
|
|
4243
|
+
if (value instanceof Uint8Array) return tv("bytea", value);
|
|
4244
|
+
if (value instanceof Date) {
|
|
4245
|
+
const ms = value.getTime();
|
|
4246
|
+
if (Number.isNaN(ms)) {
|
|
4247
|
+
throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
|
|
4248
|
+
}
|
|
4249
|
+
return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
|
|
4250
|
+
}
|
|
4251
|
+
break;
|
|
4252
|
+
}
|
|
4253
|
+
default:
|
|
4254
|
+
break;
|
|
4255
|
+
}
|
|
4256
|
+
throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
|
|
4257
|
+
}
|
|
4258
|
+
function datumToJs(t, v, ctx, int8 = "bigint") {
|
|
4259
|
+
if (v === null) return null;
|
|
4260
|
+
switch (t) {
|
|
4261
|
+
case "bool":
|
|
4262
|
+
return v;
|
|
4263
|
+
case "int2":
|
|
4264
|
+
case "int4":
|
|
4265
|
+
case "oid":
|
|
4266
|
+
return typeof v === "bigint" ? Number(v) : v;
|
|
4267
|
+
case "int8": {
|
|
4268
|
+
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
4269
|
+
if (int8 === "string") return n.toString();
|
|
4270
|
+
if (int8 === "number") return Number(n);
|
|
4271
|
+
return n;
|
|
4272
|
+
}
|
|
4273
|
+
case "float4":
|
|
4274
|
+
case "float8":
|
|
4275
|
+
return v;
|
|
4276
|
+
case "bytea":
|
|
4277
|
+
return v;
|
|
4278
|
+
default:
|
|
4279
|
+
return datumText(t === UNKNOWN ? "text" : t, v, ctx);
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
|
|
4102
4283
|
// src/types/cast.ts
|
|
4103
4284
|
var UTC_CAST_ENV = {
|
|
4104
4285
|
zoneOffsetAt: () => 0,
|
|
@@ -4294,7 +4475,7 @@ function castTo(env, value, target, opts = {}) {
|
|
|
4294
4475
|
return tv(target, makeArray(toElem, items, arr.dims.slice(), arr.lbs.slice()));
|
|
4295
4476
|
}
|
|
4296
4477
|
if (from === "oid" && target === "regnamespace") return tv(target, v);
|
|
4297
|
-
if (from === "regnamespace" && (target === "oid" || target === "int4" || target === "int8")) {
|
|
4478
|
+
if ((from === "regnamespace" || from === "regclass") && (target === "oid" || target === "int4" || target === "int8")) {
|
|
4298
4479
|
return tv(target, target === "int8" ? BigInt(v) : v);
|
|
4299
4480
|
}
|
|
4300
4481
|
if ((from === "int4" || from === "int8" || from === "int2") && (target === "oid" || target === "regnamespace")) {
|
|
@@ -4575,259 +4756,6 @@ function unifyTypes(a, b) {
|
|
|
4575
4756
|
return null;
|
|
4576
4757
|
}
|
|
4577
4758
|
|
|
4578
|
-
// src/types/resolve.ts
|
|
4579
|
-
var SERIALS = {
|
|
4580
|
-
smallserial: "int2",
|
|
4581
|
-
serial2: "int2",
|
|
4582
|
-
serial: "int4",
|
|
4583
|
-
serial4: "int4",
|
|
4584
|
-
bigserial: "int8",
|
|
4585
|
-
serial8: "int8"
|
|
4586
|
-
};
|
|
4587
|
-
function modOf(t, mods) {
|
|
4588
|
-
if (mods.length === 0) return null;
|
|
4589
|
-
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
4590
|
-
return { a: mods[0] };
|
|
4591
|
-
}
|
|
4592
|
-
function resolveTypeName(state, tn) {
|
|
4593
|
-
const joined = tn.parts.join(".");
|
|
4594
|
-
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
4595
|
-
if (bare !== null) {
|
|
4596
|
-
const serial = SERIALS[bare];
|
|
4597
|
-
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
4598
|
-
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
4599
|
-
}
|
|
4600
|
-
const builtin = normalizeTypeName(bare);
|
|
4601
|
-
if (builtin !== null) {
|
|
4602
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
4603
|
-
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
4604
|
-
}
|
|
4605
|
-
}
|
|
4606
|
-
const enumData = state.findEnum(tn.parts);
|
|
4607
|
-
if (enumData) {
|
|
4608
|
-
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
4609
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
4610
|
-
return { column: { id, mod: null }, domain: null, serial: null };
|
|
4611
|
-
}
|
|
4612
|
-
const domainData = state.findDomain(tn.parts);
|
|
4613
|
-
if (domainData) {
|
|
4614
|
-
if (tn.arrayDims > 0) {
|
|
4615
|
-
const id = arrayTypeOf(domainData.baseType.id);
|
|
4616
|
-
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
4617
|
-
}
|
|
4618
|
-
return {
|
|
4619
|
-
column: domainData.baseType,
|
|
4620
|
-
domain: `${domainData.schema}.${domainData.name}`,
|
|
4621
|
-
serial: null
|
|
4622
|
-
};
|
|
4623
|
-
}
|
|
4624
|
-
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
4625
|
-
}
|
|
4626
|
-
|
|
4627
|
-
// src/executor/relation.ts
|
|
4628
|
-
var RowScope = class {
|
|
4629
|
-
constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
|
|
4630
|
-
this.columns = columns;
|
|
4631
|
-
this.row = row;
|
|
4632
|
-
this.parent = parent;
|
|
4633
|
-
this.rangeVars = rangeVars;
|
|
4634
|
-
}
|
|
4635
|
-
columns;
|
|
4636
|
-
row;
|
|
4637
|
-
parent;
|
|
4638
|
-
rangeVars;
|
|
4639
|
-
/** Resolve a (possibly qualified) column reference. undefined = not found. */
|
|
4640
|
-
lookup(parts) {
|
|
4641
|
-
if (parts.length === 1) {
|
|
4642
|
-
const name2 = parts[0];
|
|
4643
|
-
let found2;
|
|
4644
|
-
let count2 = 0;
|
|
4645
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4646
|
-
const c = this.columns[i];
|
|
4647
|
-
if (c.name === name2 && !c.hidden) {
|
|
4648
|
-
count2++;
|
|
4649
|
-
if (count2 > 1) {
|
|
4650
|
-
throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
|
|
4651
|
-
}
|
|
4652
|
-
found2 = tv(c.type, this.row[i] ?? null);
|
|
4653
|
-
}
|
|
4654
|
-
}
|
|
4655
|
-
if (found2 !== void 0) return found2;
|
|
4656
|
-
if (this.rangeVars.has(name2)) {
|
|
4657
|
-
return this.wholeRow(name2);
|
|
4658
|
-
}
|
|
4659
|
-
return this.parent?.lookup(parts);
|
|
4660
|
-
}
|
|
4661
|
-
const table = parts[parts.length - 2];
|
|
4662
|
-
const name = parts[parts.length - 1];
|
|
4663
|
-
let found;
|
|
4664
|
-
let count = 0;
|
|
4665
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4666
|
-
const c = this.columns[i];
|
|
4667
|
-
if (c.table === table && c.name === name) {
|
|
4668
|
-
count++;
|
|
4669
|
-
if (count > 1) {
|
|
4670
|
-
throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
|
|
4671
|
-
}
|
|
4672
|
-
found = tv(c.type, this.row[i] ?? null);
|
|
4673
|
-
}
|
|
4674
|
-
}
|
|
4675
|
-
if (found !== void 0) return found;
|
|
4676
|
-
return this.parent?.lookup(parts);
|
|
4677
|
-
}
|
|
4678
|
-
/** Build a record value from all columns belonging to a range variable. */
|
|
4679
|
-
wholeRow(rangeVar) {
|
|
4680
|
-
const types = [];
|
|
4681
|
-
const values = [];
|
|
4682
|
-
const names = [];
|
|
4683
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4684
|
-
const c = this.columns[i];
|
|
4685
|
-
if (c.table === rangeVar) {
|
|
4686
|
-
types.push(c.type);
|
|
4687
|
-
values.push(this.row[i] ?? null);
|
|
4688
|
-
names.push(c.name);
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
if (types.length === 0) return void 0;
|
|
4692
|
-
return tv("record", { kind: "pgrecord", types, values, names });
|
|
4693
|
-
}
|
|
4694
|
-
hasRangeVar(name) {
|
|
4695
|
-
if (this.rangeVars.has(name)) return true;
|
|
4696
|
-
return this.parent?.hasRangeVar(name) ?? false;
|
|
4697
|
-
}
|
|
4698
|
-
};
|
|
4699
|
-
function childEnv(env, outer) {
|
|
4700
|
-
return {
|
|
4701
|
-
ctx: env.ctx,
|
|
4702
|
-
params: env.params,
|
|
4703
|
-
ctes: env.ctes,
|
|
4704
|
-
outer,
|
|
4705
|
-
allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
|
|
4706
|
-
};
|
|
4707
|
-
}
|
|
4708
|
-
function relationResult(rel3, command) {
|
|
4709
|
-
const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
|
|
4710
|
-
return {
|
|
4711
|
-
columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
|
|
4712
|
-
rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
|
|
4713
|
-
command,
|
|
4714
|
-
rowCount: rel3.rows.length
|
|
4715
|
-
};
|
|
4716
|
-
}
|
|
4717
|
-
function commandResult(command, rowCount) {
|
|
4718
|
-
return { columns: [], rows: [], command, rowCount };
|
|
4719
|
-
}
|
|
4720
|
-
function inferColumnName(e) {
|
|
4721
|
-
switch (e.type) {
|
|
4722
|
-
case "colref":
|
|
4723
|
-
return e.parts[e.parts.length - 1];
|
|
4724
|
-
case "func":
|
|
4725
|
-
return e.name[e.name.length - 1];
|
|
4726
|
-
case "cast": {
|
|
4727
|
-
const inner = inferColumnName(e.expr);
|
|
4728
|
-
if (inner !== "?column?") return inner;
|
|
4729
|
-
return e.target.parts[e.target.parts.length - 1];
|
|
4730
|
-
}
|
|
4731
|
-
case "collate":
|
|
4732
|
-
return inferColumnName(e.expr);
|
|
4733
|
-
case "case":
|
|
4734
|
-
return "case";
|
|
4735
|
-
case "array_ctor":
|
|
4736
|
-
case "array_query":
|
|
4737
|
-
return "array";
|
|
4738
|
-
case "row":
|
|
4739
|
-
return "row";
|
|
4740
|
-
case "subquery_expr":
|
|
4741
|
-
return e.kind === "exists" ? "exists" : "?column?";
|
|
4742
|
-
case "bool_lit":
|
|
4743
|
-
return "bool";
|
|
4744
|
-
case "extract":
|
|
4745
|
-
return "extract";
|
|
4746
|
-
case "position":
|
|
4747
|
-
return "position";
|
|
4748
|
-
case "substring_sql":
|
|
4749
|
-
return "substring";
|
|
4750
|
-
case "overlay":
|
|
4751
|
-
return "overlay";
|
|
4752
|
-
case "trim":
|
|
4753
|
-
return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
|
|
4754
|
-
case "field_select":
|
|
4755
|
-
return e.field === "*" ? "?column?" : e.field;
|
|
4756
|
-
case "at_time_zone":
|
|
4757
|
-
return "timezone";
|
|
4758
|
-
case "grouping_func":
|
|
4759
|
-
return "grouping";
|
|
4760
|
-
case "subscript":
|
|
4761
|
-
return inferColumnName(e.base);
|
|
4762
|
-
default:
|
|
4763
|
-
return "?column?";
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
|
|
4767
|
-
// src/api/bind.ts
|
|
4768
|
-
var INT4_MIN2 = -2147483648;
|
|
4769
|
-
var INT4_MAX2 = 2147483647;
|
|
4770
|
-
function bindValueToTyped(value, index) {
|
|
4771
|
-
if (value === null || value === void 0) return tv(UNKNOWN, null);
|
|
4772
|
-
switch (typeof value) {
|
|
4773
|
-
case "boolean":
|
|
4774
|
-
return tv("bool", value);
|
|
4775
|
-
case "number":
|
|
4776
|
-
if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
|
|
4777
|
-
return tv("int4", value);
|
|
4778
|
-
}
|
|
4779
|
-
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
4780
|
-
return tv("int8", BigInt(value));
|
|
4781
|
-
}
|
|
4782
|
-
return tv("float8", value);
|
|
4783
|
-
case "bigint":
|
|
4784
|
-
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
4785
|
-
throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
|
|
4786
|
-
}
|
|
4787
|
-
return tv("int8", value);
|
|
4788
|
-
case "string":
|
|
4789
|
-
return tv(UNKNOWN, value);
|
|
4790
|
-
case "object": {
|
|
4791
|
-
if (value instanceof Uint8Array) return tv("bytea", value);
|
|
4792
|
-
if (value instanceof Date) {
|
|
4793
|
-
const ms = value.getTime();
|
|
4794
|
-
if (Number.isNaN(ms)) {
|
|
4795
|
-
throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
|
|
4796
|
-
}
|
|
4797
|
-
return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
|
|
4798
|
-
}
|
|
4799
|
-
break;
|
|
4800
|
-
}
|
|
4801
|
-
default:
|
|
4802
|
-
break;
|
|
4803
|
-
}
|
|
4804
|
-
throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
|
|
4805
|
-
}
|
|
4806
|
-
function datumToJs(t, v, ctx, int8 = "bigint") {
|
|
4807
|
-
if (v === null) return null;
|
|
4808
|
-
switch (t) {
|
|
4809
|
-
case "bool":
|
|
4810
|
-
return v;
|
|
4811
|
-
case "int2":
|
|
4812
|
-
case "int4":
|
|
4813
|
-
case "oid":
|
|
4814
|
-
return typeof v === "bigint" ? Number(v) : v;
|
|
4815
|
-
case "int8": {
|
|
4816
|
-
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
4817
|
-
if (int8 === "string") return n.toString();
|
|
4818
|
-
if (int8 === "number") return Number(n);
|
|
4819
|
-
return n;
|
|
4820
|
-
}
|
|
4821
|
-
case "float4":
|
|
4822
|
-
case "float8":
|
|
4823
|
-
return v;
|
|
4824
|
-
case "bytea":
|
|
4825
|
-
return v;
|
|
4826
|
-
default:
|
|
4827
|
-
return datumText(t === UNKNOWN ? "text" : t, v, ctx);
|
|
4828
|
-
}
|
|
4829
|
-
}
|
|
4830
|
-
|
|
4831
4759
|
// src/functions/datetime-fns.ts
|
|
4832
4760
|
function extractDatePart(ctx, field0, src0, asNumeric) {
|
|
4833
4761
|
const field = field0.toLowerCase();
|
|
@@ -5248,6 +5176,8 @@ function datumCompare(t, a, b, ctx = DEFAULT_COMPARE_CTX) {
|
|
|
5248
5176
|
case "int2":
|
|
5249
5177
|
case "int4":
|
|
5250
5178
|
case "oid":
|
|
5179
|
+
case "regclass":
|
|
5180
|
+
case "xid":
|
|
5251
5181
|
case "regnamespace":
|
|
5252
5182
|
case "date":
|
|
5253
5183
|
return expectNumber(a, t) - expectNumber(b, t);
|
|
@@ -5367,6 +5297,8 @@ function datumKey(t, v) {
|
|
|
5367
5297
|
case "int2":
|
|
5368
5298
|
case "int4":
|
|
5369
5299
|
case "oid":
|
|
5300
|
+
case "regclass":
|
|
5301
|
+
case "xid":
|
|
5370
5302
|
case "date":
|
|
5371
5303
|
return `i${v}`;
|
|
5372
5304
|
case "float4":
|
|
@@ -5742,68 +5674,6 @@ function getArrayFunctions() {
|
|
|
5742
5674
|
return m;
|
|
5743
5675
|
}
|
|
5744
5676
|
|
|
5745
|
-
// src/types/timezone.ts
|
|
5746
|
-
var intlCache = /* @__PURE__ */ new Map();
|
|
5747
|
-
function intlFor(zone) {
|
|
5748
|
-
const cached = intlCache.get(zone);
|
|
5749
|
-
if (cached) return cached;
|
|
5750
|
-
try {
|
|
5751
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
5752
|
-
timeZone: zone,
|
|
5753
|
-
timeZoneName: "longOffset",
|
|
5754
|
-
year: "numeric"
|
|
5755
|
-
});
|
|
5756
|
-
intlCache.set(zone, fmt);
|
|
5757
|
-
return fmt;
|
|
5758
|
-
} catch {
|
|
5759
|
-
return null;
|
|
5760
|
-
}
|
|
5761
|
-
}
|
|
5762
|
-
function parseFixedOffset(zone) {
|
|
5763
|
-
const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
|
|
5764
|
-
if (!m) return null;
|
|
5765
|
-
const sign = m[1] === "-" ? -1 : 1;
|
|
5766
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
|
|
5767
|
-
}
|
|
5768
|
-
function parsePosixOffset(zone) {
|
|
5769
|
-
const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
|
|
5770
|
-
if (!m) return null;
|
|
5771
|
-
const sign = m[1] === "-" ? 1 : -1;
|
|
5772
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
|
|
5773
|
-
}
|
|
5774
|
-
function pgMicrosToUnixMs(utcMicros) {
|
|
5775
|
-
return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
|
|
5776
|
-
}
|
|
5777
|
-
function zoneOffsetAtUtc(zone, utcMicros) {
|
|
5778
|
-
const z = zone.trim();
|
|
5779
|
-
const lower = z.toLowerCase();
|
|
5780
|
-
if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
|
|
5781
|
-
const posix = parsePosixOffset(z);
|
|
5782
|
-
if (posix !== null) return posix;
|
|
5783
|
-
const fixed = parseFixedOffset(z);
|
|
5784
|
-
if (fixed !== null) return fixed;
|
|
5785
|
-
const fmt = intlFor(z);
|
|
5786
|
-
if (fmt) {
|
|
5787
|
-
const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
|
|
5788
|
-
const parts = fmt.formatToParts(new Date(ms));
|
|
5789
|
-
const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
|
|
5790
|
-
const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
|
|
5791
|
-
if (!m) return 0;
|
|
5792
|
-
const sign = m[1] === "-" ? -1 : 1;
|
|
5793
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
|
|
5794
|
-
}
|
|
5795
|
-
throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
|
|
5796
|
-
}
|
|
5797
|
-
function clampDateMs(ms) {
|
|
5798
|
-
const MAX = 864e13;
|
|
5799
|
-
return Math.max(-MAX, Math.min(MAX, ms));
|
|
5800
|
-
}
|
|
5801
|
-
function zoneOffsetForNaive(zone, naiveMicros) {
|
|
5802
|
-
const first = zoneOffsetAtUtc(zone, naiveMicros);
|
|
5803
|
-
const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
|
|
5804
|
-
return second;
|
|
5805
|
-
}
|
|
5806
|
-
|
|
5807
5677
|
// src/functions/datetime-registry.ts
|
|
5808
5678
|
function getDatetimeFunctions() {
|
|
5809
5679
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -7403,6 +7273,28 @@ function exactDegTan(x) {
|
|
|
7403
7273
|
return Math.tan(x * Math.PI / 180);
|
|
7404
7274
|
}
|
|
7405
7275
|
|
|
7276
|
+
// src/storage/indexable-relation.ts
|
|
7277
|
+
function materializedTable(view) {
|
|
7278
|
+
const table = new TableData(
|
|
7279
|
+
view.schema,
|
|
7280
|
+
view.name,
|
|
7281
|
+
(view.matColumns ?? []).map((c) => ({
|
|
7282
|
+
name: c.name,
|
|
7283
|
+
type: { id: c.type, mod: null },
|
|
7284
|
+
notNull: false,
|
|
7285
|
+
defaultExpr: null,
|
|
7286
|
+
identity: null,
|
|
7287
|
+
generated: null,
|
|
7288
|
+
collate: null,
|
|
7289
|
+
domain: null
|
|
7290
|
+
})),
|
|
7291
|
+
view.oid,
|
|
7292
|
+
view.temp
|
|
7293
|
+
);
|
|
7294
|
+
table.rows = view.matRows ?? [];
|
|
7295
|
+
return table;
|
|
7296
|
+
}
|
|
7297
|
+
|
|
7406
7298
|
// src/schema/index-catalog.ts
|
|
7407
7299
|
var isKeyConstraint = (constraint) => constraint.kind === "primary_key" || constraint.kind === "unique";
|
|
7408
7300
|
var ACCESS_METHODS = /* @__PURE__ */ new Map([
|
|
@@ -7431,6 +7323,8 @@ function syncIndexCatalog(state) {
|
|
|
7431
7323
|
const tables = /* @__PURE__ */ new Map();
|
|
7432
7324
|
for (const schema of state.schemas.values()) {
|
|
7433
7325
|
for (const table of schema.tables.values()) tables.set(table.oid, { schema, table });
|
|
7326
|
+
for (const view of schema.views.values())
|
|
7327
|
+
if (view.materialized) tables.set(view.oid, { schema, table: materializedTable(view) });
|
|
7434
7328
|
}
|
|
7435
7329
|
const backing = /* @__PURE__ */ new Map();
|
|
7436
7330
|
for (const { table } of tables.values()) {
|
|
@@ -7628,6 +7522,55 @@ function deparseExpr(e) {
|
|
|
7628
7522
|
}
|
|
7629
7523
|
}
|
|
7630
7524
|
|
|
7525
|
+
// src/types/resolve.ts
|
|
7526
|
+
var SERIALS = {
|
|
7527
|
+
smallserial: "int2",
|
|
7528
|
+
serial2: "int2",
|
|
7529
|
+
serial: "int4",
|
|
7530
|
+
serial4: "int4",
|
|
7531
|
+
bigserial: "int8",
|
|
7532
|
+
serial8: "int8"
|
|
7533
|
+
};
|
|
7534
|
+
function modOf(t, mods) {
|
|
7535
|
+
if (mods.length === 0) return null;
|
|
7536
|
+
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
7537
|
+
return { a: mods[0] };
|
|
7538
|
+
}
|
|
7539
|
+
function resolveTypeName(state, tn) {
|
|
7540
|
+
const joined = tn.parts.join(".");
|
|
7541
|
+
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
7542
|
+
if (bare !== null) {
|
|
7543
|
+
const serial = SERIALS[bare];
|
|
7544
|
+
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
7545
|
+
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
7546
|
+
}
|
|
7547
|
+
const builtin = normalizeTypeName(bare);
|
|
7548
|
+
if (builtin !== null) {
|
|
7549
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
7550
|
+
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
const enumData = state.findEnum(tn.parts);
|
|
7554
|
+
if (enumData) {
|
|
7555
|
+
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
7556
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
7557
|
+
return { column: { id, mod: null }, domain: null, serial: null };
|
|
7558
|
+
}
|
|
7559
|
+
const domainData = state.findDomain(tn.parts);
|
|
7560
|
+
if (domainData) {
|
|
7561
|
+
if (tn.arrayDims > 0) {
|
|
7562
|
+
const id = arrayTypeOf(domainData.baseType.id);
|
|
7563
|
+
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
7564
|
+
}
|
|
7565
|
+
return {
|
|
7566
|
+
column: domainData.baseType,
|
|
7567
|
+
domain: `${domainData.schema}.${domainData.name}`,
|
|
7568
|
+
serial: null
|
|
7569
|
+
};
|
|
7570
|
+
}
|
|
7571
|
+
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7631
7574
|
// src/sql/ruleutils.ts
|
|
7632
7575
|
var BOOL = "bool";
|
|
7633
7576
|
var TEXT = "text";
|
|
@@ -8261,7 +8204,7 @@ function databaseCatalogContext(state) {
|
|
|
8261
8204
|
var PG_VERSION_TEXT = "PostgreSQL 18.3 (postgres-mem) on TypeScript, in-memory engine";
|
|
8262
8205
|
function findSequenceForCall(ctx, name) {
|
|
8263
8206
|
const parts = parseQualifiedName(name);
|
|
8264
|
-
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);
|
|
8265
8208
|
if (!seq) {
|
|
8266
8209
|
throw pgError("undefined_table", `relation "${name}" does not exist`, "42P01");
|
|
8267
8210
|
}
|
|
@@ -8422,7 +8365,11 @@ function getMiscFunctions() {
|
|
|
8422
8365
|
}
|
|
8423
8366
|
const name = argText(ctx, args[0]).toLowerCase();
|
|
8424
8367
|
const value = args[1].v === null ? "" : argText(ctx, args[1]);
|
|
8425
|
-
ctx.state.settings.
|
|
8368
|
+
if (!ctx.state.settings.has(name) && !name.includes("."))
|
|
8369
|
+
throw pgError("undefined_object", `unrecognized configuration parameter "${name}"`, "42704");
|
|
8370
|
+
validateStorageSetting(name, value);
|
|
8371
|
+
const local = args[2]?.v === true;
|
|
8372
|
+
if (!local || ctx.state.inTransaction) (local ? ctx.state.localSettings : ctx.state.settings).set(name, value);
|
|
8426
8373
|
return tv("text", value);
|
|
8427
8374
|
});
|
|
8428
8375
|
m.set(
|
|
@@ -8481,17 +8428,16 @@ function getMiscFunctions() {
|
|
|
8481
8428
|
"pg_get_serial_sequence",
|
|
8482
8429
|
strict("text", (ctx, args) => {
|
|
8483
8430
|
const tableName2 = argText(ctx, args[0]);
|
|
8484
|
-
const column = argText(ctx, args[1])
|
|
8431
|
+
const column = argText(ctx, args[1]);
|
|
8485
8432
|
const table = ctx.state.findTable(parseQualifiedName(tableName2));
|
|
8486
8433
|
if (!table) throw pgError("undefined_table", `relation "${tableName2}" does not exist`, "42P01");
|
|
8487
8434
|
const col = table.columns.find((c) => c.name === column);
|
|
8488
8435
|
if (!col)
|
|
8489
8436
|
throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
"text",
|
|
8493
|
-
col.identity.sequence.split(".").map(quoteIdent).join(".")
|
|
8437
|
+
const seq = [...ctx.state.schemas.get(table.schema)?.sequences.values() ?? []].find(
|
|
8438
|
+
(candidate) => candidate.ownedBy?.table === table.name && candidate.ownedBy.column === column
|
|
8494
8439
|
);
|
|
8440
|
+
return tv("text", seq ? [seq.schema, seq.name].map(quoteIdent).join(".") : null);
|
|
8495
8441
|
})
|
|
8496
8442
|
);
|
|
8497
8443
|
m.set("format_type", (ctx, args) => {
|
|
@@ -13415,6 +13361,146 @@ function isSrfName(name) {
|
|
|
13415
13361
|
return SRFS.has(name);
|
|
13416
13362
|
}
|
|
13417
13363
|
|
|
13364
|
+
// src/executor/relation.ts
|
|
13365
|
+
var RowScope = class {
|
|
13366
|
+
constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
|
|
13367
|
+
this.columns = columns;
|
|
13368
|
+
this.row = row;
|
|
13369
|
+
this.parent = parent;
|
|
13370
|
+
this.rangeVars = rangeVars;
|
|
13371
|
+
}
|
|
13372
|
+
columns;
|
|
13373
|
+
row;
|
|
13374
|
+
parent;
|
|
13375
|
+
rangeVars;
|
|
13376
|
+
/** Resolve a (possibly qualified) column reference. undefined = not found. */
|
|
13377
|
+
lookup(parts) {
|
|
13378
|
+
if (parts.length === 1) {
|
|
13379
|
+
const name2 = parts[0];
|
|
13380
|
+
let found2;
|
|
13381
|
+
let count2 = 0;
|
|
13382
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13383
|
+
const c = this.columns[i];
|
|
13384
|
+
if (c.name === name2 && (!c.hidden || c.system)) {
|
|
13385
|
+
count2++;
|
|
13386
|
+
if (count2 > 1) {
|
|
13387
|
+
throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
|
|
13388
|
+
}
|
|
13389
|
+
found2 = tv(c.type, this.row[i] ?? null);
|
|
13390
|
+
}
|
|
13391
|
+
}
|
|
13392
|
+
if (found2 !== void 0) return found2;
|
|
13393
|
+
if (this.rangeVars.has(name2)) {
|
|
13394
|
+
return this.wholeRow(name2);
|
|
13395
|
+
}
|
|
13396
|
+
return this.parent?.lookup(parts);
|
|
13397
|
+
}
|
|
13398
|
+
const table = parts[parts.length - 2];
|
|
13399
|
+
const name = parts[parts.length - 1];
|
|
13400
|
+
let found;
|
|
13401
|
+
let count = 0;
|
|
13402
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13403
|
+
const c = this.columns[i];
|
|
13404
|
+
if (c.table === table && c.name === name) {
|
|
13405
|
+
count++;
|
|
13406
|
+
if (count > 1) {
|
|
13407
|
+
throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
|
|
13408
|
+
}
|
|
13409
|
+
found = tv(c.type, this.row[i] ?? null);
|
|
13410
|
+
}
|
|
13411
|
+
}
|
|
13412
|
+
if (found !== void 0) return found;
|
|
13413
|
+
return this.parent?.lookup(parts);
|
|
13414
|
+
}
|
|
13415
|
+
/** Build a record value from all columns belonging to a range variable. */
|
|
13416
|
+
wholeRow(rangeVar) {
|
|
13417
|
+
const types = [];
|
|
13418
|
+
const values = [];
|
|
13419
|
+
const names = [];
|
|
13420
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13421
|
+
const c = this.columns[i];
|
|
13422
|
+
if (c.table === rangeVar && !c.system) {
|
|
13423
|
+
types.push(c.type);
|
|
13424
|
+
values.push(this.row[i] ?? null);
|
|
13425
|
+
names.push(c.name);
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
if (types.length === 0) return void 0;
|
|
13429
|
+
return tv("record", { kind: "pgrecord", types, values, names });
|
|
13430
|
+
}
|
|
13431
|
+
hasRangeVar(name) {
|
|
13432
|
+
if (this.rangeVars.has(name)) return true;
|
|
13433
|
+
return this.parent?.hasRangeVar(name) ?? false;
|
|
13434
|
+
}
|
|
13435
|
+
};
|
|
13436
|
+
function childEnv(env, outer) {
|
|
13437
|
+
return {
|
|
13438
|
+
ctx: env.ctx,
|
|
13439
|
+
params: env.params,
|
|
13440
|
+
ctes: env.ctes,
|
|
13441
|
+
outer,
|
|
13442
|
+
allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
|
|
13443
|
+
};
|
|
13444
|
+
}
|
|
13445
|
+
function relationResult(rel3, command) {
|
|
13446
|
+
const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
|
|
13447
|
+
return {
|
|
13448
|
+
columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
|
|
13449
|
+
rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
|
|
13450
|
+
command,
|
|
13451
|
+
rowCount: rel3.rows.length
|
|
13452
|
+
};
|
|
13453
|
+
}
|
|
13454
|
+
function commandResult(command, rowCount) {
|
|
13455
|
+
return { columns: [], rows: [], command, rowCount };
|
|
13456
|
+
}
|
|
13457
|
+
function inferColumnName(e) {
|
|
13458
|
+
switch (e.type) {
|
|
13459
|
+
case "colref":
|
|
13460
|
+
return e.parts[e.parts.length - 1];
|
|
13461
|
+
case "func":
|
|
13462
|
+
return e.name[e.name.length - 1];
|
|
13463
|
+
case "cast": {
|
|
13464
|
+
const inner = inferColumnName(e.expr);
|
|
13465
|
+
if (inner !== "?column?") return inner;
|
|
13466
|
+
return e.target.parts[e.target.parts.length - 1];
|
|
13467
|
+
}
|
|
13468
|
+
case "collate":
|
|
13469
|
+
return inferColumnName(e.expr);
|
|
13470
|
+
case "case":
|
|
13471
|
+
return "case";
|
|
13472
|
+
case "array_ctor":
|
|
13473
|
+
case "array_query":
|
|
13474
|
+
return "array";
|
|
13475
|
+
case "row":
|
|
13476
|
+
return "row";
|
|
13477
|
+
case "subquery_expr":
|
|
13478
|
+
return e.kind === "exists" ? "exists" : "?column?";
|
|
13479
|
+
case "bool_lit":
|
|
13480
|
+
return "bool";
|
|
13481
|
+
case "extract":
|
|
13482
|
+
return "extract";
|
|
13483
|
+
case "position":
|
|
13484
|
+
return "position";
|
|
13485
|
+
case "substring_sql":
|
|
13486
|
+
return "substring";
|
|
13487
|
+
case "overlay":
|
|
13488
|
+
return "overlay";
|
|
13489
|
+
case "trim":
|
|
13490
|
+
return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
|
|
13491
|
+
case "field_select":
|
|
13492
|
+
return e.field === "*" ? "?column?" : e.field;
|
|
13493
|
+
case "at_time_zone":
|
|
13494
|
+
return "timezone";
|
|
13495
|
+
case "grouping_func":
|
|
13496
|
+
return "grouping";
|
|
13497
|
+
case "subscript":
|
|
13498
|
+
return inferColumnName(e.base);
|
|
13499
|
+
default:
|
|
13500
|
+
return "?column?";
|
|
13501
|
+
}
|
|
13502
|
+
}
|
|
13503
|
+
|
|
13418
13504
|
// src/indexes/index.ts
|
|
13419
13505
|
var IndexStore = class _IndexStore {
|
|
13420
13506
|
name;
|
|
@@ -13517,13 +13603,188 @@ function indexRemoveRow(env, table, rowIdx, row) {
|
|
|
13517
13603
|
table.indexStores.get(spec.name)?.remove(key, rowIdx);
|
|
13518
13604
|
}
|
|
13519
13605
|
}
|
|
13520
|
-
function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
|
|
13521
|
-
indexRemoveRow(env, table, rowIdx, oldRow);
|
|
13522
|
-
indexInsertRow(env, table, rowIdx, newRow);
|
|
13523
|
-
}
|
|
13524
|
-
function rebuildTableIndexes(env, table) {
|
|
13525
|
-
table.indexStores = null;
|
|
13526
|
-
ensureIndexStores(env, table);
|
|
13606
|
+
function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
|
|
13607
|
+
indexRemoveRow(env, table, rowIdx, oldRow);
|
|
13608
|
+
indexInsertRow(env, table, rowIdx, newRow);
|
|
13609
|
+
}
|
|
13610
|
+
function rebuildTableIndexes(env, table) {
|
|
13611
|
+
table.indexStores = null;
|
|
13612
|
+
ensureIndexStores(env, table);
|
|
13613
|
+
}
|
|
13614
|
+
|
|
13615
|
+
// src/constraints/enforce.ts
|
|
13616
|
+
function tableScope(table, row) {
|
|
13617
|
+
const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
|
|
13618
|
+
return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
|
|
13619
|
+
}
|
|
13620
|
+
function checkNotNull(env, table, row) {
|
|
13621
|
+
void env;
|
|
13622
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
13623
|
+
const c = table.columns[i];
|
|
13624
|
+
if (c.notNull && (row[i] ?? null) === null) {
|
|
13625
|
+
throw pgError(
|
|
13626
|
+
"not_null_violation",
|
|
13627
|
+
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
13628
|
+
"23502"
|
|
13629
|
+
);
|
|
13630
|
+
}
|
|
13631
|
+
}
|
|
13632
|
+
}
|
|
13633
|
+
function checkChecks(env, table, row) {
|
|
13634
|
+
for (const con of table.constraints) {
|
|
13635
|
+
if (con.kind !== "check") continue;
|
|
13636
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13637
|
+
const v = evalExpr(env.ctx, scope, con.expr);
|
|
13638
|
+
if (v.v === null) continue;
|
|
13639
|
+
const b = castTo(env.ctx, v, "bool", {});
|
|
13640
|
+
if (b.v !== true) {
|
|
13641
|
+
throw pgError(
|
|
13642
|
+
"check_violation",
|
|
13643
|
+
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
13644
|
+
"23514"
|
|
13645
|
+
);
|
|
13646
|
+
}
|
|
13647
|
+
}
|
|
13648
|
+
}
|
|
13649
|
+
function uniqueSpecsFor(env, table) {
|
|
13650
|
+
const specs = [];
|
|
13651
|
+
for (const con of table.constraints) {
|
|
13652
|
+
if (con.kind !== "primary_key" && con.kind !== "unique") continue;
|
|
13653
|
+
specs.push({
|
|
13654
|
+
name: con.name,
|
|
13655
|
+
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
13656
|
+
columnNames: con.columns,
|
|
13657
|
+
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
13658
|
+
where: null,
|
|
13659
|
+
isPrimary: con.kind === "primary_key"
|
|
13660
|
+
});
|
|
13661
|
+
}
|
|
13662
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
13663
|
+
if (schema) {
|
|
13664
|
+
for (const idx of schema.indexes.values()) {
|
|
13665
|
+
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
13666
|
+
specs.push(uniqueSpecOfIndex(table, idx));
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
return specs;
|
|
13670
|
+
}
|
|
13671
|
+
function uniqueSpecOfIndex(table, idx) {
|
|
13672
|
+
return {
|
|
13673
|
+
name: idx.name,
|
|
13674
|
+
keys: idx.columns.map((c) => ({
|
|
13675
|
+
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
13676
|
+
expr: c.expr
|
|
13677
|
+
})),
|
|
13678
|
+
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
13679
|
+
nullsNotDistinct: idx.nullsNotDistinct,
|
|
13680
|
+
where: idx.where,
|
|
13681
|
+
isPrimary: false
|
|
13682
|
+
};
|
|
13683
|
+
}
|
|
13684
|
+
function uniqueKeyOf(env, table, spec, row) {
|
|
13685
|
+
if (spec.where) {
|
|
13686
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13687
|
+
const v = evalExpr(env.ctx, scope, spec.where);
|
|
13688
|
+
if (v.v !== true) return null;
|
|
13689
|
+
}
|
|
13690
|
+
const parts = [];
|
|
13691
|
+
let hasNull = false;
|
|
13692
|
+
for (const k of spec.keys) {
|
|
13693
|
+
let value;
|
|
13694
|
+
let type;
|
|
13695
|
+
if (k.expr) {
|
|
13696
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13697
|
+
const v = evalExpr(env.ctx, scope, k.expr);
|
|
13698
|
+
value = v.v;
|
|
13699
|
+
type = v.t === "unknown" ? "text" : v.t;
|
|
13700
|
+
} else {
|
|
13701
|
+
value = row[k.colIdx] ?? null;
|
|
13702
|
+
type = table.columns[k.colIdx].type.id;
|
|
13703
|
+
}
|
|
13704
|
+
if (value === null) {
|
|
13705
|
+
hasNull = true;
|
|
13706
|
+
parts.push("\0N");
|
|
13707
|
+
} else {
|
|
13708
|
+
parts.push(datumKey(type, value));
|
|
13709
|
+
}
|
|
13710
|
+
}
|
|
13711
|
+
if (hasNull && !spec.nullsNotDistinct) return null;
|
|
13712
|
+
return parts.join("");
|
|
13713
|
+
}
|
|
13714
|
+
function checkUnique(env, table, row, selfIdx) {
|
|
13715
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
13716
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
13717
|
+
if (key === null) continue;
|
|
13718
|
+
indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
|
|
13719
|
+
}
|
|
13720
|
+
}
|
|
13721
|
+
function findConflict(env, table, spec, row) {
|
|
13722
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
13723
|
+
if (key === null) return null;
|
|
13724
|
+
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
13725
|
+
return hits.length > 0 ? hits[0] : null;
|
|
13726
|
+
}
|
|
13727
|
+
function checkForeignKeys(env, table, row) {
|
|
13728
|
+
const state = env.ctx.state;
|
|
13729
|
+
for (const con of table.constraints) {
|
|
13730
|
+
if (con.kind !== "foreign_key") continue;
|
|
13731
|
+
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
13732
|
+
const nulls = values.filter((v) => v === null).length;
|
|
13733
|
+
if (con.match === "simple" && nulls > 0) continue;
|
|
13734
|
+
if (con.match === "full") {
|
|
13735
|
+
if (nulls === values.length) continue;
|
|
13736
|
+
if (nulls > 0) {
|
|
13737
|
+
throw pgError(
|
|
13738
|
+
"constraint_foreign_key",
|
|
13739
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
13740
|
+
"23503"
|
|
13741
|
+
);
|
|
13742
|
+
}
|
|
13743
|
+
}
|
|
13744
|
+
const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
|
|
13745
|
+
if (!refTable) {
|
|
13746
|
+
throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
|
|
13747
|
+
}
|
|
13748
|
+
const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
|
|
13749
|
+
const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
|
|
13750
|
+
const wanted = values.map((v, i) => {
|
|
13751
|
+
const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
|
|
13752
|
+
const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
|
|
13753
|
+
return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
|
|
13754
|
+
}).join("");
|
|
13755
|
+
let found = false;
|
|
13756
|
+
for (let i = 0; i < refTable.rowCount(); i++) {
|
|
13757
|
+
const r = refTable.rowAt(i);
|
|
13758
|
+
const key = refIdxs.map((ri, j) => {
|
|
13759
|
+
const v = r[ri] ?? null;
|
|
13760
|
+
return v === null ? "\0N" : datumKey(keyTypes[j], v);
|
|
13761
|
+
}).join("");
|
|
13762
|
+
if (key === wanted) {
|
|
13763
|
+
found = true;
|
|
13764
|
+
break;
|
|
13765
|
+
}
|
|
13766
|
+
}
|
|
13767
|
+
if (!found) {
|
|
13768
|
+
throw pgError(
|
|
13769
|
+
"constraint_foreign_key",
|
|
13770
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
13771
|
+
"23503"
|
|
13772
|
+
);
|
|
13773
|
+
}
|
|
13774
|
+
}
|
|
13775
|
+
}
|
|
13776
|
+
function referencingConstraints(env, table) {
|
|
13777
|
+
const out = [];
|
|
13778
|
+
for (const schema of env.ctx.state.schemas.values()) {
|
|
13779
|
+
for (const t of schema.tables.values()) {
|
|
13780
|
+
for (const con of t.constraints) {
|
|
13781
|
+
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
13782
|
+
out.push({ table: t, constraint: con });
|
|
13783
|
+
}
|
|
13784
|
+
}
|
|
13785
|
+
}
|
|
13786
|
+
}
|
|
13787
|
+
return out;
|
|
13527
13788
|
}
|
|
13528
13789
|
|
|
13529
13790
|
// src/planner/access.ts
|
|
@@ -13601,7 +13862,7 @@ function tryIndexedFromItem(env, item, where) {
|
|
|
13601
13862
|
if (!table) return null;
|
|
13602
13863
|
const alias = item.alias ?? item.name[item.name.length - 1];
|
|
13603
13864
|
table.materializeSlab();
|
|
13604
|
-
return tryIndexedTableRows(env, table, alias, where);
|
|
13865
|
+
return tryIndexedTableRows(env, table, alias, where)?.map((row) => [...row, table.rowXmins.get(row) ?? 2]) ?? null;
|
|
13605
13866
|
}
|
|
13606
13867
|
function joinKeyFromRow(row, colIdxs, srcTypes, unifiedTypes, ctx) {
|
|
13607
13868
|
const parts = [];
|
|
@@ -15409,10 +15670,9 @@ var Parser = class {
|
|
|
15409
15670
|
return this.parseCreateFunction(orReplace);
|
|
15410
15671
|
}
|
|
15411
15672
|
if (this.atKw("trigger") || this.atKw("constraint") && this.atKw("trigger", 1)) {
|
|
15412
|
-
|
|
15413
|
-
}
|
|
15673
|
+
const constraint = this.eatKw("constraint");
|
|
15414
15674
|
this.expectKw("trigger");
|
|
15415
|
-
return this.parseCreateTrigger(orReplace);
|
|
15675
|
+
return this.parseCreateTrigger(orReplace, constraint);
|
|
15416
15676
|
}
|
|
15417
15677
|
if (this.atKw("extension")) {
|
|
15418
15678
|
this.pos++;
|
|
@@ -16092,6 +16352,7 @@ var Parser = class {
|
|
|
16092
16352
|
let sqlBody = null;
|
|
16093
16353
|
let volatility = null;
|
|
16094
16354
|
let strict2 = false;
|
|
16355
|
+
const settings = {};
|
|
16095
16356
|
for (; ; ) {
|
|
16096
16357
|
if (this.eatKw("language")) {
|
|
16097
16358
|
language = this.peek().type === "string" ? this.next().value : this.ident(true);
|
|
@@ -16149,8 +16410,23 @@ var Parser = class {
|
|
|
16149
16410
|
continue;
|
|
16150
16411
|
}
|
|
16151
16412
|
if (this.eatKw("set")) {
|
|
16152
|
-
this.ident(true);
|
|
16153
|
-
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
|
+
}
|
|
16154
16430
|
continue;
|
|
16155
16431
|
}
|
|
16156
16432
|
if (this.eatKw("window")) throw unsupported("window functions via CREATE FUNCTION");
|
|
@@ -16230,10 +16506,11 @@ var Parser = class {
|
|
|
16230
16506
|
body,
|
|
16231
16507
|
sqlBody,
|
|
16232
16508
|
volatility,
|
|
16233
|
-
strict: strict2
|
|
16509
|
+
strict: strict2,
|
|
16510
|
+
settings
|
|
16234
16511
|
};
|
|
16235
16512
|
}
|
|
16236
|
-
parseCreateTrigger(orReplace) {
|
|
16513
|
+
parseCreateTrigger(orReplace, constraint) {
|
|
16237
16514
|
const name = this.ident();
|
|
16238
16515
|
let timing;
|
|
16239
16516
|
if (this.eatKw("before")) timing = "before";
|
|
@@ -16263,9 +16540,18 @@ var Parser = class {
|
|
|
16263
16540
|
this.expectKw("on");
|
|
16264
16541
|
const table = this.qualifiedName();
|
|
16265
16542
|
if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
|
|
16266
|
-
|
|
16267
|
-
|
|
16268
|
-
|
|
16543
|
+
let deferrable = false;
|
|
16544
|
+
let initiallyDeferred = false;
|
|
16545
|
+
if (this.eatKw("not")) this.expectKw("deferrable");
|
|
16546
|
+
else if (this.eatKw("deferrable")) deferrable = true;
|
|
16547
|
+
if (this.eatKw("initially")) {
|
|
16548
|
+
if (this.eatKw("deferred")) initiallyDeferred = true;
|
|
16549
|
+
else this.expectKw("immediate");
|
|
16550
|
+
}
|
|
16551
|
+
if ((deferrable || initiallyDeferred) && !constraint)
|
|
16552
|
+
this.errorAt(this.peek(), "only constraint triggers can be deferred");
|
|
16553
|
+
if (initiallyDeferred && !deferrable)
|
|
16554
|
+
this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
|
|
16269
16555
|
let forEachRow = false;
|
|
16270
16556
|
if (this.eatKw("for")) {
|
|
16271
16557
|
this.eatKw("each");
|
|
@@ -16290,7 +16576,21 @@ var Parser = class {
|
|
|
16290
16576
|
} while (this.eatPunct(","));
|
|
16291
16577
|
}
|
|
16292
16578
|
this.expectPunct(")");
|
|
16293
|
-
return {
|
|
16579
|
+
return {
|
|
16580
|
+
type: "create_trigger",
|
|
16581
|
+
constraint,
|
|
16582
|
+
deferrable,
|
|
16583
|
+
initiallyDeferred,
|
|
16584
|
+
orReplace,
|
|
16585
|
+
name,
|
|
16586
|
+
timing,
|
|
16587
|
+
events,
|
|
16588
|
+
table,
|
|
16589
|
+
forEachRow,
|
|
16590
|
+
when,
|
|
16591
|
+
funcName,
|
|
16592
|
+
funcArgs
|
|
16593
|
+
};
|
|
16294
16594
|
}
|
|
16295
16595
|
// --- ALTER --------------------------------------------------------------------
|
|
16296
16596
|
parseAlter() {
|
|
@@ -16316,8 +16616,8 @@ var Parser = class {
|
|
|
16316
16616
|
type: "alter_sequence",
|
|
16317
16617
|
name,
|
|
16318
16618
|
ifExists,
|
|
16319
|
-
options: {
|
|
16320
|
-
|
|
16619
|
+
options: {},
|
|
16620
|
+
renameTo: to
|
|
16321
16621
|
};
|
|
16322
16622
|
}
|
|
16323
16623
|
if (this.atKw("set") && (this.atKw("logged", 1) || this.atKw("unlogged", 1))) {
|
|
@@ -16635,9 +16935,15 @@ var Parser = class {
|
|
|
16635
16935
|
this.skipToStatementEnd();
|
|
16636
16936
|
return { type: "no_op", what: "SET TRANSACTION" };
|
|
16637
16937
|
}
|
|
16638
|
-
if (this.
|
|
16639
|
-
this.
|
|
16640
|
-
|
|
16938
|
+
if (this.eatKw("constraints")) {
|
|
16939
|
+
const names = this.eatKw("all") ? null : [];
|
|
16940
|
+
if (names)
|
|
16941
|
+
do {
|
|
16942
|
+
names.push(this.qualifiedName());
|
|
16943
|
+
} while (this.eatPunct(","));
|
|
16944
|
+
const deferred = this.eatKw("deferred");
|
|
16945
|
+
if (!deferred) this.expectKw("immediate");
|
|
16946
|
+
return { type: "set_constraints", names, deferred };
|
|
16641
16947
|
}
|
|
16642
16948
|
if (this.atKw("role") || this.atKw("session", 0) && this.atKw("authorization", 1)) {
|
|
16643
16949
|
this.skipToStatementEnd();
|
|
@@ -18887,7 +19193,20 @@ function runFunctionBody(env, fn, args) {
|
|
|
18887
19193
|
}
|
|
18888
19194
|
return last;
|
|
18889
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
|
+
}
|
|
18890
19206
|
function callSqlFunctionScalar(env, fn, args) {
|
|
19207
|
+
return withFunctionSettings(env, fn, () => callSqlFunctionScalarBody(env, fn, args));
|
|
19208
|
+
}
|
|
19209
|
+
function callSqlFunctionScalarBody(env, fn, args) {
|
|
18891
19210
|
const retT = fn.returns ?? "text";
|
|
18892
19211
|
if (fn.language === "plpgsql") {
|
|
18893
19212
|
return callPlpgsqlScalar(env, fn, args);
|
|
@@ -18917,6 +19236,9 @@ function callSqlFunctionScalar(env, fn, args) {
|
|
|
18917
19236
|
return castTo(env.ctx, tv(rawT, raw), retT, {});
|
|
18918
19237
|
}
|
|
18919
19238
|
function callSqlFunctionSet(env, fn, args) {
|
|
19239
|
+
return withFunctionSettings(env, fn, () => callSqlFunctionSetBody(env, fn, args));
|
|
19240
|
+
}
|
|
19241
|
+
function callSqlFunctionSetBody(env, fn, args) {
|
|
18920
19242
|
if (fn.language === "plpgsql") {
|
|
18921
19243
|
return callPlpgsqlSet(env, fn, args);
|
|
18922
19244
|
}
|
|
@@ -19012,12 +19334,12 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19012
19334
|
table: null
|
|
19013
19335
|
}));
|
|
19014
19336
|
const seen = /* @__PURE__ */ new Set();
|
|
19015
|
-
const
|
|
19337
|
+
const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
|
|
19016
19338
|
let result = [];
|
|
19017
19339
|
let working = [];
|
|
19018
19340
|
for (const row of base.rows) {
|
|
19019
19341
|
if (!body.all) {
|
|
19020
|
-
const k =
|
|
19342
|
+
const k = keyOf2(row);
|
|
19021
19343
|
if (seen.has(k)) continue;
|
|
19022
19344
|
seen.add(k);
|
|
19023
19345
|
}
|
|
@@ -19050,7 +19372,7 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19050
19372
|
return castTo(env.ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), columns[i].type, {}).v;
|
|
19051
19373
|
});
|
|
19052
19374
|
if (!body.all) {
|
|
19053
|
-
const k =
|
|
19375
|
+
const k = keyOf2(row);
|
|
19054
19376
|
if (seen.has(k)) continue;
|
|
19055
19377
|
seen.add(k);
|
|
19056
19378
|
}
|
|
@@ -19071,15 +19393,17 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19071
19393
|
return rel3;
|
|
19072
19394
|
}
|
|
19073
19395
|
function renameWithAliases(rel3, label, colAliases, what) {
|
|
19074
|
-
|
|
19396
|
+
const visibleWidth = rel3.columns.filter((column) => !column.system).length;
|
|
19397
|
+
if (colAliases && colAliases.length > visibleWidth) {
|
|
19075
19398
|
throw pgError(
|
|
19076
19399
|
"invalid_column_reference",
|
|
19077
|
-
`table "${what}" has ${
|
|
19400
|
+
`table "${what}" has ${visibleWidth} columns available but ${colAliases.length} columns specified`,
|
|
19078
19401
|
"42P10"
|
|
19079
19402
|
);
|
|
19080
19403
|
}
|
|
19081
19404
|
const columns = rel3.columns.map((c, i) => ({
|
|
19082
|
-
|
|
19405
|
+
...c,
|
|
19406
|
+
name: c.system ? c.name : colAliases?.[i] ?? c.name,
|
|
19083
19407
|
type: c.type === UNKNOWN ? "text" : c.type,
|
|
19084
19408
|
table: label
|
|
19085
19409
|
}));
|
|
@@ -19100,8 +19424,11 @@ function materializeItem(env, item, scope) {
|
|
|
19100
19424
|
if (table) {
|
|
19101
19425
|
table.materializeSlab();
|
|
19102
19426
|
const rel3 = {
|
|
19103
|
-
columns:
|
|
19104
|
-
|
|
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])
|
|
19105
19432
|
};
|
|
19106
19433
|
return { rel: renameWithAliases(rel3, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
|
|
19107
19434
|
}
|
|
@@ -19731,7 +20058,7 @@ function resolveColIdx(columns, parts) {
|
|
|
19731
20058
|
let found = -1;
|
|
19732
20059
|
for (let i = 0; i < columns.length; i++) {
|
|
19733
20060
|
const c = columns[i];
|
|
19734
|
-
if (!c.hidden && c.name === name2) {
|
|
20061
|
+
if ((!c.hidden || c.system) && c.name === name2) {
|
|
19735
20062
|
if (found !== -1) return null;
|
|
19736
20063
|
found = i;
|
|
19737
20064
|
}
|
|
@@ -20230,7 +20557,7 @@ function setOpRelation(env, op) {
|
|
|
20230
20557
|
);
|
|
20231
20558
|
const lrows = normalize(left);
|
|
20232
20559
|
const rrows = normalize(right);
|
|
20233
|
-
const
|
|
20560
|
+
const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
|
|
20234
20561
|
let rows;
|
|
20235
20562
|
switch (op.op) {
|
|
20236
20563
|
case "union": {
|
|
@@ -20238,7 +20565,7 @@ function setOpRelation(env, op) {
|
|
|
20238
20565
|
if (!op.all) {
|
|
20239
20566
|
const seen = /* @__PURE__ */ new Set();
|
|
20240
20567
|
rows = rows.filter((r) => {
|
|
20241
|
-
const k =
|
|
20568
|
+
const k = keyOf2(r);
|
|
20242
20569
|
if (seen.has(k)) return false;
|
|
20243
20570
|
seen.add(k);
|
|
20244
20571
|
return true;
|
|
@@ -20249,13 +20576,13 @@ function setOpRelation(env, op) {
|
|
|
20249
20576
|
case "intersect": {
|
|
20250
20577
|
const rightCounts = /* @__PURE__ */ new Map();
|
|
20251
20578
|
for (const r of rrows) {
|
|
20252
|
-
const k =
|
|
20579
|
+
const k = keyOf2(r);
|
|
20253
20580
|
rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
|
|
20254
20581
|
}
|
|
20255
20582
|
rows = [];
|
|
20256
20583
|
const emitted = /* @__PURE__ */ new Set();
|
|
20257
20584
|
for (const r of lrows) {
|
|
20258
|
-
const k =
|
|
20585
|
+
const k = keyOf2(r);
|
|
20259
20586
|
const avail = rightCounts.get(k) ?? 0;
|
|
20260
20587
|
if (avail <= 0) continue;
|
|
20261
20588
|
if (op.all) {
|
|
@@ -20271,13 +20598,13 @@ function setOpRelation(env, op) {
|
|
|
20271
20598
|
case "except": {
|
|
20272
20599
|
const rightCounts = /* @__PURE__ */ new Map();
|
|
20273
20600
|
for (const r of rrows) {
|
|
20274
|
-
const k =
|
|
20601
|
+
const k = keyOf2(r);
|
|
20275
20602
|
rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
|
|
20276
20603
|
}
|
|
20277
20604
|
rows = [];
|
|
20278
20605
|
const emitted = /* @__PURE__ */ new Set();
|
|
20279
20606
|
for (const r of lrows) {
|
|
20280
|
-
const k =
|
|
20607
|
+
const k = keyOf2(r);
|
|
20281
20608
|
if (op.all) {
|
|
20282
20609
|
const avail = rightCounts.get(k) ?? 0;
|
|
20283
20610
|
if (avail > 0) {
|
|
@@ -20315,7 +20642,7 @@ function expandTargets(core, columns) {
|
|
|
20315
20642
|
let any = false;
|
|
20316
20643
|
for (let i = 0; i < columns.length; i++) {
|
|
20317
20644
|
const c = columns[i];
|
|
20318
|
-
if (c.table === label) {
|
|
20645
|
+
if (c.table === label && !c.system) {
|
|
20319
20646
|
any = true;
|
|
20320
20647
|
items.push({ name: c.name, kind: "col", colIdx: i, expr: null, hidden: false });
|
|
20321
20648
|
}
|
|
@@ -20753,202 +21080,225 @@ function executeSelectStmt(env0, stmt) {
|
|
|
20753
21080
|
return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
|
|
20754
21081
|
}
|
|
20755
21082
|
|
|
20756
|
-
// src/
|
|
20757
|
-
function
|
|
20758
|
-
const cols =
|
|
20759
|
-
|
|
20760
|
-
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
throw pgError(
|
|
20767
|
-
"not_null_violation",
|
|
20768
|
-
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
20769
|
-
"23502"
|
|
20770
|
-
);
|
|
21083
|
+
// src/executor/triggers-scope.ts
|
|
21084
|
+
function triggerScope(table, newRow, oldRow) {
|
|
21085
|
+
const cols = [];
|
|
21086
|
+
const row = [];
|
|
21087
|
+
const rangeVars = /* @__PURE__ */ new Set();
|
|
21088
|
+
if (newRow) {
|
|
21089
|
+
rangeVars.add("new");
|
|
21090
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
21091
|
+
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
|
|
21092
|
+
row.push(newRow[i] ?? null);
|
|
20771
21093
|
}
|
|
20772
21094
|
}
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
const v = evalExpr(env.ctx, scope, con.expr);
|
|
20779
|
-
if (v.v === null) continue;
|
|
20780
|
-
const b = castTo(env.ctx, v, "bool", {});
|
|
20781
|
-
if (b.v !== true) {
|
|
20782
|
-
throw pgError(
|
|
20783
|
-
"check_violation",
|
|
20784
|
-
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
20785
|
-
"23514"
|
|
20786
|
-
);
|
|
21095
|
+
if (oldRow) {
|
|
21096
|
+
rangeVars.add("old");
|
|
21097
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
21098
|
+
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
|
|
21099
|
+
row.push(oldRow[i] ?? null);
|
|
20787
21100
|
}
|
|
20788
21101
|
}
|
|
21102
|
+
return new RowScope(cols, row, null, rangeVars);
|
|
20789
21103
|
}
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
specs.push({
|
|
20795
|
-
name: con.name,
|
|
20796
|
-
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
20797
|
-
columnNames: con.columns,
|
|
20798
|
-
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
20799
|
-
where: null,
|
|
20800
|
-
isPrimary: con.kind === "primary_key"
|
|
20801
|
-
});
|
|
20802
|
-
}
|
|
20803
|
-
const schema = env.ctx.state.schemas.get(table.schema);
|
|
20804
|
-
if (schema) {
|
|
20805
|
-
for (const idx of schema.indexes.values()) {
|
|
20806
|
-
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
20807
|
-
specs.push(uniqueSpecOfIndex(table, idx));
|
|
20808
|
-
}
|
|
20809
|
-
}
|
|
20810
|
-
return specs;
|
|
21104
|
+
|
|
21105
|
+
// src/executor/triggers.ts
|
|
21106
|
+
function matches(t, event) {
|
|
21107
|
+
return t.events.some((e) => e.event === event);
|
|
20811
21108
|
}
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
keys: idx.columns.map((c) => ({
|
|
20816
|
-
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
20817
|
-
expr: c.expr
|
|
20818
|
-
})),
|
|
20819
|
-
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
20820
|
-
nullsNotDistinct: idx.nullsNotDistinct,
|
|
20821
|
-
where: idx.where,
|
|
20822
|
-
isPrimary: false
|
|
20823
|
-
};
|
|
21109
|
+
var executor = null;
|
|
21110
|
+
function setTriggerExecutor(e) {
|
|
21111
|
+
executor = e;
|
|
20824
21112
|
}
|
|
20825
|
-
function
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
if (
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
20840
|
-
|
|
20841
|
-
} else {
|
|
20842
|
-
value = row[k.colIdx] ?? null;
|
|
20843
|
-
type = table.columns[k.colIdx].type.id;
|
|
21113
|
+
function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
|
|
21114
|
+
let row = newRow ?? oldRow;
|
|
21115
|
+
for (const t of table.triggers) {
|
|
21116
|
+
if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
|
|
21117
|
+
if (!executor) throw unsupported(`trigger "${t.name}" execution`);
|
|
21118
|
+
if (t.constraint && timing === "after") {
|
|
21119
|
+
if (t.when && !evalPredicate(env, triggerScope(table, newRow, oldRow), t.when)) continue;
|
|
21120
|
+
env.ctx.state.pendingTriggers.push({
|
|
21121
|
+
schema: table.schema,
|
|
21122
|
+
table: table.name,
|
|
21123
|
+
trigger: t.name,
|
|
21124
|
+
event,
|
|
21125
|
+
oldRow: oldRow?.slice() ?? null,
|
|
21126
|
+
newRow: newRow?.slice() ?? null
|
|
21127
|
+
});
|
|
21128
|
+
continue;
|
|
20844
21129
|
}
|
|
20845
|
-
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
|
|
20849
|
-
parts.push(datumKey(type, value));
|
|
21130
|
+
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
21131
|
+
if (timing === "before") {
|
|
21132
|
+
if (result === null) return { row: null };
|
|
21133
|
+
row = result;
|
|
20850
21134
|
}
|
|
20851
21135
|
}
|
|
20852
|
-
|
|
20853
|
-
return parts.join("");
|
|
21136
|
+
return { row };
|
|
20854
21137
|
}
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
21138
|
+
var keyOf = (schema, name) => JSON.stringify([schema, name]);
|
|
21139
|
+
function flushConstraintTriggers(state, all = false) {
|
|
21140
|
+
const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
|
|
21141
|
+
for (let i = 0; i < state.pendingTriggers.length; ) {
|
|
21142
|
+
const pending = state.pendingTriggers[i];
|
|
21143
|
+
const table = state.schemas.get(pending.schema)?.tables.get(pending.table);
|
|
21144
|
+
const trigger = table?.triggers.find((t) => t.name === pending.trigger);
|
|
21145
|
+
if (!table || !trigger) {
|
|
21146
|
+
state.pendingTriggers.splice(i, 1);
|
|
21147
|
+
continue;
|
|
21148
|
+
}
|
|
21149
|
+
const deferred = trigger.deferrable && (state.constraintModes.get(keyOf(table.schema, trigger.name)) ?? trigger.initiallyDeferred);
|
|
21150
|
+
if (!all && state.inTransaction && deferred) {
|
|
21151
|
+
i++;
|
|
21152
|
+
continue;
|
|
21153
|
+
}
|
|
21154
|
+
if (!executor) throw unsupported(`trigger "${trigger.name}" execution`);
|
|
21155
|
+
state.pendingTriggers.splice(i, 1);
|
|
21156
|
+
const changes = state.changes;
|
|
21157
|
+
try {
|
|
21158
|
+
executor(env, table, { ...trigger, when: null }, pending.event, pending.oldRow, pending.newRow);
|
|
21159
|
+
} catch (error) {
|
|
21160
|
+
state.pendingTriggers.splice(i, 0, pending);
|
|
21161
|
+
throw error;
|
|
21162
|
+
} finally {
|
|
21163
|
+
state.changes = changes;
|
|
21164
|
+
}
|
|
20860
21165
|
}
|
|
20861
21166
|
}
|
|
20862
|
-
function
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
20873
|
-
const nulls = values.filter((v) => v === null).length;
|
|
20874
|
-
if (con.match === "simple" && nulls > 0) continue;
|
|
20875
|
-
if (con.match === "full") {
|
|
20876
|
-
if (nulls === values.length) continue;
|
|
20877
|
-
if (nulls > 0) {
|
|
20878
|
-
throw pgError(
|
|
20879
|
-
"constraint_foreign_key",
|
|
20880
|
-
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
20881
|
-
"23503"
|
|
21167
|
+
function setConstraints(state, stmt) {
|
|
21168
|
+
if (!state.inTransaction) return;
|
|
21169
|
+
const matches2 = [];
|
|
21170
|
+
if (stmt.names) {
|
|
21171
|
+
for (const name of stmt.names) {
|
|
21172
|
+
const schemas = name.length > 1 ? [name[name.length - 2]] : state.effectiveSearchPath();
|
|
21173
|
+
let found = false;
|
|
21174
|
+
for (const schema of schemas) {
|
|
21175
|
+
const items = [...state.schemas.get(schema)?.tables.values() ?? []].flatMap(
|
|
21176
|
+
(table) => table.triggers.filter((t) => t.constraint && t.name === name[name.length - 1])
|
|
20882
21177
|
);
|
|
21178
|
+
if (items.length) {
|
|
21179
|
+
for (const trigger of items) matches2.push({ schema, trigger });
|
|
21180
|
+
found = true;
|
|
21181
|
+
break;
|
|
21182
|
+
}
|
|
20883
21183
|
}
|
|
21184
|
+
if (!found) throw pgError("undefined_object", `constraint "${name.join(".")}" does not exist`, "42704");
|
|
20884
21185
|
}
|
|
20885
|
-
|
|
20886
|
-
|
|
20887
|
-
|
|
20888
|
-
|
|
20889
|
-
|
|
20890
|
-
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
21186
|
+
} else {
|
|
21187
|
+
for (const schema of state.schemas.values())
|
|
21188
|
+
for (const table of schema.tables.values())
|
|
21189
|
+
for (const trigger of table.triggers)
|
|
21190
|
+
if (trigger.constraint && trigger.deferrable) matches2.push({ schema: schema.name, trigger });
|
|
21191
|
+
}
|
|
21192
|
+
for (const { trigger } of matches2)
|
|
21193
|
+
if (stmt.deferred && !trigger.deferrable)
|
|
21194
|
+
throw pgError("misuse", `constraint "${trigger.name}" is not deferrable`, "55000");
|
|
21195
|
+
const previous = new Map(state.constraintModes);
|
|
21196
|
+
for (const { schema, trigger } of matches2) state.constraintModes.set(keyOf(schema, trigger.name), stmt.deferred);
|
|
21197
|
+
try {
|
|
21198
|
+
if (!stmt.deferred) flushConstraintTriggers(state);
|
|
21199
|
+
} catch (error) {
|
|
21200
|
+
state.constraintModes = previous;
|
|
21201
|
+
throw error;
|
|
21202
|
+
}
|
|
21203
|
+
}
|
|
21204
|
+
|
|
21205
|
+
// src/transactions/manager.ts
|
|
21206
|
+
var TransactionManager = class {
|
|
21207
|
+
constructor(state) {
|
|
21208
|
+
this.state = state;
|
|
21209
|
+
}
|
|
21210
|
+
state;
|
|
21211
|
+
base = null;
|
|
21212
|
+
savepoints = [];
|
|
21213
|
+
freezeDepth = 0;
|
|
21214
|
+
get inTransaction() {
|
|
21215
|
+
return this.base !== null;
|
|
21216
|
+
}
|
|
21217
|
+
takeSnapshot() {
|
|
21218
|
+
this.state.freezeShared();
|
|
21219
|
+
this.freezeDepth++;
|
|
21220
|
+
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
21221
|
+
}
|
|
21222
|
+
restore(snap) {
|
|
21223
|
+
this.state.restoreFrom(snap.state);
|
|
21224
|
+
this.state.prng.setState(snap.prngState);
|
|
21225
|
+
}
|
|
21226
|
+
thawOnce() {
|
|
21227
|
+
if (this.freezeDepth > 0) {
|
|
21228
|
+
this.state.thawShared();
|
|
21229
|
+
this.freezeDepth--;
|
|
20907
21230
|
}
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
"23503"
|
|
20913
|
-
);
|
|
21231
|
+
}
|
|
21232
|
+
begin() {
|
|
21233
|
+
if (this.base !== null) {
|
|
21234
|
+
return;
|
|
20914
21235
|
}
|
|
21236
|
+
this.base = this.takeSnapshot();
|
|
21237
|
+
this.state.inTransaction = true;
|
|
21238
|
+
assert(this.freezeDepth === 1, "BEGIN freeze depth");
|
|
20915
21239
|
}
|
|
20916
|
-
|
|
20917
|
-
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
20923
|
-
out.push({ table: t, constraint: con });
|
|
20924
|
-
}
|
|
20925
|
-
}
|
|
21240
|
+
commit() {
|
|
21241
|
+
try {
|
|
21242
|
+
flushConstraintTriggers(this.state, true);
|
|
21243
|
+
} catch (error) {
|
|
21244
|
+
this.rollback();
|
|
21245
|
+
throw error;
|
|
20926
21246
|
}
|
|
21247
|
+
this.base = null;
|
|
21248
|
+
this.savepoints = [];
|
|
21249
|
+
this.state.inTransaction = false;
|
|
21250
|
+
this.state.transactionXid = null;
|
|
21251
|
+
this.state.localSettings.clear();
|
|
21252
|
+
this.state.pendingTriggers = [];
|
|
21253
|
+
this.state.constraintModes.clear();
|
|
21254
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
20927
21255
|
}
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
if (
|
|
20943
|
-
|
|
20944
|
-
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
20945
|
-
if (timing === "before") {
|
|
20946
|
-
if (result === null) return { row: null };
|
|
20947
|
-
row = result;
|
|
21256
|
+
rollback() {
|
|
21257
|
+
if (this.base !== null) {
|
|
21258
|
+
this.restore(this.base);
|
|
21259
|
+
}
|
|
21260
|
+
this.base = null;
|
|
21261
|
+
this.savepoints = [];
|
|
21262
|
+
this.state.inTransaction = false;
|
|
21263
|
+
this.state.transactionXid = null;
|
|
21264
|
+
this.state.localSettings.clear();
|
|
21265
|
+
this.state.pendingTriggers = [];
|
|
21266
|
+
this.state.constraintModes.clear();
|
|
21267
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
21268
|
+
}
|
|
21269
|
+
savepoint(name) {
|
|
21270
|
+
if (this.base === null) {
|
|
21271
|
+
throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
|
|
20948
21272
|
}
|
|
21273
|
+
this.savepoints.push({ name, snap: this.takeSnapshot() });
|
|
20949
21274
|
}
|
|
20950
|
-
|
|
20951
|
-
|
|
21275
|
+
releaseSavepoint(name) {
|
|
21276
|
+
const idx = this.findSavepoint(name);
|
|
21277
|
+
this.savepoints.splice(idx);
|
|
21278
|
+
}
|
|
21279
|
+
rollbackToSavepoint(name) {
|
|
21280
|
+
const idx = this.findSavepoint(name);
|
|
21281
|
+
const sp = this.savepoints[idx];
|
|
21282
|
+
this.restore(sp.snap);
|
|
21283
|
+
this.savepoints.splice(idx + 1);
|
|
21284
|
+
}
|
|
21285
|
+
findSavepoint(name) {
|
|
21286
|
+
if (this.base === null) {
|
|
21287
|
+
throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
|
|
21288
|
+
}
|
|
21289
|
+
for (let i = this.savepoints.length - 1; i >= 0; i--) {
|
|
21290
|
+
if (this.savepoints[i].name === name) return i;
|
|
21291
|
+
}
|
|
21292
|
+
throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
|
|
21293
|
+
}
|
|
21294
|
+
reset() {
|
|
21295
|
+
this.base = null;
|
|
21296
|
+
this.savepoints = [];
|
|
21297
|
+
this.state.inTransaction = false;
|
|
21298
|
+
this.state.transactionXid = null;
|
|
21299
|
+
this.freezeDepth = 0;
|
|
21300
|
+
}
|
|
21301
|
+
};
|
|
20952
21302
|
|
|
20953
21303
|
// src/executor/dml.ts
|
|
20954
21304
|
function withStatementRollback(env, fn) {
|
|
@@ -21149,6 +21499,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
21149
21499
|
const updated = existing.slice();
|
|
21150
21500
|
applyUpdateSets(env, table, sets, scope, updated);
|
|
21151
21501
|
computeGeneratedColumns(env, table, updated);
|
|
21502
|
+
table.rowXmins.set(updated, env.ctx.rowVersionXid());
|
|
21152
21503
|
rows[existingIdx] = updated;
|
|
21153
21504
|
indexUpdateRow(env, table, existingIdx, existing, updated);
|
|
21154
21505
|
checkNotNull(env, table, updated);
|
|
@@ -21234,6 +21585,7 @@ function executeInsert(env0, stmt) {
|
|
|
21234
21585
|
checkChecks(env, table, newRow);
|
|
21235
21586
|
checkUnique(env, table, newRow, rows.length);
|
|
21236
21587
|
checkForeignKeys(env, table, newRow);
|
|
21588
|
+
table.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21237
21589
|
rows.push(newRow);
|
|
21238
21590
|
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
21239
21591
|
insertedCount++;
|
|
@@ -21351,6 +21703,7 @@ function executeUpdate(env0, stmt) {
|
|
|
21351
21703
|
if (fired.row === null) continue;
|
|
21352
21704
|
const finalRow = fired.row;
|
|
21353
21705
|
computeGeneratedColumns(env, table, finalRow);
|
|
21706
|
+
table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
|
|
21354
21707
|
rows[ri] = finalRow;
|
|
21355
21708
|
try {
|
|
21356
21709
|
checkNotNull(env, table, finalRow);
|
|
@@ -21470,6 +21823,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
21470
21823
|
for (const c of con.columns) {
|
|
21471
21824
|
newRow[child.columnIndex(c)] = null;
|
|
21472
21825
|
}
|
|
21826
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21473
21827
|
childRows[i] = newRow;
|
|
21474
21828
|
checkNotNull(env, child, newRow);
|
|
21475
21829
|
checkChecks(env, child, newRow);
|
|
@@ -21483,6 +21837,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
21483
21837
|
const ci = child.columnIndex(c);
|
|
21484
21838
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
21485
21839
|
}
|
|
21840
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21486
21841
|
childRows[i] = newRow;
|
|
21487
21842
|
checkNotNull(env, child, newRow);
|
|
21488
21843
|
checkChecks(env, child, newRow);
|
|
@@ -21520,6 +21875,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21520
21875
|
const refIdx = table.columnIndex(con.refColumns[c]);
|
|
21521
21876
|
newRow[localIdx] = newRows[r][refIdx] ?? null;
|
|
21522
21877
|
}
|
|
21878
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21523
21879
|
childRows[i] = newRow;
|
|
21524
21880
|
checkChecks(env, child, newRow);
|
|
21525
21881
|
}
|
|
@@ -21529,6 +21885,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21529
21885
|
for (const i of idxs) {
|
|
21530
21886
|
const newRow = childRows[i].slice();
|
|
21531
21887
|
for (const c of con.columns) newRow[child.columnIndex(c)] = null;
|
|
21888
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21532
21889
|
childRows[i] = newRow;
|
|
21533
21890
|
checkNotNull(env, child, newRow);
|
|
21534
21891
|
}
|
|
@@ -21541,6 +21898,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
21541
21898
|
const ci = child.columnIndex(c);
|
|
21542
21899
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
21543
21900
|
}
|
|
21901
|
+
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
21544
21902
|
childRows[i] = newRow;
|
|
21545
21903
|
checkForeignKeys(env, child, newRow);
|
|
21546
21904
|
}
|
|
@@ -21638,6 +21996,7 @@ function executeSet(env, stmt) {
|
|
|
21638
21996
|
else target.delete(name);
|
|
21639
21997
|
return commandResult("SET", 0);
|
|
21640
21998
|
}
|
|
21999
|
+
validateStorageSetting(name, stmt.value);
|
|
21641
22000
|
target.set(name, stmt.value);
|
|
21642
22001
|
return commandResult("SET", 0);
|
|
21643
22002
|
}
|
|
@@ -21943,64 +22302,6 @@ function executeCopyFromData(env, stmt, data) {
|
|
|
21943
22302
|
};
|
|
21944
22303
|
}
|
|
21945
22304
|
|
|
21946
|
-
// src/expressions/context.ts
|
|
21947
|
-
var EngineCtx = class {
|
|
21948
|
-
state;
|
|
21949
|
-
/** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
|
|
21950
|
-
txNow;
|
|
21951
|
-
/** statement timestamp */
|
|
21952
|
-
stmtNow;
|
|
21953
|
-
constructor(state, txNow) {
|
|
21954
|
-
this.state = state;
|
|
21955
|
-
const nowMs = state.clock().getTime();
|
|
21956
|
-
const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
|
|
21957
|
-
this.txNow = txNow ?? nowMicros;
|
|
21958
|
-
this.stmtNow = nowMicros;
|
|
21959
|
-
}
|
|
21960
|
-
timezone() {
|
|
21961
|
-
return this.state.getSetting("timezone") ?? "UTC";
|
|
21962
|
-
}
|
|
21963
|
-
namespaceOid(name) {
|
|
21964
|
-
const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
|
|
21965
|
-
if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
21966
|
-
return oid;
|
|
21967
|
-
}
|
|
21968
|
-
namespaceName(oid) {
|
|
21969
|
-
const builtin = builtinNamespaceName(oid);
|
|
21970
|
-
if (builtin !== void 0) return builtin;
|
|
21971
|
-
for (const schema of this.state.schemas.values()) {
|
|
21972
|
-
if (schema.oid === oid) return schema.name;
|
|
21973
|
-
}
|
|
21974
|
-
return null;
|
|
21975
|
-
}
|
|
21976
|
-
zoneOffsetAt(utcMicros) {
|
|
21977
|
-
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
21978
|
-
}
|
|
21979
|
-
zoneOffsetForNaive(naiveMicros) {
|
|
21980
|
-
return zoneOffsetForNaive(this.timezone(), naiveMicros);
|
|
21981
|
-
}
|
|
21982
|
-
enumLabels(enumType) {
|
|
21983
|
-
if (!isEnumType(enumType)) return null;
|
|
21984
|
-
const e = this.state.findEnumByKey(enumType.slice(5));
|
|
21985
|
-
return e?.labels ?? null;
|
|
21986
|
-
}
|
|
21987
|
-
enumHasLabel(enumType, label) {
|
|
21988
|
-
const labels = this.enumLabels(enumType);
|
|
21989
|
-
return labels === null ? true : labels.includes(label);
|
|
21990
|
-
}
|
|
21991
|
-
enumOrder(enumType, label) {
|
|
21992
|
-
const labels = this.enumLabels(enumType);
|
|
21993
|
-
if (!labels) return 0;
|
|
21994
|
-
const idx = labels.indexOf(label);
|
|
21995
|
-
return idx === -1 ? labels.length : idx;
|
|
21996
|
-
}
|
|
21997
|
-
requireEnum(enumType) {
|
|
21998
|
-
const labels = this.enumLabels(enumType);
|
|
21999
|
-
if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
|
|
22000
|
-
return labels;
|
|
22001
|
-
}
|
|
22002
|
-
};
|
|
22003
|
-
|
|
22004
22305
|
// src/runtime/clock.ts
|
|
22005
22306
|
var DEFAULT_NOW = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
|
|
22006
22307
|
function fixedClock(instant = DEFAULT_NOW) {
|
|
@@ -22968,7 +23269,7 @@ function pgClass(ctx) {
|
|
|
22968
23269
|
v.materialized ? "m" : "v",
|
|
22969
23270
|
v.columns?.length ?? 0,
|
|
22970
23271
|
v.matRows?.length ?? 0,
|
|
22971
|
-
|
|
23272
|
+
v.materialized && [...schemaData.indexes.values()].some((i) => i.tableOid === v.oid),
|
|
22972
23273
|
!v.materialized || v.matRows !== null,
|
|
22973
23274
|
relPersistence(v)
|
|
22974
23275
|
);
|
|
@@ -23234,7 +23535,7 @@ function pgIndex(ctx) {
|
|
|
23234
23535
|
const valid = i.valid !== false;
|
|
23235
23536
|
rows.push([
|
|
23236
23537
|
i.oid ?? 0,
|
|
23237
|
-
table?.oid ?? 0,
|
|
23538
|
+
table?.oid ?? i.tableOid ?? 0,
|
|
23238
23539
|
indexAttributeCount(i),
|
|
23239
23540
|
i.columns.length,
|
|
23240
23541
|
i.unique,
|
|
@@ -23406,7 +23707,7 @@ function pgStatUserIndexes(ctx) {
|
|
|
23406
23707
|
for (const index of schema.indexes.values()) {
|
|
23407
23708
|
const table = schema.tables.get(index.table);
|
|
23408
23709
|
rows.push([
|
|
23409
|
-
table?.oid ?? 0,
|
|
23710
|
+
table?.oid ?? index.tableOid ?? 0,
|
|
23410
23711
|
index.oid ?? 0,
|
|
23411
23712
|
schema.name,
|
|
23412
23713
|
index.table,
|
|
@@ -24001,26 +24302,6 @@ function runTriggerBody(env, table, stmts, vars) {
|
|
|
24001
24302
|
}
|
|
24002
24303
|
}
|
|
24003
24304
|
}
|
|
24004
|
-
function triggerScope(table, newRow, oldRow) {
|
|
24005
|
-
const cols = [];
|
|
24006
|
-
const row = [];
|
|
24007
|
-
const rangeVars = /* @__PURE__ */ new Set();
|
|
24008
|
-
if (newRow) {
|
|
24009
|
-
rangeVars.add("new");
|
|
24010
|
-
for (let i = 0; i < table.columns.length; i++) {
|
|
24011
|
-
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
|
|
24012
|
-
row.push(newRow[i] ?? null);
|
|
24013
|
-
}
|
|
24014
|
-
}
|
|
24015
|
-
if (oldRow) {
|
|
24016
|
-
rangeVars.add("old");
|
|
24017
|
-
for (let i = 0; i < table.columns.length; i++) {
|
|
24018
|
-
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
|
|
24019
|
-
row.push(oldRow[i] ?? null);
|
|
24020
|
-
}
|
|
24021
|
-
}
|
|
24022
|
-
return new RowScope(cols, row, null, rangeVars);
|
|
24023
|
-
}
|
|
24024
24305
|
function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
|
|
24025
24306
|
const state = env.ctx.state;
|
|
24026
24307
|
if (trigger.when !== null && !evalPredicate(env, triggerScope(table, newRow, oldRow), trigger.when)) {
|
|
@@ -24662,13 +24943,63 @@ function executeCreateSequence(env, stmt) {
|
|
|
24662
24943
|
schema.sequences.set(name, seq);
|
|
24663
24944
|
return commandResult("CREATE SEQUENCE", 0);
|
|
24664
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
|
+
}
|
|
24665
24985
|
function executeAlterSequence(env, stmt) {
|
|
24666
24986
|
const state = env.ctx.state;
|
|
24667
|
-
const
|
|
24668
|
-
if (!
|
|
24987
|
+
const foundSequence = state.findSequence(stmt.name);
|
|
24988
|
+
if (!foundSequence) {
|
|
24669
24989
|
if (stmt.ifExists) return commandResult("ALTER SEQUENCE", 0);
|
|
24670
24990
|
throw pgError("undefined_table", `relation "${stmt.name.join(".")}" does not exist`, "42P01");
|
|
24671
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
|
+
}
|
|
24672
25003
|
const o = stmt.options;
|
|
24673
25004
|
if (o.logged !== void 0) {
|
|
24674
25005
|
setSequenceLogged(state, seq, o.logged);
|
|
@@ -24726,7 +25057,7 @@ function buildColumn(env, schema, tableName2, def, out, taken) {
|
|
|
24726
25057
|
col.defaultExpr = {
|
|
24727
25058
|
type: "func",
|
|
24728
25059
|
name: ["nextval"],
|
|
24729
|
-
args: [{ type: "string_lit", value:
|
|
25060
|
+
args: [{ type: "string_lit", value: [schema.name, seqName].map(quoteIdent).join(".") }]
|
|
24730
25061
|
};
|
|
24731
25062
|
}
|
|
24732
25063
|
for (const c of def.constraints) {
|
|
@@ -24979,7 +25310,11 @@ function executeCreateTableAs(env, stmt) {
|
|
|
24979
25310
|
const table = new TableData(schema.name, name, columns, state.nextOid(), stmt.temp);
|
|
24980
25311
|
table.unlogged = stmt.unlogged === true;
|
|
24981
25312
|
if (stmt.withData) {
|
|
24982
|
-
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
|
+
}
|
|
24983
25318
|
}
|
|
24984
25319
|
schema.tables.set(name, table);
|
|
24985
25320
|
const count = stmt.withData ? table.rowCount() : 0;
|
|
@@ -24987,7 +25322,8 @@ function executeCreateTableAs(env, stmt) {
|
|
|
24987
25322
|
}
|
|
24988
25323
|
function executeCreateIndex(env, stmt) {
|
|
24989
25324
|
const state = env.ctx.state;
|
|
24990
|
-
const
|
|
25325
|
+
const view = state.findView(stmt.table);
|
|
25326
|
+
const table = state.findTable(stmt.table) ?? (view?.materialized ? materializedTable(view) : void 0);
|
|
24991
25327
|
if (!table) {
|
|
24992
25328
|
throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
|
|
24993
25329
|
}
|
|
@@ -25099,13 +25435,22 @@ function executeRefreshMatView(env, stmt) {
|
|
|
25099
25435
|
if (!found?.materialized) {
|
|
25100
25436
|
throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
|
|
25101
25437
|
}
|
|
25438
|
+
const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), found.query);
|
|
25439
|
+
const next = {
|
|
25440
|
+
...found,
|
|
25441
|
+
matColumns: rel3.columns.map((c, i) => ({
|
|
25442
|
+
name: found.columns?.[i] ?? c.name,
|
|
25443
|
+
type: c.type === UNKNOWN ? "text" : c.type
|
|
25444
|
+
})),
|
|
25445
|
+
matRows: stmt.withData ? rel3.rows : null
|
|
25446
|
+
};
|
|
25447
|
+
const table = materializedTable(next);
|
|
25448
|
+
for (const index of env.ctx.state.getSchema(found.schema).indexes.values()) {
|
|
25449
|
+
if (index.tableOid === found.oid && index.valid !== false) assertUniqueIndexBuildable(env, table, index);
|
|
25450
|
+
}
|
|
25102
25451
|
const view = env.ctx.state.ensureWritableView(found);
|
|
25103
|
-
|
|
25104
|
-
view.
|
|
25105
|
-
name: view.columns?.[i] ?? c.name,
|
|
25106
|
-
type: c.type === UNKNOWN ? "text" : c.type
|
|
25107
|
-
}));
|
|
25108
|
-
view.matRows = stmt.withData ? rel3.rows : null;
|
|
25452
|
+
view.matColumns = next.matColumns;
|
|
25453
|
+
view.matRows = next.matRows;
|
|
25109
25454
|
return commandResult("REFRESH MATERIALIZED VIEW", 0);
|
|
25110
25455
|
}
|
|
25111
25456
|
function executeCreateSchema(env, stmt) {
|
|
@@ -25238,6 +25583,12 @@ function executeCreateFunction(env, stmt) {
|
|
|
25238
25583
|
language: lang,
|
|
25239
25584
|
body,
|
|
25240
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
|
+
),
|
|
25241
25592
|
strict: stmt.strict,
|
|
25242
25593
|
oid: state.nextOid()
|
|
25243
25594
|
};
|
|
@@ -25247,6 +25598,8 @@ function executeCreateFunction(env, stmt) {
|
|
|
25247
25598
|
return commandResult("CREATE FUNCTION", 0);
|
|
25248
25599
|
}
|
|
25249
25600
|
function executeCreateTrigger(env, stmt) {
|
|
25601
|
+
if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
|
|
25602
|
+
throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
|
|
25250
25603
|
const state = env.ctx.state;
|
|
25251
25604
|
const found = state.findTable(stmt.table);
|
|
25252
25605
|
if (!found) {
|
|
@@ -25265,6 +25618,9 @@ function executeCreateTrigger(env, stmt) {
|
|
|
25265
25618
|
}
|
|
25266
25619
|
const meta = {
|
|
25267
25620
|
name: stmt.name,
|
|
25621
|
+
constraint: stmt.constraint,
|
|
25622
|
+
deferrable: stmt.deferrable,
|
|
25623
|
+
initiallyDeferred: stmt.initiallyDeferred,
|
|
25268
25624
|
timing: stmt.timing,
|
|
25269
25625
|
events: stmt.events,
|
|
25270
25626
|
forEachRow: stmt.forEachRow,
|
|
@@ -25417,6 +25773,7 @@ function executeAlterTable(env, stmt) {
|
|
|
25417
25773
|
}
|
|
25418
25774
|
const next = row.slice();
|
|
25419
25775
|
next[idx] = nv;
|
|
25776
|
+
table.rowXmins.set(next, table.rowXmins.get(rows[i]) ?? 2);
|
|
25420
25777
|
rows[i] = next;
|
|
25421
25778
|
}
|
|
25422
25779
|
col.type = resolved.column;
|
|
@@ -25518,6 +25875,11 @@ function executeAlterTable(env, stmt) {
|
|
|
25518
25875
|
);
|
|
25519
25876
|
}
|
|
25520
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
|
+
}
|
|
25521
25883
|
for (const con of table.constraints) {
|
|
25522
25884
|
if (con.kind === "primary_key" || con.kind === "unique" || con.kind === "foreign_key") {
|
|
25523
25885
|
con.columns = con.columns.map((c) => c === action.from ? action.to : c);
|
|
@@ -25554,6 +25916,11 @@ function executeAlterTable(env, stmt) {
|
|
|
25554
25916
|
throw pgError("duplicate_table", `relation "${action.to}" already exists`, "42P07");
|
|
25555
25917
|
}
|
|
25556
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
|
+
}
|
|
25557
25924
|
schema.tables.delete(table.name);
|
|
25558
25925
|
table.name = action.to;
|
|
25559
25926
|
schema.tables.set(action.to, table);
|
|
@@ -26033,7 +26400,16 @@ function alterTableIsSelfContained(stmt) {
|
|
|
26033
26400
|
return stmt.actions.length === 1 && action !== void 0 && SELF_CONTAINED_ALTER_ACTIONS.has(action.kind);
|
|
26034
26401
|
}
|
|
26035
26402
|
function executeStatement(env, stmt) {
|
|
26036
|
-
if (!RESHAPES_TABLES.has(stmt.type))
|
|
26403
|
+
if (!RESHAPES_TABLES.has(stmt.type)) {
|
|
26404
|
+
if ((stmt.type === "insert" || stmt.type === "update" || stmt.type === "delete") && (env.ctx.state.pendingTriggers.length > 0 || env.ctx.state.findTable(stmt.table)?.triggers.some((t) => t.constraint))) {
|
|
26405
|
+
return withStatementRollback(env, () => {
|
|
26406
|
+
const result = dispatchStatement(env, stmt);
|
|
26407
|
+
flushConstraintTriggers(env.ctx.state);
|
|
26408
|
+
return result;
|
|
26409
|
+
});
|
|
26410
|
+
}
|
|
26411
|
+
return dispatchStatement(env, stmt);
|
|
26412
|
+
}
|
|
26037
26413
|
try {
|
|
26038
26414
|
return dispatchStatement(env, stmt);
|
|
26039
26415
|
} finally {
|
|
@@ -26092,6 +26468,9 @@ function dispatchStatement(env, stmt) {
|
|
|
26092
26468
|
return executeRefreshMatView(env, stmt);
|
|
26093
26469
|
case "transaction":
|
|
26094
26470
|
return executeTransaction(env, stmt);
|
|
26471
|
+
case "set_constraints":
|
|
26472
|
+
setConstraints(env.ctx.state, stmt);
|
|
26473
|
+
return commandResult("SET CONSTRAINTS", 0);
|
|
26095
26474
|
case "set":
|
|
26096
26475
|
return executeSet(env, stmt);
|
|
26097
26476
|
case "show":
|
|
@@ -27131,6 +27510,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
27131
27510
|
writeBjv(
|
|
27132
27511
|
w,
|
|
27133
27512
|
{
|
|
27513
|
+
xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
27134
27514
|
name: table.name,
|
|
27135
27515
|
schema: table.schema,
|
|
27136
27516
|
columns: table.columns,
|
|
@@ -27228,8 +27608,14 @@ function decodeInner(snapshot, prng, clock) {
|
|
|
27228
27608
|
table.constraints = meta.constraints;
|
|
27229
27609
|
table.triggers = meta.triggers;
|
|
27230
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
|
+
);
|
|
27231
27616
|
if (meta.stats) state.tableStats.set(table.oid, meta.stats);
|
|
27232
27617
|
const rowCount = readVarintU32(r);
|
|
27618
|
+
if (meta.xmins && meta.xmins.length !== rowCount) throw snapshotError();
|
|
27233
27619
|
const slabColumns = [];
|
|
27234
27620
|
for (let c = 0; c < meta.columns.length; c++)
|
|
27235
27621
|
slabColumns.push(readPackedColumn(r, rowCount, intern, meta.columns[c].type.id));
|
|
@@ -27673,6 +28059,8 @@ function validateTableMetaBjv(value) {
|
|
|
27673
28059
|
if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
|
|
27674
28060
|
if (!Array.isArray(value.columns)) throw snapshotError();
|
|
27675
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();
|
|
27676
28064
|
}
|
|
27677
28065
|
function validateViewMetaBjv(value) {
|
|
27678
28066
|
if (!isRecord(value) || typeof value.name !== "string" || typeof value.schema !== "string") throw snapshotError();
|
|
@@ -27766,6 +28154,7 @@ function forceSchemaIntern(pool, state, schemas) {
|
|
|
27766
28154
|
pool.forceId(table.name);
|
|
27767
28155
|
pool.forceId(table.schema);
|
|
27768
28156
|
forceBjvStrings(pool, {
|
|
28157
|
+
xmins: table.allRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
27769
28158
|
name: table.name,
|
|
27770
28159
|
schema: table.schema,
|
|
27771
28160
|
columns: table.columns,
|
|
@@ -27843,10 +28232,188 @@ function forceBjvStrings(pool, value) {
|
|
|
27843
28232
|
}
|
|
27844
28233
|
}
|
|
27845
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
|
+
}
|
|
27846
28412
|
export {
|
|
27847
28413
|
Database,
|
|
27848
28414
|
PostgresError,
|
|
27849
28415
|
Snapshot,
|
|
27850
|
-
Statement
|
|
28416
|
+
Statement,
|
|
28417
|
+
analyzeSchema
|
|
27851
28418
|
};
|
|
27852
28419
|
//# sourceMappingURL=index.js.map
|