@crvouga/mockingbird-service-postgres 3.4.1 → 3.6.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 +18 -0
- package/COMPATIBILITY.md +15 -0
- package/compat/sections/trg.ts +2 -0
- package/dist/admin.js +925 -736
- package/dist/admin.js.map +4 -4
- package/dist/ast/nodes.d.ts +9 -1
- package/dist/executor/triggers-scope.d.ts +4 -0
- package/dist/executor/triggers.d.ts +4 -0
- package/dist/index.js +924 -735
- package/dist/index.js.map +4 -4
- package/dist/server.js +1375 -720
- package/dist/server.js.map +4 -4
- package/dist/storage/database-state.d.ts +13 -0
- package/dist/storage/guc.d.ts +2 -0
- package/dist/storage/indexable-relation.d.ts +3 -0
- package/dist/unstable.js +16938 -16749
- package/dist/unstable.js.map +4 -4
- package/dist/wire/cli.js +6336 -2094
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/fleet.d.ts +38 -2
- package/dist/wire/index.js +942 -736
- package/dist/wire/index.js.map +4 -4
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -257,6 +257,13 @@ function isPostgresError(err) {
|
|
|
257
257
|
return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
// src/storage/guc.ts
|
|
261
|
+
function validateStorageSetting(name, value) {
|
|
262
|
+
if (name === "default_table_access_method" && value !== "heap" || name === "default_tablespace" && value !== "" && value !== "pg_default") {
|
|
263
|
+
throw pgError("invalid_parameter_value", `invalid value for parameter "${name}": "${value}"`, "22023");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
260
267
|
// src/runtime/prng.ts
|
|
261
268
|
var Prng = class _Prng {
|
|
262
269
|
state;
|
|
@@ -3712,6 +3719,9 @@ var DatabaseState = class _DatabaseState {
|
|
|
3712
3719
|
changes = 0;
|
|
3713
3720
|
oidCounter = 16384;
|
|
3714
3721
|
inTransaction = false;
|
|
3722
|
+
/** Transaction-only events: included in rollback clones, never in exported snapshots. */
|
|
3723
|
+
pendingTriggers = [];
|
|
3724
|
+
constraintModes = /* @__PURE__ */ new Map();
|
|
3715
3725
|
/** most recent sequence touched by nextval/setval, for lastval() */
|
|
3716
3726
|
lastSequence = null;
|
|
3717
3727
|
/** Session currval per sequence; absent entry means currval is undefined. */
|
|
@@ -3754,6 +3764,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3754
3764
|
this.settings.set("row_security", "on");
|
|
3755
3765
|
this.settings.set("array_nulls", "on");
|
|
3756
3766
|
this.settings.set("backslash_quote", "safe_encoding");
|
|
3767
|
+
this.settings.set("default_tablespace", "");
|
|
3768
|
+
this.settings.set("default_table_access_method", "heap");
|
|
3757
3769
|
}
|
|
3758
3770
|
nextOid() {
|
|
3759
3771
|
return this.oidCounter++;
|
|
@@ -3913,6 +3925,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3913
3925
|
s.prepared = new Map(this.prepared);
|
|
3914
3926
|
s.changes = this.changes;
|
|
3915
3927
|
s.inTransaction = this.inTransaction;
|
|
3928
|
+
s.pendingTriggers = this.pendingTriggers.slice();
|
|
3929
|
+
s.constraintModes = new Map(this.constraintModes);
|
|
3916
3930
|
s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
|
|
3917
3931
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3918
3932
|
s.tableStats = new Map(this.tableStats);
|
|
@@ -3930,6 +3944,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
3930
3944
|
s.prepared = new Map(this.prepared);
|
|
3931
3945
|
s.changes = this.changes;
|
|
3932
3946
|
s.inTransaction = this.inTransaction;
|
|
3947
|
+
s.pendingTriggers = this.pendingTriggers.slice();
|
|
3948
|
+
s.constraintModes = new Map(this.constraintModes);
|
|
3933
3949
|
s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
|
|
3934
3950
|
s.sequenceCurrval = new Map(this.sequenceCurrval);
|
|
3935
3951
|
s.tableStats = new Map(this.tableStats);
|
|
@@ -4002,6 +4018,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
4002
4018
|
this.settings = other.settings;
|
|
4003
4019
|
this.localSettings = other.localSettings;
|
|
4004
4020
|
this.prepared = other.prepared;
|
|
4021
|
+
this.pendingTriggers = other.pendingTriggers.slice();
|
|
4022
|
+
this.constraintModes = new Map(other.constraintModes);
|
|
4005
4023
|
this.changes = other.changes;
|
|
4006
4024
|
this.lastSequence = other.lastSequence;
|
|
4007
4025
|
this.sequenceCurrval = other.sequenceCurrval;
|
|
@@ -4018,90 +4036,192 @@ var DatabaseState = class _DatabaseState {
|
|
|
4018
4036
|
}
|
|
4019
4037
|
};
|
|
4020
4038
|
|
|
4021
|
-
// src/
|
|
4022
|
-
var
|
|
4023
|
-
|
|
4024
|
-
|
|
4039
|
+
// src/types/timezone.ts
|
|
4040
|
+
var intlCache = /* @__PURE__ */ new Map();
|
|
4041
|
+
function intlFor(zone) {
|
|
4042
|
+
const cached = intlCache.get(zone);
|
|
4043
|
+
if (cached) return cached;
|
|
4044
|
+
try {
|
|
4045
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
4046
|
+
timeZone: zone,
|
|
4047
|
+
timeZoneName: "longOffset",
|
|
4048
|
+
year: "numeric"
|
|
4049
|
+
});
|
|
4050
|
+
intlCache.set(zone, fmt);
|
|
4051
|
+
return fmt;
|
|
4052
|
+
} catch {
|
|
4053
|
+
return null;
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
function parseFixedOffset(zone) {
|
|
4057
|
+
const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
|
|
4058
|
+
if (!m) return null;
|
|
4059
|
+
const sign = m[1] === "-" ? -1 : 1;
|
|
4060
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
|
|
4061
|
+
}
|
|
4062
|
+
function parsePosixOffset(zone) {
|
|
4063
|
+
const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
|
|
4064
|
+
if (!m) return null;
|
|
4065
|
+
const sign = m[1] === "-" ? 1 : -1;
|
|
4066
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
|
|
4067
|
+
}
|
|
4068
|
+
function pgMicrosToUnixMs(utcMicros) {
|
|
4069
|
+
return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
|
|
4070
|
+
}
|
|
4071
|
+
function zoneOffsetAtUtc(zone, utcMicros) {
|
|
4072
|
+
const z = zone.trim();
|
|
4073
|
+
const lower = z.toLowerCase();
|
|
4074
|
+
if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
|
|
4075
|
+
const posix = parsePosixOffset(z);
|
|
4076
|
+
if (posix !== null) return posix;
|
|
4077
|
+
const fixed = parseFixedOffset(z);
|
|
4078
|
+
if (fixed !== null) return fixed;
|
|
4079
|
+
const fmt = intlFor(z);
|
|
4080
|
+
if (fmt) {
|
|
4081
|
+
const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
|
|
4082
|
+
const parts = fmt.formatToParts(new Date(ms));
|
|
4083
|
+
const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
|
|
4084
|
+
const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
|
|
4085
|
+
if (!m) return 0;
|
|
4086
|
+
const sign = m[1] === "-" ? -1 : 1;
|
|
4087
|
+
return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
|
|
4025
4088
|
}
|
|
4089
|
+
throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
|
|
4090
|
+
}
|
|
4091
|
+
function clampDateMs(ms) {
|
|
4092
|
+
const MAX = 864e13;
|
|
4093
|
+
return Math.max(-MAX, Math.min(MAX, ms));
|
|
4094
|
+
}
|
|
4095
|
+
function zoneOffsetForNaive(zone, naiveMicros) {
|
|
4096
|
+
const first = zoneOffsetAtUtc(zone, naiveMicros);
|
|
4097
|
+
const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
|
|
4098
|
+
return second;
|
|
4099
|
+
}
|
|
4100
|
+
function validateZone(zone) {
|
|
4101
|
+
zoneOffsetAtUtc(zone, 0n);
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
// src/expressions/context.ts
|
|
4105
|
+
var EngineCtx = class {
|
|
4026
4106
|
state;
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4107
|
+
/** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
|
|
4108
|
+
txNow;
|
|
4109
|
+
/** statement timestamp */
|
|
4110
|
+
stmtNow;
|
|
4111
|
+
constructor(state, txNow) {
|
|
4112
|
+
this.state = state;
|
|
4113
|
+
const nowMs = state.clock().getTime();
|
|
4114
|
+
const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
|
|
4115
|
+
this.txNow = txNow ?? nowMicros;
|
|
4116
|
+
this.stmtNow = nowMicros;
|
|
4032
4117
|
}
|
|
4033
|
-
|
|
4034
|
-
this.state.
|
|
4035
|
-
this.freezeDepth++;
|
|
4036
|
-
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
4118
|
+
timezone() {
|
|
4119
|
+
return this.state.getSetting("timezone") ?? "UTC";
|
|
4037
4120
|
}
|
|
4038
|
-
|
|
4039
|
-
this.state.
|
|
4040
|
-
|
|
4121
|
+
namespaceOid(name) {
|
|
4122
|
+
const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
|
|
4123
|
+
if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
4124
|
+
return oid;
|
|
4041
4125
|
}
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4126
|
+
namespaceName(oid) {
|
|
4127
|
+
const builtin = builtinNamespaceName(oid);
|
|
4128
|
+
if (builtin !== void 0) return builtin;
|
|
4129
|
+
for (const schema of this.state.schemas.values()) {
|
|
4130
|
+
if (schema.oid === oid) return schema.name;
|
|
4046
4131
|
}
|
|
4132
|
+
return null;
|
|
4047
4133
|
}
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
return;
|
|
4051
|
-
}
|
|
4052
|
-
this.base = this.takeSnapshot();
|
|
4053
|
-
this.state.inTransaction = true;
|
|
4054
|
-
assert(this.freezeDepth === 1, "BEGIN freeze depth");
|
|
4134
|
+
zoneOffsetAt(utcMicros) {
|
|
4135
|
+
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
4055
4136
|
}
|
|
4056
|
-
|
|
4057
|
-
this.
|
|
4058
|
-
this.savepoints = [];
|
|
4059
|
-
this.state.inTransaction = false;
|
|
4060
|
-
this.state.localSettings.clear();
|
|
4061
|
-
while (this.freezeDepth > 0) this.thawOnce();
|
|
4137
|
+
zoneOffsetForNaive(naiveMicros) {
|
|
4138
|
+
return zoneOffsetForNaive(this.timezone(), naiveMicros);
|
|
4062
4139
|
}
|
|
4063
|
-
|
|
4064
|
-
if (
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
this.base = null;
|
|
4068
|
-
this.savepoints = [];
|
|
4069
|
-
this.state.inTransaction = false;
|
|
4070
|
-
this.state.localSettings.clear();
|
|
4071
|
-
while (this.freezeDepth > 0) this.thawOnce();
|
|
4140
|
+
enumLabels(enumType) {
|
|
4141
|
+
if (!isEnumType(enumType)) return null;
|
|
4142
|
+
const e = this.state.findEnumByKey(enumType.slice(5));
|
|
4143
|
+
return e?.labels ?? null;
|
|
4072
4144
|
}
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
}
|
|
4077
|
-
this.savepoints.push({ name, snap: this.takeSnapshot() });
|
|
4145
|
+
enumHasLabel(enumType, label) {
|
|
4146
|
+
const labels = this.enumLabels(enumType);
|
|
4147
|
+
return labels === null ? true : labels.includes(label);
|
|
4078
4148
|
}
|
|
4079
|
-
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4149
|
+
enumOrder(enumType, label) {
|
|
4150
|
+
const labels = this.enumLabels(enumType);
|
|
4151
|
+
if (!labels) return 0;
|
|
4152
|
+
const idx = labels.indexOf(label);
|
|
4153
|
+
return idx === -1 ? labels.length : idx;
|
|
4082
4154
|
}
|
|
4083
|
-
|
|
4084
|
-
const
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
this.savepoints.splice(idx + 1);
|
|
4155
|
+
requireEnum(enumType) {
|
|
4156
|
+
const labels = this.enumLabels(enumType);
|
|
4157
|
+
if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
|
|
4158
|
+
return labels;
|
|
4088
4159
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4160
|
+
};
|
|
4161
|
+
|
|
4162
|
+
// src/api/bind.ts
|
|
4163
|
+
var INT4_MIN2 = -2147483648;
|
|
4164
|
+
var INT4_MAX2 = 2147483647;
|
|
4165
|
+
function bindValueToTyped(value, index) {
|
|
4166
|
+
if (value === null || value === void 0) return tv(UNKNOWN, null);
|
|
4167
|
+
switch (typeof value) {
|
|
4168
|
+
case "boolean":
|
|
4169
|
+
return tv("bool", value);
|
|
4170
|
+
case "number":
|
|
4171
|
+
if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
|
|
4172
|
+
return tv("int4", value);
|
|
4173
|
+
}
|
|
4174
|
+
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
4175
|
+
return tv("int8", BigInt(value));
|
|
4176
|
+
}
|
|
4177
|
+
return tv("float8", value);
|
|
4178
|
+
case "bigint":
|
|
4179
|
+
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
4180
|
+
throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
|
|
4181
|
+
}
|
|
4182
|
+
return tv("int8", value);
|
|
4183
|
+
case "string":
|
|
4184
|
+
return tv(UNKNOWN, value);
|
|
4185
|
+
case "object": {
|
|
4186
|
+
if (value instanceof Uint8Array) return tv("bytea", value);
|
|
4187
|
+
if (value instanceof Date) {
|
|
4188
|
+
const ms = value.getTime();
|
|
4189
|
+
if (Number.isNaN(ms)) {
|
|
4190
|
+
throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
|
|
4191
|
+
}
|
|
4192
|
+
return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
|
|
4193
|
+
}
|
|
4194
|
+
break;
|
|
4095
4195
|
}
|
|
4096
|
-
|
|
4196
|
+
default:
|
|
4197
|
+
break;
|
|
4097
4198
|
}
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4199
|
+
throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
|
|
4200
|
+
}
|
|
4201
|
+
function datumToJs(t, v, ctx, int8 = "bigint") {
|
|
4202
|
+
if (v === null) return null;
|
|
4203
|
+
switch (t) {
|
|
4204
|
+
case "bool":
|
|
4205
|
+
return v;
|
|
4206
|
+
case "int2":
|
|
4207
|
+
case "int4":
|
|
4208
|
+
case "oid":
|
|
4209
|
+
return typeof v === "bigint" ? Number(v) : v;
|
|
4210
|
+
case "int8": {
|
|
4211
|
+
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
4212
|
+
if (int8 === "string") return n.toString();
|
|
4213
|
+
if (int8 === "number") return Number(n);
|
|
4214
|
+
return n;
|
|
4215
|
+
}
|
|
4216
|
+
case "float4":
|
|
4217
|
+
case "float8":
|
|
4218
|
+
return v;
|
|
4219
|
+
case "bytea":
|
|
4220
|
+
return v;
|
|
4221
|
+
default:
|
|
4222
|
+
return datumText(t === UNKNOWN ? "text" : t, v, ctx);
|
|
4103
4223
|
}
|
|
4104
|
-
}
|
|
4224
|
+
}
|
|
4105
4225
|
|
|
4106
4226
|
// src/types/cast.ts
|
|
4107
4227
|
var UTC_CAST_ENV = {
|
|
@@ -4579,259 +4699,6 @@ function unifyTypes(a, b) {
|
|
|
4579
4699
|
return null;
|
|
4580
4700
|
}
|
|
4581
4701
|
|
|
4582
|
-
// src/types/resolve.ts
|
|
4583
|
-
var SERIALS = {
|
|
4584
|
-
smallserial: "int2",
|
|
4585
|
-
serial2: "int2",
|
|
4586
|
-
serial: "int4",
|
|
4587
|
-
serial4: "int4",
|
|
4588
|
-
bigserial: "int8",
|
|
4589
|
-
serial8: "int8"
|
|
4590
|
-
};
|
|
4591
|
-
function modOf(t, mods) {
|
|
4592
|
-
if (mods.length === 0) return null;
|
|
4593
|
-
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
4594
|
-
return { a: mods[0] };
|
|
4595
|
-
}
|
|
4596
|
-
function resolveTypeName(state, tn) {
|
|
4597
|
-
const joined = tn.parts.join(".");
|
|
4598
|
-
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
4599
|
-
if (bare !== null) {
|
|
4600
|
-
const serial = SERIALS[bare];
|
|
4601
|
-
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
4602
|
-
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
4603
|
-
}
|
|
4604
|
-
const builtin = normalizeTypeName(bare);
|
|
4605
|
-
if (builtin !== null) {
|
|
4606
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
4607
|
-
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
4608
|
-
}
|
|
4609
|
-
}
|
|
4610
|
-
const enumData = state.findEnum(tn.parts);
|
|
4611
|
-
if (enumData) {
|
|
4612
|
-
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
4613
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
4614
|
-
return { column: { id, mod: null }, domain: null, serial: null };
|
|
4615
|
-
}
|
|
4616
|
-
const domainData = state.findDomain(tn.parts);
|
|
4617
|
-
if (domainData) {
|
|
4618
|
-
if (tn.arrayDims > 0) {
|
|
4619
|
-
const id = arrayTypeOf(domainData.baseType.id);
|
|
4620
|
-
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
4621
|
-
}
|
|
4622
|
-
return {
|
|
4623
|
-
column: domainData.baseType,
|
|
4624
|
-
domain: `${domainData.schema}.${domainData.name}`,
|
|
4625
|
-
serial: null
|
|
4626
|
-
};
|
|
4627
|
-
}
|
|
4628
|
-
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
4629
|
-
}
|
|
4630
|
-
|
|
4631
|
-
// src/executor/relation.ts
|
|
4632
|
-
var RowScope = class {
|
|
4633
|
-
constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
|
|
4634
|
-
this.columns = columns;
|
|
4635
|
-
this.row = row;
|
|
4636
|
-
this.parent = parent;
|
|
4637
|
-
this.rangeVars = rangeVars;
|
|
4638
|
-
}
|
|
4639
|
-
columns;
|
|
4640
|
-
row;
|
|
4641
|
-
parent;
|
|
4642
|
-
rangeVars;
|
|
4643
|
-
/** Resolve a (possibly qualified) column reference. undefined = not found. */
|
|
4644
|
-
lookup(parts) {
|
|
4645
|
-
if (parts.length === 1) {
|
|
4646
|
-
const name2 = parts[0];
|
|
4647
|
-
let found2;
|
|
4648
|
-
let count2 = 0;
|
|
4649
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4650
|
-
const c = this.columns[i];
|
|
4651
|
-
if (c.name === name2 && !c.hidden) {
|
|
4652
|
-
count2++;
|
|
4653
|
-
if (count2 > 1) {
|
|
4654
|
-
throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
|
|
4655
|
-
}
|
|
4656
|
-
found2 = tv(c.type, this.row[i] ?? null);
|
|
4657
|
-
}
|
|
4658
|
-
}
|
|
4659
|
-
if (found2 !== void 0) return found2;
|
|
4660
|
-
if (this.rangeVars.has(name2)) {
|
|
4661
|
-
return this.wholeRow(name2);
|
|
4662
|
-
}
|
|
4663
|
-
return this.parent?.lookup(parts);
|
|
4664
|
-
}
|
|
4665
|
-
const table = parts[parts.length - 2];
|
|
4666
|
-
const name = parts[parts.length - 1];
|
|
4667
|
-
let found;
|
|
4668
|
-
let count = 0;
|
|
4669
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4670
|
-
const c = this.columns[i];
|
|
4671
|
-
if (c.table === table && c.name === name) {
|
|
4672
|
-
count++;
|
|
4673
|
-
if (count > 1) {
|
|
4674
|
-
throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
|
|
4675
|
-
}
|
|
4676
|
-
found = tv(c.type, this.row[i] ?? null);
|
|
4677
|
-
}
|
|
4678
|
-
}
|
|
4679
|
-
if (found !== void 0) return found;
|
|
4680
|
-
return this.parent?.lookup(parts);
|
|
4681
|
-
}
|
|
4682
|
-
/** Build a record value from all columns belonging to a range variable. */
|
|
4683
|
-
wholeRow(rangeVar) {
|
|
4684
|
-
const types = [];
|
|
4685
|
-
const values = [];
|
|
4686
|
-
const names = [];
|
|
4687
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4688
|
-
const c = this.columns[i];
|
|
4689
|
-
if (c.table === rangeVar) {
|
|
4690
|
-
types.push(c.type);
|
|
4691
|
-
values.push(this.row[i] ?? null);
|
|
4692
|
-
names.push(c.name);
|
|
4693
|
-
}
|
|
4694
|
-
}
|
|
4695
|
-
if (types.length === 0) return void 0;
|
|
4696
|
-
return tv("record", { kind: "pgrecord", types, values, names });
|
|
4697
|
-
}
|
|
4698
|
-
hasRangeVar(name) {
|
|
4699
|
-
if (this.rangeVars.has(name)) return true;
|
|
4700
|
-
return this.parent?.hasRangeVar(name) ?? false;
|
|
4701
|
-
}
|
|
4702
|
-
};
|
|
4703
|
-
function childEnv(env, outer) {
|
|
4704
|
-
return {
|
|
4705
|
-
ctx: env.ctx,
|
|
4706
|
-
params: env.params,
|
|
4707
|
-
ctes: env.ctes,
|
|
4708
|
-
outer,
|
|
4709
|
-
allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
|
|
4710
|
-
};
|
|
4711
|
-
}
|
|
4712
|
-
function relationResult(rel3, command2) {
|
|
4713
|
-
const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
|
|
4714
|
-
return {
|
|
4715
|
-
columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
|
|
4716
|
-
rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
|
|
4717
|
-
command: command2,
|
|
4718
|
-
rowCount: rel3.rows.length
|
|
4719
|
-
};
|
|
4720
|
-
}
|
|
4721
|
-
function commandResult(command2, rowCount) {
|
|
4722
|
-
return { columns: [], rows: [], command: command2, rowCount };
|
|
4723
|
-
}
|
|
4724
|
-
function inferColumnName(e) {
|
|
4725
|
-
switch (e.type) {
|
|
4726
|
-
case "colref":
|
|
4727
|
-
return e.parts[e.parts.length - 1];
|
|
4728
|
-
case "func":
|
|
4729
|
-
return e.name[e.name.length - 1];
|
|
4730
|
-
case "cast": {
|
|
4731
|
-
const inner = inferColumnName(e.expr);
|
|
4732
|
-
if (inner !== "?column?") return inner;
|
|
4733
|
-
return e.target.parts[e.target.parts.length - 1];
|
|
4734
|
-
}
|
|
4735
|
-
case "collate":
|
|
4736
|
-
return inferColumnName(e.expr);
|
|
4737
|
-
case "case":
|
|
4738
|
-
return "case";
|
|
4739
|
-
case "array_ctor":
|
|
4740
|
-
case "array_query":
|
|
4741
|
-
return "array";
|
|
4742
|
-
case "row":
|
|
4743
|
-
return "row";
|
|
4744
|
-
case "subquery_expr":
|
|
4745
|
-
return e.kind === "exists" ? "exists" : "?column?";
|
|
4746
|
-
case "bool_lit":
|
|
4747
|
-
return "bool";
|
|
4748
|
-
case "extract":
|
|
4749
|
-
return "extract";
|
|
4750
|
-
case "position":
|
|
4751
|
-
return "position";
|
|
4752
|
-
case "substring_sql":
|
|
4753
|
-
return "substring";
|
|
4754
|
-
case "overlay":
|
|
4755
|
-
return "overlay";
|
|
4756
|
-
case "trim":
|
|
4757
|
-
return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
|
|
4758
|
-
case "field_select":
|
|
4759
|
-
return e.field === "*" ? "?column?" : e.field;
|
|
4760
|
-
case "at_time_zone":
|
|
4761
|
-
return "timezone";
|
|
4762
|
-
case "grouping_func":
|
|
4763
|
-
return "grouping";
|
|
4764
|
-
case "subscript":
|
|
4765
|
-
return inferColumnName(e.base);
|
|
4766
|
-
default:
|
|
4767
|
-
return "?column?";
|
|
4768
|
-
}
|
|
4769
|
-
}
|
|
4770
|
-
|
|
4771
|
-
// src/api/bind.ts
|
|
4772
|
-
var INT4_MIN2 = -2147483648;
|
|
4773
|
-
var INT4_MAX2 = 2147483647;
|
|
4774
|
-
function bindValueToTyped(value, index) {
|
|
4775
|
-
if (value === null || value === void 0) return tv(UNKNOWN, null);
|
|
4776
|
-
switch (typeof value) {
|
|
4777
|
-
case "boolean":
|
|
4778
|
-
return tv("bool", value);
|
|
4779
|
-
case "number":
|
|
4780
|
-
if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
|
|
4781
|
-
return tv("int4", value);
|
|
4782
|
-
}
|
|
4783
|
-
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
4784
|
-
return tv("int8", BigInt(value));
|
|
4785
|
-
}
|
|
4786
|
-
return tv("float8", value);
|
|
4787
|
-
case "bigint":
|
|
4788
|
-
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
4789
|
-
throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
|
|
4790
|
-
}
|
|
4791
|
-
return tv("int8", value);
|
|
4792
|
-
case "string":
|
|
4793
|
-
return tv(UNKNOWN, value);
|
|
4794
|
-
case "object": {
|
|
4795
|
-
if (value instanceof Uint8Array) return tv("bytea", value);
|
|
4796
|
-
if (value instanceof Date) {
|
|
4797
|
-
const ms = value.getTime();
|
|
4798
|
-
if (Number.isNaN(ms)) {
|
|
4799
|
-
throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
|
|
4800
|
-
}
|
|
4801
|
-
return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
|
|
4802
|
-
}
|
|
4803
|
-
break;
|
|
4804
|
-
}
|
|
4805
|
-
default:
|
|
4806
|
-
break;
|
|
4807
|
-
}
|
|
4808
|
-
throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
|
|
4809
|
-
}
|
|
4810
|
-
function datumToJs(t, v, ctx, int8 = "bigint") {
|
|
4811
|
-
if (v === null) return null;
|
|
4812
|
-
switch (t) {
|
|
4813
|
-
case "bool":
|
|
4814
|
-
return v;
|
|
4815
|
-
case "int2":
|
|
4816
|
-
case "int4":
|
|
4817
|
-
case "oid":
|
|
4818
|
-
return typeof v === "bigint" ? Number(v) : v;
|
|
4819
|
-
case "int8": {
|
|
4820
|
-
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
4821
|
-
if (int8 === "string") return n.toString();
|
|
4822
|
-
if (int8 === "number") return Number(n);
|
|
4823
|
-
return n;
|
|
4824
|
-
}
|
|
4825
|
-
case "float4":
|
|
4826
|
-
case "float8":
|
|
4827
|
-
return v;
|
|
4828
|
-
case "bytea":
|
|
4829
|
-
return v;
|
|
4830
|
-
default:
|
|
4831
|
-
return datumText(t === UNKNOWN ? "text" : t, v, ctx);
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
|
|
4835
4702
|
// src/functions/datetime-fns.ts
|
|
4836
4703
|
function extractDatePart(ctx, field0, src0, asNumeric) {
|
|
4837
4704
|
const field = field0.toLowerCase();
|
|
@@ -5746,71 +5613,6 @@ function getArrayFunctions() {
|
|
|
5746
5613
|
return m;
|
|
5747
5614
|
}
|
|
5748
5615
|
|
|
5749
|
-
// src/types/timezone.ts
|
|
5750
|
-
var intlCache = /* @__PURE__ */ new Map();
|
|
5751
|
-
function intlFor(zone) {
|
|
5752
|
-
const cached = intlCache.get(zone);
|
|
5753
|
-
if (cached) return cached;
|
|
5754
|
-
try {
|
|
5755
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
5756
|
-
timeZone: zone,
|
|
5757
|
-
timeZoneName: "longOffset",
|
|
5758
|
-
year: "numeric"
|
|
5759
|
-
});
|
|
5760
|
-
intlCache.set(zone, fmt);
|
|
5761
|
-
return fmt;
|
|
5762
|
-
} catch {
|
|
5763
|
-
return null;
|
|
5764
|
-
}
|
|
5765
|
-
}
|
|
5766
|
-
function parseFixedOffset(zone) {
|
|
5767
|
-
const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
|
|
5768
|
-
if (!m) return null;
|
|
5769
|
-
const sign = m[1] === "-" ? -1 : 1;
|
|
5770
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
|
|
5771
|
-
}
|
|
5772
|
-
function parsePosixOffset(zone) {
|
|
5773
|
-
const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
|
|
5774
|
-
if (!m) return null;
|
|
5775
|
-
const sign = m[1] === "-" ? 1 : -1;
|
|
5776
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
|
|
5777
|
-
}
|
|
5778
|
-
function pgMicrosToUnixMs(utcMicros) {
|
|
5779
|
-
return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
|
|
5780
|
-
}
|
|
5781
|
-
function zoneOffsetAtUtc(zone, utcMicros) {
|
|
5782
|
-
const z = zone.trim();
|
|
5783
|
-
const lower = z.toLowerCase();
|
|
5784
|
-
if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
|
|
5785
|
-
const posix = parsePosixOffset(z);
|
|
5786
|
-
if (posix !== null) return posix;
|
|
5787
|
-
const fixed = parseFixedOffset(z);
|
|
5788
|
-
if (fixed !== null) return fixed;
|
|
5789
|
-
const fmt = intlFor(z);
|
|
5790
|
-
if (fmt) {
|
|
5791
|
-
const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
|
|
5792
|
-
const parts = fmt.formatToParts(new Date(ms));
|
|
5793
|
-
const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
|
|
5794
|
-
const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
|
|
5795
|
-
if (!m) return 0;
|
|
5796
|
-
const sign = m[1] === "-" ? -1 : 1;
|
|
5797
|
-
return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
|
|
5798
|
-
}
|
|
5799
|
-
throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
|
|
5800
|
-
}
|
|
5801
|
-
function clampDateMs(ms) {
|
|
5802
|
-
const MAX = 864e13;
|
|
5803
|
-
return Math.max(-MAX, Math.min(MAX, ms));
|
|
5804
|
-
}
|
|
5805
|
-
function zoneOffsetForNaive(zone, naiveMicros) {
|
|
5806
|
-
const first = zoneOffsetAtUtc(zone, naiveMicros);
|
|
5807
|
-
const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
|
|
5808
|
-
return second;
|
|
5809
|
-
}
|
|
5810
|
-
function validateZone(zone) {
|
|
5811
|
-
zoneOffsetAtUtc(zone, 0n);
|
|
5812
|
-
}
|
|
5813
|
-
|
|
5814
5616
|
// src/functions/datetime-registry.ts
|
|
5815
5617
|
function getDatetimeFunctions() {
|
|
5816
5618
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -7410,6 +7212,28 @@ function exactDegTan(x) {
|
|
|
7410
7212
|
return Math.tan(x * Math.PI / 180);
|
|
7411
7213
|
}
|
|
7412
7214
|
|
|
7215
|
+
// src/storage/indexable-relation.ts
|
|
7216
|
+
function materializedTable(view) {
|
|
7217
|
+
const table = new TableData(
|
|
7218
|
+
view.schema,
|
|
7219
|
+
view.name,
|
|
7220
|
+
(view.matColumns ?? []).map((c) => ({
|
|
7221
|
+
name: c.name,
|
|
7222
|
+
type: { id: c.type, mod: null },
|
|
7223
|
+
notNull: false,
|
|
7224
|
+
defaultExpr: null,
|
|
7225
|
+
identity: null,
|
|
7226
|
+
generated: null,
|
|
7227
|
+
collate: null,
|
|
7228
|
+
domain: null
|
|
7229
|
+
})),
|
|
7230
|
+
view.oid,
|
|
7231
|
+
view.temp
|
|
7232
|
+
);
|
|
7233
|
+
table.rows = view.matRows ?? [];
|
|
7234
|
+
return table;
|
|
7235
|
+
}
|
|
7236
|
+
|
|
7413
7237
|
// src/schema/index-catalog.ts
|
|
7414
7238
|
var isKeyConstraint = (constraint) => constraint.kind === "primary_key" || constraint.kind === "unique";
|
|
7415
7239
|
var ACCESS_METHODS = /* @__PURE__ */ new Map([
|
|
@@ -7438,6 +7262,8 @@ function syncIndexCatalog(state) {
|
|
|
7438
7262
|
const tables = /* @__PURE__ */ new Map();
|
|
7439
7263
|
for (const schema of state.schemas.values()) {
|
|
7440
7264
|
for (const table of schema.tables.values()) tables.set(table.oid, { schema, table });
|
|
7265
|
+
for (const view of schema.views.values())
|
|
7266
|
+
if (view.materialized) tables.set(view.oid, { schema, table: materializedTable(view) });
|
|
7441
7267
|
}
|
|
7442
7268
|
const backing = /* @__PURE__ */ new Map();
|
|
7443
7269
|
for (const { table } of tables.values()) {
|
|
@@ -7635,6 +7461,55 @@ function deparseExpr(e) {
|
|
|
7635
7461
|
}
|
|
7636
7462
|
}
|
|
7637
7463
|
|
|
7464
|
+
// src/types/resolve.ts
|
|
7465
|
+
var SERIALS = {
|
|
7466
|
+
smallserial: "int2",
|
|
7467
|
+
serial2: "int2",
|
|
7468
|
+
serial: "int4",
|
|
7469
|
+
serial4: "int4",
|
|
7470
|
+
bigserial: "int8",
|
|
7471
|
+
serial8: "int8"
|
|
7472
|
+
};
|
|
7473
|
+
function modOf(t, mods) {
|
|
7474
|
+
if (mods.length === 0) return null;
|
|
7475
|
+
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
7476
|
+
return { a: mods[0] };
|
|
7477
|
+
}
|
|
7478
|
+
function resolveTypeName(state, tn) {
|
|
7479
|
+
const joined = tn.parts.join(".");
|
|
7480
|
+
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
7481
|
+
if (bare !== null) {
|
|
7482
|
+
const serial = SERIALS[bare];
|
|
7483
|
+
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
7484
|
+
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
7485
|
+
}
|
|
7486
|
+
const builtin = normalizeTypeName(bare);
|
|
7487
|
+
if (builtin !== null) {
|
|
7488
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
7489
|
+
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
const enumData = state.findEnum(tn.parts);
|
|
7493
|
+
if (enumData) {
|
|
7494
|
+
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
7495
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
7496
|
+
return { column: { id, mod: null }, domain: null, serial: null };
|
|
7497
|
+
}
|
|
7498
|
+
const domainData = state.findDomain(tn.parts);
|
|
7499
|
+
if (domainData) {
|
|
7500
|
+
if (tn.arrayDims > 0) {
|
|
7501
|
+
const id = arrayTypeOf(domainData.baseType.id);
|
|
7502
|
+
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
7503
|
+
}
|
|
7504
|
+
return {
|
|
7505
|
+
column: domainData.baseType,
|
|
7506
|
+
domain: `${domainData.schema}.${domainData.name}`,
|
|
7507
|
+
serial: null
|
|
7508
|
+
};
|
|
7509
|
+
}
|
|
7510
|
+
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7638
7513
|
// src/sql/ruleutils.ts
|
|
7639
7514
|
var BOOL = "bool";
|
|
7640
7515
|
var TEXT = "text";
|
|
@@ -8432,7 +8307,11 @@ function getMiscFunctions() {
|
|
|
8432
8307
|
}
|
|
8433
8308
|
const name = argText(ctx, args[0]).toLowerCase();
|
|
8434
8309
|
const value = args[1].v === null ? "" : argText(ctx, args[1]);
|
|
8435
|
-
ctx.state.settings.
|
|
8310
|
+
if (!ctx.state.settings.has(name) && !name.includes("."))
|
|
8311
|
+
throw pgError("undefined_object", `unrecognized configuration parameter "${name}"`, "42704");
|
|
8312
|
+
validateStorageSetting(name, value);
|
|
8313
|
+
const local = args[2]?.v === true;
|
|
8314
|
+
if (!local || ctx.state.inTransaction) (local ? ctx.state.localSettings : ctx.state.settings).set(name, value);
|
|
8436
8315
|
return tv("text", value);
|
|
8437
8316
|
});
|
|
8438
8317
|
m.set(
|
|
@@ -8498,10 +8377,7 @@ function getMiscFunctions() {
|
|
|
8498
8377
|
if (!col)
|
|
8499
8378
|
throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
|
|
8500
8379
|
if (!col.identity) return tv("text", null);
|
|
8501
|
-
return tv(
|
|
8502
|
-
"text",
|
|
8503
|
-
col.identity.sequence.split(".").map(quoteIdent).join(".")
|
|
8504
|
-
);
|
|
8380
|
+
return tv("text", col.identity.sequence.split(".").map(quoteIdent).join("."));
|
|
8505
8381
|
})
|
|
8506
8382
|
);
|
|
8507
8383
|
m.set("format_type", (ctx, args) => {
|
|
@@ -13398,31 +13274,171 @@ function jsonEachSrf(asText, jsonOut) {
|
|
|
13398
13274
|
return { columns, rows };
|
|
13399
13275
|
};
|
|
13400
13276
|
}
|
|
13401
|
-
SRFS.set("json_each", jsonEachSrf(false, true));
|
|
13402
|
-
SRFS.set("jsonb_each", jsonEachSrf(false, false));
|
|
13403
|
-
SRFS.set("json_each_text", jsonEachSrf(true, true));
|
|
13404
|
-
SRFS.set("jsonb_each_text", jsonEachSrf(true, false));
|
|
13405
|
-
function jsonKeysSrf() {
|
|
13406
|
-
return (ctx, args, alias) => {
|
|
13407
|
-
if (args[0].v === null) return { columns: [{ name: alias, type: "text" }], rows: [] };
|
|
13408
|
-
const v = jsonInput(ctx, args[0]);
|
|
13409
|
-
if (v.j !== "obj") {
|
|
13410
|
-
throw pgError(
|
|
13411
|
-
"invalid_parameter_value",
|
|
13412
|
-
`cannot call ${alias} on a ${v.j === "arr" ? "array" : "scalar"}`,
|
|
13413
|
-
"22023"
|
|
13414
|
-
);
|
|
13415
|
-
}
|
|
13416
|
-
return { columns: [{ name: alias, type: "text" }], rows: [...v.v.keys()].map((k) => [k]) };
|
|
13277
|
+
SRFS.set("json_each", jsonEachSrf(false, true));
|
|
13278
|
+
SRFS.set("jsonb_each", jsonEachSrf(false, false));
|
|
13279
|
+
SRFS.set("json_each_text", jsonEachSrf(true, true));
|
|
13280
|
+
SRFS.set("jsonb_each_text", jsonEachSrf(true, false));
|
|
13281
|
+
function jsonKeysSrf() {
|
|
13282
|
+
return (ctx, args, alias) => {
|
|
13283
|
+
if (args[0].v === null) return { columns: [{ name: alias, type: "text" }], rows: [] };
|
|
13284
|
+
const v = jsonInput(ctx, args[0]);
|
|
13285
|
+
if (v.j !== "obj") {
|
|
13286
|
+
throw pgError(
|
|
13287
|
+
"invalid_parameter_value",
|
|
13288
|
+
`cannot call ${alias} on a ${v.j === "arr" ? "array" : "scalar"}`,
|
|
13289
|
+
"22023"
|
|
13290
|
+
);
|
|
13291
|
+
}
|
|
13292
|
+
return { columns: [{ name: alias, type: "text" }], rows: [...v.v.keys()].map((k) => [k]) };
|
|
13293
|
+
};
|
|
13294
|
+
}
|
|
13295
|
+
SRFS.set("json_object_keys", jsonKeysSrf());
|
|
13296
|
+
SRFS.set("jsonb_object_keys", jsonKeysSrf());
|
|
13297
|
+
function getSrfFunctions() {
|
|
13298
|
+
return SRFS;
|
|
13299
|
+
}
|
|
13300
|
+
function isSrfName(name) {
|
|
13301
|
+
return SRFS.has(name);
|
|
13302
|
+
}
|
|
13303
|
+
|
|
13304
|
+
// src/executor/relation.ts
|
|
13305
|
+
var RowScope = class {
|
|
13306
|
+
constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
|
|
13307
|
+
this.columns = columns;
|
|
13308
|
+
this.row = row;
|
|
13309
|
+
this.parent = parent;
|
|
13310
|
+
this.rangeVars = rangeVars;
|
|
13311
|
+
}
|
|
13312
|
+
columns;
|
|
13313
|
+
row;
|
|
13314
|
+
parent;
|
|
13315
|
+
rangeVars;
|
|
13316
|
+
/** Resolve a (possibly qualified) column reference. undefined = not found. */
|
|
13317
|
+
lookup(parts) {
|
|
13318
|
+
if (parts.length === 1) {
|
|
13319
|
+
const name2 = parts[0];
|
|
13320
|
+
let found2;
|
|
13321
|
+
let count2 = 0;
|
|
13322
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13323
|
+
const c = this.columns[i];
|
|
13324
|
+
if (c.name === name2 && !c.hidden) {
|
|
13325
|
+
count2++;
|
|
13326
|
+
if (count2 > 1) {
|
|
13327
|
+
throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
|
|
13328
|
+
}
|
|
13329
|
+
found2 = tv(c.type, this.row[i] ?? null);
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13332
|
+
if (found2 !== void 0) return found2;
|
|
13333
|
+
if (this.rangeVars.has(name2)) {
|
|
13334
|
+
return this.wholeRow(name2);
|
|
13335
|
+
}
|
|
13336
|
+
return this.parent?.lookup(parts);
|
|
13337
|
+
}
|
|
13338
|
+
const table = parts[parts.length - 2];
|
|
13339
|
+
const name = parts[parts.length - 1];
|
|
13340
|
+
let found;
|
|
13341
|
+
let count = 0;
|
|
13342
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13343
|
+
const c = this.columns[i];
|
|
13344
|
+
if (c.table === table && c.name === name) {
|
|
13345
|
+
count++;
|
|
13346
|
+
if (count > 1) {
|
|
13347
|
+
throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
|
|
13348
|
+
}
|
|
13349
|
+
found = tv(c.type, this.row[i] ?? null);
|
|
13350
|
+
}
|
|
13351
|
+
}
|
|
13352
|
+
if (found !== void 0) return found;
|
|
13353
|
+
return this.parent?.lookup(parts);
|
|
13354
|
+
}
|
|
13355
|
+
/** Build a record value from all columns belonging to a range variable. */
|
|
13356
|
+
wholeRow(rangeVar) {
|
|
13357
|
+
const types = [];
|
|
13358
|
+
const values = [];
|
|
13359
|
+
const names = [];
|
|
13360
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
13361
|
+
const c = this.columns[i];
|
|
13362
|
+
if (c.table === rangeVar) {
|
|
13363
|
+
types.push(c.type);
|
|
13364
|
+
values.push(this.row[i] ?? null);
|
|
13365
|
+
names.push(c.name);
|
|
13366
|
+
}
|
|
13367
|
+
}
|
|
13368
|
+
if (types.length === 0) return void 0;
|
|
13369
|
+
return tv("record", { kind: "pgrecord", types, values, names });
|
|
13370
|
+
}
|
|
13371
|
+
hasRangeVar(name) {
|
|
13372
|
+
if (this.rangeVars.has(name)) return true;
|
|
13373
|
+
return this.parent?.hasRangeVar(name) ?? false;
|
|
13374
|
+
}
|
|
13375
|
+
};
|
|
13376
|
+
function childEnv(env, outer) {
|
|
13377
|
+
return {
|
|
13378
|
+
ctx: env.ctx,
|
|
13379
|
+
params: env.params,
|
|
13380
|
+
ctes: env.ctes,
|
|
13381
|
+
outer,
|
|
13382
|
+
allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
|
|
13383
|
+
};
|
|
13384
|
+
}
|
|
13385
|
+
function relationResult(rel3, command2) {
|
|
13386
|
+
const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
|
|
13387
|
+
return {
|
|
13388
|
+
columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
|
|
13389
|
+
rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
|
|
13390
|
+
command: command2,
|
|
13391
|
+
rowCount: rel3.rows.length
|
|
13417
13392
|
};
|
|
13418
13393
|
}
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
function getSrfFunctions() {
|
|
13422
|
-
return SRFS;
|
|
13394
|
+
function commandResult(command2, rowCount) {
|
|
13395
|
+
return { columns: [], rows: [], command: command2, rowCount };
|
|
13423
13396
|
}
|
|
13424
|
-
function
|
|
13425
|
-
|
|
13397
|
+
function inferColumnName(e) {
|
|
13398
|
+
switch (e.type) {
|
|
13399
|
+
case "colref":
|
|
13400
|
+
return e.parts[e.parts.length - 1];
|
|
13401
|
+
case "func":
|
|
13402
|
+
return e.name[e.name.length - 1];
|
|
13403
|
+
case "cast": {
|
|
13404
|
+
const inner = inferColumnName(e.expr);
|
|
13405
|
+
if (inner !== "?column?") return inner;
|
|
13406
|
+
return e.target.parts[e.target.parts.length - 1];
|
|
13407
|
+
}
|
|
13408
|
+
case "collate":
|
|
13409
|
+
return inferColumnName(e.expr);
|
|
13410
|
+
case "case":
|
|
13411
|
+
return "case";
|
|
13412
|
+
case "array_ctor":
|
|
13413
|
+
case "array_query":
|
|
13414
|
+
return "array";
|
|
13415
|
+
case "row":
|
|
13416
|
+
return "row";
|
|
13417
|
+
case "subquery_expr":
|
|
13418
|
+
return e.kind === "exists" ? "exists" : "?column?";
|
|
13419
|
+
case "bool_lit":
|
|
13420
|
+
return "bool";
|
|
13421
|
+
case "extract":
|
|
13422
|
+
return "extract";
|
|
13423
|
+
case "position":
|
|
13424
|
+
return "position";
|
|
13425
|
+
case "substring_sql":
|
|
13426
|
+
return "substring";
|
|
13427
|
+
case "overlay":
|
|
13428
|
+
return "overlay";
|
|
13429
|
+
case "trim":
|
|
13430
|
+
return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
|
|
13431
|
+
case "field_select":
|
|
13432
|
+
return e.field === "*" ? "?column?" : e.field;
|
|
13433
|
+
case "at_time_zone":
|
|
13434
|
+
return "timezone";
|
|
13435
|
+
case "grouping_func":
|
|
13436
|
+
return "grouping";
|
|
13437
|
+
case "subscript":
|
|
13438
|
+
return inferColumnName(e.base);
|
|
13439
|
+
default:
|
|
13440
|
+
return "?column?";
|
|
13441
|
+
}
|
|
13426
13442
|
}
|
|
13427
13443
|
|
|
13428
13444
|
// src/indexes/index.ts
|
|
@@ -13536,6 +13552,181 @@ function rebuildTableIndexes(env, table) {
|
|
|
13536
13552
|
ensureIndexStores(env, table);
|
|
13537
13553
|
}
|
|
13538
13554
|
|
|
13555
|
+
// src/constraints/enforce.ts
|
|
13556
|
+
function tableScope(table, row) {
|
|
13557
|
+
const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
|
|
13558
|
+
return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
|
|
13559
|
+
}
|
|
13560
|
+
function checkNotNull(env, table, row) {
|
|
13561
|
+
void env;
|
|
13562
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
13563
|
+
const c = table.columns[i];
|
|
13564
|
+
if (c.notNull && (row[i] ?? null) === null) {
|
|
13565
|
+
throw pgError(
|
|
13566
|
+
"not_null_violation",
|
|
13567
|
+
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
13568
|
+
"23502"
|
|
13569
|
+
);
|
|
13570
|
+
}
|
|
13571
|
+
}
|
|
13572
|
+
}
|
|
13573
|
+
function checkChecks(env, table, row) {
|
|
13574
|
+
for (const con of table.constraints) {
|
|
13575
|
+
if (con.kind !== "check") continue;
|
|
13576
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13577
|
+
const v = evalExpr(env.ctx, scope, con.expr);
|
|
13578
|
+
if (v.v === null) continue;
|
|
13579
|
+
const b = castTo(env.ctx, v, "bool", {});
|
|
13580
|
+
if (b.v !== true) {
|
|
13581
|
+
throw pgError(
|
|
13582
|
+
"check_violation",
|
|
13583
|
+
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
13584
|
+
"23514"
|
|
13585
|
+
);
|
|
13586
|
+
}
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13589
|
+
function uniqueSpecsFor(env, table) {
|
|
13590
|
+
const specs = [];
|
|
13591
|
+
for (const con of table.constraints) {
|
|
13592
|
+
if (con.kind !== "primary_key" && con.kind !== "unique") continue;
|
|
13593
|
+
specs.push({
|
|
13594
|
+
name: con.name,
|
|
13595
|
+
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
13596
|
+
columnNames: con.columns,
|
|
13597
|
+
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
13598
|
+
where: null,
|
|
13599
|
+
isPrimary: con.kind === "primary_key"
|
|
13600
|
+
});
|
|
13601
|
+
}
|
|
13602
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
13603
|
+
if (schema) {
|
|
13604
|
+
for (const idx of schema.indexes.values()) {
|
|
13605
|
+
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
13606
|
+
specs.push(uniqueSpecOfIndex(table, idx));
|
|
13607
|
+
}
|
|
13608
|
+
}
|
|
13609
|
+
return specs;
|
|
13610
|
+
}
|
|
13611
|
+
function uniqueSpecOfIndex(table, idx) {
|
|
13612
|
+
return {
|
|
13613
|
+
name: idx.name,
|
|
13614
|
+
keys: idx.columns.map((c) => ({
|
|
13615
|
+
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
13616
|
+
expr: c.expr
|
|
13617
|
+
})),
|
|
13618
|
+
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
13619
|
+
nullsNotDistinct: idx.nullsNotDistinct,
|
|
13620
|
+
where: idx.where,
|
|
13621
|
+
isPrimary: false
|
|
13622
|
+
};
|
|
13623
|
+
}
|
|
13624
|
+
function uniqueKeyOf(env, table, spec, row) {
|
|
13625
|
+
if (spec.where) {
|
|
13626
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13627
|
+
const v = evalExpr(env.ctx, scope, spec.where);
|
|
13628
|
+
if (v.v !== true) return null;
|
|
13629
|
+
}
|
|
13630
|
+
const parts = [];
|
|
13631
|
+
let hasNull = false;
|
|
13632
|
+
for (const k of spec.keys) {
|
|
13633
|
+
let value;
|
|
13634
|
+
let type;
|
|
13635
|
+
if (k.expr) {
|
|
13636
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
13637
|
+
const v = evalExpr(env.ctx, scope, k.expr);
|
|
13638
|
+
value = v.v;
|
|
13639
|
+
type = v.t === "unknown" ? "text" : v.t;
|
|
13640
|
+
} else {
|
|
13641
|
+
value = row[k.colIdx] ?? null;
|
|
13642
|
+
type = table.columns[k.colIdx].type.id;
|
|
13643
|
+
}
|
|
13644
|
+
if (value === null) {
|
|
13645
|
+
hasNull = true;
|
|
13646
|
+
parts.push("\0N");
|
|
13647
|
+
} else {
|
|
13648
|
+
parts.push(datumKey(type, value));
|
|
13649
|
+
}
|
|
13650
|
+
}
|
|
13651
|
+
if (hasNull && !spec.nullsNotDistinct) return null;
|
|
13652
|
+
return parts.join("");
|
|
13653
|
+
}
|
|
13654
|
+
function checkUnique(env, table, row, selfIdx) {
|
|
13655
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
13656
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
13657
|
+
if (key === null) continue;
|
|
13658
|
+
indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13661
|
+
function findConflict(env, table, spec, row) {
|
|
13662
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
13663
|
+
if (key === null) return null;
|
|
13664
|
+
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
13665
|
+
return hits.length > 0 ? hits[0] : null;
|
|
13666
|
+
}
|
|
13667
|
+
function checkForeignKeys(env, table, row) {
|
|
13668
|
+
const state = env.ctx.state;
|
|
13669
|
+
for (const con of table.constraints) {
|
|
13670
|
+
if (con.kind !== "foreign_key") continue;
|
|
13671
|
+
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
13672
|
+
const nulls = values.filter((v) => v === null).length;
|
|
13673
|
+
if (con.match === "simple" && nulls > 0) continue;
|
|
13674
|
+
if (con.match === "full") {
|
|
13675
|
+
if (nulls === values.length) continue;
|
|
13676
|
+
if (nulls > 0) {
|
|
13677
|
+
throw pgError(
|
|
13678
|
+
"constraint_foreign_key",
|
|
13679
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
13680
|
+
"23503"
|
|
13681
|
+
);
|
|
13682
|
+
}
|
|
13683
|
+
}
|
|
13684
|
+
const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
|
|
13685
|
+
if (!refTable) {
|
|
13686
|
+
throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
|
|
13687
|
+
}
|
|
13688
|
+
const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
|
|
13689
|
+
const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
|
|
13690
|
+
const wanted = values.map((v, i) => {
|
|
13691
|
+
const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
|
|
13692
|
+
const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
|
|
13693
|
+
return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
|
|
13694
|
+
}).join("");
|
|
13695
|
+
let found = false;
|
|
13696
|
+
for (let i = 0; i < refTable.rowCount(); i++) {
|
|
13697
|
+
const r = refTable.rowAt(i);
|
|
13698
|
+
const key = refIdxs.map((ri, j) => {
|
|
13699
|
+
const v = r[ri] ?? null;
|
|
13700
|
+
return v === null ? "\0N" : datumKey(keyTypes[j], v);
|
|
13701
|
+
}).join("");
|
|
13702
|
+
if (key === wanted) {
|
|
13703
|
+
found = true;
|
|
13704
|
+
break;
|
|
13705
|
+
}
|
|
13706
|
+
}
|
|
13707
|
+
if (!found) {
|
|
13708
|
+
throw pgError(
|
|
13709
|
+
"constraint_foreign_key",
|
|
13710
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
13711
|
+
"23503"
|
|
13712
|
+
);
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
}
|
|
13716
|
+
function referencingConstraints(env, table) {
|
|
13717
|
+
const out = [];
|
|
13718
|
+
for (const schema of env.ctx.state.schemas.values()) {
|
|
13719
|
+
for (const t of schema.tables.values()) {
|
|
13720
|
+
for (const con of t.constraints) {
|
|
13721
|
+
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
13722
|
+
out.push({ table: t, constraint: con });
|
|
13723
|
+
}
|
|
13724
|
+
}
|
|
13725
|
+
}
|
|
13726
|
+
}
|
|
13727
|
+
return out;
|
|
13728
|
+
}
|
|
13729
|
+
|
|
13539
13730
|
// src/planner/access.ts
|
|
13540
13731
|
function isRowIndependentExpr(expr) {
|
|
13541
13732
|
return expr.type !== "colref";
|
|
@@ -15419,10 +15610,9 @@ var Parser = class {
|
|
|
15419
15610
|
return this.parseCreateFunction(orReplace);
|
|
15420
15611
|
}
|
|
15421
15612
|
if (this.atKw("trigger") || this.atKw("constraint") && this.atKw("trigger", 1)) {
|
|
15422
|
-
|
|
15423
|
-
}
|
|
15613
|
+
const constraint = this.eatKw("constraint");
|
|
15424
15614
|
this.expectKw("trigger");
|
|
15425
|
-
return this.parseCreateTrigger(orReplace);
|
|
15615
|
+
return this.parseCreateTrigger(orReplace, constraint);
|
|
15426
15616
|
}
|
|
15427
15617
|
if (this.atKw("extension")) {
|
|
15428
15618
|
this.pos++;
|
|
@@ -16243,7 +16433,7 @@ var Parser = class {
|
|
|
16243
16433
|
strict: strict2
|
|
16244
16434
|
};
|
|
16245
16435
|
}
|
|
16246
|
-
parseCreateTrigger(orReplace) {
|
|
16436
|
+
parseCreateTrigger(orReplace, constraint) {
|
|
16247
16437
|
const name = this.ident();
|
|
16248
16438
|
let timing;
|
|
16249
16439
|
if (this.eatKw("before")) timing = "before";
|
|
@@ -16273,9 +16463,18 @@ var Parser = class {
|
|
|
16273
16463
|
this.expectKw("on");
|
|
16274
16464
|
const table = this.qualifiedName();
|
|
16275
16465
|
if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
|
|
16276
|
-
|
|
16277
|
-
|
|
16278
|
-
|
|
16466
|
+
let deferrable = false;
|
|
16467
|
+
let initiallyDeferred = false;
|
|
16468
|
+
if (this.eatKw("not")) this.expectKw("deferrable");
|
|
16469
|
+
else if (this.eatKw("deferrable")) deferrable = true;
|
|
16470
|
+
if (this.eatKw("initially")) {
|
|
16471
|
+
if (this.eatKw("deferred")) initiallyDeferred = true;
|
|
16472
|
+
else this.expectKw("immediate");
|
|
16473
|
+
}
|
|
16474
|
+
if ((deferrable || initiallyDeferred) && !constraint)
|
|
16475
|
+
this.errorAt(this.peek(), "only constraint triggers can be deferred");
|
|
16476
|
+
if (initiallyDeferred && !deferrable)
|
|
16477
|
+
this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
|
|
16279
16478
|
let forEachRow = false;
|
|
16280
16479
|
if (this.eatKw("for")) {
|
|
16281
16480
|
this.eatKw("each");
|
|
@@ -16300,7 +16499,21 @@ var Parser = class {
|
|
|
16300
16499
|
} while (this.eatPunct(","));
|
|
16301
16500
|
}
|
|
16302
16501
|
this.expectPunct(")");
|
|
16303
|
-
return {
|
|
16502
|
+
return {
|
|
16503
|
+
type: "create_trigger",
|
|
16504
|
+
constraint,
|
|
16505
|
+
deferrable,
|
|
16506
|
+
initiallyDeferred,
|
|
16507
|
+
orReplace,
|
|
16508
|
+
name,
|
|
16509
|
+
timing,
|
|
16510
|
+
events,
|
|
16511
|
+
table,
|
|
16512
|
+
forEachRow,
|
|
16513
|
+
when,
|
|
16514
|
+
funcName,
|
|
16515
|
+
funcArgs
|
|
16516
|
+
};
|
|
16304
16517
|
}
|
|
16305
16518
|
// --- ALTER --------------------------------------------------------------------
|
|
16306
16519
|
parseAlter() {
|
|
@@ -16645,9 +16858,15 @@ var Parser = class {
|
|
|
16645
16858
|
this.skipToStatementEnd();
|
|
16646
16859
|
return { type: "no_op", what: "SET TRANSACTION" };
|
|
16647
16860
|
}
|
|
16648
|
-
if (this.
|
|
16649
|
-
this.
|
|
16650
|
-
|
|
16861
|
+
if (this.eatKw("constraints")) {
|
|
16862
|
+
const names = this.eatKw("all") ? null : [];
|
|
16863
|
+
if (names)
|
|
16864
|
+
do {
|
|
16865
|
+
names.push(this.qualifiedName());
|
|
16866
|
+
} while (this.eatPunct(","));
|
|
16867
|
+
const deferred = this.eatKw("deferred");
|
|
16868
|
+
if (!deferred) this.expectKw("immediate");
|
|
16869
|
+
return { type: "set_constraints", names, deferred };
|
|
16651
16870
|
}
|
|
16652
16871
|
if (this.atKw("role") || this.atKw("session", 0) && this.atKw("authorization", 1)) {
|
|
16653
16872
|
this.skipToStatementEnd();
|
|
@@ -19022,12 +19241,12 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19022
19241
|
table: null
|
|
19023
19242
|
}));
|
|
19024
19243
|
const seen = /* @__PURE__ */ new Set();
|
|
19025
|
-
const
|
|
19244
|
+
const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
|
|
19026
19245
|
let result = [];
|
|
19027
19246
|
let working = [];
|
|
19028
19247
|
for (const row of base.rows) {
|
|
19029
19248
|
if (!body.all) {
|
|
19030
|
-
const k =
|
|
19249
|
+
const k = keyOf2(row);
|
|
19031
19250
|
if (seen.has(k)) continue;
|
|
19032
19251
|
seen.add(k);
|
|
19033
19252
|
}
|
|
@@ -19060,7 +19279,7 @@ function executeRecursiveCte(env, cte, query) {
|
|
|
19060
19279
|
return castTo(env.ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), columns[i].type, {}).v;
|
|
19061
19280
|
});
|
|
19062
19281
|
if (!body.all) {
|
|
19063
|
-
const k =
|
|
19282
|
+
const k = keyOf2(row);
|
|
19064
19283
|
if (seen.has(k)) continue;
|
|
19065
19284
|
seen.add(k);
|
|
19066
19285
|
}
|
|
@@ -20240,7 +20459,7 @@ function setOpRelation(env, op) {
|
|
|
20240
20459
|
);
|
|
20241
20460
|
const lrows = normalize2(left);
|
|
20242
20461
|
const rrows = normalize2(right);
|
|
20243
|
-
const
|
|
20462
|
+
const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
|
|
20244
20463
|
let rows;
|
|
20245
20464
|
switch (op.op) {
|
|
20246
20465
|
case "union": {
|
|
@@ -20248,7 +20467,7 @@ function setOpRelation(env, op) {
|
|
|
20248
20467
|
if (!op.all) {
|
|
20249
20468
|
const seen = /* @__PURE__ */ new Set();
|
|
20250
20469
|
rows = rows.filter((r) => {
|
|
20251
|
-
const k =
|
|
20470
|
+
const k = keyOf2(r);
|
|
20252
20471
|
if (seen.has(k)) return false;
|
|
20253
20472
|
seen.add(k);
|
|
20254
20473
|
return true;
|
|
@@ -20259,13 +20478,13 @@ function setOpRelation(env, op) {
|
|
|
20259
20478
|
case "intersect": {
|
|
20260
20479
|
const rightCounts = /* @__PURE__ */ new Map();
|
|
20261
20480
|
for (const r of rrows) {
|
|
20262
|
-
const k =
|
|
20481
|
+
const k = keyOf2(r);
|
|
20263
20482
|
rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
|
|
20264
20483
|
}
|
|
20265
20484
|
rows = [];
|
|
20266
20485
|
const emitted = /* @__PURE__ */ new Set();
|
|
20267
20486
|
for (const r of lrows) {
|
|
20268
|
-
const k =
|
|
20487
|
+
const k = keyOf2(r);
|
|
20269
20488
|
const avail = rightCounts.get(k) ?? 0;
|
|
20270
20489
|
if (avail <= 0) continue;
|
|
20271
20490
|
if (op.all) {
|
|
@@ -20281,13 +20500,13 @@ function setOpRelation(env, op) {
|
|
|
20281
20500
|
case "except": {
|
|
20282
20501
|
const rightCounts = /* @__PURE__ */ new Map();
|
|
20283
20502
|
for (const r of rrows) {
|
|
20284
|
-
const k =
|
|
20503
|
+
const k = keyOf2(r);
|
|
20285
20504
|
rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
|
|
20286
20505
|
}
|
|
20287
20506
|
rows = [];
|
|
20288
20507
|
const emitted = /* @__PURE__ */ new Set();
|
|
20289
20508
|
for (const r of lrows) {
|
|
20290
|
-
const k =
|
|
20509
|
+
const k = keyOf2(r);
|
|
20291
20510
|
if (op.all) {
|
|
20292
20511
|
const avail = rightCounts.get(k) ?? 0;
|
|
20293
20512
|
if (avail > 0) {
|
|
@@ -20763,202 +20982,222 @@ function executeSelectStmt(env0, stmt) {
|
|
|
20763
20982
|
return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
|
|
20764
20983
|
}
|
|
20765
20984
|
|
|
20766
|
-
// src/
|
|
20767
|
-
function
|
|
20768
|
-
const cols =
|
|
20769
|
-
|
|
20770
|
-
|
|
20771
|
-
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
throw pgError(
|
|
20777
|
-
"not_null_violation",
|
|
20778
|
-
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
20779
|
-
"23502"
|
|
20780
|
-
);
|
|
20985
|
+
// src/executor/triggers-scope.ts
|
|
20986
|
+
function triggerScope(table, newRow, oldRow) {
|
|
20987
|
+
const cols = [];
|
|
20988
|
+
const row = [];
|
|
20989
|
+
const rangeVars = /* @__PURE__ */ new Set();
|
|
20990
|
+
if (newRow) {
|
|
20991
|
+
rangeVars.add("new");
|
|
20992
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
20993
|
+
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
|
|
20994
|
+
row.push(newRow[i] ?? null);
|
|
20781
20995
|
}
|
|
20782
20996
|
}
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
20786
|
-
|
|
20787
|
-
|
|
20788
|
-
const v = evalExpr(env.ctx, scope, con.expr);
|
|
20789
|
-
if (v.v === null) continue;
|
|
20790
|
-
const b = castTo(env.ctx, v, "bool", {});
|
|
20791
|
-
if (b.v !== true) {
|
|
20792
|
-
throw pgError(
|
|
20793
|
-
"check_violation",
|
|
20794
|
-
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
20795
|
-
"23514"
|
|
20796
|
-
);
|
|
20997
|
+
if (oldRow) {
|
|
20998
|
+
rangeVars.add("old");
|
|
20999
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
21000
|
+
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
|
|
21001
|
+
row.push(oldRow[i] ?? null);
|
|
20797
21002
|
}
|
|
20798
21003
|
}
|
|
21004
|
+
return new RowScope(cols, row, null, rangeVars);
|
|
20799
21005
|
}
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
20803
|
-
|
|
20804
|
-
specs.push({
|
|
20805
|
-
name: con.name,
|
|
20806
|
-
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
20807
|
-
columnNames: con.columns,
|
|
20808
|
-
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
20809
|
-
where: null,
|
|
20810
|
-
isPrimary: con.kind === "primary_key"
|
|
20811
|
-
});
|
|
20812
|
-
}
|
|
20813
|
-
const schema = env.ctx.state.schemas.get(table.schema);
|
|
20814
|
-
if (schema) {
|
|
20815
|
-
for (const idx of schema.indexes.values()) {
|
|
20816
|
-
if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
|
|
20817
|
-
specs.push(uniqueSpecOfIndex(table, idx));
|
|
20818
|
-
}
|
|
20819
|
-
}
|
|
20820
|
-
return specs;
|
|
21006
|
+
|
|
21007
|
+
// src/executor/triggers.ts
|
|
21008
|
+
function matches(t, event) {
|
|
21009
|
+
return t.events.some((e) => e.event === event);
|
|
20821
21010
|
}
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
keys: idx.columns.map((c) => ({
|
|
20826
|
-
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
20827
|
-
expr: c.expr
|
|
20828
|
-
})),
|
|
20829
|
-
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
20830
|
-
nullsNotDistinct: idx.nullsNotDistinct,
|
|
20831
|
-
where: idx.where,
|
|
20832
|
-
isPrimary: false
|
|
20833
|
-
};
|
|
21011
|
+
var executor = null;
|
|
21012
|
+
function setTriggerExecutor(e) {
|
|
21013
|
+
executor = e;
|
|
20834
21014
|
}
|
|
20835
|
-
function
|
|
20836
|
-
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
if (
|
|
20840
|
-
|
|
20841
|
-
|
|
20842
|
-
|
|
20843
|
-
|
|
20844
|
-
|
|
20845
|
-
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
|
|
20849
|
-
|
|
20850
|
-
|
|
20851
|
-
} else {
|
|
20852
|
-
value = row[k.colIdx] ?? null;
|
|
20853
|
-
type = table.columns[k.colIdx].type.id;
|
|
21015
|
+
function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
|
|
21016
|
+
let row = newRow ?? oldRow;
|
|
21017
|
+
for (const t of table.triggers) {
|
|
21018
|
+
if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
|
|
21019
|
+
if (!executor) throw unsupported(`trigger "${t.name}" execution`);
|
|
21020
|
+
if (t.constraint && timing === "after") {
|
|
21021
|
+
if (t.when && !evalPredicate(env, triggerScope(table, newRow, oldRow), t.when)) continue;
|
|
21022
|
+
env.ctx.state.pendingTriggers.push({
|
|
21023
|
+
schema: table.schema,
|
|
21024
|
+
table: table.name,
|
|
21025
|
+
trigger: t.name,
|
|
21026
|
+
event,
|
|
21027
|
+
oldRow: oldRow?.slice() ?? null,
|
|
21028
|
+
newRow: newRow?.slice() ?? null
|
|
21029
|
+
});
|
|
21030
|
+
continue;
|
|
20854
21031
|
}
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
parts.push(datumKey(type, value));
|
|
21032
|
+
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
21033
|
+
if (timing === "before") {
|
|
21034
|
+
if (result === null) return { row: null };
|
|
21035
|
+
row = result;
|
|
20860
21036
|
}
|
|
20861
21037
|
}
|
|
20862
|
-
|
|
20863
|
-
return parts.join("");
|
|
21038
|
+
return { row };
|
|
20864
21039
|
}
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
21040
|
+
var keyOf = (schema, name) => JSON.stringify([schema, name]);
|
|
21041
|
+
function flushConstraintTriggers(state, all = false) {
|
|
21042
|
+
const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
|
|
21043
|
+
for (let i = 0; i < state.pendingTriggers.length; ) {
|
|
21044
|
+
const pending = state.pendingTriggers[i];
|
|
21045
|
+
const table = state.schemas.get(pending.schema)?.tables.get(pending.table);
|
|
21046
|
+
const trigger = table?.triggers.find((t) => t.name === pending.trigger);
|
|
21047
|
+
if (!table || !trigger) {
|
|
21048
|
+
state.pendingTriggers.splice(i, 1);
|
|
21049
|
+
continue;
|
|
21050
|
+
}
|
|
21051
|
+
const deferred = trigger.deferrable && (state.constraintModes.get(keyOf(table.schema, trigger.name)) ?? trigger.initiallyDeferred);
|
|
21052
|
+
if (!all && state.inTransaction && deferred) {
|
|
21053
|
+
i++;
|
|
21054
|
+
continue;
|
|
21055
|
+
}
|
|
21056
|
+
if (!executor) throw unsupported(`trigger "${trigger.name}" execution`);
|
|
21057
|
+
state.pendingTriggers.splice(i, 1);
|
|
21058
|
+
const changes = state.changes;
|
|
21059
|
+
try {
|
|
21060
|
+
executor(env, table, { ...trigger, when: null }, pending.event, pending.oldRow, pending.newRow);
|
|
21061
|
+
} catch (error) {
|
|
21062
|
+
state.pendingTriggers.splice(i, 0, pending);
|
|
21063
|
+
throw error;
|
|
21064
|
+
} finally {
|
|
21065
|
+
state.changes = changes;
|
|
21066
|
+
}
|
|
20870
21067
|
}
|
|
20871
21068
|
}
|
|
20872
|
-
function
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
20883
|
-
const nulls = values.filter((v) => v === null).length;
|
|
20884
|
-
if (con.match === "simple" && nulls > 0) continue;
|
|
20885
|
-
if (con.match === "full") {
|
|
20886
|
-
if (nulls === values.length) continue;
|
|
20887
|
-
if (nulls > 0) {
|
|
20888
|
-
throw pgError(
|
|
20889
|
-
"constraint_foreign_key",
|
|
20890
|
-
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
20891
|
-
"23503"
|
|
21069
|
+
function setConstraints(state, stmt) {
|
|
21070
|
+
if (!state.inTransaction) return;
|
|
21071
|
+
const matches2 = [];
|
|
21072
|
+
if (stmt.names) {
|
|
21073
|
+
for (const name of stmt.names) {
|
|
21074
|
+
const schemas = name.length > 1 ? [name[name.length - 2]] : state.effectiveSearchPath();
|
|
21075
|
+
let found = false;
|
|
21076
|
+
for (const schema of schemas) {
|
|
21077
|
+
const items = [...state.schemas.get(schema)?.tables.values() ?? []].flatMap(
|
|
21078
|
+
(table) => table.triggers.filter((t) => t.constraint && t.name === name[name.length - 1])
|
|
20892
21079
|
);
|
|
21080
|
+
if (items.length) {
|
|
21081
|
+
for (const trigger of items) matches2.push({ schema, trigger });
|
|
21082
|
+
found = true;
|
|
21083
|
+
break;
|
|
21084
|
+
}
|
|
20893
21085
|
}
|
|
21086
|
+
if (!found) throw pgError("undefined_object", `constraint "${name.join(".")}" does not exist`, "42704");
|
|
20894
21087
|
}
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
21088
|
+
} else {
|
|
21089
|
+
for (const schema of state.schemas.values())
|
|
21090
|
+
for (const table of schema.tables.values())
|
|
21091
|
+
for (const trigger of table.triggers)
|
|
21092
|
+
if (trigger.constraint && trigger.deferrable) matches2.push({ schema: schema.name, trigger });
|
|
21093
|
+
}
|
|
21094
|
+
for (const { trigger } of matches2)
|
|
21095
|
+
if (stmt.deferred && !trigger.deferrable)
|
|
21096
|
+
throw pgError("misuse", `constraint "${trigger.name}" is not deferrable`, "55000");
|
|
21097
|
+
const previous = new Map(state.constraintModes);
|
|
21098
|
+
for (const { schema, trigger } of matches2) state.constraintModes.set(keyOf(schema, trigger.name), stmt.deferred);
|
|
21099
|
+
try {
|
|
21100
|
+
if (!stmt.deferred) flushConstraintTriggers(state);
|
|
21101
|
+
} catch (error) {
|
|
21102
|
+
state.constraintModes = previous;
|
|
21103
|
+
throw error;
|
|
21104
|
+
}
|
|
21105
|
+
}
|
|
21106
|
+
|
|
21107
|
+
// src/transactions/manager.ts
|
|
21108
|
+
var TransactionManager = class {
|
|
21109
|
+
constructor(state) {
|
|
21110
|
+
this.state = state;
|
|
21111
|
+
}
|
|
21112
|
+
state;
|
|
21113
|
+
base = null;
|
|
21114
|
+
savepoints = [];
|
|
21115
|
+
freezeDepth = 0;
|
|
21116
|
+
get inTransaction() {
|
|
21117
|
+
return this.base !== null;
|
|
21118
|
+
}
|
|
21119
|
+
takeSnapshot() {
|
|
21120
|
+
this.state.freezeShared();
|
|
21121
|
+
this.freezeDepth++;
|
|
21122
|
+
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
21123
|
+
}
|
|
21124
|
+
restore(snap) {
|
|
21125
|
+
this.state.restoreFrom(snap.state);
|
|
21126
|
+
this.state.prng.setState(snap.prngState);
|
|
21127
|
+
}
|
|
21128
|
+
thawOnce() {
|
|
21129
|
+
if (this.freezeDepth > 0) {
|
|
21130
|
+
this.state.thawShared();
|
|
21131
|
+
this.freezeDepth--;
|
|
20898
21132
|
}
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
|
|
20904
|
-
return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
|
|
20905
|
-
}).join("");
|
|
20906
|
-
let found = false;
|
|
20907
|
-
for (let i = 0; i < refTable.rowCount(); i++) {
|
|
20908
|
-
const r = refTable.rowAt(i);
|
|
20909
|
-
const key = refIdxs.map((ri, j) => {
|
|
20910
|
-
const v = r[ri] ?? null;
|
|
20911
|
-
return v === null ? "\0N" : datumKey(keyTypes[j], v);
|
|
20912
|
-
}).join("");
|
|
20913
|
-
if (key === wanted) {
|
|
20914
|
-
found = true;
|
|
20915
|
-
break;
|
|
20916
|
-
}
|
|
21133
|
+
}
|
|
21134
|
+
begin() {
|
|
21135
|
+
if (this.base !== null) {
|
|
21136
|
+
return;
|
|
20917
21137
|
}
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
21138
|
+
this.base = this.takeSnapshot();
|
|
21139
|
+
this.state.inTransaction = true;
|
|
21140
|
+
assert(this.freezeDepth === 1, "BEGIN freeze depth");
|
|
21141
|
+
}
|
|
21142
|
+
commit() {
|
|
21143
|
+
try {
|
|
21144
|
+
flushConstraintTriggers(this.state, true);
|
|
21145
|
+
} catch (error) {
|
|
21146
|
+
this.rollback();
|
|
21147
|
+
throw error;
|
|
21148
|
+
}
|
|
21149
|
+
this.base = null;
|
|
21150
|
+
this.savepoints = [];
|
|
21151
|
+
this.state.inTransaction = false;
|
|
21152
|
+
this.state.localSettings.clear();
|
|
21153
|
+
this.state.pendingTriggers = [];
|
|
21154
|
+
this.state.constraintModes.clear();
|
|
21155
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
21156
|
+
}
|
|
21157
|
+
rollback() {
|
|
21158
|
+
if (this.base !== null) {
|
|
21159
|
+
this.restore(this.base);
|
|
20924
21160
|
}
|
|
21161
|
+
this.base = null;
|
|
21162
|
+
this.savepoints = [];
|
|
21163
|
+
this.state.inTransaction = false;
|
|
21164
|
+
this.state.localSettings.clear();
|
|
21165
|
+
this.state.pendingTriggers = [];
|
|
21166
|
+
this.state.constraintModes.clear();
|
|
21167
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
20925
21168
|
}
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
for (const schema of env.ctx.state.schemas.values()) {
|
|
20930
|
-
for (const t of schema.tables.values()) {
|
|
20931
|
-
for (const con of t.constraints) {
|
|
20932
|
-
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
20933
|
-
out.push({ table: t, constraint: con });
|
|
20934
|
-
}
|
|
20935
|
-
}
|
|
21169
|
+
savepoint(name) {
|
|
21170
|
+
if (this.base === null) {
|
|
21171
|
+
throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
|
|
20936
21172
|
}
|
|
21173
|
+
this.savepoints.push({ name, snap: this.takeSnapshot() });
|
|
20937
21174
|
}
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
20955
|
-
if (timing === "before") {
|
|
20956
|
-
if (result === null) return { row: null };
|
|
20957
|
-
row = result;
|
|
21175
|
+
releaseSavepoint(name) {
|
|
21176
|
+
const idx = this.findSavepoint(name);
|
|
21177
|
+
this.savepoints.splice(idx);
|
|
21178
|
+
}
|
|
21179
|
+
rollbackToSavepoint(name) {
|
|
21180
|
+
const idx = this.findSavepoint(name);
|
|
21181
|
+
const sp = this.savepoints[idx];
|
|
21182
|
+
this.restore(sp.snap);
|
|
21183
|
+
this.savepoints.splice(idx + 1);
|
|
21184
|
+
}
|
|
21185
|
+
findSavepoint(name) {
|
|
21186
|
+
if (this.base === null) {
|
|
21187
|
+
throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
|
|
21188
|
+
}
|
|
21189
|
+
for (let i = this.savepoints.length - 1; i >= 0; i--) {
|
|
21190
|
+
if (this.savepoints[i].name === name) return i;
|
|
20958
21191
|
}
|
|
21192
|
+
throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
|
|
20959
21193
|
}
|
|
20960
|
-
|
|
20961
|
-
|
|
21194
|
+
reset() {
|
|
21195
|
+
this.base = null;
|
|
21196
|
+
this.savepoints = [];
|
|
21197
|
+
this.state.inTransaction = false;
|
|
21198
|
+
this.freezeDepth = 0;
|
|
21199
|
+
}
|
|
21200
|
+
};
|
|
20962
21201
|
|
|
20963
21202
|
// src/executor/dml.ts
|
|
20964
21203
|
function withStatementRollback(env, fn) {
|
|
@@ -21648,6 +21887,7 @@ function executeSet(env, stmt) {
|
|
|
21648
21887
|
else target.delete(name);
|
|
21649
21888
|
return commandResult("SET", 0);
|
|
21650
21889
|
}
|
|
21890
|
+
validateStorageSetting(name, stmt.value);
|
|
21651
21891
|
target.set(name, stmt.value);
|
|
21652
21892
|
return commandResult("SET", 0);
|
|
21653
21893
|
}
|
|
@@ -21953,64 +22193,6 @@ function executeCopyFromData(env, stmt, data) {
|
|
|
21953
22193
|
};
|
|
21954
22194
|
}
|
|
21955
22195
|
|
|
21956
|
-
// src/expressions/context.ts
|
|
21957
|
-
var EngineCtx = class {
|
|
21958
|
-
state;
|
|
21959
|
-
/** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
|
|
21960
|
-
txNow;
|
|
21961
|
-
/** statement timestamp */
|
|
21962
|
-
stmtNow;
|
|
21963
|
-
constructor(state, txNow) {
|
|
21964
|
-
this.state = state;
|
|
21965
|
-
const nowMs = state.clock().getTime();
|
|
21966
|
-
const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
|
|
21967
|
-
this.txNow = txNow ?? nowMicros;
|
|
21968
|
-
this.stmtNow = nowMicros;
|
|
21969
|
-
}
|
|
21970
|
-
timezone() {
|
|
21971
|
-
return this.state.getSetting("timezone") ?? "UTC";
|
|
21972
|
-
}
|
|
21973
|
-
namespaceOid(name) {
|
|
21974
|
-
const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
|
|
21975
|
-
if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
|
|
21976
|
-
return oid;
|
|
21977
|
-
}
|
|
21978
|
-
namespaceName(oid) {
|
|
21979
|
-
const builtin = builtinNamespaceName(oid);
|
|
21980
|
-
if (builtin !== void 0) return builtin;
|
|
21981
|
-
for (const schema of this.state.schemas.values()) {
|
|
21982
|
-
if (schema.oid === oid) return schema.name;
|
|
21983
|
-
}
|
|
21984
|
-
return null;
|
|
21985
|
-
}
|
|
21986
|
-
zoneOffsetAt(utcMicros) {
|
|
21987
|
-
return zoneOffsetAtUtc(this.timezone(), utcMicros);
|
|
21988
|
-
}
|
|
21989
|
-
zoneOffsetForNaive(naiveMicros) {
|
|
21990
|
-
return zoneOffsetForNaive(this.timezone(), naiveMicros);
|
|
21991
|
-
}
|
|
21992
|
-
enumLabels(enumType) {
|
|
21993
|
-
if (!isEnumType(enumType)) return null;
|
|
21994
|
-
const e = this.state.findEnumByKey(enumType.slice(5));
|
|
21995
|
-
return e?.labels ?? null;
|
|
21996
|
-
}
|
|
21997
|
-
enumHasLabel(enumType, label) {
|
|
21998
|
-
const labels = this.enumLabels(enumType);
|
|
21999
|
-
return labels === null ? true : labels.includes(label);
|
|
22000
|
-
}
|
|
22001
|
-
enumOrder(enumType, label) {
|
|
22002
|
-
const labels = this.enumLabels(enumType);
|
|
22003
|
-
if (!labels) return 0;
|
|
22004
|
-
const idx = labels.indexOf(label);
|
|
22005
|
-
return idx === -1 ? labels.length : idx;
|
|
22006
|
-
}
|
|
22007
|
-
requireEnum(enumType) {
|
|
22008
|
-
const labels = this.enumLabels(enumType);
|
|
22009
|
-
if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
|
|
22010
|
-
return labels;
|
|
22011
|
-
}
|
|
22012
|
-
};
|
|
22013
|
-
|
|
22014
22196
|
// src/runtime/clock.ts
|
|
22015
22197
|
var DEFAULT_NOW = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
|
|
22016
22198
|
function fixedClock(instant = DEFAULT_NOW) {
|
|
@@ -22978,7 +23160,7 @@ function pgClass(ctx) {
|
|
|
22978
23160
|
v.materialized ? "m" : "v",
|
|
22979
23161
|
v.columns?.length ?? 0,
|
|
22980
23162
|
v.matRows?.length ?? 0,
|
|
22981
|
-
|
|
23163
|
+
v.materialized && [...schemaData.indexes.values()].some((i) => i.tableOid === v.oid),
|
|
22982
23164
|
!v.materialized || v.matRows !== null,
|
|
22983
23165
|
relPersistence(v)
|
|
22984
23166
|
);
|
|
@@ -23244,7 +23426,7 @@ function pgIndex(ctx) {
|
|
|
23244
23426
|
const valid = i.valid !== false;
|
|
23245
23427
|
rows.push([
|
|
23246
23428
|
i.oid ?? 0,
|
|
23247
|
-
table?.oid ?? 0,
|
|
23429
|
+
table?.oid ?? i.tableOid ?? 0,
|
|
23248
23430
|
indexAttributeCount(i),
|
|
23249
23431
|
i.columns.length,
|
|
23250
23432
|
i.unique,
|
|
@@ -23416,7 +23598,7 @@ function pgStatUserIndexes(ctx) {
|
|
|
23416
23598
|
for (const index of schema.indexes.values()) {
|
|
23417
23599
|
const table = schema.tables.get(index.table);
|
|
23418
23600
|
rows.push([
|
|
23419
|
-
table?.oid ?? 0,
|
|
23601
|
+
table?.oid ?? index.tableOid ?? 0,
|
|
23420
23602
|
index.oid ?? 0,
|
|
23421
23603
|
schema.name,
|
|
23422
23604
|
index.table,
|
|
@@ -24011,26 +24193,6 @@ function runTriggerBody(env, table, stmts, vars) {
|
|
|
24011
24193
|
}
|
|
24012
24194
|
}
|
|
24013
24195
|
}
|
|
24014
|
-
function triggerScope(table, newRow, oldRow) {
|
|
24015
|
-
const cols = [];
|
|
24016
|
-
const row = [];
|
|
24017
|
-
const rangeVars = /* @__PURE__ */ new Set();
|
|
24018
|
-
if (newRow) {
|
|
24019
|
-
rangeVars.add("new");
|
|
24020
|
-
for (let i = 0; i < table.columns.length; i++) {
|
|
24021
|
-
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
|
|
24022
|
-
row.push(newRow[i] ?? null);
|
|
24023
|
-
}
|
|
24024
|
-
}
|
|
24025
|
-
if (oldRow) {
|
|
24026
|
-
rangeVars.add("old");
|
|
24027
|
-
for (let i = 0; i < table.columns.length; i++) {
|
|
24028
|
-
cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
|
|
24029
|
-
row.push(oldRow[i] ?? null);
|
|
24030
|
-
}
|
|
24031
|
-
}
|
|
24032
|
-
return new RowScope(cols, row, null, rangeVars);
|
|
24033
|
-
}
|
|
24034
24196
|
function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
|
|
24035
24197
|
const state = env.ctx.state;
|
|
24036
24198
|
if (trigger.when !== null && !evalPredicate(env, triggerScope(table, newRow, oldRow), trigger.when)) {
|
|
@@ -25000,7 +25162,8 @@ function executeCreateTableAs(env, stmt) {
|
|
|
25000
25162
|
}
|
|
25001
25163
|
function executeCreateIndex(env, stmt) {
|
|
25002
25164
|
const state = env.ctx.state;
|
|
25003
|
-
const
|
|
25165
|
+
const view = state.findView(stmt.table);
|
|
25166
|
+
const table = state.findTable(stmt.table) ?? (view?.materialized ? materializedTable(view) : void 0);
|
|
25004
25167
|
if (!table) {
|
|
25005
25168
|
throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
|
|
25006
25169
|
}
|
|
@@ -25112,13 +25275,22 @@ function executeRefreshMatView(env, stmt) {
|
|
|
25112
25275
|
if (!found?.materialized) {
|
|
25113
25276
|
throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
|
|
25114
25277
|
}
|
|
25278
|
+
const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), found.query);
|
|
25279
|
+
const next = {
|
|
25280
|
+
...found,
|
|
25281
|
+
matColumns: rel3.columns.map((c, i) => ({
|
|
25282
|
+
name: found.columns?.[i] ?? c.name,
|
|
25283
|
+
type: c.type === UNKNOWN ? "text" : c.type
|
|
25284
|
+
})),
|
|
25285
|
+
matRows: stmt.withData ? rel3.rows : null
|
|
25286
|
+
};
|
|
25287
|
+
const table = materializedTable(next);
|
|
25288
|
+
for (const index of env.ctx.state.getSchema(found.schema).indexes.values()) {
|
|
25289
|
+
if (index.tableOid === found.oid && index.valid !== false) assertUniqueIndexBuildable(env, table, index);
|
|
25290
|
+
}
|
|
25115
25291
|
const view = env.ctx.state.ensureWritableView(found);
|
|
25116
|
-
|
|
25117
|
-
view.
|
|
25118
|
-
name: view.columns?.[i] ?? c.name,
|
|
25119
|
-
type: c.type === UNKNOWN ? "text" : c.type
|
|
25120
|
-
}));
|
|
25121
|
-
view.matRows = stmt.withData ? rel3.rows : null;
|
|
25292
|
+
view.matColumns = next.matColumns;
|
|
25293
|
+
view.matRows = next.matRows;
|
|
25122
25294
|
return commandResult("REFRESH MATERIALIZED VIEW", 0);
|
|
25123
25295
|
}
|
|
25124
25296
|
function executeCreateSchema(env, stmt) {
|
|
@@ -25260,6 +25432,8 @@ function executeCreateFunction(env, stmt) {
|
|
|
25260
25432
|
return commandResult("CREATE FUNCTION", 0);
|
|
25261
25433
|
}
|
|
25262
25434
|
function executeCreateTrigger(env, stmt) {
|
|
25435
|
+
if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
|
|
25436
|
+
throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
|
|
25263
25437
|
const state = env.ctx.state;
|
|
25264
25438
|
const found = state.findTable(stmt.table);
|
|
25265
25439
|
if (!found) {
|
|
@@ -25278,6 +25452,9 @@ function executeCreateTrigger(env, stmt) {
|
|
|
25278
25452
|
}
|
|
25279
25453
|
const meta = {
|
|
25280
25454
|
name: stmt.name,
|
|
25455
|
+
constraint: stmt.constraint,
|
|
25456
|
+
deferrable: stmt.deferrable,
|
|
25457
|
+
initiallyDeferred: stmt.initiallyDeferred,
|
|
25281
25458
|
timing: stmt.timing,
|
|
25282
25459
|
events: stmt.events,
|
|
25283
25460
|
forEachRow: stmt.forEachRow,
|
|
@@ -26046,7 +26223,16 @@ function alterTableIsSelfContained(stmt) {
|
|
|
26046
26223
|
return stmt.actions.length === 1 && action !== void 0 && SELF_CONTAINED_ALTER_ACTIONS.has(action.kind);
|
|
26047
26224
|
}
|
|
26048
26225
|
function executeStatement(env, stmt) {
|
|
26049
|
-
if (!RESHAPES_TABLES.has(stmt.type))
|
|
26226
|
+
if (!RESHAPES_TABLES.has(stmt.type)) {
|
|
26227
|
+
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))) {
|
|
26228
|
+
return withStatementRollback(env, () => {
|
|
26229
|
+
const result = dispatchStatement(env, stmt);
|
|
26230
|
+
flushConstraintTriggers(env.ctx.state);
|
|
26231
|
+
return result;
|
|
26232
|
+
});
|
|
26233
|
+
}
|
|
26234
|
+
return dispatchStatement(env, stmt);
|
|
26235
|
+
}
|
|
26050
26236
|
try {
|
|
26051
26237
|
return dispatchStatement(env, stmt);
|
|
26052
26238
|
} finally {
|
|
@@ -26105,6 +26291,9 @@ function dispatchStatement(env, stmt) {
|
|
|
26105
26291
|
return executeRefreshMatView(env, stmt);
|
|
26106
26292
|
case "transaction":
|
|
26107
26293
|
return executeTransaction(env, stmt);
|
|
26294
|
+
case "set_constraints":
|
|
26295
|
+
setConstraints(env.ctx.state, stmt);
|
|
26296
|
+
return commandResult("SET CONSTRAINTS", 0);
|
|
26108
26297
|
case "set":
|
|
26109
26298
|
return executeSet(env, stmt);
|
|
26110
26299
|
case "show":
|
|
@@ -28543,11 +28732,13 @@ var ScramServer = class {
|
|
|
28543
28732
|
var splitStatements = (script) => {
|
|
28544
28733
|
const out = [];
|
|
28545
28734
|
let start2 = 0;
|
|
28735
|
+
let hasSql = false;
|
|
28546
28736
|
let i = 0;
|
|
28547
28737
|
const n = script.length;
|
|
28548
28738
|
const push = (end) => {
|
|
28549
28739
|
const text = script.slice(start2, end).trim();
|
|
28550
|
-
if (
|
|
28740
|
+
if (hasSql) out.push(text);
|
|
28741
|
+
hasSql = false;
|
|
28551
28742
|
};
|
|
28552
28743
|
while (i < n) {
|
|
28553
28744
|
const c = script[i];
|
|
@@ -28569,9 +28760,11 @@ var splitStatements = (script) => {
|
|
|
28569
28760
|
i += 2;
|
|
28570
28761
|
} else i++;
|
|
28571
28762
|
}
|
|
28763
|
+
if (depth > 0) hasSql = true;
|
|
28572
28764
|
continue;
|
|
28573
28765
|
}
|
|
28574
28766
|
if (c === "'" || c === '"') {
|
|
28767
|
+
hasSql = true;
|
|
28575
28768
|
const escaped = c === "'" && /[eE]/.test(script[i - 1] ?? "") && !/[\w$]/.test(script[i - 2] ?? "");
|
|
28576
28769
|
i++;
|
|
28577
28770
|
while (i < n) {
|
|
@@ -28594,6 +28787,7 @@ var splitStatements = (script) => {
|
|
|
28594
28787
|
if (c === "$") {
|
|
28595
28788
|
const m = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(script.slice(i, i + 64));
|
|
28596
28789
|
if (m) {
|
|
28790
|
+
hasSql = true;
|
|
28597
28791
|
const tag = m[0];
|
|
28598
28792
|
const end = script.indexOf(tag, i + tag.length);
|
|
28599
28793
|
i = end === -1 ? n : end + tag.length;
|
|
@@ -28604,6 +28798,7 @@ var splitStatements = (script) => {
|
|
|
28604
28798
|
push(i);
|
|
28605
28799
|
start2 = i + 1;
|
|
28606
28800
|
}
|
|
28801
|
+
if (c !== ";" && !/\s/.test(c)) hasSql = true;
|
|
28607
28802
|
i++;
|
|
28608
28803
|
}
|
|
28609
28804
|
push(n);
|
|
@@ -29036,7 +29231,16 @@ var binaryValue = (type, text) => {
|
|
|
29036
29231
|
}
|
|
29037
29232
|
};
|
|
29038
29233
|
var binaryEncodable = (type) => TEXT_TYPES2.has(type) || ["bool", "int2", "int4", "oid", "int8", "float4", "float8", "bytea", "uuid"].includes(type);
|
|
29039
|
-
var
|
|
29234
|
+
var setsSessionConfig = (node) => {
|
|
29235
|
+
if (Array.isArray(node)) return node.some(setsSessionConfig);
|
|
29236
|
+
if (typeof node !== "object" || node === null) return false;
|
|
29237
|
+
const call = node;
|
|
29238
|
+
if (call.type === "func" && call.name?.at(-1) === "set_config") return true;
|
|
29239
|
+
return Object.values(node).some(setsSessionConfig);
|
|
29240
|
+
};
|
|
29241
|
+
var mutatesDatabase = (sql) => parse(sql).some(
|
|
29242
|
+
(stmt) => !["select", "show", "explain", "transaction", "no_op"].includes(stmt.type) || stmt.type === "select" && setsSessionConfig(stmt)
|
|
29243
|
+
);
|
|
29040
29244
|
var NEVER_WRITES = /* @__PURE__ */ new Set(["show", "set", "reset", "transaction", "no_op", "prepare", "deallocate"]);
|
|
29041
29245
|
var WRITE_NODES = /* @__PURE__ */ new Set(["insert", "update", "delete"]);
|
|
29042
29246
|
var containsWrite = (db, node) => {
|
|
@@ -29661,6 +29865,7 @@ var Connection = class {
|
|
|
29661
29865
|
}
|
|
29662
29866
|
return outcome;
|
|
29663
29867
|
} finally {
|
|
29868
|
+
workspace?.transactions.rollback();
|
|
29664
29869
|
workspace?.close();
|
|
29665
29870
|
}
|
|
29666
29871
|
}
|
|
@@ -29735,8 +29940,9 @@ var Connection = class {
|
|
|
29735
29940
|
});
|
|
29736
29941
|
workspace.state.settings = new Map(this.settings);
|
|
29737
29942
|
workspace.state.settingDefaults = this.settingDefaults;
|
|
29943
|
+
workspace.transactions.begin();
|
|
29738
29944
|
for (const logged of this.transaction?.statements ?? []) {
|
|
29739
|
-
|
|
29945
|
+
this.replay(workspace, logged);
|
|
29740
29946
|
}
|
|
29741
29947
|
return workspace;
|
|
29742
29948
|
}
|
|
@@ -30533,6 +30739,9 @@ var start = async (options) => {
|
|
|
30533
30739
|
});
|
|
30534
30740
|
};
|
|
30535
30741
|
|
|
30742
|
+
// src/wire/fleet.ts
|
|
30743
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
30744
|
+
|
|
30536
30745
|
// ../core/dist/clock.js
|
|
30537
30746
|
var createClock = (source = Date.now) => {
|
|
30538
30747
|
let offsetMs = 0;
|
|
@@ -30574,6 +30783,310 @@ var createClock = (source = Date.now) => {
|
|
|
30574
30783
|
};
|
|
30575
30784
|
};
|
|
30576
30785
|
|
|
30786
|
+
// ../core/dist/controls.js
|
|
30787
|
+
var ControlError = class extends RangeError {
|
|
30788
|
+
constructor(message2) {
|
|
30789
|
+
super(message2);
|
|
30790
|
+
this.name = "ControlError";
|
|
30791
|
+
}
|
|
30792
|
+
};
|
|
30793
|
+
var UNITS = {
|
|
30794
|
+
ms: 1,
|
|
30795
|
+
s: 1e3,
|
|
30796
|
+
m: 6e4,
|
|
30797
|
+
h: 36e5,
|
|
30798
|
+
d: 864e5
|
|
30799
|
+
};
|
|
30800
|
+
var parseDuration = (value) => {
|
|
30801
|
+
if (typeof value === "number")
|
|
30802
|
+
return Number.isFinite(value) ? value : void 0;
|
|
30803
|
+
if (typeof value !== "string")
|
|
30804
|
+
return void 0;
|
|
30805
|
+
const match = /^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/.exec(value.trim());
|
|
30806
|
+
if (!match)
|
|
30807
|
+
return void 0;
|
|
30808
|
+
return Number(match[1]) * UNITS[match[2] ?? "ms"];
|
|
30809
|
+
};
|
|
30810
|
+
var parseInstant = (value) => {
|
|
30811
|
+
if (typeof value === "number")
|
|
30812
|
+
return Number.isFinite(value) ? value : void 0;
|
|
30813
|
+
if (typeof value !== "string")
|
|
30814
|
+
return void 0;
|
|
30815
|
+
const text = value.trim();
|
|
30816
|
+
if (/^-?\d+$/.test(text))
|
|
30817
|
+
return Number(text);
|
|
30818
|
+
const parsed = Date.parse(text);
|
|
30819
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
30820
|
+
};
|
|
30821
|
+
var durationMs = (value, label, negative = false) => {
|
|
30822
|
+
const parsed = parseDuration(value);
|
|
30823
|
+
if (parsed === void 0)
|
|
30824
|
+
throw new ControlError(`${label}: expected milliseconds or a duration like "250ms" or "15m"`);
|
|
30825
|
+
if (!negative && parsed < 0)
|
|
30826
|
+
throw new ControlError(`${label}: must not be negative`);
|
|
30827
|
+
return parsed;
|
|
30828
|
+
};
|
|
30829
|
+
var instantMs = (value, label) => {
|
|
30830
|
+
const parsed = parseInstant(value);
|
|
30831
|
+
if (parsed === void 0)
|
|
30832
|
+
throw new ControlError(`${label}: expected epoch ms or ISO-8601`);
|
|
30833
|
+
return parsed;
|
|
30834
|
+
};
|
|
30835
|
+
var normalizeSeed = (seed, label = "seed") => {
|
|
30836
|
+
if (typeof seed === "number" && Number.isFinite(seed))
|
|
30837
|
+
return seed;
|
|
30838
|
+
if (typeof seed === "string" && seed !== "")
|
|
30839
|
+
return /^\d+$/.test(seed) ? Number(seed) : seed;
|
|
30840
|
+
throw new ControlError(`${label}: expected a number or a non-empty string`);
|
|
30841
|
+
};
|
|
30842
|
+
var SNAPSHOT_FORMAT = "mockingbird.snapshot/v1";
|
|
30843
|
+
var NAMESPACE_KEYS = /* @__PURE__ */ new Set(["seed", "clock", "virtualTime", "latency", "faults", "snapshot"]);
|
|
30844
|
+
var MOCK_KEYS = /* @__PURE__ */ new Set([...NAMESPACE_KEYS, "credentials", "namespaces"]);
|
|
30845
|
+
var REPORTED_KEYS = /* @__PURE__ */ new Set(["namespace", "snapshots"]);
|
|
30846
|
+
var REPORTED_CLOCK_KEYS = /* @__PURE__ */ new Set(["iso", "offsetMs"]);
|
|
30847
|
+
var KEY_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
30848
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30849
|
+
var unknownKeys = (value, known, label, reported) => {
|
|
30850
|
+
for (const key of Object.keys(value)) {
|
|
30851
|
+
if (!known.has(key) && !reported?.has(key))
|
|
30852
|
+
throw new ControlError(`${label}: unknown control ${JSON.stringify(key)}; expected ${[...known].join(", ")}`);
|
|
30853
|
+
}
|
|
30854
|
+
};
|
|
30855
|
+
var latencyRule = (value, label) => {
|
|
30856
|
+
if (!isRecord2(value))
|
|
30857
|
+
return { delayMs: durationMs(value, label), jitterMs: 0 };
|
|
30858
|
+
unknownKeys(value, /* @__PURE__ */ new Set(["delay", "jitter", "delayMs", "jitterMs", "operations"]), label);
|
|
30859
|
+
return {
|
|
30860
|
+
delayMs: durationMs(value.delay ?? value.delayMs ?? 0, `${label}.delay`),
|
|
30861
|
+
jitterMs: durationMs(value.jitter ?? value.jitterMs ?? 0, `${label}.jitter`)
|
|
30862
|
+
};
|
|
30863
|
+
};
|
|
30864
|
+
var normalizeLatency = (value, label = "latency") => {
|
|
30865
|
+
const base = latencyRule(value, label);
|
|
30866
|
+
const operations = {};
|
|
30867
|
+
if (isRecord2(value) && value.operations !== void 0) {
|
|
30868
|
+
if (!isRecord2(value.operations))
|
|
30869
|
+
throw new ControlError(`${label}.operations: expected an object keyed by operationId`);
|
|
30870
|
+
for (const [operationId, rule] of Object.entries(value.operations)) {
|
|
30871
|
+
if (isRecord2(rule) && rule.operations !== void 0)
|
|
30872
|
+
throw new ControlError(`${label}.operations.${operationId}: cannot nest operations`);
|
|
30873
|
+
operations[operationId] = latencyRule(rule, `${label}.operations.${operationId}`);
|
|
30874
|
+
}
|
|
30875
|
+
}
|
|
30876
|
+
return { ...base, operations };
|
|
30877
|
+
};
|
|
30878
|
+
var normalizeClock = (value, label = "clock") => {
|
|
30879
|
+
if (!isRecord2(value))
|
|
30880
|
+
throw new ControlError(`${label}: expected an object`);
|
|
30881
|
+
unknownKeys(value, /* @__PURE__ */ new Set(["now", "set", "frozen", "freeze", "autoAdvance", "autoAdvanceMs"]), label, REPORTED_CLOCK_KEYS);
|
|
30882
|
+
const plan = {};
|
|
30883
|
+
const now = value.now ?? value.set;
|
|
30884
|
+
if (now !== void 0)
|
|
30885
|
+
plan.now = instantMs(now, `${label}.now`);
|
|
30886
|
+
const frozen = value.frozen ?? value.freeze;
|
|
30887
|
+
if (frozen !== void 0) {
|
|
30888
|
+
if (typeof frozen !== "boolean")
|
|
30889
|
+
throw new ControlError(`${label}.frozen: expected a boolean`);
|
|
30890
|
+
plan.frozen = frozen;
|
|
30891
|
+
}
|
|
30892
|
+
const auto = value.autoAdvance ?? value.autoAdvanceMs;
|
|
30893
|
+
if (auto !== void 0)
|
|
30894
|
+
plan.autoAdvanceMs = durationMs(auto, `${label}.autoAdvance`);
|
|
30895
|
+
return plan;
|
|
30896
|
+
};
|
|
30897
|
+
var assertSnapshotDocument = (value, label = "snapshot") => {
|
|
30898
|
+
if (!isRecord2(value))
|
|
30899
|
+
throw new ControlError(`${label}: expected a snapshot document`);
|
|
30900
|
+
if (value.format !== SNAPSHOT_FORMAT)
|
|
30901
|
+
throw new ControlError(`${label}.format: expected ${JSON.stringify(SNAPSHOT_FORMAT)}`);
|
|
30902
|
+
if (!Array.isArray(value.records))
|
|
30903
|
+
throw new ControlError(`${label}.records: expected a list`);
|
|
30904
|
+
value.records.forEach((row, index) => {
|
|
30905
|
+
if (!isRecord2(row) || typeof row.collection !== "string" || typeof row.id !== "string" || row.value === void 0 && typeof row.raw !== "string" || row.seq !== void 0 && !Number.isSafeInteger(row.seq))
|
|
30906
|
+
throw new ControlError(`${label}.records[${index}]: expected {collection, id, value, seq?}`);
|
|
30907
|
+
});
|
|
30908
|
+
if (value.sequences !== void 0) {
|
|
30909
|
+
if (!Array.isArray(value.sequences))
|
|
30910
|
+
throw new ControlError(`${label}.sequences: expected a list`);
|
|
30911
|
+
value.sequences.forEach((row, index) => {
|
|
30912
|
+
if (!isRecord2(row) || typeof row.name !== "string" || typeof row.kind !== "string" || !Number.isSafeInteger(row.value))
|
|
30913
|
+
throw new ControlError(`${label}.sequences[${index}]: expected {name, kind, value}`);
|
|
30914
|
+
});
|
|
30915
|
+
}
|
|
30916
|
+
if (value.clock !== void 0) {
|
|
30917
|
+
if (!isRecord2(value.clock) || typeof value.clock.now !== "number" || !Number.isFinite(value.clock.now) || typeof value.clock.frozen !== "boolean")
|
|
30918
|
+
throw new ControlError(`${label}.clock: expected {now, frozen}`);
|
|
30919
|
+
}
|
|
30920
|
+
return value;
|
|
30921
|
+
};
|
|
30922
|
+
var normalizeControls = (value, label = "controls") => {
|
|
30923
|
+
if (!isRecord2(value))
|
|
30924
|
+
throw new ControlError(`${label}: expected an object`);
|
|
30925
|
+
unknownKeys(value, NAMESPACE_KEYS, label, REPORTED_KEYS);
|
|
30926
|
+
const plan = {};
|
|
30927
|
+
if (value.seed !== void 0)
|
|
30928
|
+
plan.seed = normalizeSeed(value.seed, `${label}.seed`);
|
|
30929
|
+
if (value.clock !== void 0)
|
|
30930
|
+
plan.clock = normalizeClock(value.clock, `${label}.clock`);
|
|
30931
|
+
if (value.virtualTime !== void 0) {
|
|
30932
|
+
if (typeof value.virtualTime !== "boolean")
|
|
30933
|
+
throw new ControlError(`${label}.virtualTime: expected a boolean`);
|
|
30934
|
+
plan.virtualTime = value.virtualTime;
|
|
30935
|
+
}
|
|
30936
|
+
if (value.latency !== void 0)
|
|
30937
|
+
plan.latency = normalizeLatency(value.latency, `${label}.latency`);
|
|
30938
|
+
if (value.faults !== void 0) {
|
|
30939
|
+
if (!Array.isArray(value.faults) || !value.faults.every(isRecord2))
|
|
30940
|
+
throw new ControlError(`${label}.faults: expected a list of fault rules`);
|
|
30941
|
+
plan.faults = value.faults;
|
|
30942
|
+
}
|
|
30943
|
+
if (value.snapshot !== void 0)
|
|
30944
|
+
plan.snapshot = assertSnapshotDocument(value.snapshot, `${label}.snapshot`);
|
|
30945
|
+
return plan;
|
|
30946
|
+
};
|
|
30947
|
+
var normalizeBootControls = (value, label = "controls") => {
|
|
30948
|
+
if (value === void 0)
|
|
30949
|
+
return { shared: {}, namespaces: {}, credentials: {} };
|
|
30950
|
+
if (!isRecord2(value))
|
|
30951
|
+
throw new ControlError(`${label}: expected an object`);
|
|
30952
|
+
unknownKeys(value, MOCK_KEYS, label);
|
|
30953
|
+
const { credentials, namespaces, ...shared } = value;
|
|
30954
|
+
const plan = {
|
|
30955
|
+
shared: normalizeControls(shared, label),
|
|
30956
|
+
namespaces: {},
|
|
30957
|
+
credentials: {}
|
|
30958
|
+
};
|
|
30959
|
+
if (namespaces !== void 0) {
|
|
30960
|
+
if (!isRecord2(namespaces))
|
|
30961
|
+
throw new ControlError(`${label}.namespaces: expected an object`);
|
|
30962
|
+
for (const [name, each] of Object.entries(namespaces)) {
|
|
30963
|
+
if (!KEY_PATTERN.test(name))
|
|
30964
|
+
throw new ControlError(`${label}.namespaces: bad namespace ${JSON.stringify(name)}`);
|
|
30965
|
+
plan.namespaces[name] = normalizeControls(each, `${label}.namespaces.${name}`);
|
|
30966
|
+
}
|
|
30967
|
+
}
|
|
30968
|
+
if (credentials !== void 0) {
|
|
30969
|
+
if (!isRecord2(credentials))
|
|
30970
|
+
throw new ControlError(`${label}.credentials: expected an object`);
|
|
30971
|
+
for (const [credential, namespace] of Object.entries(credentials)) {
|
|
30972
|
+
if (typeof namespace !== "string" || !KEY_PATTERN.test(namespace))
|
|
30973
|
+
throw new ControlError(`${label}.credentials: bad namespace for a credential`);
|
|
30974
|
+
plan.credentials[credential] = namespace;
|
|
30975
|
+
}
|
|
30976
|
+
}
|
|
30977
|
+
return plan;
|
|
30978
|
+
};
|
|
30979
|
+
var CONTROL_HEADERS = {
|
|
30980
|
+
namespace: "x-mockingbird-namespace",
|
|
30981
|
+
adminKey: "x-mockingbird-admin-key",
|
|
30982
|
+
branch: "x-mockingbird-branch",
|
|
30983
|
+
at: "x-mockingbird-at",
|
|
30984
|
+
reset: "x-mockingbird-reset",
|
|
30985
|
+
restore: "x-mockingbird-restore",
|
|
30986
|
+
seed: "x-mockingbird-seed",
|
|
30987
|
+
time: "x-mockingbird-time",
|
|
30988
|
+
advance: "x-mockingbird-advance",
|
|
30989
|
+
virtualTime: "x-mockingbird-virtual-time",
|
|
30990
|
+
fault: "x-mockingbird-fault",
|
|
30991
|
+
delay: "x-mockingbird-delay",
|
|
30992
|
+
timeout: "x-mockingbird-timeout",
|
|
30993
|
+
snapshot: "x-mockingbird-snapshot"
|
|
30994
|
+
};
|
|
30995
|
+
var CONTROL_HEADER_REFERENCE = [
|
|
30996
|
+
{
|
|
30997
|
+
header: CONTROL_HEADERS.namespace,
|
|
30998
|
+
value: "<key>",
|
|
30999
|
+
scope: "select",
|
|
31000
|
+
guarded: false,
|
|
31001
|
+
summary: "Isolation key. Each key has its own records, clock, random streams, faults, latency, journal and snapshots."
|
|
31002
|
+
},
|
|
31003
|
+
{
|
|
31004
|
+
header: CONTROL_HEADERS.branch,
|
|
31005
|
+
value: "<name>",
|
|
31006
|
+
scope: "select",
|
|
31007
|
+
guarded: false,
|
|
31008
|
+
summary: "Read and write a named copy-on-write branch of the namespace's history."
|
|
31009
|
+
},
|
|
31010
|
+
{
|
|
31011
|
+
header: CONTROL_HEADERS.at,
|
|
31012
|
+
value: "<checkpoint>",
|
|
31013
|
+
scope: "select",
|
|
31014
|
+
guarded: false,
|
|
31015
|
+
summary: "Read the namespace as it was at a checkpoint. With a branch, start the branch there."
|
|
31016
|
+
},
|
|
31017
|
+
{
|
|
31018
|
+
header: CONTROL_HEADERS.reset,
|
|
31019
|
+
value: "true",
|
|
31020
|
+
scope: "namespace",
|
|
31021
|
+
guarded: true,
|
|
31022
|
+
summary: "Reset the namespace to its boot state before the request is handled."
|
|
31023
|
+
},
|
|
31024
|
+
{
|
|
31025
|
+
header: CONTROL_HEADERS.restore,
|
|
31026
|
+
value: "<snapshot>",
|
|
31027
|
+
scope: "namespace",
|
|
31028
|
+
guarded: true,
|
|
31029
|
+
summary: "Restore a snapshot before the request is handled."
|
|
31030
|
+
},
|
|
31031
|
+
{
|
|
31032
|
+
header: CONTROL_HEADERS.seed,
|
|
31033
|
+
value: "<seed>",
|
|
31034
|
+
scope: "namespace",
|
|
31035
|
+
guarded: true,
|
|
31036
|
+
summary: "Restart the namespace's random streams from this seed."
|
|
31037
|
+
},
|
|
31038
|
+
{
|
|
31039
|
+
header: CONTROL_HEADERS.time,
|
|
31040
|
+
value: "<epoch ms | ISO-8601>",
|
|
31041
|
+
scope: "namespace",
|
|
31042
|
+
guarded: true,
|
|
31043
|
+
summary: "Freeze the namespace clock at this instant."
|
|
31044
|
+
},
|
|
31045
|
+
{
|
|
31046
|
+
header: CONTROL_HEADERS.advance,
|
|
31047
|
+
value: "<duration>",
|
|
31048
|
+
scope: "namespace",
|
|
31049
|
+
guarded: true,
|
|
31050
|
+
summary: "Move the namespace clock by this much, forward or (negative) back."
|
|
31051
|
+
},
|
|
31052
|
+
{
|
|
31053
|
+
header: CONTROL_HEADERS.virtualTime,
|
|
31054
|
+
value: "true | false",
|
|
31055
|
+
scope: "namespace",
|
|
31056
|
+
guarded: true,
|
|
31057
|
+
summary: "Spend injected waits on the mock clock (true) or the wall clock (false)."
|
|
31058
|
+
},
|
|
31059
|
+
{
|
|
31060
|
+
header: CONTROL_HEADERS.delay,
|
|
31061
|
+
value: "<duration>",
|
|
31062
|
+
scope: "request",
|
|
31063
|
+
guarded: true,
|
|
31064
|
+
summary: "Hold this response back this long, on top of the namespace's latency."
|
|
31065
|
+
},
|
|
31066
|
+
{
|
|
31067
|
+
header: CONTROL_HEADERS.timeout,
|
|
31068
|
+
value: "<duration>",
|
|
31069
|
+
scope: "request",
|
|
31070
|
+
guarded: true,
|
|
31071
|
+
summary: "Never answer this request: hold it this long, then drop the connection."
|
|
31072
|
+
},
|
|
31073
|
+
{
|
|
31074
|
+
header: CONTROL_HEADERS.fault,
|
|
31075
|
+
value: '<status> | drop | <preset> | key=value; \u2026 | {"status": 503, \u2026}',
|
|
31076
|
+
scope: "request",
|
|
31077
|
+
guarded: true,
|
|
31078
|
+
summary: "Fault this request only. Stored fault rules are not consulted when it answers."
|
|
31079
|
+
},
|
|
31080
|
+
{
|
|
31081
|
+
header: CONTROL_HEADERS.snapshot,
|
|
31082
|
+
value: "<name>",
|
|
31083
|
+
scope: "namespace",
|
|
31084
|
+
guarded: true,
|
|
31085
|
+
summary: "Snapshot the namespace after the request is handled. Echoed on the response."
|
|
31086
|
+
}
|
|
31087
|
+
];
|
|
31088
|
+
var GUARDED = CONTROL_HEADER_REFERENCE.filter((each) => each.guarded).map((each) => each.header);
|
|
31089
|
+
|
|
30577
31090
|
// src/wire/probe.ts
|
|
30578
31091
|
import { createHash as createHash3, createHmac as createHmac2, pbkdf2Sync as pbkdf2Sync2, randomBytes as randomBytes2 } from "node:crypto";
|
|
30579
31092
|
import { createConnection } from "node:net";
|
|
@@ -30678,26 +31191,38 @@ function probe(connectionString) {
|
|
|
30678
31191
|
async function startProtocol(entry) {
|
|
30679
31192
|
const namespaces = /* @__PURE__ */ new Map();
|
|
30680
31193
|
const selectors = { default: entry.database ?? "postgres", ...entry.namespaces };
|
|
31194
|
+
if (entry.seed !== void 0 && (!String(entry.seed).trim() || !Number.isFinite(Number(entry.seed))))
|
|
31195
|
+
throw new Error("Postgres seed must be a finite number");
|
|
31196
|
+
let admission;
|
|
31197
|
+
const boot = normalizeBootControls(entry.adminControls);
|
|
31198
|
+
const create = async (name, databaseName, port = 0) => {
|
|
31199
|
+
if (!/^[A-Za-z0-9_.-]{1,64}$/.test(name) || typeof databaseName !== "string" || !databaseName.length)
|
|
31200
|
+
throw new Error("invalid Postgres namespace/database name");
|
|
31201
|
+
const clock = createClock();
|
|
31202
|
+
const control = { ...boot.shared.clock, ...boot.namespaces[name]?.clock };
|
|
31203
|
+
if (control.frozen) clock.freeze();
|
|
31204
|
+
if (control.now !== void 0) clock.set(control.now);
|
|
31205
|
+
const database = new Database({
|
|
31206
|
+
now: () => new Date(clock.now()),
|
|
31207
|
+
...entry.seed !== void 0 ? { seed: Number(entry.seed) } : {}
|
|
31208
|
+
});
|
|
31209
|
+
const server = await serve({
|
|
31210
|
+
database,
|
|
31211
|
+
databaseName,
|
|
31212
|
+
allowDatabaseSelection: true,
|
|
31213
|
+
host: entry.host ?? "127.0.0.1",
|
|
31214
|
+
port,
|
|
31215
|
+
...entry.password !== void 0 ? { password: entry.password } : {},
|
|
31216
|
+
...entry.user !== void 0 ? { user: entry.user } : {}
|
|
31217
|
+
});
|
|
31218
|
+
server.server.prependListener("connection", (socket) => {
|
|
31219
|
+
if (admission?.(name) === false) socket.destroy();
|
|
31220
|
+
});
|
|
31221
|
+
namespaces.set(name, { server, clock });
|
|
31222
|
+
};
|
|
30681
31223
|
try {
|
|
30682
|
-
for (const [name, databaseName] of Object.entries(selectors))
|
|
30683
|
-
|
|
30684
|
-
throw new Error("invalid Postgres namespace/database name");
|
|
30685
|
-
const clock = createClock();
|
|
30686
|
-
const database = new Database({
|
|
30687
|
-
now: () => new Date(clock.now()),
|
|
30688
|
-
...entry.seed !== void 0 ? { seed: Number(entry.seed) } : {}
|
|
30689
|
-
});
|
|
30690
|
-
const server = await serve({
|
|
30691
|
-
database,
|
|
30692
|
-
databaseName,
|
|
30693
|
-
allowDatabaseSelection: true,
|
|
30694
|
-
host: entry.host ?? "127.0.0.1",
|
|
30695
|
-
port: name === "default" ? entry.port ?? 0 : 0,
|
|
30696
|
-
...entry.password !== void 0 ? { password: entry.password } : {},
|
|
30697
|
-
...entry.user !== void 0 ? { user: entry.user } : {}
|
|
30698
|
-
});
|
|
30699
|
-
namespaces.set(name, { server, clock });
|
|
30700
|
-
}
|
|
31224
|
+
for (const [name, databaseName] of Object.entries(selectors))
|
|
31225
|
+
await create(name, databaseName, name === "default" ? entry.port ?? 0 : 0);
|
|
30701
31226
|
} catch (error) {
|
|
30702
31227
|
await Promise.allSettled([...namespaces.values()].map(({ server }) => server.close()));
|
|
30703
31228
|
throw error;
|
|
@@ -30719,17 +31244,143 @@ async function startProtocol(entry) {
|
|
|
30719
31244
|
protocol: "postgres",
|
|
30720
31245
|
url: publicUrl("default"),
|
|
30721
31246
|
connection: get("default").server.connectionString,
|
|
30722
|
-
connections
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
31247
|
+
get connections() {
|
|
31248
|
+
return Object.fromEntries([...namespaces].map(([name, { server }]) => [name, server.connectionString]));
|
|
31249
|
+
},
|
|
31250
|
+
get namespaces() {
|
|
31251
|
+
return {
|
|
31252
|
+
mechanism: "endpoint",
|
|
31253
|
+
endpoints: Object.fromEntries([...namespaces.keys()].map((name) => [name, publicUrl(name)]))
|
|
31254
|
+
};
|
|
31255
|
+
},
|
|
31256
|
+
async createNamespace(name) {
|
|
31257
|
+
if (namespaces.has(name)) throw new Error("namespace already exists");
|
|
31258
|
+
await create(name, entry.database ?? "postgres");
|
|
31259
|
+
origins.set(name, get(name).server.snapshotAll());
|
|
31260
|
+
},
|
|
31261
|
+
async deleteNamespace(name) {
|
|
31262
|
+
const { server } = get(name);
|
|
31263
|
+
if (server.connections) throw new Error("Postgres namespace has open sessions");
|
|
31264
|
+
await server.close();
|
|
31265
|
+
for (const database of server.databaseNames()) server.getDatabase(database).close();
|
|
31266
|
+
namespaces.delete(name);
|
|
31267
|
+
origins.delete(name);
|
|
31268
|
+
},
|
|
31269
|
+
async exportCheckpoint(namespace, handle) {
|
|
31270
|
+
const point = typeof handle === "object" && handle !== null ? snapshots.get(handle) : void 0;
|
|
31271
|
+
if (!point || point.namespace !== namespace) throw new Error("invalid Postgres checkpoint");
|
|
31272
|
+
return {
|
|
31273
|
+
format: "mockingbird.postgres/v1",
|
|
31274
|
+
inventory: Object.fromEntries(
|
|
31275
|
+
[...point.databases].map(([name, snapshot]) => {
|
|
31276
|
+
const database = snapshot.open();
|
|
31277
|
+
try {
|
|
31278
|
+
const tables = database.prepare("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'public'").all();
|
|
31279
|
+
return [
|
|
31280
|
+
name,
|
|
31281
|
+
Object.fromEntries(
|
|
31282
|
+
tables.map((table) => {
|
|
31283
|
+
const identifier2 = table.table_name.replaceAll('"', '""');
|
|
31284
|
+
const rows = database.prepare(`SELECT * FROM public."${identifier2}"`).all();
|
|
31285
|
+
return [
|
|
31286
|
+
table.table_name,
|
|
31287
|
+
{
|
|
31288
|
+
count: rows.length,
|
|
31289
|
+
records: Object.fromEntries(
|
|
31290
|
+
rows.map((row, index) => [
|
|
31291
|
+
String(row.id ?? index),
|
|
31292
|
+
createHash4("sha256").update(
|
|
31293
|
+
JSON.stringify(
|
|
31294
|
+
row,
|
|
31295
|
+
(_key, value) => typeof value === "bigint" ? String(value) : value
|
|
31296
|
+
)
|
|
31297
|
+
).digest("hex")
|
|
31298
|
+
])
|
|
31299
|
+
)
|
|
31300
|
+
}
|
|
31301
|
+
];
|
|
31302
|
+
})
|
|
31303
|
+
)
|
|
31304
|
+
];
|
|
31305
|
+
} finally {
|
|
31306
|
+
database.close();
|
|
31307
|
+
}
|
|
31308
|
+
})
|
|
31309
|
+
),
|
|
31310
|
+
databases: [...point.databases].map(([name, snapshot]) => ({
|
|
31311
|
+
name,
|
|
31312
|
+
bytes: Buffer.from(snapshot.encode()).toString("base64")
|
|
31313
|
+
})),
|
|
31314
|
+
clock: point.clock,
|
|
31315
|
+
faults: point.faults
|
|
31316
|
+
};
|
|
31317
|
+
},
|
|
31318
|
+
async importCheckpoint(namespace, document) {
|
|
31319
|
+
const doc = document;
|
|
31320
|
+
if (doc?.format !== "mockingbird.postgres/v1" || !Array.isArray(doc.databases) || !doc.databases.length || !Number.isFinite(doc.clock?.now) || typeof doc.clock?.frozen !== "boolean")
|
|
31321
|
+
throw new Error("invalid Postgres archive");
|
|
31322
|
+
const databases = /* @__PURE__ */ new Map();
|
|
31323
|
+
for (const row of doc.databases) {
|
|
31324
|
+
if (typeof row.name !== "string" || databases.has(row.name) || typeof row.bytes !== "string" || !/^[A-Za-z0-9+/]*={0,2}$/.test(row.bytes))
|
|
31325
|
+
throw new Error("invalid Postgres archive database");
|
|
31326
|
+
databases.set(row.name, Snapshot.decode(Buffer.from(row.bytes, "base64")));
|
|
31327
|
+
}
|
|
31328
|
+
const handle = {};
|
|
31329
|
+
snapshots.set(handle, { namespace, databases, clock: doc.clock, faults: doc.faults ?? {} });
|
|
31330
|
+
return handle;
|
|
31331
|
+
},
|
|
31332
|
+
async inspect(namespace, { offset, limit, collection }) {
|
|
31333
|
+
const { server } = get(namespace);
|
|
31334
|
+
const database = server.getDatabase(server.databaseNames()[0]);
|
|
31335
|
+
const tables = database.prepare("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'public'").all();
|
|
31336
|
+
if (!collection) return { tables };
|
|
31337
|
+
const table = collection.replaceAll('"', '""');
|
|
31338
|
+
return {
|
|
31339
|
+
records: database.prepare(`SELECT * FROM public."${table}" LIMIT ${limit} OFFSET ${offset}`).all(),
|
|
31340
|
+
offset
|
|
31341
|
+
};
|
|
31342
|
+
},
|
|
31343
|
+
async initializationAccess() {
|
|
31344
|
+
const privateServers = [];
|
|
31345
|
+
const connections = {};
|
|
31346
|
+
try {
|
|
31347
|
+
for (const [name, { server }] of namespaces) {
|
|
31348
|
+
const databaseName = server.databaseNames()[0];
|
|
31349
|
+
const seedServer = await serve({
|
|
31350
|
+
database: server.getDatabase(databaseName),
|
|
31351
|
+
databaseName,
|
|
31352
|
+
port: 0,
|
|
31353
|
+
host: "127.0.0.1",
|
|
31354
|
+
...entry.user ? { user: entry.user } : {},
|
|
31355
|
+
...entry.password ? { password: entry.password } : {}
|
|
31356
|
+
});
|
|
31357
|
+
privateServers.push(seedServer);
|
|
31358
|
+
connections[name] = seedServer.connectionString;
|
|
31359
|
+
}
|
|
31360
|
+
} catch (error) {
|
|
31361
|
+
await Promise.allSettled(privateServers.map((s) => s.close()));
|
|
31362
|
+
throw error;
|
|
31363
|
+
}
|
|
31364
|
+
return {
|
|
31365
|
+
connections,
|
|
31366
|
+
close: async () => {
|
|
31367
|
+
await Promise.all(privateServers.map((s) => s.close()));
|
|
31368
|
+
}
|
|
31369
|
+
};
|
|
31370
|
+
},
|
|
31371
|
+
setAdmission(allow) {
|
|
31372
|
+
admission = allow;
|
|
30726
31373
|
},
|
|
30727
31374
|
async ready() {
|
|
31375
|
+
if (admission) return [...namespaces.values()].every(({ server }) => server.server.listening);
|
|
30728
31376
|
const results = await Promise.all(
|
|
30729
31377
|
[...namespaces.values()].map(
|
|
30730
31378
|
({ server }) => server.server.listening ? probe(server.connectionString) : false
|
|
30731
31379
|
)
|
|
30732
31380
|
);
|
|
31381
|
+
const deadline = Date.now() + 2e3;
|
|
31382
|
+
while ([...namespaces.values()].some(({ server }) => server.connections > 0) && Date.now() < deadline)
|
|
31383
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
30733
31384
|
return results.every(Boolean);
|
|
30734
31385
|
},
|
|
30735
31386
|
async close() {
|
|
@@ -30791,7 +31442,11 @@ async function startProtocol(entry) {
|
|
|
30791
31442
|
for (const name of server.databaseNames()) server.getDatabase(name).now = () => new Date(clock.now());
|
|
30792
31443
|
server.clearFaults();
|
|
30793
31444
|
clock.reset();
|
|
31445
|
+
const control = { ...boot.shared.clock, ...boot.namespaces[namespace]?.clock };
|
|
31446
|
+
if (control.frozen) clock.freeze();
|
|
31447
|
+
if (control.now !== void 0) clock.set(control.now);
|
|
30794
31448
|
},
|
|
31449
|
+
nextEvent: () => void 0,
|
|
30795
31450
|
clock: (namespace) => get(namespace).clock
|
|
30796
31451
|
};
|
|
30797
31452
|
}
|