@rebasepro/server-postgres 0.17.3-canary.gdd23447 → 0.18.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/LICENSE +0 -1
- package/README.md +31 -0
- package/dist/backup-argv.d.ts +19 -0
- package/dist/branch-argv.d.ts +28 -0
- package/dist/branch-prune.d.ts +77 -0
- package/dist/cli-collections-path.d.ts +31 -0
- package/dist/cli-errors.d.ts +123 -0
- package/dist/cli-flags.d.ts +101 -0
- package/dist/cli-helpers.d.ts +94 -10
- package/dist/collection-index-C7LopMyX.js +247 -0
- package/dist/collection-index-C7LopMyX.js.map +1 -0
- package/dist/databasePoolManager.d.ts +27 -0
- package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-CagOzEPS.js} +6 -6
- package/dist/{ensure-collection-policies-UCqgv_8c.js.map → ensure-collection-policies-CagOzEPS.js.map} +1 -1
- package/dist/{ensure-collection-tables-DgVixhX3.js → ensure-collection-tables-DbAhrjnG.js} +614 -52
- package/dist/ensure-collection-tables-DbAhrjnG.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.es.js +19307 -3059
- package/dist/index.es.js.map +1 -1
- package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-ClJoTboT.js} +53 -189
- package/dist/rls-enforcement-ClJoTboT.js.map +1 -0
- package/dist/schema/column-type-drift.d.ts +79 -0
- package/dist/schema/doctor.d.ts +42 -0
- package/dist/schema/ensure-collection-tables.d.ts +90 -0
- package/dist/schema/generate-next-step.d.ts +30 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
- package/dist/schema/generated-schema-staleness.d.ts +37 -0
- package/dist/schema/introspect-db-queries.d.ts +34 -0
- package/dist/security/rls-enforcement.d.ts +63 -2
- package/dist/services/BranchService.d.ts +86 -1
- package/dist/services/collection-helpers.d.ts +1 -1
- package/dist/services/realtimeService.d.ts +30 -2
- package/dist/services/row-pipeline.d.ts +12 -1
- package/dist/utils/pg-error-utils.d.ts +3 -1
- package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
- package/package.json +32 -27
- package/dist/backup-service-DCk7KhhL.js +0 -9044
- package/dist/backup-service-DCk7KhhL.js.map +0 -1
- package/dist/collection-index-BRUg10H5.js +0 -5690
- package/dist/collection-index-BRUg10H5.js.map +0 -1
- package/dist/connection-GOKU3Hu5.js +0 -277
- package/dist/connection-GOKU3Hu5.js.map +0 -1
- package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
- package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
- package/dist/src-DiB5RP2Z.js +0 -541
- package/dist/src-DiB5RP2Z.js.map +0 -1
- package/dist/websocket-BZ4H5wUz.js +0 -8198
- package/dist/websocket-BZ4H5wUz.js.map +0 -1
- package/src/PostgresAdapter.ts +0 -91
- package/src/PostgresBackendDriver.ts +0 -1857
- package/src/PostgresBootstrapper.ts +0 -1157
- package/src/auth/ensure-tables.ts +0 -896
- package/src/auth/schema-version.ts +0 -260
- package/src/auth/services.ts +0 -1610
- package/src/backup/backup-cli.ts +0 -520
- package/src/backup/backup-cron.ts +0 -212
- package/src/backup/backup-logic.ts +0 -93
- package/src/backup/backup-service.ts +0 -485
- package/src/backup/index.ts +0 -12
- package/src/backup/pg-tools.ts +0 -403
- package/src/backup/retention.ts +0 -86
- package/src/cli-errors.ts +0 -265
- package/src/cli-helpers.ts +0 -569
- package/src/cli-output.ts +0 -43
- package/src/cli.ts +0 -1339
- package/src/collections/PostgresCollectionRegistry.ts +0 -108
- package/src/collections/buildRegistry.ts +0 -68
- package/src/collections/validate-relations.ts +0 -402
- package/src/connection.ts +0 -333
- package/src/data-transformer.ts +0 -845
- package/src/databasePoolManager.ts +0 -95
- package/src/history/HistoryService.ts +0 -254
- package/src/history/ensure-history-table.ts +0 -53
- package/src/index.ts +0 -19
- package/src/interfaces.ts +0 -60
- package/src/module-dir.ts +0 -7
- package/src/schema/atlas-argv.ts +0 -94
- package/src/schema/auth-schema.ts +0 -298
- package/src/schema/auth-users-columns.ts +0 -131
- package/src/schema/carved-out-migration.ts +0 -404
- package/src/schema/classify-change.ts +0 -436
- package/src/schema/collection-index.ts +0 -427
- package/src/schema/collections-schema-version.ts +0 -103
- package/src/schema/destructive-sql.ts +0 -94
- package/src/schema/doctor-cli.ts +0 -66
- package/src/schema/doctor-policy-checks.ts +0 -105
- package/src/schema/doctor.ts +0 -837
- package/src/schema/drizzle-ddl.ts +0 -33
- package/src/schema/dynamic-tables.test.ts +0 -302
- package/src/schema/dynamic-tables.ts +0 -293
- package/src/schema/ensure-collection-policies.ts +0 -198
- package/src/schema/ensure-collection-tables.test.ts +0 -518
- package/src/schema/ensure-collection-tables.ts +0 -1267
- package/src/schema/generate-drizzle-schema-logic.ts +0 -957
- package/src/schema/generate-drizzle-schema.ts +0 -143
- package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
- package/src/schema/generate-postgres-ddl.ts +0 -180
- package/src/schema/generate-schema-commit.ts +0 -267
- package/src/schema/generated-schema-staleness.ts +0 -284
- package/src/schema/introspect-db-constraints.ts +0 -385
- package/src/schema/introspect-db-inference.ts +0 -255
- package/src/schema/introspect-db-logic.ts +0 -1387
- package/src/schema/introspect-db-naming.ts +0 -15
- package/src/schema/introspect-db-project.ts +0 -78
- package/src/schema/introspect-db-queries.ts +0 -326
- package/src/schema/introspect-db-structure.ts +0 -670
- package/src/schema/introspect-db-types.ts +0 -56
- package/src/schema/introspect-db.ts +0 -257
- package/src/schema/introspect-runtime.test.ts +0 -260
- package/src/schema/introspect-runtime.ts +0 -327
- package/src/schema/non-sql-collections.test.ts +0 -131
- package/src/schema/rls-bootstrap-sql.ts +0 -288
- package/src/schema/search-column.ts +0 -643
- package/src/schema/test-schema.ts +0 -11
- package/src/schema/vector-index.ts +0 -491
- package/src/security/anonymous-grants.test.ts +0 -73
- package/src/security/policy-drift.test.ts +0 -435
- package/src/security/policy-drift.ts +0 -473
- package/src/security/rls-enforcement.ts +0 -505
- package/src/services/BranchService.ts +0 -326
- package/src/services/FetchService.ts +0 -2115
- package/src/services/PersistService.ts +0 -557
- package/src/services/RelationService.ts +0 -978
- package/src/services/RelationWriteService.ts +0 -653
- package/src/services/cdc/CdcListener.ts +0 -103
- package/src/services/cdc/junction-tables.ts +0 -91
- package/src/services/cdc/trigger-cdc.ts +0 -173
- package/src/services/channel-bus/ChannelBus.ts +0 -44
- package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
- package/src/services/channel-bus/index.ts +0 -114
- package/src/services/channel-history.ts +0 -424
- package/src/services/channel-presence.ts +0 -185
- package/src/services/collection-helpers.ts +0 -420
- package/src/services/dataService.ts +0 -256
- package/src/services/index.ts +0 -14
- package/src/services/junction-writes.ts +0 -295
- package/src/services/nested-path.ts +0 -145
- package/src/services/pg-notify-listener.ts +0 -151
- package/src/services/realtimeService.ts +0 -2545
- package/src/services/row-pipeline.ts +0 -240
- package/src/services/write-denial.ts +0 -55
- package/src/types.ts +0 -4
- package/src/utils/connection-string.ts +0 -58
- package/src/utils/drizzle-conditions.ts +0 -2743
- package/src/utils/pg-array-null-patch.ts +0 -42
- package/src/utils/pg-error-utils.ts +0 -391
- package/src/websocket.ts +0 -855
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BranchService
|
|
3
|
-
*
|
|
4
|
-
* Manages database branching by creating/deleting PostgreSQL databases
|
|
5
|
-
* using `CREATE DATABASE ... TEMPLATE`. Branch metadata is stored in the
|
|
6
|
-
* `rebase.branches` table in the default (main) database, following the
|
|
7
|
-
* same `rebase` schema convention used by entity_history, auth, etc.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { sql } from "drizzle-orm";
|
|
11
|
-
import { BranchInfo } from "@rebasepro/types";
|
|
12
|
-
import { revokeInternalTableSql } from "@rebasepro/common";
|
|
13
|
-
import { DrizzleClient } from "../interfaces";
|
|
14
|
-
import { DatabasePoolManager } from "../databasePoolManager";
|
|
15
|
-
import { extractPgError, extractCauseMessage } from "../utils/pg-error-utils";
|
|
16
|
-
|
|
17
|
-
/** Internal prefix applied to branch database names to avoid collisions. */
|
|
18
|
-
const BRANCH_DB_PREFIX = "rb_";
|
|
19
|
-
|
|
20
|
-
/** `duplicate_database` — the target database name is already taken. */
|
|
21
|
-
const PG_DUPLICATE_DATABASE = "42P04";
|
|
22
|
-
|
|
23
|
-
/** `object_in_use` — the database still has connections attached. */
|
|
24
|
-
const PG_OBJECT_IN_USE = "55006";
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Describe a failed branch DDL statement in terms a user can act on.
|
|
28
|
-
*
|
|
29
|
-
* Drizzle reports failures as `Failed query: <sql> params:` and hides the real
|
|
30
|
-
* PostgreSQL error in the `cause` chain, so matching on `err.message` never sees
|
|
31
|
-
* the actual problem. Match on the PG error code instead — it survives wrapping
|
|
32
|
-
* and, unlike the message text, is not locale-dependent.
|
|
33
|
-
*/
|
|
34
|
-
function describeBranchDdlError(err: unknown, fallbackContext: string): Error {
|
|
35
|
-
const pgError = extractPgError(err);
|
|
36
|
-
|
|
37
|
-
if (pgError?.code === PG_DUPLICATE_DATABASE) {
|
|
38
|
-
return new Error(`Database "${fallbackContext}" already exists on the server. Choose a different branch name.`);
|
|
39
|
-
}
|
|
40
|
-
if (pgError?.code === PG_OBJECT_IN_USE) {
|
|
41
|
-
return new Error(
|
|
42
|
-
`Cannot complete the operation: the database "${fallbackContext}" has active connections. ` +
|
|
43
|
-
"Close other clients or connections and try again."
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Unknown failure: surface the real PG message rather than the Drizzle
|
|
48
|
-
// wrapper, which would otherwise show the raw SQL and no reason at all.
|
|
49
|
-
const detail = pgError?.message ?? extractCauseMessage(err);
|
|
50
|
-
if (detail) return new Error(detail);
|
|
51
|
-
return err instanceof Error ? err : new Error(String(err));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Fully-qualified metadata table in the rebase schema. */
|
|
55
|
-
const BRANCHES_TABLE = "rebase.branches";
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Validate that a user-provided identifier only contains safe characters.
|
|
59
|
-
* Throws if the value contains characters outside [a-zA-Z0-9_-].
|
|
60
|
-
*/
|
|
61
|
-
function validateIdentifier(value: string, label: string): void {
|
|
62
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
|
|
63
|
-
throw new Error(`Invalid ${label}: only letters, digits, underscores, and hyphens are allowed.`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Postgres truncates identifiers at NAMEDATALEN-1 = 63 bytes, and does it
|
|
69
|
-
* silently. The prefix comes out of the same budget.
|
|
70
|
-
*/
|
|
71
|
-
const MAX_BRANCH_NAME_LENGTH = 63 - BRANCH_DB_PREFIX.length;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Check a user-provided branch name, and otherwise leave it exactly as given.
|
|
75
|
-
*
|
|
76
|
-
* This used to strip everything outside [a-zA-Z0-9_], so `my-feature` was
|
|
77
|
-
* quietly created as `myfeature`: the name you typed was not the name `list`
|
|
78
|
-
* gave back. Nothing ever needed that. Every identifier this service builds is
|
|
79
|
-
* double-quoted (see `CREATE DATABASE` below), which is what makes a hyphen
|
|
80
|
-
* safe, and `validateIdentifier` has always accepted hyphens for the `--from`
|
|
81
|
-
* source database — the two disagreed about the same character class.
|
|
82
|
-
*
|
|
83
|
-
* Refusing a name we cannot represent is better than representing a different
|
|
84
|
-
* one, which is also why the length is checked here rather than left to
|
|
85
|
-
* Postgres, whose answer to an over-long identifier is a silent rename.
|
|
86
|
-
*/
|
|
87
|
-
function assertValidBranchName(name: string): void {
|
|
88
|
-
validateIdentifier(name, "branch name");
|
|
89
|
-
if (name.length > MAX_BRANCH_NAME_LENGTH) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`Branch name "${name}" is too long: ${name.length} characters, maximum ${MAX_BRANCH_NAME_LENGTH}. ` +
|
|
92
|
-
"Postgres truncates identifiers past 63 bytes, which would give the branch a name you did not choose."
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Convert a user-facing branch name to the actual PostgreSQL database name.
|
|
99
|
-
*/
|
|
100
|
-
function toBranchDbName(name: string): string {
|
|
101
|
-
assertValidBranchName(name);
|
|
102
|
-
return `${BRANCH_DB_PREFIX}${name}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export class BranchService {
|
|
106
|
-
constructor(
|
|
107
|
-
private db: DrizzleClient,
|
|
108
|
-
private poolManager: DatabasePoolManager
|
|
109
|
-
) {}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Ensure the `rebase.branches` metadata table exists in the default database.
|
|
113
|
-
* Idempotent — safe to call on every startup.
|
|
114
|
-
*/
|
|
115
|
-
async ensureBranchMetadataTable(): Promise<void> {
|
|
116
|
-
// Create the rebase schema (idempotent — may already exist from auth/history init)
|
|
117
|
-
await this.db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);
|
|
118
|
-
|
|
119
|
-
await this.db.execute(sql.raw(`
|
|
120
|
-
CREATE TABLE IF NOT EXISTS ${BRANCHES_TABLE} (
|
|
121
|
-
name TEXT PRIMARY KEY,
|
|
122
|
-
db_name TEXT NOT NULL UNIQUE,
|
|
123
|
-
parent_db TEXT NOT NULL,
|
|
124
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
125
|
-
metadata JSONB DEFAULT '{}'
|
|
126
|
-
);
|
|
127
|
-
`));
|
|
128
|
-
|
|
129
|
-
// Not a collection, so no RLS — and it names every branch database on
|
|
130
|
-
// this server. The driver's schema-wide grant reaches it, so revoke.
|
|
131
|
-
await this.db.execute(sql.raw(revokeInternalTableSql("rebase", "branches")));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Create a new branch database by templating the source database.
|
|
136
|
-
*
|
|
137
|
-
* Uses `CREATE DATABASE ... TEMPLATE` for an instant, full-fidelity copy
|
|
138
|
-
* of both schema and data.
|
|
139
|
-
*
|
|
140
|
-
* @param name User-facing branch name (e.g., "feature_auth")
|
|
141
|
-
* @param options.source Source database to clone; defaults to the main database.
|
|
142
|
-
*/
|
|
143
|
-
async createBranch(name: string, options?: { source?: string }): Promise<BranchInfo> {
|
|
144
|
-
if (options?.source) {
|
|
145
|
-
validateIdentifier(options.source, "source database name");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const dbName = toBranchDbName(name);
|
|
149
|
-
const sourceDb = options?.source || this.poolManager.defaultDatabaseName;
|
|
150
|
-
|
|
151
|
-
// Check if branch already exists
|
|
152
|
-
const existing = await this.db.execute(
|
|
153
|
-
sql`SELECT name FROM rebase.branches WHERE name = ${name} OR db_name = ${dbName}`
|
|
154
|
-
);
|
|
155
|
-
if ((existing.rows as unknown[]).length > 0) {
|
|
156
|
-
throw new Error(`Branch "${name}" already exists.`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Disconnect any idle pools to the source DB so TEMPLATE works.
|
|
160
|
-
// CREATE DATABASE ... TEMPLATE requires no other connections to the template.
|
|
161
|
-
await this.poolManager.disconnectDatabase(sourceDb);
|
|
162
|
-
|
|
163
|
-
// Create the database using the source as a template.
|
|
164
|
-
// Note: Identifiers must be double-quoted, not parameterized.
|
|
165
|
-
const safeDbName = dbName.replace(/"/g, '""');
|
|
166
|
-
const safeSourceDb = sourceDb.replace(/"/g, '""');
|
|
167
|
-
try {
|
|
168
|
-
await this.db.execute(
|
|
169
|
-
sql.raw(`CREATE DATABASE "${safeDbName}" TEMPLATE "${safeSourceDb}"`)
|
|
170
|
-
);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
const pgError = extractPgError(err);
|
|
173
|
-
if (pgError?.code === PG_OBJECT_IN_USE) {
|
|
174
|
-
// The template — not the new database — is the one still in use.
|
|
175
|
-
throw new Error(
|
|
176
|
-
`Cannot create branch: the source database "${sourceDb}" has active connections. ` +
|
|
177
|
-
"Close other clients or connections and try again."
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
throw describeBranchDdlError(err, dbName);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Record metadata in the default database
|
|
184
|
-
const now = new Date();
|
|
185
|
-
await this.db.execute(
|
|
186
|
-
sql`INSERT INTO rebase.branches (name, db_name, parent_db, created_at)
|
|
187
|
-
VALUES (${name}, ${dbName}, ${sourceDb}, ${now.toISOString()})`
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
name,
|
|
192
|
-
parentDatabase: sourceDb,
|
|
193
|
-
createdAt: now
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Delete a branch database and remove its metadata.
|
|
199
|
-
* Cannot delete the main/default database.
|
|
200
|
-
*/
|
|
201
|
-
async deleteBranch(name: string): Promise<void> {
|
|
202
|
-
assertValidBranchName(name);
|
|
203
|
-
|
|
204
|
-
// Safety, first pass: a request that would target the default database
|
|
205
|
-
// is refused before any metadata is read, so the answer costs nothing
|
|
206
|
-
// and cannot depend on what a row happens to say.
|
|
207
|
-
if (toBranchDbName(name) === this.poolManager.defaultDatabaseName) {
|
|
208
|
-
throw new Error("Cannot delete the main database.");
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Verify the branch exists, and take the database name from the row
|
|
212
|
-
// rather than deriving it again. A branch created before names stopped
|
|
213
|
-
// being stripped is recorded as `rb_myfeature` while `my-feature` now
|
|
214
|
-
// derives `rb_my-feature`; re-deriving would drop a database this row
|
|
215
|
-
// never named — or, if that name happened to exist, somebody else's.
|
|
216
|
-
// The stored value is the only one that is true by construction.
|
|
217
|
-
const existing = await this.db.execute(
|
|
218
|
-
sql`SELECT db_name FROM rebase.branches WHERE name = ${name}`
|
|
219
|
-
);
|
|
220
|
-
const existingRows = existing.rows as Record<string, unknown>[];
|
|
221
|
-
if (existingRows.length === 0) {
|
|
222
|
-
throw new Error(`Branch "${name}" not found.`);
|
|
223
|
-
}
|
|
224
|
-
const dbName = existingRows[0].db_name as string;
|
|
225
|
-
|
|
226
|
-
// Safety, second pass: the first checked a name we derived, this checks
|
|
227
|
-
// the one we are about to drop. They differ for any row written under
|
|
228
|
-
// the old scheme, and it is this one that governs.
|
|
229
|
-
if (dbName === this.poolManager.defaultDatabaseName) {
|
|
230
|
-
throw new Error("Cannot delete the main database.");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Disconnect any pools to this branch before dropping
|
|
234
|
-
await this.poolManager.disconnectDatabase(dbName);
|
|
235
|
-
|
|
236
|
-
// Drop the database
|
|
237
|
-
const safeDbName = dbName.replace(/"/g, '""');
|
|
238
|
-
try {
|
|
239
|
-
await this.db.execute(sql.raw(`DROP DATABASE "${safeDbName}"`));
|
|
240
|
-
} catch (err) {
|
|
241
|
-
const pgError = extractPgError(err);
|
|
242
|
-
if (pgError?.code === PG_OBJECT_IN_USE) {
|
|
243
|
-
throw new Error(
|
|
244
|
-
`Cannot delete branch "${name}": the database has active connections. ` +
|
|
245
|
-
"Close other clients and try again."
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
throw describeBranchDdlError(err, dbName);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Remove metadata
|
|
252
|
-
await this.db.execute(
|
|
253
|
-
sql`DELETE FROM rebase.branches WHERE name = ${name}`
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* List all branches recorded in the metadata table.
|
|
259
|
-
* Optionally fetches database sizes from pg_database.
|
|
260
|
-
*/
|
|
261
|
-
async listBranches(): Promise<BranchInfo[]> {
|
|
262
|
-
const result = await this.db.execute(sql.raw(`
|
|
263
|
-
SELECT
|
|
264
|
-
b.name,
|
|
265
|
-
b.parent_db,
|
|
266
|
-
b.created_at,
|
|
267
|
-
pg_database_size(b.db_name) as size_bytes
|
|
268
|
-
FROM ${BRANCHES_TABLE} b
|
|
269
|
-
JOIN pg_database d ON d.datname = b.db_name
|
|
270
|
-
ORDER BY b.created_at DESC
|
|
271
|
-
`));
|
|
272
|
-
|
|
273
|
-
return (result.rows as Record<string, unknown>[]).map((row) => ({
|
|
274
|
-
name: row.name as string,
|
|
275
|
-
parentDatabase: row.parent_db as string,
|
|
276
|
-
createdAt: new Date(row.created_at as string),
|
|
277
|
-
sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : undefined
|
|
278
|
-
}));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Get info about a specific branch.
|
|
283
|
-
*/
|
|
284
|
-
async getBranchInfo(name: string): Promise<BranchInfo | undefined> {
|
|
285
|
-
assertValidBranchName(name);
|
|
286
|
-
|
|
287
|
-
const result = await this.db.execute(sql`
|
|
288
|
-
SELECT
|
|
289
|
-
b.name,
|
|
290
|
-
b.db_name,
|
|
291
|
-
b.parent_db,
|
|
292
|
-
b.created_at
|
|
293
|
-
FROM rebase.branches b
|
|
294
|
-
WHERE b.name = ${name}
|
|
295
|
-
`);
|
|
296
|
-
|
|
297
|
-
const rows = result.rows as Record<string, unknown>[];
|
|
298
|
-
if (rows.length === 0) return undefined;
|
|
299
|
-
|
|
300
|
-
const row = rows[0];
|
|
301
|
-
|
|
302
|
-
// Attempt to get size — may fail if the DB was externally dropped.
|
|
303
|
-
// Same reason as `deleteBranch`: the size of a re-derived name is the
|
|
304
|
-
// size of some other database, or of nothing.
|
|
305
|
-
let sizeBytes: number | undefined;
|
|
306
|
-
try {
|
|
307
|
-
const dbName = row.db_name as string;
|
|
308
|
-
const sizeResult = await this.db.execute(
|
|
309
|
-
sql`SELECT pg_database_size(${dbName}) as size_bytes`
|
|
310
|
-
);
|
|
311
|
-
const sizeRows = sizeResult.rows as Record<string, unknown>[];
|
|
312
|
-
if (sizeRows.length > 0 && sizeRows[0].size_bytes != null) {
|
|
313
|
-
sizeBytes = Number(sizeRows[0].size_bytes);
|
|
314
|
-
}
|
|
315
|
-
} catch {
|
|
316
|
-
// Database might not exist anymore
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return {
|
|
320
|
-
name: row.name as string,
|
|
321
|
-
parentDatabase: row.parent_db as string,
|
|
322
|
-
createdAt: new Date(row.created_at as string),
|
|
323
|
-
sizeBytes
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
}
|