@rebasepro/server-postgres 0.17.3 → 0.18.1

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.
Files changed (204) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/BranchService-ByLfM_Tt.js +360 -0
  4. package/dist/BranchService-ByLfM_Tt.js.map +1 -0
  5. package/dist/__vite-browser-external-BnuHet1e.js +46 -0
  6. package/dist/__vite-browser-external-BnuHet1e.js.map +1 -0
  7. package/dist/arg-Dni7MzLB.js +87 -0
  8. package/dist/arg-Dni7MzLB.js.map +1 -0
  9. package/dist/auth-users-columns-D2LBFrMH.js +116 -0
  10. package/dist/auth-users-columns-D2LBFrMH.js.map +1 -0
  11. package/dist/backup-argv.d.ts +19 -0
  12. package/dist/backup-cli-oM9WQVlV.js +408 -0
  13. package/dist/backup-cli-oM9WQVlV.js.map +1 -0
  14. package/dist/backup-service-Br3tqwsy.js +715 -0
  15. package/dist/backup-service-Br3tqwsy.js.map +1 -0
  16. package/dist/branch-argv.d.ts +28 -0
  17. package/dist/branch-prune.d.ts +77 -0
  18. package/dist/cli-collections-path.d.ts +31 -0
  19. package/dist/cli-errors-DxN4yzdH.js +657 -0
  20. package/dist/cli-errors-DxN4yzdH.js.map +1 -0
  21. package/dist/cli-errors.d.ts +123 -0
  22. package/dist/cli-flags.d.ts +101 -0
  23. package/dist/cli-helpers.d.ts +94 -10
  24. package/dist/cli-output-CNdMql-L.js +47 -0
  25. package/dist/cli-output-CNdMql-L.js.map +1 -0
  26. package/dist/cli.js +2020 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/collection-index-BHgT_TKd.js +276 -0
  29. package/dist/collection-index-BHgT_TKd.js.map +1 -0
  30. package/dist/{collections-schema-version-BMeu3cgv.js → collections-schema-version-T6cGvrDi.js} +1 -2
  31. package/dist/{collections-schema-version-BMeu3cgv.js.map → collections-schema-version-T6cGvrDi.js.map} +1 -1
  32. package/dist/{connection-GOKU3Hu5.js → databasePoolManager-Bj5FbeAs.js} +112 -40
  33. package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
  34. package/dist/databasePoolManager.d.ts +27 -0
  35. package/dist/doctor-D7HWmGzs.js +974 -0
  36. package/dist/doctor-D7HWmGzs.js.map +1 -0
  37. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-2c2NV8zX.js} +7 -27
  38. package/dist/ensure-collection-policies-2c2NV8zX.js.map +1 -0
  39. package/dist/ensure-collection-tables-D4wJzM4y.js +820 -0
  40. package/dist/ensure-collection-tables-D4wJzM4y.js.map +1 -0
  41. package/dist/ensure-tables-CIojK-Rx.js +650 -0
  42. package/dist/ensure-tables-CIojK-Rx.js.map +1 -0
  43. package/dist/generate-drizzle-schema-CwEgVyjO.js +123 -0
  44. package/dist/generate-drizzle-schema-CwEgVyjO.js.map +1 -0
  45. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +528 -0
  46. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +1 -0
  47. package/dist/{ensure-collection-tables-DgVixhX3.js → generate-postgres-ddl-logic-CtnpIBVv.js} +126 -984
  48. package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +1 -0
  49. package/dist/generated-schema-staleness-DQdfJl2A.js +157 -0
  50. package/dist/generated-schema-staleness-DQdfJl2A.js.map +1 -0
  51. package/dist/index.d.ts +12 -0
  52. package/dist/index.es.js +7423 -1133
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/introspect-db-logic-C6LQdTxj.js +1378 -0
  55. package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -0
  56. package/dist/policy-drift-kNJ6X2Uj.js +313 -0
  57. package/dist/policy-drift-kNJ6X2Uj.js.map +1 -0
  58. package/dist/{backup-service-DCk7KhhL.js → rls-bootstrap-sql-DLDnUjtx.js} +737 -2689
  59. package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
  60. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-DeYEjYLt.js} +65 -190
  61. package/dist/rls-enforcement-DeYEjYLt.js.map +1 -0
  62. package/dist/schema/bin/generate-drizzle-schema.d.ts +1 -0
  63. package/dist/schema/bin/generate-postgres-ddl.d.ts +1 -0
  64. package/dist/schema/column-type-drift.d.ts +79 -0
  65. package/dist/schema/doctor-cli.js +129 -0
  66. package/dist/schema/doctor-cli.js.map +1 -0
  67. package/dist/schema/doctor.d.ts +42 -0
  68. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  69. package/dist/schema/generate-drizzle-schema.d.ts +1 -1
  70. package/dist/schema/generate-drizzle-schema.js +24 -0
  71. package/dist/schema/generate-drizzle-schema.js.map +1 -0
  72. package/dist/schema/generate-next-step.d.ts +30 -0
  73. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  74. package/dist/schema/generate-postgres-ddl.d.ts +1 -1
  75. package/dist/schema/generate-postgres-ddl.js +138 -0
  76. package/dist/schema/generate-postgres-ddl.js.map +1 -0
  77. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  78. package/dist/schema/introspect-db-queries.d.ts +34 -0
  79. package/dist/schema/introspect-db.js +866 -0
  80. package/dist/schema/introspect-db.js.map +1 -0
  81. package/dist/security/rls-enforcement.d.ts +63 -2
  82. package/dist/services/BranchService.d.ts +86 -1
  83. package/dist/services/collection-helpers.d.ts +1 -1
  84. package/dist/services/realtimeService.d.ts +30 -2
  85. package/dist/services/row-pipeline.d.ts +12 -1
  86. package/dist/source-Br7L7GOI.js +410 -0
  87. package/dist/source-Br7L7GOI.js.map +1 -0
  88. package/dist/utils/pg-error-utils.d.ts +3 -1
  89. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  90. package/dist/vector-index-DkWxm2de.js +309 -0
  91. package/dist/vector-index-DkWxm2de.js.map +1 -0
  92. package/package.json +32 -27
  93. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  94. package/dist/collection-index-BRUg10H5.js +0 -5690
  95. package/dist/collection-index-BRUg10H5.js.map +0 -1
  96. package/dist/connection-GOKU3Hu5.js.map +0 -1
  97. package/dist/ensure-collection-policies-UCqgv_8c.js.map +0 -1
  98. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  99. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  100. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  101. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  102. package/dist/src-DiB5RP2Z.js +0 -541
  103. package/dist/src-DiB5RP2Z.js.map +0 -1
  104. package/dist/websocket-BZ4H5wUz.js +0 -8198
  105. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  106. package/src/PostgresAdapter.ts +0 -91
  107. package/src/PostgresBackendDriver.ts +0 -1857
  108. package/src/PostgresBootstrapper.ts +0 -1157
  109. package/src/auth/ensure-tables.ts +0 -896
  110. package/src/auth/schema-version.ts +0 -260
  111. package/src/auth/services.ts +0 -1610
  112. package/src/backup/backup-cli.ts +0 -520
  113. package/src/backup/backup-cron.ts +0 -212
  114. package/src/backup/backup-logic.ts +0 -93
  115. package/src/backup/backup-service.ts +0 -485
  116. package/src/backup/index.ts +0 -12
  117. package/src/backup/pg-tools.ts +0 -403
  118. package/src/backup/retention.ts +0 -86
  119. package/src/cli-errors.ts +0 -265
  120. package/src/cli-helpers.ts +0 -569
  121. package/src/cli-output.ts +0 -43
  122. package/src/cli.ts +0 -1339
  123. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  124. package/src/collections/buildRegistry.ts +0 -68
  125. package/src/collections/validate-relations.ts +0 -402
  126. package/src/connection.ts +0 -333
  127. package/src/data-transformer.ts +0 -845
  128. package/src/databasePoolManager.ts +0 -95
  129. package/src/history/HistoryService.ts +0 -254
  130. package/src/history/ensure-history-table.ts +0 -53
  131. package/src/index.ts +0 -19
  132. package/src/interfaces.ts +0 -60
  133. package/src/module-dir.ts +0 -7
  134. package/src/schema/atlas-argv.ts +0 -94
  135. package/src/schema/auth-schema.ts +0 -298
  136. package/src/schema/auth-users-columns.ts +0 -131
  137. package/src/schema/carved-out-migration.ts +0 -404
  138. package/src/schema/classify-change.ts +0 -436
  139. package/src/schema/collection-index.ts +0 -427
  140. package/src/schema/collections-schema-version.ts +0 -103
  141. package/src/schema/destructive-sql.ts +0 -94
  142. package/src/schema/doctor-cli.ts +0 -66
  143. package/src/schema/doctor-policy-checks.ts +0 -105
  144. package/src/schema/doctor.ts +0 -837
  145. package/src/schema/drizzle-ddl.ts +0 -33
  146. package/src/schema/dynamic-tables.test.ts +0 -302
  147. package/src/schema/dynamic-tables.ts +0 -293
  148. package/src/schema/ensure-collection-policies.ts +0 -198
  149. package/src/schema/ensure-collection-tables.test.ts +0 -518
  150. package/src/schema/ensure-collection-tables.ts +0 -1267
  151. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  152. package/src/schema/generate-drizzle-schema.ts +0 -143
  153. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  154. package/src/schema/generate-postgres-ddl.ts +0 -180
  155. package/src/schema/generate-schema-commit.ts +0 -267
  156. package/src/schema/generated-schema-staleness.ts +0 -284
  157. package/src/schema/introspect-db-constraints.ts +0 -385
  158. package/src/schema/introspect-db-inference.ts +0 -255
  159. package/src/schema/introspect-db-logic.ts +0 -1387
  160. package/src/schema/introspect-db-naming.ts +0 -15
  161. package/src/schema/introspect-db-project.ts +0 -78
  162. package/src/schema/introspect-db-queries.ts +0 -326
  163. package/src/schema/introspect-db-structure.ts +0 -670
  164. package/src/schema/introspect-db-types.ts +0 -56
  165. package/src/schema/introspect-db.ts +0 -257
  166. package/src/schema/introspect-runtime.test.ts +0 -260
  167. package/src/schema/introspect-runtime.ts +0 -327
  168. package/src/schema/non-sql-collections.test.ts +0 -131
  169. package/src/schema/rls-bootstrap-sql.ts +0 -288
  170. package/src/schema/search-column.ts +0 -643
  171. package/src/schema/test-schema.ts +0 -11
  172. package/src/schema/vector-index.ts +0 -491
  173. package/src/security/anonymous-grants.test.ts +0 -73
  174. package/src/security/policy-drift.test.ts +0 -435
  175. package/src/security/policy-drift.ts +0 -473
  176. package/src/security/rls-enforcement.ts +0 -505
  177. package/src/services/BranchService.ts +0 -326
  178. package/src/services/FetchService.ts +0 -2115
  179. package/src/services/PersistService.ts +0 -557
  180. package/src/services/RelationService.ts +0 -978
  181. package/src/services/RelationWriteService.ts +0 -653
  182. package/src/services/cdc/CdcListener.ts +0 -103
  183. package/src/services/cdc/junction-tables.ts +0 -91
  184. package/src/services/cdc/trigger-cdc.ts +0 -173
  185. package/src/services/channel-bus/ChannelBus.ts +0 -44
  186. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  187. package/src/services/channel-bus/index.ts +0 -114
  188. package/src/services/channel-history.ts +0 -424
  189. package/src/services/channel-presence.ts +0 -185
  190. package/src/services/collection-helpers.ts +0 -420
  191. package/src/services/dataService.ts +0 -256
  192. package/src/services/index.ts +0 -14
  193. package/src/services/junction-writes.ts +0 -295
  194. package/src/services/nested-path.ts +0 -145
  195. package/src/services/pg-notify-listener.ts +0 -151
  196. package/src/services/realtimeService.ts +0 -2545
  197. package/src/services/row-pipeline.ts +0 -240
  198. package/src/services/write-denial.ts +0 -55
  199. package/src/types.ts +0 -4
  200. package/src/utils/connection-string.ts +0 -58
  201. package/src/utils/drizzle-conditions.ts +0 -2743
  202. package/src/utils/pg-array-null-patch.ts +0 -42
  203. package/src/utils/pg-error-utils.ts +0 -391
  204. package/src/websocket.ts +0 -855
@@ -1,404 +0,0 @@
1
- /**
2
- * Keep the objects Rebase carved out of Atlas's view out of the migrations
3
- * Atlas writes.
4
- *
5
- * `db push` protects them with `--exclude`. `atlas migrate diff` has no such
6
- * flag — measured on the pinned 1.2.3, `--exclude` is rejected outright with
7
- * `unknown flag`, and the `exclude` attribute of an `atlas.hcl` `env` block is
8
- * accepted and then silently ignored on that path. So the diff sees the search
9
- * column that the migration directory's own appended DDL created when it
10
- * replayed, does not see it in `schema.sql`, and plans a drop:
11
- *
12
- * ALTER TABLE "public"."talents" DROP COLUMN "search_vector";
13
- *
14
- * That statement is removed here, after Atlas has written the file and before
15
- * anything is appended to it — so the input is always Atlas's own plain output,
16
- * never the dollar-quoted helper bodies that get appended afterwards.
17
- *
18
- * The drop does not arrive alone. Atlas folds every change to one table into a
19
- * single statement, so a real edit in the same run produces
20
- *
21
- * ALTER TABLE "public"."talents"
22
- * DROP COLUMN "search_vector", DROP COLUMN "interests", ADD COLUMN "headline" text NULL;
23
- *
24
- * and dropping the whole statement would silently throw away a migration the
25
- * project asked for. The removal is therefore per *clause*.
26
- *
27
- * Fail-safe throughout: a statement that mentions a carved-out object but does
28
- * not match a shape this understands is left exactly as it is and reported as
29
- * `unhandled`, so the caller can say so out loud. A spurious drop the developer
30
- * can see beats a migration quietly rewritten wrong.
31
- */
32
-
33
- /** Anything Postgres accepts in an unquoted identifier past ASCII. */
34
- const IDENT = "(?:\"(?:[^\"]|\"\")*\"|[A-Za-z_\\u0080-\\uffff][\\w$\\u0080-\\uffff]*)";
35
- const QUALIFIED = `${IDENT}(?:\\s*\\.\\s*${IDENT})*`;
36
-
37
- const ALTER_TABLE_RE = new RegExp(`^ALTER\\s+TABLE\\s+(?:ONLY\\s+)?(${QUALIFIED})\\s+([\\s\\S]+)$`, "i");
38
- const DROP_COLUMN_RE = new RegExp(`^DROP\\s+(?:COLUMN\\s+)?(?:IF\\s+EXISTS\\s+)?(${IDENT})$`, "i");
39
- const DROP_INDEX_RE = new RegExp(`^DROP\\s+INDEX\\s+(?:CONCURRENTLY\\s+)?(?:IF\\s+EXISTS\\s+)?(${QUALIFIED})$`, "i");
40
-
41
- /**
42
- * Does this statement destroy anything at all?
43
- *
44
- * The gate on reporting. A statement that merely *names* a carved-out object —
45
- * `COMMENT ON COLUMN … "search_vector"` — is not a problem and must not be
46
- * reported as one, because an unrewritable drop stops the caller outright.
47
- */
48
- const MENTIONS_DROP = /\bDROP\b/i;
49
-
50
- /** One `schema.table.object` exclude pattern, taken apart. */
51
- export interface CarvedOutObject {
52
- schema: string;
53
- table: string;
54
- object: string;
55
- }
56
-
57
- export interface StripResult {
58
- /** The migration text with the carved-out clauses gone. */
59
- sql: string;
60
- /** What was removed, as SQL fragments, for reporting. */
61
- removed: string[];
62
- /**
63
- * Statements naming a carved-out object that this could not rewrite with
64
- * confidence. Left untouched in `sql`.
65
- */
66
- unhandled: string[];
67
- /** True when nothing but comments and whitespace survives. */
68
- empty: boolean;
69
- }
70
-
71
- /**
72
- * Split `schema.table.object` patterns. Anything not in three parts is
73
- * ignored rather than guessed at — the two-part form is the one Atlas itself
74
- * silently mis-reads, and repeating that mistake here would be worse than
75
- * doing nothing.
76
- */
77
- export function parseExcludePatterns(patterns: string[]): CarvedOutObject[] {
78
- const parsed: CarvedOutObject[] = [];
79
- for (const pattern of patterns) {
80
- const parts = pattern.split(".");
81
- if (parts.length !== 3) continue;
82
- if (parts.some((p) => p.length === 0)) continue;
83
- parsed.push({ schema: parts[0], table: parts[1], object: parts[2] });
84
- }
85
- return parsed;
86
- }
87
-
88
- /** `"public"` and `public` are the same name; `"Public"` is not `public`. */
89
- function unquoteIdentifier(raw: string): string {
90
- const trimmed = raw.trim();
91
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) {
92
- return trimmed.slice(1, -1).replace(/""/g, "\"");
93
- }
94
- // An unquoted identifier is folded to lower case by Postgres.
95
- return trimmed.toLowerCase();
96
- }
97
-
98
- /** Split a dotted identifier at its top-level dots, respecting quoting. */
99
- function splitQualifiedName(raw: string): string[] | null {
100
- const parts: string[] = [];
101
- let current = "";
102
- let inQuote = false;
103
- for (let i = 0; i < raw.length; i++) {
104
- const ch = raw[i];
105
- if (inQuote) {
106
- if (ch === "\"") {
107
- if (raw[i + 1] === "\"") { current += "\"\""; i++; continue; }
108
- inQuote = false;
109
- }
110
- current += ch;
111
- continue;
112
- }
113
- if (ch === "\"") { inQuote = true; current += ch; continue; }
114
- if (ch === ".") { parts.push(current); current = ""; continue; }
115
- current += ch;
116
- }
117
- if (inQuote) return null;
118
- parts.push(current);
119
- return parts.map(unquoteIdentifier);
120
- }
121
-
122
- /**
123
- * One statement located in the source text, with the comment lines that
124
- * introduce it. Atlas writes `-- Modify "talents" table` above each statement;
125
- * removing the statement without its comment leaves a caption over nothing.
126
- */
127
- interface LocatedStatement {
128
- /** Offset of the first character of the leading comment block. */
129
- start: number;
130
- /** Offset just past the terminating semicolon. */
131
- end: number;
132
- /** Offset of the first character of the statement itself. */
133
- bodyStart: number;
134
- /** The statement without its leading comments or trailing semicolon. */
135
- body: string;
136
- }
137
-
138
- /**
139
- * Walk the script and locate every statement. Quote-, comment- and
140
- * dollar-aware: the appended halves of a migration are not passed here, but a
141
- * splitter that shreds a `DO $tag$ ... $tag$` block is a trap worth not
142
- * leaving behind — it is how the derived-names renderer broke.
143
- *
144
- * Returns null if the text ends inside a quote or a dollar-quoted body, or
145
- * trails off in a statement with no semicolon. Either means this cannot be
146
- * reasoned about safely.
147
- */
148
- function locateStatements(sql: string): LocatedStatement[] | null {
149
- const statements: LocatedStatement[] = [];
150
- let i = 0;
151
- let statementStart = 0;
152
- let sawCode = false;
153
-
154
- const pushStatement = (endExclusive: number) => {
155
- const leading = sql.slice(statementStart, endExclusive);
156
- // The leading run of comment/blank lines belongs to this statement.
157
- let consumed = 0;
158
- for (const line of leading.split("\n")) {
159
- const trimmed = line.trim();
160
- if (trimmed === "" || trimmed.startsWith("--")) {
161
- consumed += line.length + 1;
162
- continue;
163
- }
164
- break;
165
- }
166
- const bodyStart = statementStart + consumed;
167
- statements.push({
168
- start: statementStart,
169
- end: endExclusive,
170
- bodyStart,
171
- body: sql.slice(bodyStart, endExclusive).replace(/;\s*$/, "").trim()
172
- });
173
- };
174
-
175
- while (i < sql.length) {
176
- const ch = sql[i];
177
-
178
- if (ch === "-" && sql[i + 1] === "-") {
179
- const nl = sql.indexOf("\n", i);
180
- i = nl === -1 ? sql.length : nl + 1;
181
- continue;
182
- }
183
- if (ch === "/" && sql[i + 1] === "*") {
184
- const close = sql.indexOf("*/", i + 2);
185
- if (close === -1) return null;
186
- i = close + 2;
187
- sawCode = true;
188
- continue;
189
- }
190
- if (ch === "'" || ch === "\"") {
191
- const quote = ch;
192
- i++;
193
- let closed = false;
194
- while (i < sql.length) {
195
- if (sql[i] === quote) {
196
- if (sql[i + 1] === quote) { i += 2; continue; }
197
- i++;
198
- closed = true;
199
- break;
200
- }
201
- i++;
202
- }
203
- if (!closed) return null;
204
- sawCode = true;
205
- continue;
206
- }
207
- if (ch === "$") {
208
- const tag = /^\$(?:[A-Za-z_\u0080-\uffff][\w\u0080-\uffff]*)?\$/.exec(sql.slice(i));
209
- if (tag) {
210
- const close = sql.indexOf(tag[0], i + tag[0].length);
211
- if (close === -1) return null;
212
- i = close + tag[0].length;
213
- sawCode = true;
214
- continue;
215
- }
216
- }
217
- if (ch === ";") {
218
- pushStatement(i + 1);
219
- i++;
220
- statementStart = i;
221
- sawCode = false;
222
- continue;
223
- }
224
- if (!/\s/.test(ch)) sawCode = true;
225
- i++;
226
- }
227
-
228
- // Trailing text with no semicolon: only comments and whitespace is fine.
229
- if (sawCode) return null;
230
- return statements;
231
- }
232
-
233
- /** Split an ALTER TABLE action list at its top-level commas. */
234
- function splitClauses(actions: string): string[] | null {
235
- const clauses: string[] = [];
236
- let depth = 0;
237
- let current = "";
238
- let i = 0;
239
- while (i < actions.length) {
240
- const ch = actions[i];
241
- if (ch === "'" || ch === "\"") {
242
- const quote = ch;
243
- current += ch;
244
- i++;
245
- let closed = false;
246
- while (i < actions.length) {
247
- current += actions[i];
248
- if (actions[i] === quote) {
249
- if (actions[i + 1] === quote) { current += actions[i + 1]; i += 2; continue; }
250
- i++;
251
- closed = true;
252
- break;
253
- }
254
- i++;
255
- }
256
- if (!closed) return null;
257
- continue;
258
- }
259
- if (ch === "(") { depth++; current += ch; i++; continue; }
260
- if (ch === ")") { depth--; current += ch; i++; continue; }
261
- if (ch === "," && depth === 0) { clauses.push(current); current = ""; i++; continue; }
262
- current += ch;
263
- i++;
264
- }
265
- if (depth !== 0) return null;
266
- clauses.push(current);
267
- return clauses.map((c) => c.trim()).filter((c) => c.length > 0);
268
- }
269
-
270
- /**
271
- * Remove every clause and statement that would drop one of `patterns`.
272
- *
273
- * `patterns` are the `schema.table.object` strings the CLI already builds for
274
- * Atlas's `--exclude`, so the diff path and the apply path protect exactly the
275
- * same set by construction.
276
- */
277
- export function stripCarvedOutStatements(migrationSql: string, patterns: string[]): StripResult {
278
- const carved = parseExcludePatterns(patterns);
279
- if (carved.length === 0) {
280
- return { sql: migrationSql, removed: [], unhandled: [], empty: isBlank(migrationSql) };
281
- }
282
-
283
- const located = locateStatements(migrationSql);
284
- if (located === null) {
285
- // Nothing can be trusted about the offsets, so nothing moves. Worth
286
- // stopping the caller over only if one of ours is being dropped in
287
- // there; otherwise this file has nothing to do with us.
288
- const atStake = MENTIONS_DROP.test(migrationSql)
289
- && carved.some((c) => migrationSql.includes(c.object));
290
- return {
291
- sql: migrationSql,
292
- removed: [],
293
- unhandled: atStake ? [migrationSql.trim()] : [],
294
- empty: isBlank(migrationSql)
295
- };
296
- }
297
-
298
- const removed: string[] = [];
299
- const unhandled: string[] = [];
300
- // Edits as [start, end) → replacement, applied back-to-front so the
301
- // offsets of the ones still pending stay valid.
302
- const edits: { start: number; end: number; replacement: string }[] = [];
303
-
304
- for (const statement of located) {
305
- if (!carved.some((c) => statement.body.includes(c.object))) continue;
306
-
307
- const dropIndex = DROP_INDEX_RE.exec(statement.body);
308
- if (dropIndex) {
309
- const parts = splitQualifiedName(dropIndex[1]);
310
- const name = parts?.[parts.length - 1];
311
- const schema = parts && parts.length > 1 ? parts[parts.length - 2] : undefined;
312
- if (name !== undefined && carved.some((c) =>
313
- c.object === name && (schema === undefined || c.schema === schema))) {
314
- removed.push(statement.body);
315
- edits.push({ start: statement.start, end: statement.end, replacement: "" });
316
- } else {
317
- // A near-miss on the name — some other index whose name
318
- // contains ours. Left alone, and still a drop, so still
319
- // reported.
320
- unhandled.push(statement.body);
321
- }
322
- continue;
323
- }
324
-
325
- // Anything else naming a carved-out object is only our business if it
326
- // destroys something. `COMMENT ON COLUMN … "search_vector"` does not.
327
- const alter = ALTER_TABLE_RE.exec(statement.body);
328
- if (!alter) {
329
- if (MENTIONS_DROP.test(statement.body)) unhandled.push(statement.body);
330
- continue;
331
- }
332
-
333
- const target = splitQualifiedName(alter[1]);
334
- if (!target) {
335
- if (MENTIONS_DROP.test(statement.body)) unhandled.push(statement.body);
336
- continue;
337
- }
338
- const table = target[target.length - 1];
339
- const schema = target.length > 1 ? target[target.length - 2] : undefined;
340
-
341
- const clauses = splitClauses(alter[2]);
342
- if (!clauses) {
343
- if (MENTIONS_DROP.test(statement.body)) unhandled.push(statement.body);
344
- continue;
345
- }
346
-
347
- const survivors: string[] = [];
348
- let removedHere = 0;
349
- let unhandledHere = false;
350
- for (const clause of clauses) {
351
- const drop = DROP_COLUMN_RE.exec(clause);
352
- const column = drop ? unquoteIdentifier(drop[1]) : undefined;
353
- const isCarved = column !== undefined && carved.some((c) =>
354
- c.object === column &&
355
- c.table === table &&
356
- (schema === undefined || c.schema === schema));
357
- if (isCarved) {
358
- removed.push(`ALTER TABLE ${alter[1]}: ${clause}`);
359
- removedHere++;
360
- continue;
361
- }
362
- // A clause naming a carved-out object in some other shape is not
363
- // something to guess at — if it destroys it.
364
- if (MENTIONS_DROP.test(clause) && carved.some((c) => clause.includes(c.object))) {
365
- unhandledHere = true;
366
- }
367
- survivors.push(clause);
368
- }
369
-
370
- if (unhandledHere) unhandled.push(statement.body);
371
- if (removedHere === 0) continue;
372
-
373
- if (survivors.length === 0) {
374
- edits.push({ start: statement.start, end: statement.end, replacement: "" });
375
- } else {
376
- edits.push({
377
- start: statement.bodyStart,
378
- end: statement.end,
379
- replacement: `ALTER TABLE ${alter[1]} ${survivors.join(", ")};`
380
- });
381
- }
382
- }
383
-
384
- let sql = migrationSql;
385
- for (const edit of edits.sort((a, b) => b.start - a.start)) {
386
- sql = sql.slice(0, edit.start) + edit.replacement + sql.slice(edit.end);
387
- }
388
- if (edits.length > 0) sql = tidy(sql);
389
-
390
- return { sql, removed, unhandled, empty: isBlank(sql) };
391
- }
392
-
393
- /** Only comments and whitespace left — nothing a migration would do. */
394
- function isBlank(sql: string): boolean {
395
- return sql
396
- .split("\n")
397
- .every((line) => line.trim() === "" || line.trim().startsWith("--"));
398
- }
399
-
400
- /** Collapse the blank runs an excision leaves behind. */
401
- function tidy(sql: string): string {
402
- const trimmed = sql.replace(/\n{3,}/g, "\n\n").replace(/^\s*\n/, "").trimEnd();
403
- return trimmed.length === 0 ? "" : `${trimmed}\n`;
404
- }