@crvouga/mockingbird-service-postgres 1.5.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -6
- package/CHANGELOG.md +27 -0
- package/COMPATIBILITY.md +14 -5
- package/README.md +30 -13
- package/dist/admin.js +191 -54
- package/dist/admin.js.map +4 -4
- package/dist/ast/nodes.d.ts +1 -0
- package/dist/index.js +118 -32
- package/dist/index.js.map +3 -3
- package/dist/runtime/database-context.d.ts +4 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +25808 -0
- package/dist/server.js.map +7 -0
- package/dist/sql/deparse.d.ts +1 -0
- package/dist/unstable.js +96 -31
- package/dist/unstable.js.map +3 -3
- package/dist/wire/cli.js +1766 -173
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/cluster.d.ts +23 -8
- package/dist/wire/connection.d.ts +25 -1
- package/dist/wire/fleet.d.ts +80 -0
- package/dist/wire/index.d.ts +11 -1
- package/dist/wire/index.js +608 -78
- package/dist/wire/index.js.map +3 -3
- package/dist/wire/probe.d.ts +2 -0
- package/dist/wire/protocol.d.ts +4 -0
- package/package.json +13 -2
package/dist/wire/cluster.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Database } from "../api/database.js";
|
|
2
|
+
import type { Snapshot } from "../api/snapshot.js";
|
|
2
3
|
/**
|
|
3
4
|
* What every connection of one server shares: the engine, and the coordination the engine
|
|
4
5
|
* (single-session, synchronous) does not do itself.
|
|
5
6
|
*
|
|
6
|
-
* - The **turn
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Uncommitted rows are therefore never visible to another connection (read committed holds),
|
|
10
|
-
* at the cost of running transaction blocks one at a time.
|
|
7
|
+
* - The **turn** serializes individual synchronous engine calls. Explicit transactions use
|
|
8
|
+
* per-session copy-on-write workspaces in the connection, so the turn is released after every
|
|
9
|
+
* statement and unrelated sessions keep progressing.
|
|
11
10
|
* - **Advisory locks** per session, with waiters woken in order, and deadlock detection
|
|
12
11
|
* between a lock and the turn (`40P01`).
|
|
13
12
|
* - **LISTEN / NOTIFY** fan-out, delivered when a listening connection is idle.
|
|
@@ -15,7 +14,7 @@ import type { Database } from "../api/database.js";
|
|
|
15
14
|
* (`pg_backend_pid`, `pg_advisory_lock`, `pg_notify`, …) know whom they serve.
|
|
16
15
|
*/
|
|
17
16
|
export declare class Cluster {
|
|
18
|
-
readonly
|
|
17
|
+
readonly defaultDatabaseName: string;
|
|
19
18
|
turnHolder: Session | null;
|
|
20
19
|
private readonly turnQueue;
|
|
21
20
|
readonly locks: Map<string, {
|
|
@@ -28,7 +27,21 @@ export declare class Cluster {
|
|
|
28
27
|
readonly sessions: Map<number, Session>;
|
|
29
28
|
current: Session | null;
|
|
30
29
|
private nextPid;
|
|
31
|
-
|
|
30
|
+
private readonly databases;
|
|
31
|
+
constructor(db: Database, defaultDatabaseName?: string);
|
|
32
|
+
/** The initial database, retained for the server's backwards-compatible public handle. */
|
|
33
|
+
get db(): Database;
|
|
34
|
+
databaseNames(): string[];
|
|
35
|
+
snapshotAll(): Map<string, Snapshot>;
|
|
36
|
+
restoreAll(points: ReadonlyMap<string, Snapshot>): void;
|
|
37
|
+
getDatabase(name: string): Database | undefined;
|
|
38
|
+
requireDatabase(name: string): Database;
|
|
39
|
+
createDatabase(name: string, template?: string): void;
|
|
40
|
+
dropDatabase(name: string, requester: Session): void;
|
|
41
|
+
renameDatabase(from: string, to: string, requester: Session): void;
|
|
42
|
+
private requireTemplate;
|
|
43
|
+
private activeConnections;
|
|
44
|
+
private installDatabase;
|
|
32
45
|
newPid(): number;
|
|
33
46
|
/** Wait until this session may execute: at once when nobody holds the turn, or it does. */
|
|
34
47
|
acquireTurn(session: Session): Promise<void>;
|
|
@@ -59,6 +72,8 @@ export declare class LockWait extends Error {
|
|
|
59
72
|
/** What the cluster needs from a connection. */
|
|
60
73
|
export interface Session {
|
|
61
74
|
readonly pid: number;
|
|
75
|
+
database: Database;
|
|
76
|
+
databaseName: string;
|
|
62
77
|
waitingForTurn: Waiter | null;
|
|
63
78
|
waitingForLock: string | null;
|
|
64
79
|
/** NOTIFYs issued inside the current transaction block, sent at commit. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Socket } from "node:net";
|
|
2
|
+
import type { Database } from "../api/database.js";
|
|
2
3
|
import { type Cluster, type Session } from "./cluster.js";
|
|
3
4
|
export type ServerFaults = {
|
|
4
5
|
/** Destroy the socket of the next connection that runs a statement inside a transaction block. */
|
|
@@ -43,6 +44,8 @@ export declare class Connection implements Session {
|
|
|
43
44
|
channel: string;
|
|
44
45
|
payload: string;
|
|
45
46
|
}[];
|
|
47
|
+
database: Database;
|
|
48
|
+
databaseName: string;
|
|
46
49
|
private readonly frames;
|
|
47
50
|
private readonly inbox;
|
|
48
51
|
private pumping;
|
|
@@ -59,6 +62,10 @@ export declare class Connection implements Session {
|
|
|
59
62
|
private readonly prepared;
|
|
60
63
|
private readonly portals;
|
|
61
64
|
private readonly notifications;
|
|
65
|
+
private copyIn;
|
|
66
|
+
private copyDrain;
|
|
67
|
+
/** Successful writes in this session's READ COMMITTED transaction workspace. */
|
|
68
|
+
private transaction;
|
|
62
69
|
private closed;
|
|
63
70
|
constructor(socket: Socket, cluster: Cluster, options: ConnectionOptions);
|
|
64
71
|
private receive;
|
|
@@ -88,12 +95,29 @@ export declare class Connection implements Session {
|
|
|
88
95
|
private waitForLock;
|
|
89
96
|
/** The engine call itself: statement-atomic, and transaction control handled as the server sees it. */
|
|
90
97
|
private runStatement;
|
|
91
|
-
/** Release the turn
|
|
98
|
+
/** Release the statement turn; transaction-scoped locks live until COMMIT / ROLLBACK. */
|
|
92
99
|
private afterStatement;
|
|
100
|
+
private transactionControl;
|
|
101
|
+
private findSavepoint;
|
|
102
|
+
/** Rebase this transaction's successful writes onto the latest committed state. */
|
|
103
|
+
private openWorkspace;
|
|
104
|
+
private replay;
|
|
105
|
+
private runQuery;
|
|
106
|
+
private lockRows;
|
|
107
|
+
private rowLockKey;
|
|
93
108
|
private flushNotifies;
|
|
109
|
+
private executeClusterDdl;
|
|
94
110
|
private listen;
|
|
95
111
|
private unlisten;
|
|
96
112
|
private query;
|
|
113
|
+
private copyStatement;
|
|
114
|
+
private copyColumnCount;
|
|
115
|
+
private handleCopyIn;
|
|
116
|
+
private beginCopyIn;
|
|
117
|
+
private finishCopyIn;
|
|
118
|
+
private flushCopyRecords;
|
|
119
|
+
private abortCopyIn;
|
|
120
|
+
private sendCopyOut;
|
|
97
121
|
private sendOutcome;
|
|
98
122
|
private columnFormats;
|
|
99
123
|
private fields;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source of time for a service.
|
|
3
|
+
*
|
|
4
|
+
* Every timestamp a mock writes reads from here, so a suite moves time instead of
|
|
5
|
+
* sleeping: appointment windows, result delays and expiries become reachable in
|
|
6
|
+
* milliseconds. A frozen clock also makes timestamps reproducible from a seed.
|
|
7
|
+
*/
|
|
8
|
+
type ClockState = {
|
|
9
|
+
/** Current epoch milliseconds. */
|
|
10
|
+
now: number;
|
|
11
|
+
/** True while time does not advance on its own. */
|
|
12
|
+
frozen: boolean;
|
|
13
|
+
/** Milliseconds this clock adds to its underlying source. */
|
|
14
|
+
offsetMs: number;
|
|
15
|
+
};
|
|
16
|
+
type Clock = {
|
|
17
|
+
now(): number;
|
|
18
|
+
/** Pin the clock to an exact instant, keeping it frozen if it already was. */
|
|
19
|
+
set(epochMs: number): void;
|
|
20
|
+
/** Move the clock forward, or back with a negative delta. */
|
|
21
|
+
advance(deltaMs: number): void;
|
|
22
|
+
/** Stop time at the current instant. */
|
|
23
|
+
freeze(): void;
|
|
24
|
+
/** Resume from the current instant. */
|
|
25
|
+
unfreeze(): void;
|
|
26
|
+
/** Drop back to the underlying source, live. */
|
|
27
|
+
reset(): void;
|
|
28
|
+
state(): ClockState;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type FleetDiagnostics = {
|
|
32
|
+
activeRequests: number;
|
|
33
|
+
activeConnections: number;
|
|
34
|
+
blockedCommands: number;
|
|
35
|
+
pendingJobs: number;
|
|
36
|
+
activeJobs?: number;
|
|
37
|
+
pendingWebhooks: number;
|
|
38
|
+
unmatchedRequests: number;
|
|
39
|
+
};
|
|
40
|
+
/** Protocol packages own their state, listeners, credentials and namespace selection. */
|
|
41
|
+
type FleetChild = {
|
|
42
|
+
protocol: "http" | "postgres" | "redis";
|
|
43
|
+
url: string;
|
|
44
|
+
connection?: string;
|
|
45
|
+
connections?: Record<string, string>;
|
|
46
|
+
healthUrl?: string;
|
|
47
|
+
adminUrl?: string;
|
|
48
|
+
namespaces: Record<string, unknown>;
|
|
49
|
+
ready(): Promise<boolean>;
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
/** Fence new work synchronously; diagnostics must also account for work already in flight. */
|
|
52
|
+
lock(namespace: string): () => void;
|
|
53
|
+
diagnostics(namespace: string): FleetDiagnostics;
|
|
54
|
+
checkpoint(namespace: string): Promise<unknown>;
|
|
55
|
+
restore(namespace: string, checkpoint: unknown): Promise<void>;
|
|
56
|
+
reset(namespace: string): Promise<void>;
|
|
57
|
+
clock(namespace: string): Clock;
|
|
58
|
+
settle?(namespace: string): Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** A service entry in `mockingbird.json`. */
|
|
62
|
+
type ConfigService = {
|
|
63
|
+
protocol?: "http" | "postgres" | "redis";
|
|
64
|
+
port?: number;
|
|
65
|
+
host?: string;
|
|
66
|
+
adminKey?: string;
|
|
67
|
+
seed?: string;
|
|
68
|
+
password?: string;
|
|
69
|
+
user?: string;
|
|
70
|
+
database?: string;
|
|
71
|
+
/** Protocol namespace selectors: database names for Postgres, logical DB indexes for Redis. */
|
|
72
|
+
namespaces?: Record<string, string | number>;
|
|
73
|
+
/** Service-specific serve flags, by long name: `{ "webhook-url": "…" }`. */
|
|
74
|
+
options?: Record<string, string | boolean>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Separate database listeners make the protocol namespace explicit in discovery. */
|
|
78
|
+
declare function startProtocol(entry: ConfigService): Promise<FleetChild>;
|
|
79
|
+
|
|
80
|
+
export { startProtocol };
|
package/dist/wire/index.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ export type ServeOptions = {
|
|
|
55
55
|
* SQLSTATE `3D000`. Set from `url` when the path is non-empty. Unset accepts any name.
|
|
56
56
|
*/
|
|
57
57
|
databaseName?: string;
|
|
58
|
+
/** Accept other databases in this listener's catalog while retaining its default URL. */
|
|
59
|
+
allowDatabaseSelection?: boolean;
|
|
58
60
|
/** Role shown in `connectionString`. A URI's username fills this. Default `postgres`. */
|
|
59
61
|
user?: string;
|
|
60
62
|
};
|
|
@@ -67,14 +69,22 @@ export type PostgresServer = {
|
|
|
67
69
|
readonly connectionString: string;
|
|
68
70
|
/** The shared engine, for seeding, snapshotting or asserting from the test process. */
|
|
69
71
|
readonly database: Database;
|
|
72
|
+
/** Names in the cluster catalog, in deterministic order. */
|
|
73
|
+
databaseNames(): readonly string[];
|
|
74
|
+
/** Resolve a named database, or throw SQLSTATE `3D000`. */
|
|
75
|
+
getDatabase(name: string): Database;
|
|
70
76
|
/** The underlying `net.Server`. */
|
|
71
77
|
readonly server: Server;
|
|
72
78
|
/** Open connections right now. */
|
|
73
79
|
readonly connections: number;
|
|
74
80
|
/** Arm a fault preset for the next statement / connection (see {@link ServerFaults}). */
|
|
75
81
|
fault(fault: ServerFaults): void;
|
|
82
|
+
clearFaults(): void;
|
|
76
83
|
/** Freeze the live state (the admin `snapshot()` control). */
|
|
77
|
-
snapshot(): Snapshot;
|
|
84
|
+
snapshot(name?: string): Snapshot;
|
|
85
|
+
snapshotAll(): Map<string, Snapshot>;
|
|
86
|
+
restoreAll(points: ReadonlyMap<string, Snapshot>): void;
|
|
87
|
+
faultState(): ServerFaults;
|
|
78
88
|
/** Stop listening and close every connection. */
|
|
79
89
|
close(): Promise<void>;
|
|
80
90
|
};
|