Skip to content

Commit 117303b

Browse files
authored
Merge pull request #1567 from constructive-io/feat/semantic-diff-node-inverses
feat(pgpm): semantic-diff inverses via @pgsql/scripts node-level API
2 parents 45cd961 + bae34ae commit 117303b

1 file changed

Lines changed: 44 additions & 13 deletions

File tree

pgpm/transform/src/semantic-diff-driver.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
* modified objects routed through the granularity pipeline
1717
* (`restructureChanges`), so names come from the naming spec and `requires`
1818
* from the statement graph — the same derivation every other projection uses.
19-
* Every emitted change carries a full deploy/revert/verify triple: drops are
20-
* `@pgsql/scripts`' `revertFor` inverses of the object's own deploy
21-
* statements (never hand-templated), reverts recreate what was dropped, and
22-
* verifies come from `verifyFor`.
19+
* Every emitted change carries a full deploy/revert/verify triple: inverses
20+
* and existence checks are composed at the AST level via `@pgsql/scripts`'
21+
* node-level API (`invertStatement`/`existenceCheck`) — never hand-templated
22+
* and never round-tripped through deparsed text — deparsing only once at the
23+
* end. Statements the node layer cannot derive fall back to the text layer
24+
* (`revertFor`/`verifyFor`) solely to surface its not-derivable comment and
25+
* warning.
2326
*
2427
* Requires `loadModule()` from `plpgsql-parser` before use.
2528
*/
2629
import { pathFor, PathStyle } from '@pgpmjs/naming-spec';
27-
import { revertFor, verifyFor } from '@pgsql/scripts';
30+
import { existenceCheck, invertStatement, revertFor, verifyFor } from '@pgsql/scripts';
2831
import type { ObjectIdentity, StatementFacts } from '@pgsql/transform';
2932
import {
3033
buildStatementGraph,
@@ -344,17 +347,45 @@ export function diffSchemas(
344347
const modifiedChanges: SemanticDeltaChange[] = [];
345348
const dropUnits: ObjectUnit[] = [];
346349

347-
/** `@pgsql/scripts` inverse of a deploy script (drops in reverse topo order). */
350+
/**
351+
* `@pgsql/scripts` inverse of a deploy script (drops in reverse topo
352+
* order), composed at the AST level via `invertStatement` and deparsed
353+
* once. Underivable statements fall back to `revertFor` on the single
354+
* statement to surface its not-derivable comment and warning.
355+
*/
348356
const inverseOf = (deploy: string, context: string): string => {
349-
const { sql, warnings: w } = revertFor(classifyStatements(deploy));
350-
warnings.push(...w.map(x => `${context}: ${x}`));
351-
return sql;
357+
const facts = classifyStatements(deploy);
358+
const graph = buildStatementGraph(facts);
359+
const pieces: string[] = [];
360+
for (const i of [...graph.order].reverse()) {
361+
const nodes = invertStatement(facts[i]);
362+
if (nodes === null) {
363+
const { sql, warnings: w } = revertFor([facts[i]]);
364+
warnings.push(...w.map(x => `${context}: ${x}`));
365+
if (sql.trim()) pieces.push(sql);
366+
continue;
367+
}
368+
pieces.push(...nodes.map(n => deparseStmt(n as Record<string, unknown>)));
369+
}
370+
return pieces.join('\n');
352371
};
353-
/** `@pgsql/scripts` existence checks for a deploy script. */
372+
/**
373+
* `@pgsql/scripts` existence checks for a deploy script, composed from
374+
* `existenceCheck`'s SelectStmt nodes and deparsed once. Underivable
375+
* statements fall back to `verifyFor` to surface the warning.
376+
*/
354377
const verifyOf = (deploy: string, context: string): string => {
355-
const { sql, warnings: w } = verifyFor(classifyStatements(deploy));
356-
warnings.push(...w.map(x => `${context}: ${x}`));
357-
return sql;
378+
const pieces: string[] = [];
379+
for (const fact of classifyStatements(deploy)) {
380+
const nodes = existenceCheck(fact);
381+
if (nodes === null) {
382+
const { warnings: w } = verifyFor([fact]);
383+
warnings.push(...w.map(x => `${context}: ${x}`));
384+
continue;
385+
}
386+
pieces.push(...nodes.map(n => deparseStmt(n as Record<string, unknown>)));
387+
}
388+
return pieces.join('\n\n');
358389
};
359390

360391
for (const key of b.order) {

0 commit comments

Comments
 (0)