@rebasepro/server-postgres 0.22.0 → 0.24.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/dist/{BranchService-CucnFcSE.js → BranchService-BRt78gfa.js} +35 -18
- package/dist/BranchService-BRt78gfa.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +116 -4
- package/dist/auth/services.d.ts +99 -22
- package/dist/{auth-users-columns-D2LBFrMH.js → auth-users-columns-C72EMoDJ.js} +17 -1
- package/dist/{auth-users-columns-D2LBFrMH.js.map → auth-users-columns-C72EMoDJ.js.map} +1 -1
- package/dist/backup/backup-cli.d.ts +22 -0
- package/dist/{backup-cli-DqBakiMO.js → backup-cli-DW5p9_zv.js} +17 -14
- package/dist/backup-cli-DW5p9_zv.js.map +1 -0
- package/dist/{backup-service-CaMOS76G.js → backup-service-EwcDVG-8.js} +7 -9
- package/dist/{backup-service-CaMOS76G.js.map → backup-service-EwcDVG-8.js.map} +1 -1
- package/dist/{cli-errors-Dka89exj.js → cli-errors-DsA-K9uP.js} +101 -1
- package/dist/cli-errors-DsA-K9uP.js.map +1 -0
- package/dist/cli-errors.d.ts +42 -0
- package/dist/cli-flags-BglvpjHv.js +138 -0
- package/dist/cli-flags-BglvpjHv.js.map +1 -0
- package/dist/cli-flags.d.ts +28 -0
- package/dist/cli-helpers.d.ts +18 -12
- package/dist/cli-scratch-database.d.ts +34 -0
- package/dist/cli.js +177 -287
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-CpILzHJS.js → column-plan-helpers-1-LQD0yI.js} +44 -38
- package/dist/column-plan-helpers-1-LQD0yI.js.map +1 -0
- package/dist/data-transformer.d.ts +0 -8
- package/dist/{doctor-CU9IogdL.js → doctor-C-sYWbmt.js} +228 -46
- package/dist/doctor-C-sYWbmt.js.map +1 -0
- package/dist/{ensure-collection-policies-Dzd-2S81.js → ensure-collection-policies-B1ureSIV.js} +73 -18
- package/dist/ensure-collection-policies-B1ureSIV.js.map +1 -0
- package/dist/{ensure-collection-tables-CpAgy51F.js → ensure-collection-tables-kkkHk8oo.js} +124 -37
- package/dist/ensure-collection-tables-kkkHk8oo.js.map +1 -0
- package/dist/{ensure-tables-Cr5B4UmH.js → ensure-tables-BmI_tRxc.js} +10 -3
- package/dist/ensure-tables-BmI_tRxc.js.map +1 -0
- package/dist/{generate-drizzle-schema-B537GIvz.js → generate-drizzle-schema-93M0lUxK.js} +2 -2
- package/dist/{generate-drizzle-schema-B537GIvz.js.map → generate-drizzle-schema-93M0lUxK.js.map} +1 -1
- package/dist/{generate-drizzle-schema-logic-BcMl7VSy.js → generate-drizzle-schema-logic-sSFDp6LR.js} +26 -8
- package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-BJsLaVNX.js +152 -0
- package/dist/generate-postgres-ddl-logic-BJsLaVNX.js.map +1 -0
- package/dist/generated-sql.d.ts +28 -0
- package/dist/index.es.js +4768 -1117
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-C6LQdTxj.js → introspect-db-logic-kCETE8TY.js} +532 -39
- package/dist/introspect-db-logic-kCETE8TY.js.map +1 -0
- package/dist/introspect-db-queries-C_Q5VgQw.js +317 -0
- package/dist/introspect-db-queries-C_Q5VgQw.js.map +1 -0
- package/dist/{plan-schema-CboAIwLN.js → plan-schema-DU9exq6C.js} +291 -529
- package/dist/plan-schema-DU9exq6C.js.map +1 -0
- package/dist/{policy-drift-xJfy9xG7.js → policy-drift-B-J2hhm0.js} +3 -3
- package/dist/policy-drift-B-J2hhm0.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-D7imhYV8.js → render-ddl-Ds2t_d9V.js} +11 -149
- package/dist/render-ddl-Ds2t_d9V.js.map +1 -0
- package/dist/{rls-bootstrap-sql-H3rCFi3F.js → rls-bootstrap-sql-_KNnjanK.js} +562 -35
- package/dist/rls-bootstrap-sql-_KNnjanK.js.map +1 -0
- package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-CfXOJJaW.js} +10 -2
- package/dist/rls-enforcement-CfXOJJaW.js.map +1 -0
- package/dist/schema/atlas-argv.d.ts +15 -0
- package/dist/schema/auth-schema.d.ts +170 -0
- package/dist/schema/classify-change.d.ts +29 -1
- package/dist/schema/column-plan-helpers.d.ts +51 -20
- package/dist/schema/destructive-sql.d.ts +71 -1
- package/dist/schema/doctor-cli.js +4 -4
- package/dist/schema/doctor.d.ts +34 -1
- package/dist/schema/ensure-collection-policies.d.ts +22 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
- package/dist/schema/generate-postgres-ddl.js +1 -1
- package/dist/schema/generate-schema-commit.d.ts +12 -0
- package/dist/schema/introspect-db-logic.d.ts +31 -0
- package/dist/schema/introspect-db-queries.d.ts +1 -1
- package/dist/schema/introspect-db-search.d.ts +22 -0
- package/dist/schema/introspect-db-storage.d.ts +83 -0
- package/dist/schema/introspect-db.js +15 -314
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/plan/diff-plan.d.ts +4 -3
- package/dist/schema/plan/plan-schema.d.ts +26 -11
- package/dist/schema/plan/render-ddl.d.ts +6 -0
- package/dist/schema/plan/types.d.ts +43 -12
- package/dist/search-column-BM-GV6vH.js +442 -0
- package/dist/search-column-BM-GV6vH.js.map +1 -0
- package/dist/security/policy-drift.d.ts +1 -1
- package/dist/security/rls-enforcement.d.ts +7 -0
- package/dist/services/BranchService.d.ts +22 -1
- package/dist/services/FetchService.d.ts +102 -32
- package/dist/services/PersistService.d.ts +41 -5
- package/dist/services/RelationService.d.ts +29 -0
- package/dist/services/RelationWriteService.d.ts +6 -0
- package/dist/services/cdc/CdcListener.d.ts +15 -5
- package/dist/services/cdc/identity-columns.d.ts +19 -0
- package/dist/services/cdc/trigger-cdc.d.ts +45 -7
- package/dist/services/channel-bus/PostgresChannelBus.d.ts +12 -7
- package/dist/services/channel-history.d.ts +17 -1
- package/dist/services/collection-helpers.d.ts +21 -0
- package/dist/services/dataService.d.ts +3 -20
- package/dist/services/field-op-sql.d.ts +71 -0
- package/dist/services/junction-writes.d.ts +19 -3
- package/dist/services/pg-notify-listener.d.ts +95 -6
- package/dist/services/read-field-access.d.ts +19 -0
- package/dist/services/realtimeService.d.ts +232 -44
- package/dist/services/row-pipeline.d.ts +5 -0
- package/dist/services/socket-liveness.d.ts +45 -0
- package/dist/services/soft-delete.d.ts +12 -2
- package/dist/services/sql-script.d.ts +71 -0
- package/dist/services/write-depth.d.ts +16 -0
- package/dist/services/write-transaction-scope.d.ts +42 -0
- package/dist/utils/drizzle-conditions.d.ts +49 -4
- package/dist/utils/sql-redaction.d.ts +21 -0
- package/dist/websocket.d.ts +57 -18
- package/package.json +9 -9
- package/dist/BranchService-CucnFcSE.js.map +0 -1
- package/dist/backup-cli-DqBakiMO.js.map +0 -1
- package/dist/cli-errors-Dka89exj.js.map +0 -1
- package/dist/column-plan-helpers-CpILzHJS.js.map +0 -1
- package/dist/doctor-CU9IogdL.js.map +0 -1
- package/dist/ensure-collection-policies-Dzd-2S81.js.map +0 -1
- package/dist/ensure-collection-tables-CpAgy51F.js.map +0 -1
- package/dist/ensure-tables-Cr5B4UmH.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-BcMl7VSy.js.map +0 -1
- package/dist/generate-postgres-ddl-logic-D7imhYV8.js.map +0 -1
- package/dist/introspect-db-logic-C6LQdTxj.js.map +0 -1
- package/dist/plan-schema-CboAIwLN.js.map +0 -1
- package/dist/policy-drift-xJfy9xG7.js.map +0 -1
- package/dist/rls-bootstrap-sql-H3rCFi3F.js.map +0 -1
- package/dist/rls-enforcement-C6Xk0lA6.js.map +0 -1
|
@@ -4,7 +4,7 @@ import { a as __toESM, i as __require, n as __commonJSMin, r as __exportAll, t a
|
|
|
4
4
|
import { o as isRebaseIndexName } from "./collection-index-DdnsxiJ_.js";
|
|
5
5
|
import { r as outWarn, t as out } from "./cli-output-CNdMql-L.js";
|
|
6
6
|
import { t as chalk } from "./source-Br7L7GOI.js";
|
|
7
|
-
import { LEGACY_RLS_SCHEMA, REBASE_SCHEMA
|
|
7
|
+
import { LEGACY_RLS_SCHEMA, REBASE_SCHEMA } from "@rebasepro/types";
|
|
8
8
|
import { sqlRows } from "@rebasepro/common";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path from "path";
|
|
@@ -6252,10 +6252,529 @@ createExeca(mapNode);
|
|
|
6252
6252
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
6253
6253
|
var { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6254
6254
|
//#endregion
|
|
6255
|
-
//#region src/
|
|
6255
|
+
//#region src/schema/destructive-sql.ts
|
|
6256
6256
|
var import___vite_browser_external = /* @__PURE__ */ __toESM(require___vite_browser_external(), 1);
|
|
6257
|
+
/**
|
|
6258
|
+
* Pure helpers that classify an Atlas declarative-apply plan as destructive
|
|
6259
|
+
* or not, and decide what `rebase db push` should do about it.
|
|
6260
|
+
*
|
|
6261
|
+
* `db push` runs `atlas schema apply` to make the live database match the
|
|
6262
|
+
* generated `schema.sql`. Removing a collection field compiles to
|
|
6263
|
+
* `DROP COLUMN`; renaming compiles to drop-then-add — either destroys data.
|
|
6264
|
+
* We first run the apply with `--dry-run` to obtain the planned SQL, scan it
|
|
6265
|
+
* here, and refuse to auto-approve anything destructive without an explicit
|
|
6266
|
+
* opt-in.
|
|
6267
|
+
*
|
|
6268
|
+
* A type change is a destructive plan that names no DROP. `ALTER COLUMN
|
|
6269
|
+
* "price" TYPE integer` rounds every 19.99 to 20, and `TYPE date` throws away
|
|
6270
|
+
* every timestamp's time of day, in a statement that reads like a routine
|
|
6271
|
+
* alter. So a type change is gated too, unless it is a widening every value
|
|
6272
|
+
* survives unchanged — which needs the column's current type, read from the
|
|
6273
|
+
* catalogue by the caller.
|
|
6274
|
+
*
|
|
6275
|
+
* A primary key change names no DROP of data either: `DROP CONSTRAINT
|
|
6276
|
+
* "<table>_pkey", ADD PRIMARY KEY ("id", "locale")` keeps every value and
|
|
6277
|
+
* gives every row a new address. It is gated like a drop.
|
|
6278
|
+
*
|
|
6279
|
+
* Everything in this file is side-effect free so it can be unit-tested
|
|
6280
|
+
* without Atlas or a database.
|
|
6281
|
+
*/
|
|
6282
|
+
/**
|
|
6283
|
+
* SQL fragments that destroy data or data-bearing objects. Matched
|
|
6284
|
+
* case-insensitively against each statement of the plan. `IF EXISTS` /
|
|
6285
|
+
* whitespace variations are tolerated by the regexes below.
|
|
6286
|
+
*/
|
|
6287
|
+
var DESTRUCTIVE_PATTERNS = [
|
|
6288
|
+
{
|
|
6289
|
+
label: "DROP TABLE",
|
|
6290
|
+
re: /\bDROP\s+TABLE\b/i
|
|
6291
|
+
},
|
|
6292
|
+
{
|
|
6293
|
+
label: "DROP COLUMN",
|
|
6294
|
+
re: /\bDROP\s+COLUMN\b/i
|
|
6295
|
+
},
|
|
6296
|
+
{
|
|
6297
|
+
label: "DROP SCHEMA",
|
|
6298
|
+
re: /\bDROP\s+SCHEMA\b/i
|
|
6299
|
+
},
|
|
6300
|
+
{
|
|
6301
|
+
label: "DROP VIEW",
|
|
6302
|
+
re: /\bDROP\s+(MATERIALIZED\s+)?VIEW\b/i
|
|
6303
|
+
},
|
|
6304
|
+
{
|
|
6305
|
+
label: "DROP TYPE",
|
|
6306
|
+
re: /\bDROP\s+TYPE\b/i
|
|
6307
|
+
},
|
|
6308
|
+
{
|
|
6309
|
+
label: "TRUNCATE",
|
|
6310
|
+
re: /\bTRUNCATE\b/i
|
|
6311
|
+
}
|
|
6312
|
+
];
|
|
6313
|
+
/**
|
|
6314
|
+
* Split a SQL script into individual statements, dropping blank lines and
|
|
6315
|
+
* `--` comment lines. Deliberately simple: Atlas emits one plain statement
|
|
6316
|
+
* per `;`, without string literals that contain semicolons in a schema DDL
|
|
6317
|
+
* plan, so a naive split is safe and keeps this dependency-free.
|
|
6318
|
+
*/
|
|
6319
|
+
function splitSqlStatements(sql) {
|
|
6320
|
+
return sql.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n").split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6321
|
+
}
|
|
6322
|
+
/** `->`, the marker Atlas prints in front of the first line of every statement. */
|
|
6323
|
+
var PLAN_MARKER_RE = /^\s*->\s?/;
|
|
6324
|
+
/** `-------`, both the rule between sections and the one above a summary. */
|
|
6325
|
+
var PLAN_RULE_RE = /^\s*-{5,}\s*$/;
|
|
6326
|
+
/** `Planning migration statements (3 in total):` — and the count it announces. */
|
|
6327
|
+
var PLAN_HEADING_RE = /^Planning migration statements \((\d+) in total\):/m;
|
|
6328
|
+
/** What Atlas prints, and nothing else, when there is nothing to plan. */
|
|
6329
|
+
var PLAN_SYNCED_RE = /^\s*Schema is synced, no changes to be made\s*$/;
|
|
6330
|
+
/**
|
|
6331
|
+
* Read the statements out of whatever Atlas printed.
|
|
6332
|
+
*
|
|
6333
|
+
* `atlas schema apply --dry-run` does not print SQL. It prints a report, and
|
|
6334
|
+
* the same statements appear in it twice:
|
|
6335
|
+
*
|
|
6336
|
+
* Planning migration statements (2 in total):
|
|
6337
|
+
*
|
|
6338
|
+
* -- modify "people" table:
|
|
6339
|
+
* -> ALTER TABLE "public"."people" ALTER COLUMN "rating" TYPE numeric(4,1);
|
|
6340
|
+
* -- create "notes" table:
|
|
6341
|
+
* -> CREATE TABLE "public"."notes" (
|
|
6342
|
+
* "body" text NULL DEFAULT 'a;b',
|
|
6343
|
+
* …
|
|
6344
|
+
* );
|
|
6345
|
+
*
|
|
6346
|
+
* -------------------------------------------
|
|
6347
|
+
*
|
|
6348
|
+
* Analyzing planned statements (2 in total):
|
|
6349
|
+
* …
|
|
6350
|
+
* Running dry-run migration (2 statements in total):
|
|
6351
|
+
* -- modify "people" table
|
|
6352
|
+
* -> ALTER TABLE …; ← the same statement, again
|
|
6353
|
+
* -- ok (19µs)
|
|
6354
|
+
*
|
|
6355
|
+
* Splitting that on `;` is how the push gate went blind: the heading has no
|
|
6356
|
+
* `;` and does not start with `--`, so it became the first line of the first
|
|
6357
|
+
* statement, and an `ALTER TABLE` parse anchored at the start of a statement
|
|
6358
|
+
* read nothing — every narrowing type change applied without a prompt. Reading
|
|
6359
|
+
* both sections was a second bug, counting every destructive statement twice.
|
|
6360
|
+
*
|
|
6361
|
+
* So the report is read as a report: a statement is a `->` line and the lines
|
|
6362
|
+
* indented under it, up to the next `--` heading, `->`, blank line or rule;
|
|
6363
|
+
* only the first section that holds a `->` is read; and `;` plays no part,
|
|
6364
|
+
* because a default like `'a;b'` contains one.
|
|
6365
|
+
*
|
|
6366
|
+
* Plain SQL — a migration file, or an Atlas that printed SQL — has no `->`
|
|
6367
|
+
* and is split the old way.
|
|
6368
|
+
*/
|
|
6369
|
+
function readPlan(plan) {
|
|
6370
|
+
if (PLAN_SYNCED_RE.test(plan)) return { statements: [] };
|
|
6371
|
+
const lines = plan.split(/\r?\n/);
|
|
6372
|
+
if (!lines.some((line) => PLAN_MARKER_RE.test(line))) return { statements: splitSqlStatements(plan) };
|
|
6373
|
+
const heading = PLAN_HEADING_RE.exec(plan);
|
|
6374
|
+
const statements = [];
|
|
6375
|
+
let current = null;
|
|
6376
|
+
const flush = () => {
|
|
6377
|
+
if (current) statements.push(current.join("\n").trim().replace(/;\s*$/, "").trimEnd());
|
|
6378
|
+
current = null;
|
|
6379
|
+
};
|
|
6380
|
+
for (const line of lines) {
|
|
6381
|
+
if (PLAN_MARKER_RE.test(line)) {
|
|
6382
|
+
flush();
|
|
6383
|
+
current = [line.replace(PLAN_MARKER_RE, "")];
|
|
6384
|
+
continue;
|
|
6385
|
+
}
|
|
6386
|
+
if (PLAN_RULE_RE.test(line) || /^\S/.test(line) && !line.startsWith("--")) {
|
|
6387
|
+
flush();
|
|
6388
|
+
if (statements.length > 0) break;
|
|
6389
|
+
continue;
|
|
6390
|
+
}
|
|
6391
|
+
if (!current) continue;
|
|
6392
|
+
if (line.trim() === "" || line.trim().startsWith("--")) {
|
|
6393
|
+
flush();
|
|
6394
|
+
continue;
|
|
6395
|
+
}
|
|
6396
|
+
current.push(line);
|
|
6397
|
+
}
|
|
6398
|
+
flush();
|
|
6399
|
+
return {
|
|
6400
|
+
statements,
|
|
6401
|
+
announced: heading ? Number(heading[1]) : void 0
|
|
6402
|
+
};
|
|
6403
|
+
}
|
|
6404
|
+
/**
|
|
6405
|
+
* The statements of an Atlas plan, each exactly once, without Atlas's
|
|
6406
|
+
* rendering and without the trailing `;`. See {@link readPlan} for the shape
|
|
6407
|
+
* it reads; `test/fixtures/atlas-dry-run/` holds real captures of it.
|
|
6408
|
+
*/
|
|
6409
|
+
function extractPlanStatements(plan) {
|
|
6410
|
+
return readPlan(plan).statements;
|
|
6411
|
+
}
|
|
6412
|
+
/**
|
|
6413
|
+
* Every column of every ordinary table outside the system schemas, with its
|
|
6414
|
+
* type. What {@link detectDestructiveStatements} compares a planned type
|
|
6415
|
+
* change against; `format_type` because it carries the modifier (`(255)`,
|
|
6416
|
+
* `(10,2)`) that decides whether a change widens or narrows.
|
|
6417
|
+
*/
|
|
6418
|
+
var COLUMN_TYPES_SQL = `
|
|
6419
|
+
SELECT n.nspname AS "schema",
|
|
6420
|
+
c.relname AS "table",
|
|
6421
|
+
a.attname AS "column",
|
|
6422
|
+
format_type(a.atttypid, a.atttypmod) AS "type"
|
|
6423
|
+
FROM pg_attribute a
|
|
6424
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
6425
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
6426
|
+
WHERE c.relkind IN ('r', 'p')
|
|
6427
|
+
AND a.attnum > 0
|
|
6428
|
+
AND NOT a.attisdropped
|
|
6429
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
6430
|
+
AND n.nspname NOT LIKE 'pg\\_%'
|
|
6431
|
+
`;
|
|
6432
|
+
/**
|
|
6433
|
+
* Scan an Atlas plan (the SQL printed by `schema apply --dry-run`) and return
|
|
6434
|
+
* the statements that would destroy data. An empty array means the plan is
|
|
6435
|
+
* safe to auto-approve.
|
|
6436
|
+
*
|
|
6437
|
+
* `columnTypes` is what the database holds now. A type change to a column it
|
|
6438
|
+
* does not list is flagged: not knowing what a column is means not knowing
|
|
6439
|
+
* whether the change keeps its values.
|
|
6440
|
+
*/
|
|
6441
|
+
function detectDestructiveStatements(planSql, columnTypes = []) {
|
|
6442
|
+
const found = [];
|
|
6443
|
+
const { statements, announced } = readPlan(planSql);
|
|
6444
|
+
if (announced !== void 0 && announced !== statements.length) found.push({
|
|
6445
|
+
statement: `Planning migration statements (${announced} in total)`,
|
|
6446
|
+
kind: "UNREADABLE PLAN",
|
|
6447
|
+
detail: `Atlas announced ${announced} statement(s) and ${statements.length} could be read — read the plan yourself before allowing it`
|
|
6448
|
+
});
|
|
6449
|
+
for (const statement of statements) {
|
|
6450
|
+
const dropped = DESTRUCTIVE_PATTERNS.find(({ re }) => re.test(statement));
|
|
6451
|
+
const rekeyed = describePrimaryKeyChange(statement);
|
|
6452
|
+
const lossy = parseColumnTypeChanges(statement).map((change) => ({
|
|
6453
|
+
change,
|
|
6454
|
+
from: currentTypeOf(change, columnTypes)
|
|
6455
|
+
})).filter(({ change, from }) => from === void 0 || !isLosslessTypeChange(from, change.to)).map(({ change, from }) => `"${change.column}" ${from ?? "(current type unknown)"} → ${change.to}`).join("; ");
|
|
6456
|
+
const kind = dropped?.label ?? (rekeyed ? "PRIMARY KEY CHANGE" : lossy ? "ALTER COLUMN TYPE" : void 0);
|
|
6457
|
+
if (!kind) continue;
|
|
6458
|
+
const detail = [rekeyed, lossy].filter(Boolean).join("; ");
|
|
6459
|
+
found.push(detail ? {
|
|
6460
|
+
statement,
|
|
6461
|
+
kind,
|
|
6462
|
+
detail
|
|
6463
|
+
} : {
|
|
6464
|
+
statement,
|
|
6465
|
+
kind
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6468
|
+
return found;
|
|
6469
|
+
}
|
|
6470
|
+
var IDENT = String.raw`(?:"(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_$]*)`;
|
|
6471
|
+
var ALTER_TABLE_RE = new RegExp(String.raw`^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?(${IDENT})(?:\.(${IDENT}))?\s+([\s\S]*)$`, "i");
|
|
6472
|
+
var TYPE_CLAUSE_RE = new RegExp(String.raw`^ALTER\s+(?:COLUMN\s+)?(${IDENT})\s+(?:SET\s+DATA\s+)?TYPE\s+([\s\S]+)$`, "i");
|
|
6473
|
+
var unquote = (ident) => ident.startsWith("\"") ? ident.slice(1, -1).replace(/""/g, "\"") : ident;
|
|
6474
|
+
/**
|
|
6475
|
+
* Split an `ALTER TABLE`'s clause list on its top-level commas — the ones
|
|
6476
|
+
* outside parentheses and quotes, so `TYPE numeric(10, 2)` stays one clause.
|
|
6477
|
+
*/
|
|
6478
|
+
function splitClauses(body) {
|
|
6479
|
+
const clauses = [];
|
|
6480
|
+
let depth = 0;
|
|
6481
|
+
let quote;
|
|
6482
|
+
let start = 0;
|
|
6483
|
+
for (let i = 0; i < body.length; i++) {
|
|
6484
|
+
const ch = body[i];
|
|
6485
|
+
if (quote) {
|
|
6486
|
+
if (ch === quote) quote = void 0;
|
|
6487
|
+
} else if (ch === "\"" || ch === "'") quote = ch;
|
|
6488
|
+
else if (ch === "(") depth++;
|
|
6489
|
+
else if (ch === ")") depth--;
|
|
6490
|
+
else if (ch === "," && depth === 0) {
|
|
6491
|
+
clauses.push(body.slice(start, i).trim());
|
|
6492
|
+
start = i + 1;
|
|
6493
|
+
}
|
|
6494
|
+
}
|
|
6495
|
+
clauses.push(body.slice(start).trim());
|
|
6496
|
+
return clauses.filter((clause) => clause.length > 0);
|
|
6497
|
+
}
|
|
6498
|
+
/**
|
|
6499
|
+
* The statement from its `ALTER TABLE` on, wherever on a line that starts.
|
|
6500
|
+
*
|
|
6501
|
+
* {@link readPlan} hands over clean statements. This is the second line of
|
|
6502
|
+
* defence for text it could not clean — a rendering it does not know yet —
|
|
6503
|
+
* where a line of prose above the statement must not hide the statement, which
|
|
6504
|
+
* is exactly how an anchored match once read Atlas's whole plan as safe.
|
|
6505
|
+
*/
|
|
6506
|
+
function fromAlterTable(statement) {
|
|
6507
|
+
const start = /(?:^|\n)[ \t]*(?:->[ \t]*)?(ALTER\s+TABLE\b)/i.exec(statement);
|
|
6508
|
+
return start ? statement.slice(start.index + start[0].length - start[1].length).trim() : statement.trim();
|
|
6509
|
+
}
|
|
6510
|
+
/**
|
|
6511
|
+
* Every `ALTER COLUMN … [SET DATA] TYPE` in one statement. Atlas prints its
|
|
6512
|
+
* dry-run statements under a `-> ` marker and puts several clauses in one
|
|
6513
|
+
* `ALTER TABLE`, so both are handled.
|
|
6514
|
+
*/
|
|
6515
|
+
function parseColumnTypeChanges(statement) {
|
|
6516
|
+
const table = ALTER_TABLE_RE.exec(fromAlterTable(statement));
|
|
6517
|
+
if (!table) return [];
|
|
6518
|
+
const [schema, name] = table[2] ? [unquote(table[1]), unquote(table[2])] : [void 0, unquote(table[1])];
|
|
6519
|
+
const changes = [];
|
|
6520
|
+
for (const clause of splitClauses(table[3])) {
|
|
6521
|
+
const match = TYPE_CLAUSE_RE.exec(clause);
|
|
6522
|
+
if (!match) continue;
|
|
6523
|
+
const to = match[2].split(/\s+(?:USING|COLLATE)\s/i)[0].trim();
|
|
6524
|
+
changes.push({
|
|
6525
|
+
schema,
|
|
6526
|
+
table: name,
|
|
6527
|
+
column: unquote(match[1]),
|
|
6528
|
+
to
|
|
6529
|
+
});
|
|
6530
|
+
}
|
|
6531
|
+
return changes;
|
|
6532
|
+
}
|
|
6533
|
+
/** `ADD [CONSTRAINT "x"] PRIMARY KEY ("a", "b")`, one clause of an `ALTER TABLE`. */
|
|
6534
|
+
var ADD_PRIMARY_KEY_RE = new RegExp(String.raw`^ADD\s+(?:CONSTRAINT\s+${IDENT}\s+)?PRIMARY\s+KEY\b\s*(?:\(([^)]*)\))?`, "i");
|
|
6535
|
+
/** `DROP CONSTRAINT [IF EXISTS] "x"`, one clause of an `ALTER TABLE`. */
|
|
6536
|
+
var DROP_CONSTRAINT_RE = new RegExp(String.raw`^DROP\s+CONSTRAINT\s+(?:IF\s+EXISTS\s+)?(${IDENT})`, "i");
|
|
6537
|
+
/** What a row loses when the columns that address it change. */
|
|
6538
|
+
var ROW_ADDRESS_LOST = "every row's id changes, so anything that refers to a row by its id — a URL, a foreign key, an id stored elsewhere — stops finding it";
|
|
6539
|
+
/**
|
|
6540
|
+
* What an `ALTER TABLE` does to the table's primary key, in a sentence — or
|
|
6541
|
+
* `undefined` when it leaves the key alone.
|
|
6542
|
+
*
|
|
6543
|
+
* The key is a row's address: Rebase reads, links and routes every row by it.
|
|
6544
|
+
* Atlas plans a key change as `DROP CONSTRAINT "<table>_pkey", ADD PRIMARY KEY
|
|
6545
|
+
* (…)` — one routine-looking statement that names no DROP of data — so moving
|
|
6546
|
+
* a collection from `(id)` to `(id, locale)` re-keyed a populated table with
|
|
6547
|
+
* no prompt. A `CREATE TABLE`'s own `PRIMARY KEY` is not a change and is never
|
|
6548
|
+
* read here: only an `ALTER TABLE`'s clauses are. A dropped constraint counts
|
|
6549
|
+
* as the key when it carries Postgres's default name for one, `<table>_pkey`;
|
|
6550
|
+
* a key under another name is caught by the `ADD PRIMARY KEY` that replaces it.
|
|
6551
|
+
*/
|
|
6552
|
+
function describePrimaryKeyChange(statement) {
|
|
6553
|
+
const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));
|
|
6554
|
+
if (!alter) return void 0;
|
|
6555
|
+
const table = alter[2] ? `${alter[1]}.${alter[2]}` : alter[1];
|
|
6556
|
+
let added;
|
|
6557
|
+
let dropped;
|
|
6558
|
+
for (const clause of splitClauses(alter[3])) {
|
|
6559
|
+
const add = ADD_PRIMARY_KEY_RE.exec(clause);
|
|
6560
|
+
if (add) {
|
|
6561
|
+
added = add[1] === void 0 ? "a new key" : `(${add[1].split(",").map((c) => c.trim()).join(", ")})`;
|
|
6562
|
+
continue;
|
|
6563
|
+
}
|
|
6564
|
+
const drop = DROP_CONSTRAINT_RE.exec(clause);
|
|
6565
|
+
if (drop && unquote(drop[1]).endsWith("_pkey")) dropped = drop[1];
|
|
6566
|
+
}
|
|
6567
|
+
if (added && dropped) return `${table} is re-keyed on ${added}: ${ROW_ADDRESS_LOST}`;
|
|
6568
|
+
if (added) return `${table} gets the primary key ${added}: rows are addressed by it from now on, and the push fails if two rows share a value`;
|
|
6569
|
+
if (dropped) return `${table} loses its primary key ${dropped}: ${ROW_ADDRESS_LOST}`;
|
|
6570
|
+
}
|
|
6571
|
+
/**
|
|
6572
|
+
* The column's current type. A table named without its schema matches in any
|
|
6573
|
+
* schema, but only when every match agrees — two same-named tables with
|
|
6574
|
+
* different types leave the answer unknown.
|
|
6575
|
+
*/
|
|
6576
|
+
function currentTypeOf(change, columnTypes) {
|
|
6577
|
+
const types = new Set(columnTypes.filter((col) => col.table === change.table && col.column === change.column && (change.schema === void 0 || col.schema === change.schema)).map((col) => col.type));
|
|
6578
|
+
return types.size === 1 ? [...types][0] : void 0;
|
|
6579
|
+
}
|
|
6580
|
+
/** Spellings of one type, mapped to one name. Anything absent compares verbatim. */
|
|
6581
|
+
var TYPE_ALIASES = {
|
|
6582
|
+
"int": "integer",
|
|
6583
|
+
"int4": "integer",
|
|
6584
|
+
"int2": "smallint",
|
|
6585
|
+
"int8": "bigint",
|
|
6586
|
+
"character varying": "varchar",
|
|
6587
|
+
"decimal": "numeric",
|
|
6588
|
+
"timestamp without time zone": "timestamp",
|
|
6589
|
+
"timestamp with time zone": "timestamptz",
|
|
6590
|
+
"time without time zone": "time",
|
|
6591
|
+
"time with time zone": "timetz",
|
|
6592
|
+
"bool": "boolean",
|
|
6593
|
+
"float4": "real",
|
|
6594
|
+
"float8": "double precision",
|
|
6595
|
+
"character": "bpchar",
|
|
6596
|
+
"char": "bpchar"
|
|
6597
|
+
};
|
|
6598
|
+
/** Decimal digits each integer type holds, which is what a `numeric` must hold too. */
|
|
6599
|
+
var INTEGER_DIGITS = {
|
|
6600
|
+
smallint: 5,
|
|
6601
|
+
integer: 10,
|
|
6602
|
+
bigint: 19
|
|
6603
|
+
};
|
|
6604
|
+
/** Types whose modifier is a fractional-seconds precision, 6 when absent. */
|
|
6605
|
+
var SECONDS_PRECISION = /* @__PURE__ */ new Set([
|
|
6606
|
+
"timestamp",
|
|
6607
|
+
"timestamptz",
|
|
6608
|
+
"time",
|
|
6609
|
+
"timetz"
|
|
6610
|
+
]);
|
|
6611
|
+
function parseType(raw) {
|
|
6612
|
+
let text = raw.trim().replace(/\s+/g, " ");
|
|
6613
|
+
let dims = 0;
|
|
6614
|
+
for (let suffix = /\s*\[\d*\]$/.exec(text); suffix; suffix = /\s*\[\d*\]$/.exec(text)) {
|
|
6615
|
+
dims++;
|
|
6616
|
+
text = text.slice(0, suffix.index);
|
|
6617
|
+
}
|
|
6618
|
+
if (text.includes("\"")) return {
|
|
6619
|
+
base: text,
|
|
6620
|
+
dims
|
|
6621
|
+
};
|
|
6622
|
+
text = text.toLowerCase().replace(/^pg_catalog\./, "");
|
|
6623
|
+
let args;
|
|
6624
|
+
const modifier = /\(([^)]*)\)/.exec(text);
|
|
6625
|
+
if (modifier) {
|
|
6626
|
+
args = modifier[1].split(",").map((part) => Number(part.trim()));
|
|
6627
|
+
text = `${text.slice(0, modifier.index)} ${text.slice(modifier.index + modifier[0].length)}`.replace(/\s+/g, " ").trim();
|
|
6628
|
+
}
|
|
6629
|
+
return {
|
|
6630
|
+
base: TYPE_ALIASES[text] ?? text,
|
|
6631
|
+
args,
|
|
6632
|
+
dims
|
|
6633
|
+
};
|
|
6634
|
+
}
|
|
6635
|
+
/** Does moving from one modifier to another, on the same type, keep every value? */
|
|
6636
|
+
function modifierWidens(base, from, to) {
|
|
6637
|
+
if (JSON.stringify(from) === JSON.stringify(to)) return true;
|
|
6638
|
+
if (base === "varchar") return to === void 0 || from !== void 0 && to[0] >= from[0];
|
|
6639
|
+
if (base === "numeric") {
|
|
6640
|
+
if (to === void 0) return true;
|
|
6641
|
+
if (from === void 0) return false;
|
|
6642
|
+
const [p1, s1 = 0] = from;
|
|
6643
|
+
const [p2, s2 = 0] = to;
|
|
6644
|
+
return s2 >= s1 && p2 - s2 >= p1 - s1;
|
|
6645
|
+
}
|
|
6646
|
+
if (SECONDS_PRECISION.has(base)) return (to?.[0] ?? 6) >= (from?.[0] ?? 6);
|
|
6647
|
+
return false;
|
|
6648
|
+
}
|
|
6649
|
+
/**
|
|
6650
|
+
* Is `ALTER COLUMN … TYPE to` a widening, one that keeps every value exactly
|
|
6651
|
+
* as it was?
|
|
6652
|
+
*
|
|
6653
|
+
* Deliberately short. Everything not listed needs a confirmation, including
|
|
6654
|
+
* changes that are usually harmless: `timestamp` → `timestamptz` reads each
|
|
6655
|
+
* value in the session's time zone, `json` → `jsonb` drops duplicate keys,
|
|
6656
|
+
* `real` → `double precision` prints 0.1 as 0.100000001490116, and `integer` →
|
|
6657
|
+
* `text` keeps the digits but changes what the column is.
|
|
6658
|
+
*
|
|
6659
|
+
* - the same type, with a modifier at least as wide (`varchar(100)` →
|
|
6660
|
+
* `varchar(255)`, `numeric(10,2)` → `numeric(12,2)` or unbounded,
|
|
6661
|
+
* `timestamptz(3)` → `timestamptz`);
|
|
6662
|
+
* - `smallint` → `integer` → `bigint`, and any of them to a `numeric` with
|
|
6663
|
+
* room for all their digits;
|
|
6664
|
+
* - `varchar(n)` → `text`, and `text` → an unbounded `varchar`.
|
|
6665
|
+
*
|
|
6666
|
+
* Arrays follow their element type, and never gain or lose a dimension.
|
|
6667
|
+
*/
|
|
6668
|
+
function isLosslessTypeChange(from, to) {
|
|
6669
|
+
const a = parseType(from);
|
|
6670
|
+
const b = parseType(to);
|
|
6671
|
+
if (a.dims !== b.dims) return false;
|
|
6672
|
+
if (a.args?.some(Number.isNaN) || b.args?.some(Number.isNaN)) return false;
|
|
6673
|
+
if (a.base === b.base) return modifierWidens(a.base, a.args, b.args);
|
|
6674
|
+
const fromDigits = INTEGER_DIGITS[a.base];
|
|
6675
|
+
if (fromDigits !== void 0) {
|
|
6676
|
+
const toDigits = INTEGER_DIGITS[b.base];
|
|
6677
|
+
if (toDigits !== void 0) return toDigits >= fromDigits;
|
|
6678
|
+
if (b.base === "numeric") return b.args === void 0 || b.args[0] - (b.args[1] ?? 0) >= fromDigits;
|
|
6679
|
+
return false;
|
|
6680
|
+
}
|
|
6681
|
+
if (a.base === "varchar" && b.base === "text") return true;
|
|
6682
|
+
if (a.base === "text" && b.base === "varchar") return b.args === void 0;
|
|
6683
|
+
return false;
|
|
6684
|
+
}
|
|
6685
|
+
/**
|
|
6686
|
+
* Decide how `db push` should proceed given the plan's destructiveness and
|
|
6687
|
+
* the invocation context.
|
|
6688
|
+
*
|
|
6689
|
+
* - No destructive statements → `apply` (safe to auto-approve).
|
|
6690
|
+
* - Destructive + `--allow-destructive` → `apply` (operator opted in).
|
|
6691
|
+
* - Destructive + interactive TTY → `confirm` (prompt before applying).
|
|
6692
|
+
* - Destructive + non-interactive → `refuse` (never silently drop data in
|
|
6693
|
+
* CI / scripts / agents).
|
|
6694
|
+
*/
|
|
6695
|
+
function decidePushSafety(opts) {
|
|
6696
|
+
if (opts.destructiveCount === 0) return "apply";
|
|
6697
|
+
if (opts.allowDestructive) return "apply";
|
|
6698
|
+
return opts.interactive ? "confirm" : "refuse";
|
|
6699
|
+
}
|
|
6700
|
+
//#endregion
|
|
6701
|
+
//#region src/module-dir.ts
|
|
6257
6702
|
var moduleDir = path.dirname(fileURLToPath$1(import.meta.url));
|
|
6258
6703
|
//#endregion
|
|
6704
|
+
//#region src/generated-sql.ts
|
|
6705
|
+
/**
|
|
6706
|
+
* Where `db push` and `db generate` put the SQL they render from the collections.
|
|
6707
|
+
*
|
|
6708
|
+
* Five files — `schema.sql` (Atlas's desired state), `policies.sql`,
|
|
6709
|
+
* `search.sql`, `vector.sql` and `triggers.sql` — and every one of them is
|
|
6710
|
+
* scratch. Both commands regenerate all five before reading any of them, and
|
|
6711
|
+
* nothing else reads them at all. They used to be written into `drizzle/`, beside
|
|
6712
|
+
* the migrations, and committed: a copy nobody read, which every collection
|
|
6713
|
+
* change rewrote in the diff and every two branches touching collections
|
|
6714
|
+
* conflicted on.
|
|
6715
|
+
*
|
|
6716
|
+
* Relative to the directory the command runs in (the backend), like the
|
|
6717
|
+
* migrations are.
|
|
6718
|
+
*/
|
|
6719
|
+
/** The directory, relative to the working directory. POSIX, because it is also a URL path. */
|
|
6720
|
+
var GENERATED_SQL_DIR = ".rebase/sql";
|
|
6721
|
+
/**
|
|
6722
|
+
* `schema.sql` as Atlas takes it. Relative on purpose, like the
|
|
6723
|
+
* `file://drizzle/migrations` beside it: Atlas joins the URL's host and path, so
|
|
6724
|
+
* `.rebase` is read as the first path segment, measured against the pinned 1.2.3.
|
|
6725
|
+
*/
|
|
6726
|
+
var DESIRED_STATE_URL = `file://${GENERATED_SQL_DIR}/schema.sql`;
|
|
6727
|
+
/** The files the DDL generator writes, all of them derived from the collections. */
|
|
6728
|
+
var GENERATED_SQL_FILES = [
|
|
6729
|
+
"schema.sql",
|
|
6730
|
+
"policies.sql",
|
|
6731
|
+
"search.sql",
|
|
6732
|
+
"vector.sql",
|
|
6733
|
+
"triggers.sql"
|
|
6734
|
+
];
|
|
6735
|
+
/** The absolute directory, for the working directory the command runs in. */
|
|
6736
|
+
function generatedSqlDir(cwd = process.cwd()) {
|
|
6737
|
+
return path.resolve(cwd, GENERATED_SQL_DIR);
|
|
6738
|
+
}
|
|
6739
|
+
/**
|
|
6740
|
+
* The first line of every file the generator writes, in every release that
|
|
6741
|
+
* wrote them into `drizzle/`. Matching it is what makes removing the old copy
|
|
6742
|
+
* safe: a file there under one of these names that does not open this way was
|
|
6743
|
+
* written by a person, and is left alone.
|
|
6744
|
+
*/
|
|
6745
|
+
var GENERATED_HEADER = /^-- This file (is auto-generated by the Rebase DDL generator|contains RLS policies generated by Rebase)\./;
|
|
6746
|
+
/**
|
|
6747
|
+
* Get the directory ready to be written, and retire the copies an older release
|
|
6748
|
+
* committed.
|
|
6749
|
+
*
|
|
6750
|
+
* The directory ignores itself, rather than relying on the project's
|
|
6751
|
+
* `.gitignore`: a scaffold ignores `.rebase/`, but a project created before it
|
|
6752
|
+
* did would otherwise start committing these files from their new home.
|
|
6753
|
+
*
|
|
6754
|
+
* The old copies in `drizzle/` are deleted, not left behind. Nothing writes them
|
|
6755
|
+
* any more, so from the first push after an upgrade they only fall further
|
|
6756
|
+
* behind — and a stale `policies.sql` in the repository is exactly what someone
|
|
6757
|
+
* reviewing the project's RLS would read.
|
|
6758
|
+
*
|
|
6759
|
+
* @returns the old files it removed, relative to the working directory.
|
|
6760
|
+
*/
|
|
6761
|
+
function prepareGeneratedSqlDir(cwd = process.cwd()) {
|
|
6762
|
+
const dir = generatedSqlDir(cwd);
|
|
6763
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
6764
|
+
const ignore = path.join(dir, ".gitignore");
|
|
6765
|
+
if (!fs.existsSync(ignore)) fs.writeFileSync(ignore, "# Generated by `rebase db push` / `rebase db generate` from the collections.\n*\n");
|
|
6766
|
+
const removed = [];
|
|
6767
|
+
for (const name of GENERATED_SQL_FILES) {
|
|
6768
|
+
const legacy = path.join(cwd, "drizzle", name);
|
|
6769
|
+
if (!fs.existsSync(legacy)) continue;
|
|
6770
|
+
const firstLine = fs.readFileSync(legacy, "utf-8").split("\n", 1)[0];
|
|
6771
|
+
if (!GENERATED_HEADER.test(firstLine)) continue;
|
|
6772
|
+
fs.rmSync(legacy);
|
|
6773
|
+
removed.push(path.posix.join("drizzle", name));
|
|
6774
|
+
}
|
|
6775
|
+
return removed;
|
|
6776
|
+
}
|
|
6777
|
+
//#endregion
|
|
6259
6778
|
//#region src/cli-helpers.ts
|
|
6260
6779
|
/**
|
|
6261
6780
|
* Which package manager installed this project, read off its lockfile.
|
|
@@ -6419,7 +6938,7 @@ function resolveLocalBin(binName) {
|
|
|
6419
6938
|
return null;
|
|
6420
6939
|
}
|
|
6421
6940
|
async function getTableIncludesFromCollections(allCollections) {
|
|
6422
|
-
const { getTableName,
|
|
6941
|
+
const { getTableName, relationalCollections, resolveJunctionSpecs } = await import("@rebasepro/common");
|
|
6423
6942
|
const { isPostgresCollectionConfig } = await import("@rebasepro/types");
|
|
6424
6943
|
const collections = relationalCollections(allCollections);
|
|
6425
6944
|
const includes = [];
|
|
@@ -6427,14 +6946,8 @@ async function getTableIncludesFromCollections(allCollections) {
|
|
|
6427
6946
|
const tableName = getTableName(col);
|
|
6428
6947
|
const schema = isPostgresCollectionConfig(col) && col.schema ? col.schema : "public";
|
|
6429
6948
|
if (tableName) includes.push(`${schema}.${tableName}`);
|
|
6430
|
-
const resolvedRelations = resolveCollectionRelations(col);
|
|
6431
|
-
for (const relation of Object.values(resolvedRelations)) if (isManyToMany(relation)) {
|
|
6432
|
-
const junctionTableName = relation.through.table;
|
|
6433
|
-
const targetCollection = relation.target();
|
|
6434
|
-
const targetSchema = isPostgresCollectionConfig(targetCollection) && targetCollection.schema ? targetCollection.schema : "public";
|
|
6435
|
-
includes.push(`${targetSchema}.${junctionTableName}`);
|
|
6436
|
-
}
|
|
6437
6949
|
}
|
|
6950
|
+
for (const junction of resolveJunctionSpecs(collections).values()) includes.push(`${junction.schema}.${junction.table}`);
|
|
6438
6951
|
return Array.from(new Set(includes));
|
|
6439
6952
|
}
|
|
6440
6953
|
async function getTableIncludes(collectionsPath) {
|
|
@@ -6569,11 +7082,11 @@ async function dropDevDatabase(databaseUrl, devDatabaseUrl) {
|
|
|
6569
7082
|
/**
|
|
6570
7083
|
* The generated SQL for the project's `search` blocks, if it has any.
|
|
6571
7084
|
*
|
|
6572
|
-
* @param
|
|
6573
|
-
* under the working directory.
|
|
7085
|
+
* @param sqlDir directory holding the generated SQL. Defaults to
|
|
7086
|
+
* `.rebase/sql` under the working directory.
|
|
6574
7087
|
*/
|
|
6575
|
-
function readSearchDdl(
|
|
6576
|
-
const searchFile = path.join(
|
|
7088
|
+
function readSearchDdl(sqlDir = generatedSqlDir()) {
|
|
7089
|
+
const searchFile = path.join(sqlDir, "search.sql");
|
|
6577
7090
|
if (!fs.existsSync(searchFile)) return "";
|
|
6578
7091
|
return fs.readFileSync(searchFile, "utf-8").trim();
|
|
6579
7092
|
}
|
|
@@ -6590,8 +7103,8 @@ function readSearchDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
|
6590
7103
|
* silently pushing a schema whose search column never appeared is how a
|
|
6591
7104
|
* collection ends up with search configured, no error anywhere, and no results.
|
|
6592
7105
|
*/
|
|
6593
|
-
async function applySearchDdl(databaseUrl,
|
|
6594
|
-
const sql = readSearchDdl(
|
|
7106
|
+
async function applySearchDdl(databaseUrl, sqlDir = generatedSqlDir()) {
|
|
7107
|
+
const sql = readSearchDdl(sqlDir);
|
|
6595
7108
|
if (!sql) return;
|
|
6596
7109
|
const { Client } = await import("pg");
|
|
6597
7110
|
const client = new Client({ connectionString: databaseUrl });
|
|
@@ -6610,7 +7123,7 @@ async function applySearchDdl(databaseUrl, drizzleDir = path.resolve(process.cwd
|
|
|
6610
7123
|
* `search` block, which is every project that has not opted in.
|
|
6611
7124
|
*/
|
|
6612
7125
|
async function getSearchExcludes(collectionsPath) {
|
|
6613
|
-
const { searchExcludePatterns } = await import("./generate-postgres-ddl-logic-
|
|
7126
|
+
const { searchExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
|
|
6614
7127
|
return searchExcludePatterns(await loadCollectionsForCli(collectionsPath));
|
|
6615
7128
|
}
|
|
6616
7129
|
/**
|
|
@@ -6654,6 +7167,20 @@ async function queryGeneratedColumnDependencies(databaseUrl) {
|
|
|
6654
7167
|
}
|
|
6655
7168
|
}
|
|
6656
7169
|
/**
|
|
7170
|
+
* Every column in the database with its current type, for the destructive
|
|
7171
|
+
* gate to tell a widening `ALTER COLUMN … TYPE` from one that loses data.
|
|
7172
|
+
*/
|
|
7173
|
+
async function queryColumnTypes(databaseUrl) {
|
|
7174
|
+
const { Client } = await import("pg");
|
|
7175
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
7176
|
+
await client.connect();
|
|
7177
|
+
try {
|
|
7178
|
+
return (await client.query(COLUMN_TYPES_SQL)).rows;
|
|
7179
|
+
} finally {
|
|
7180
|
+
await client.end();
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
/**
|
|
6657
7184
|
* Drop the generated columns standing in the way of an apply.
|
|
6658
7185
|
*
|
|
6659
7186
|
* Their indexes and their `COMMENT ON COLUMN` stamp go with them, which is
|
|
@@ -6674,11 +7201,11 @@ async function dropGeneratedColumns(databaseUrl, statements) {
|
|
|
6674
7201
|
/**
|
|
6675
7202
|
* The generated SQL for the project's `vector` properties, if it has any.
|
|
6676
7203
|
*
|
|
6677
|
-
* @param
|
|
6678
|
-
* under the working directory.
|
|
7204
|
+
* @param sqlDir directory holding the generated SQL. Defaults to
|
|
7205
|
+
* `.rebase/sql` under the working directory.
|
|
6679
7206
|
*/
|
|
6680
|
-
function readVectorDdl(
|
|
6681
|
-
const vectorFile = path.join(
|
|
7207
|
+
function readVectorDdl(sqlDir = generatedSqlDir()) {
|
|
7208
|
+
const vectorFile = path.join(sqlDir, "vector.sql");
|
|
6682
7209
|
if (!fs.existsSync(vectorFile)) return "";
|
|
6683
7210
|
return fs.readFileSync(vectorFile, "utf-8").trim();
|
|
6684
7211
|
}
|
|
@@ -6697,8 +7224,8 @@ function readVectorDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
|
6697
7224
|
* appeared is how a project ends up with vector search configured, no error
|
|
6698
7225
|
* anywhere, and no results.
|
|
6699
7226
|
*/
|
|
6700
|
-
async function applyVectorDdl(databaseUrl,
|
|
6701
|
-
const sql = readVectorDdl(
|
|
7227
|
+
async function applyVectorDdl(databaseUrl, sqlDir = generatedSqlDir()) {
|
|
7228
|
+
const sql = readVectorDdl(sqlDir);
|
|
6702
7229
|
if (!sql) return;
|
|
6703
7230
|
const { vectorExtensionHint } = await import("./vector-index-CMJKb3C9.js").then((n) => n.f);
|
|
6704
7231
|
const { Client } = await import("pg");
|
|
@@ -6723,18 +7250,18 @@ async function applyVectorDdl(databaseUrl, drizzleDir = path.resolve(process.cwd
|
|
|
6723
7250
|
* `vector` property, which is every project that has not opted in.
|
|
6724
7251
|
*/
|
|
6725
7252
|
async function getVectorExcludes(collectionsPath) {
|
|
6726
|
-
const { vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-
|
|
7253
|
+
const { vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
|
|
6727
7254
|
return vectorExcludePatterns(await loadCollectionsForCli(collectionsPath));
|
|
6728
7255
|
}
|
|
6729
7256
|
/**
|
|
6730
7257
|
* The generated SQL for the project's `autoValue: "on_update"` properties, if
|
|
6731
7258
|
* it has any.
|
|
6732
7259
|
*
|
|
6733
|
-
* @param
|
|
6734
|
-
* under the working directory.
|
|
7260
|
+
* @param sqlDir directory holding the generated SQL. Defaults to
|
|
7261
|
+
* `.rebase/sql` under the working directory.
|
|
6735
7262
|
*/
|
|
6736
|
-
function readTriggersDdl(
|
|
6737
|
-
const triggersFile = path.join(
|
|
7263
|
+
function readTriggersDdl(sqlDir = generatedSqlDir()) {
|
|
7264
|
+
const triggersFile = path.join(sqlDir, "triggers.sql");
|
|
6738
7265
|
if (!fs.existsSync(triggersFile)) return "";
|
|
6739
7266
|
return fs.readFileSync(triggersFile, "utf-8").trim();
|
|
6740
7267
|
}
|
|
@@ -6752,8 +7279,8 @@ function readTriggersDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
|
6752
7279
|
* appeared is how `updated_at` ends up silently stale for every writer that is
|
|
6753
7280
|
* not the driver, which is the whole reason the trigger exists.
|
|
6754
7281
|
*/
|
|
6755
|
-
async function applyTriggersDdl(databaseUrl,
|
|
6756
|
-
const sql = readTriggersDdl(
|
|
7282
|
+
async function applyTriggersDdl(databaseUrl, sqlDir = generatedSqlDir()) {
|
|
7283
|
+
const sql = readTriggersDdl(sqlDir);
|
|
6757
7284
|
if (!sql) return;
|
|
6758
7285
|
const { Client } = await import("pg");
|
|
6759
7286
|
const client = new Client({ connectionString: databaseUrl });
|
|
@@ -6767,7 +7294,7 @@ async function applyTriggersDdl(databaseUrl, drizzleDir = path.resolve(process.c
|
|
|
6767
7294
|
}
|
|
6768
7295
|
/** @see getSearchExcludes */
|
|
6769
7296
|
async function getTriggerExcludes(collectionsPath) {
|
|
6770
|
-
const { triggerExcludePatterns } = await import("./generate-postgres-ddl-logic-
|
|
7297
|
+
const { triggerExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
|
|
6771
7298
|
return triggerExcludePatterns(await loadCollectionsForCli(collectionsPath));
|
|
6772
7299
|
}
|
|
6773
7300
|
/**
|
|
@@ -6853,8 +7380,8 @@ async function getForeignIndexExcludes(databaseUrl, collectionsPath, deps = {})
|
|
|
6853
7380
|
*/
|
|
6854
7381
|
async function managedIndexNames(collectionsPath) {
|
|
6855
7382
|
const collections = await loadCollectionsForCli(collectionsPath);
|
|
6856
|
-
const { searchExcludePatterns, vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-
|
|
6857
|
-
const { resolveColumnName } = await import("./column-plan-helpers-
|
|
7383
|
+
const { searchExcludePatterns, vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
|
|
7384
|
+
const { resolveColumnName } = await import("./column-plan-helpers-1-LQD0yI.js").then((n) => n.t);
|
|
6858
7385
|
const { buildCollectionIndexPlan } = await import("./collection-index-DdnsxiJ_.js").then((n) => n.i);
|
|
6859
7386
|
const names = new Set(buildCollectionIndexPlan(collections, resolveColumnName).map((spec) => spec.indexName));
|
|
6860
7387
|
for (const pattern of [...searchExcludePatterns(collections), ...vectorExcludePatterns(collections)]) names.add(pattern.slice(pattern.lastIndexOf(".") + 1));
|
|
@@ -7191,6 +7718,6 @@ async function dropLegacyAuthSchema(run, report) {
|
|
|
7191
7718
|
}
|
|
7192
7719
|
}
|
|
7193
7720
|
//#endregion
|
|
7194
|
-
export {
|
|
7721
|
+
export { resolveLocalBin as A, forLibpq as B, loadCollectionsForCli as C, readSearchDdl as D, queryGeneratedColumnDependencies as E, decidePushSafety as F, detectDestructiveStatements as I, extractPlanStatements as L, GENERATED_SQL_DIR as M, generatedSqlDir as N, readTriggersDdl as O, prepareGeneratedSqlDir as P, fromAlterTable as R, getVectorExcludes as S, queryColumnTypes as T, getDevDatabaseUrl as _, ExcludeIntrospectionError as a, getTableExcludes as b, applyVectorDdl as c, describeReinstallCommand as d, detectProjectPackageManager as f, ensureDevDatabaseExists as g, dropGeneratedColumns as h, rls_bootstrap_sql_exports as i, DESIRED_STATE_URL as j, readVectorDdl as k, describeBuildScriptRemedy as l, dropDevDatabase as m, RLS_BOOTSTRAP_STATEMENTS as n, applySearchDdl as o, diagnoseMissingBin as p, dropLegacyAuthSchema as r, applyTriggersDdl as s, RLS_BOOTSTRAP_SQL as t, describeDevAddCommand as u, getForeignIndexExcludes as v, promptConfirm as w, getTriggerExcludes as x, getSearchExcludes as y, execa as z };
|
|
7195
7722
|
|
|
7196
|
-
//# sourceMappingURL=rls-bootstrap-sql-
|
|
7723
|
+
//# sourceMappingURL=rls-bootstrap-sql-_KNnjanK.js.map
|