@pgsql/transform 18.11.1 → 18.14.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/README.md CHANGED
@@ -58,6 +58,29 @@ Qualify unqualified object references against an inventory of known objects, wit
58
58
 
59
59
  `normalizeTree` / `cleanTree` / `validateRoundTrip` — dependency-free AST normalization and mutation-aware parse→deparse→re-parse validation.
60
60
 
61
+ ## Relationship to `@pgsql/traverse`
62
+
63
+ This package owns **policy**, not traversal. Every entry point above parses once,
64
+ then drives `walk` from `@pgsql/traverse` over the parsed script — statements and
65
+ hydrated PL/pgSQL bodies alike — with a visitor built here:
66
+
67
+ | Concern | Lives here |
68
+ |---|---|
69
+ | Which schema/role/extension a name maps to | `SchemaRouter`, `RoleRouter`, extension routes |
70
+ | What counts as a reference, a creation, a security-relevant statement | `classifyStatements` |
71
+ | When an unqualified name should be qualified | `qualifyUnqualified` |
72
+ | Whether the rewritten SQL still parses to the same tree | `validateRoundTrip` |
73
+ | How to reach every node of a SQL or PL/pgSQL AST | **`@pgsql/traverse`** |
74
+
75
+ So mapping logic never moves into the walker, and traversal logic never moves in
76
+ here. Some passes keep per-statement state (CTE names in `qualifyUnqualified`,
77
+ per-statement facts in `classifyStatements`) and drive the `walkSqlAst` /
78
+ `walkPlpgsqlAst` primitives directly with a visitor per statement.
79
+
80
+ Consequence for contributors: `__fixtures__/output/` is the regression gate for
81
+ traversal changes made *anywhere* in the ecosystem. A walker change that alters
82
+ these golden files is a behavior change, not a refactor.
83
+
61
84
  ## Scripts
62
85
 
63
86
  - `npm run fixtures` — regenerate `__fixtures__/output/` golden files from `__fixtures__/input/`
@@ -14,8 +14,8 @@
14
14
  * inventory, so built-ins that merely share a name with an extension symbol
15
15
  * (and symbols that have graduated into core) are left untouched.
16
16
  */
17
- import { walk as walkSql } from '@pgsql/traverse';
18
- import { Deparser, parseSql, transformSync, walk as walkPlpgsql } from 'plpgsql-parser';
17
+ import { walk, walkSqlAst } from '@pgsql/traverse';
18
+ import { Deparser, parseSql, transformSync } from 'plpgsql-parser';
19
19
  import { ExtensionRouter } from './extension-router';
20
20
  /** Create a fresh result tracker. */
21
21
  export function createExtensionResult() {
@@ -167,7 +167,7 @@ function rewriteSqlBodyString(body, router, result) {
167
167
  for (const stmt of stmts) {
168
168
  if (!stmt?.stmt)
169
169
  continue;
170
- walkSql(stmt.stmt, visitor);
170
+ walkSqlAst(stmt.stmt, visitor);
171
171
  pieces.push(Deparser.deparse(stmt.stmt));
172
172
  }
173
173
  return pieces.join(';\n');
@@ -181,22 +181,11 @@ export function transformExtensions(sql, router, options = {}) {
181
181
  const resolved = ExtensionRouter.from(router, options);
182
182
  const result = createExtensionResult();
183
183
  const out = transformSync(sql, (ctx) => {
184
- const stmts = ctx.sql?.stmts ?? [];
185
- const visitor = createExtensionVisitor(resolved, result);
186
- for (const stmt of stmts) {
187
- if (stmt?.stmt)
188
- walkSql(stmt.stmt, visitor);
189
- }
190
- if (resolved.hasSymbolRoutes()) {
191
- for (const fn of ctx.functions ?? []) {
192
- if (fn.plpgsql?.hydrated) {
193
- walkPlpgsql(fn.plpgsql.hydrated, {}, {
194
- walkSqlExpressions: true,
195
- sqlVisitor: visitor
196
- });
197
- }
198
- }
199
- }
184
+ walk(ctx, createExtensionVisitor(resolved, result), {
185
+ // Bodies only matter when a symbol (function/operator/type) is routed;
186
+ // a bare extension rename never appears inside one.
187
+ walkFunctionBodies: resolved.hasSymbolRoutes()
188
+ });
200
189
  }, { hydrate: true, pretty: true });
201
190
  return { sql: out, result };
202
191
  }
package/esm/facts.js CHANGED
@@ -1,5 +1,5 @@
1
- import { walk as walkSql } from '@pgsql/traverse';
2
- import { parseSql, transformSync, walk as walkPlpgsql } from 'plpgsql-parser';
1
+ import { walkSqlAst } from '@pgsql/traverse';
2
+ import { parseSql, transformSync, walkPlpgsqlAst } from 'plpgsql-parser';
3
3
  const SECURITY_TAGS = new Set([
4
4
  'CreatePolicyStmt',
5
5
  'AlterPolicyStmt',
@@ -332,7 +332,7 @@ function collectSqlBodyReferences(node, facts) {
332
332
  const visitor = createFactsVisitor(facts, facts.bodyReferences);
333
333
  for (const stmt of stmts) {
334
334
  if (stmt?.stmt)
335
- walkSql(stmt.stmt, visitor);
335
+ walkSqlAst(stmt.stmt, visitor);
336
336
  }
337
337
  }
338
338
  catch {
@@ -359,7 +359,7 @@ export function classifyStatements(sql) {
359
359
  facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
360
360
  if (stmtNode) {
361
361
  facts.stmt = stmtNode;
362
- walkSql(stmtNode, createFactsVisitor(facts));
362
+ walkSqlAst(stmtNode, createFactsVisitor(facts));
363
363
  }
364
364
  if (nodeTag === 'CreateFunctionStmt') {
365
365
  collectSqlBodyReferences(node, facts);
@@ -373,7 +373,7 @@ export function classifyStatements(sql) {
373
373
  // Refs already found outside the body (signature types, defaults)
374
374
  // constrain deploy order and are not body-only.
375
375
  const outer = new Set(facts.references.map(r => `${r.schema ?? '?'}.${r.name}`));
376
- walkPlpgsql(fn.plpgsql.hydrated, {
376
+ walkPlpgsqlAst(fn.plpgsql.hydrated, {
377
377
  PLpgSQL_stmt_dynexecute: () => { facts.dynamicSql = true; },
378
378
  PLpgSQL_stmt_dynfors: () => { facts.dynamicSql = true; }
379
379
  }, {
package/esm/index.js CHANGED
@@ -1,13 +1,13 @@
1
+ export { COMMON_EXTENSIONS, ExtensionRouter } from './extension-router';
2
+ export { createExtensionResult, createExtensionVisitor, transformExtensions, } from './extension-transform';
1
3
  export { classifyStatements } from './facts';
2
4
  export { buildStatementGraph } from './graph';
3
5
  export { identityOf } from './naming';
6
+ export { collectCreatedObjects, createQualifyVisitor, mergeInventories, qualifyUnqualified, } from './qualify';
4
7
  export { orderStatements, restructureSql } from './restructure';
5
- export { COMMON_EXTENSIONS, ExtensionRouter } from './extension-router';
6
- export { createExtensionResult, createExtensionVisitor, transformExtensions, } from './extension-transform';
7
8
  export { RoleRouter } from './role-router';
8
9
  export { createRoleResult, createRoleVisitor, transformRoles, } from './role-transform';
9
- export { collectCreatedObjects, createQualifyVisitor, mergeInventories, qualifyUnqualified, } from './qualify';
10
- export { SchemaRouter } from './router';
11
10
  export { captureAstsFromSql, captureTransformAsts, firstDifference, normalizeParseTree, validateRoundTrip, } from './round-trip';
12
11
  export { CLEAN_TREE_VOLATILE_KEYS, cleanTree, normalizeTree, trimDefElemBody, } from './round-trip-core';
12
+ export { SchemaRouter } from './router';
13
13
  export { createPlpgsqlVisitor, createSqlVisitor, escapeRegexp, extractPgpmHeader, shouldTransformSchema, transformComments, transformJsonStringValues, transformNameList, transformPlpgsqlTypeAst, transformPlpgsqlTypeString, transformRelation, transformSchemaName, transformSql, transformSqlStatement, transformVerifyCalls, validateNoUntransformedSchemas, walkPlpgsqlForSchemas, } from './transform';
package/esm/qualify.js CHANGED
@@ -14,8 +14,8 @@
14
14
  * defined by a CTE in the same statement are excluded even when they collide
15
15
  * with a routed relation.
16
16
  */
17
- import { walk as walkSql } from '@pgsql/traverse';
18
- import { Deparser, parseSql, transformSync, walk as walkPlpgsql } from 'plpgsql-parser';
17
+ import { walkSqlAst } from '@pgsql/traverse';
18
+ import { Deparser, parseSql, transformSync, walkPlpgsqlAst } from 'plpgsql-parser';
19
19
  import { classifyStatements } from './facts';
20
20
  const RELATION_TAGS = new Set([
21
21
  'CreateStmt',
@@ -119,7 +119,7 @@ function resolveRoutes(sql, options) {
119
119
  }
120
120
  function collectCteNames(stmt) {
121
121
  const names = new Set();
122
- walkSql(stmt, {
122
+ walkSqlAst(stmt, {
123
123
  CommonTableExpr: (path) => {
124
124
  if (path.node?.ctename)
125
125
  names.add(path.node.ctename);
@@ -142,7 +142,7 @@ function qualifySqlBodyString(body, routes, result) {
142
142
  if (!stmt?.stmt)
143
143
  continue;
144
144
  const visitor = createQualifyVisitor({ routes, cteNames: collectCteNames(stmt.stmt) }, result);
145
- walkSql(stmt.stmt, visitor);
145
+ walkSqlAst(stmt.stmt, visitor);
146
146
  pieces.push(Deparser.deparse(stmt.stmt));
147
147
  }
148
148
  return pieces.join(';\n');
@@ -259,13 +259,13 @@ export function qualifyUnqualified(sql, options) {
259
259
  for (const stmt of stmts) {
260
260
  if (stmt?.stmt) {
261
261
  const visitor = createQualifyVisitor({ routes, cteNames: collectCteNames(stmt.stmt) }, result);
262
- walkSql(stmt.stmt, visitor);
262
+ walkSqlAst(stmt.stmt, visitor);
263
263
  }
264
264
  }
265
265
  const bodyVisitor = createQualifyVisitor({ routes }, result);
266
266
  for (const fn of ctx.functions) {
267
267
  if (fn.plpgsql?.hydrated) {
268
- walkPlpgsql(fn.plpgsql.hydrated, {}, {
268
+ walkPlpgsqlAst(fn.plpgsql.hydrated, {}, {
269
269
  walkSqlExpressions: true,
270
270
  sqlVisitor: bodyVisitor
271
271
  });
@@ -20,7 +20,7 @@
20
20
  * carry no `rolename` and a non-`CSTRING` `roletype`, so they are never
21
21
  * rewritten.
22
22
  */
23
- import { walk as walkSql } from '@pgsql/traverse';
23
+ import { walkSqlAst } from '@pgsql/traverse';
24
24
  import { transformSync } from 'plpgsql-parser';
25
25
  import { RoleRouter } from './role-router';
26
26
  export function createRoleResult() {
@@ -122,7 +122,7 @@ export function transformRoles(sql, router) {
122
122
  const visitor = createRoleVisitor(resolved, result);
123
123
  for (const stmt of stmts) {
124
124
  if (stmt?.stmt)
125
- walkSql(stmt.stmt, visitor);
125
+ walkSqlAst(stmt.stmt, visitor);
126
126
  }
127
127
  }, { hydrate: true, pretty: true });
128
128
  return { sql: out, result };
package/esm/transform.js CHANGED
@@ -24,8 +24,8 @@
24
24
  * - Comment headers, verify function calls, JSON string values (regex fallback)
25
25
  */
26
26
  import { QuoteUtils } from '@pgsql/quotes';
27
- import { walk as walkSql } from '@pgsql/traverse';
28
- import { Deparser, parseSql, transformSync, walk as walkPlpgsql } from 'plpgsql-parser';
27
+ import { walk, walkSqlAst } from '@pgsql/traverse';
28
+ import { Deparser, parseSql, transformSync } from 'plpgsql-parser';
29
29
  import { qualifyUnqualified } from './qualify';
30
30
  import { captureTransformAsts, validateRoundTrip } from './round-trip';
31
31
  import { SchemaRouter } from './router';
@@ -721,7 +721,7 @@ export function transformPlpgsqlTypeAst(typname, schemaMapping, result) {
721
721
  }
722
722
  }
723
723
  };
724
- walkSql(parseResult.stmts[0].stmt, sqlVisitor);
724
+ walkSqlAst(parseResult.stmts[0].stmt, sqlVisitor);
725
725
  const deparsed = Deparser.deparse(parseResult.stmts[0].stmt);
726
726
  const match = deparsed.match(/SELECT\s+NULL::(.+)/i);
727
727
  if (match) {
@@ -786,7 +786,7 @@ export function walkPlpgsqlForSchemas(node, schemaMapping, result) {
786
786
  if (Array.isArray(expr.query.parseResult?.stmts)) {
787
787
  for (const stmt of expr.query.parseResult.stmts) {
788
788
  if (stmt?.stmt) {
789
- walkSql(stmt.stmt, sqlVisitor);
789
+ walkSqlAst(stmt.stmt, sqlVisitor);
790
790
  }
791
791
  }
792
792
  }
@@ -807,7 +807,7 @@ export function walkPlpgsqlForSchemas(node, schemaMapping, result) {
807
807
  if (plType.typname) {
808
808
  if (typeof plType.typname === 'object' && plType.typname.kind === 'type-name') {
809
809
  // Transform schema names directly in the typeNameNode.names array.
810
- // We cannot use walkSql here because the raw typeNameNode is not
810
+ // We cannot use walkSqlAst here because the raw typeNameNode is not
811
811
  // wrapped in the expected AST envelope that the traverse walker needs.
812
812
  if (plType.typname.typeNameNode?.names) {
813
813
  transformNameList(plType.typname.typeNameNode.names, schemaMapping, result, 'type');
@@ -849,7 +849,7 @@ function transformSqlBodyString(body, router, result) {
849
849
  for (const stmt of stmts) {
850
850
  if (!stmt?.stmt)
851
851
  continue;
852
- walkSql(stmt.stmt, visitor);
852
+ walkSqlAst(stmt.stmt, visitor);
853
853
  pieces.push(Deparser.deparse(stmt.stmt));
854
854
  }
855
855
  return pieces.join(';\n');
@@ -858,6 +858,20 @@ function transformSqlBodyString(body, router, result) {
858
858
  return body.includes('.') ? transformSchemaRefsInString(body, router, result) : body;
859
859
  }
860
860
  }
861
+ /**
862
+ * Apply schema renaming to a hydrated parse context: every top-level statement
863
+ * and every hydrated PL/pgSQL body in one walk. `walkPlpgsqlForSchemas` handles
864
+ * what the SQL visitor cannot see — PL/pgSQL-only nodes such as declared
865
+ * variable types, which carry schema-qualified names as plain strings.
866
+ */
867
+ function transformSchemasInContext(ctx, schemaMapping, result, visitorOptions) {
868
+ walk(ctx, createSqlVisitor(schemaMapping, result, visitorOptions));
869
+ for (const fn of ctx.functions ?? []) {
870
+ if (fn.plpgsql?.hydrated) {
871
+ walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, result);
872
+ }
873
+ }
874
+ }
861
875
  /**
862
876
  * Escape a string for use in a regular expression
863
877
  */
@@ -1037,23 +1051,7 @@ export function transformSqlStatement(sql, schemaMapping, result) {
1037
1051
  }
1038
1052
  try {
1039
1053
  const transformed = transformSync(sql, (ctx) => {
1040
- const sqlVisitor = createSqlVisitor(schemaMapping, r);
1041
- if (ctx.sql?.stmts) {
1042
- for (const stmt of ctx.sql.stmts) {
1043
- if (stmt?.stmt) {
1044
- walkSql(stmt.stmt, sqlVisitor);
1045
- }
1046
- }
1047
- }
1048
- for (const fn of ctx.functions) {
1049
- if (fn.plpgsql?.hydrated) {
1050
- walkPlpgsql(fn.plpgsql.hydrated, {}, {
1051
- walkSqlExpressions: true,
1052
- sqlVisitor: sqlVisitor
1053
- });
1054
- walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, r);
1055
- }
1056
- }
1054
+ transformSchemasInContext(ctx, schemaMapping, r);
1057
1055
  }, { hydrate: true, pretty: true });
1058
1056
  return { sql: transformed, result: r };
1059
1057
  }
@@ -1095,23 +1093,7 @@ function transformSqlContentAst(content, schemaMapping, result, options) {
1095
1093
  let before;
1096
1094
  try {
1097
1095
  transformedBody = transformSync(transformedBody, (ctx) => {
1098
- const sqlVisitor = createSqlVisitor(schemaMapping, result, { assumeSchemasExist });
1099
- if (ctx.sql?.stmts) {
1100
- for (const stmt of ctx.sql.stmts) {
1101
- if (stmt?.stmt) {
1102
- walkSql(stmt.stmt, sqlVisitor);
1103
- }
1104
- }
1105
- }
1106
- for (const fn of ctx.functions) {
1107
- if (fn.plpgsql?.hydrated) {
1108
- walkPlpgsql(fn.plpgsql.hydrated, {}, {
1109
- walkSqlExpressions: true,
1110
- sqlVisitor: sqlVisitor
1111
- });
1112
- walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, result);
1113
- }
1114
- }
1096
+ transformSchemasInContext(ctx, schemaMapping, result, { assumeSchemasExist });
1115
1097
  if (roundTrip) {
1116
1098
  before = captureTransformAsts(ctx);
1117
1099
  }
@@ -14,7 +14,7 @@
14
14
  * inventory, so built-ins that merely share a name with an extension symbol
15
15
  * (and symbols that have graduated into core) are left untouched.
16
16
  */
17
- import type { ExtensionRouteSpec, ExtensionRouterOptions } from './extension-router';
17
+ import type { ExtensionRouterOptions, ExtensionRouteSpec } from './extension-router';
18
18
  import { ExtensionRouter } from './extension-router';
19
19
  /** What an extension transform changed. */
20
20
  export interface ExtensionTransformResult {
@@ -172,7 +172,7 @@ function rewriteSqlBodyString(body, router, result) {
172
172
  for (const stmt of stmts) {
173
173
  if (!stmt?.stmt)
174
174
  continue;
175
- (0, traverse_1.walk)(stmt.stmt, visitor);
175
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
176
176
  pieces.push(plpgsql_parser_1.Deparser.deparse(stmt.stmt));
177
177
  }
178
178
  return pieces.join(';\n');
@@ -186,22 +186,11 @@ function transformExtensions(sql, router, options = {}) {
186
186
  const resolved = extension_router_1.ExtensionRouter.from(router, options);
187
187
  const result = createExtensionResult();
188
188
  const out = (0, plpgsql_parser_1.transformSync)(sql, (ctx) => {
189
- const stmts = ctx.sql?.stmts ?? [];
190
- const visitor = createExtensionVisitor(resolved, result);
191
- for (const stmt of stmts) {
192
- if (stmt?.stmt)
193
- (0, traverse_1.walk)(stmt.stmt, visitor);
194
- }
195
- if (resolved.hasSymbolRoutes()) {
196
- for (const fn of ctx.functions ?? []) {
197
- if (fn.plpgsql?.hydrated) {
198
- (0, plpgsql_parser_1.walk)(fn.plpgsql.hydrated, {}, {
199
- walkSqlExpressions: true,
200
- sqlVisitor: visitor
201
- });
202
- }
203
- }
204
- }
189
+ (0, traverse_1.walk)(ctx, createExtensionVisitor(resolved, result), {
190
+ // Bodies only matter when a symbol (function/operator/type) is routed;
191
+ // a bare extension rename never appears inside one.
192
+ walkFunctionBodies: resolved.hasSymbolRoutes()
193
+ });
205
194
  }, { hydrate: true, pretty: true });
206
195
  return { sql: out, result };
207
196
  }
package/facts.js CHANGED
@@ -335,7 +335,7 @@ function collectSqlBodyReferences(node, facts) {
335
335
  const visitor = createFactsVisitor(facts, facts.bodyReferences);
336
336
  for (const stmt of stmts) {
337
337
  if (stmt?.stmt)
338
- (0, traverse_1.walk)(stmt.stmt, visitor);
338
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
339
339
  }
340
340
  }
341
341
  catch {
@@ -362,7 +362,7 @@ function classifyStatements(sql) {
362
362
  facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
363
363
  if (stmtNode) {
364
364
  facts.stmt = stmtNode;
365
- (0, traverse_1.walk)(stmtNode, createFactsVisitor(facts));
365
+ (0, traverse_1.walkSqlAst)(stmtNode, createFactsVisitor(facts));
366
366
  }
367
367
  if (nodeTag === 'CreateFunctionStmt') {
368
368
  collectSqlBodyReferences(node, facts);
@@ -376,7 +376,7 @@ function classifyStatements(sql) {
376
376
  // Refs already found outside the body (signature types, defaults)
377
377
  // constrain deploy order and are not body-only.
378
378
  const outer = new Set(facts.references.map(r => `${r.schema ?? '?'}.${r.name}`));
379
- (0, plpgsql_parser_1.walk)(fn.plpgsql.hydrated, {
379
+ (0, plpgsql_parser_1.walkPlpgsqlAst)(fn.plpgsql.hydrated, {
380
380
  PLpgSQL_stmt_dynexecute: () => { facts.dynamicSql = true; },
381
381
  PLpgSQL_stmt_dynfors: () => { facts.dynamicSql = true; }
382
382
  }, {
package/index.d.ts CHANGED
@@ -1,26 +1,26 @@
1
+ export type { ExtensionDefinition, ExtensionRoute, ExtensionRouterOptions, ExtensionRouteSpec, ExtensionSymbol, ExtensionSymbolNamespace, SymbolRewrite, } from './extension-router';
2
+ export { COMMON_EXTENSIONS, ExtensionRouter } from './extension-router';
3
+ export type { ExtensionTransformResult } from './extension-transform';
4
+ export { createExtensionResult, createExtensionVisitor, transformExtensions, } from './extension-transform';
1
5
  export type { ExtensionAction, ExtensionFact, QualifiedName, StatementFacts, StatementKind, } from './facts';
2
6
  export { classifyStatements } from './facts';
3
7
  export type { EdgeKind, StatementEdge, StatementGraph, StatementNode, } from './graph';
4
8
  export { buildStatementGraph } from './graph';
5
9
  export type { ObjectIdentity, ObjectIdentityKind, } from './naming';
6
10
  export { identityOf } from './naming';
11
+ export type { ObjectInventory, QualifyResult, QualifyRoutes, QualifyTargetSelector, QualifyUnqualifiedOptions, } from './qualify';
12
+ export { collectCreatedObjects, createQualifyVisitor, mergeInventories, qualifyUnqualified, } from './qualify';
7
13
  export type { Granularity, RestructureOptions, RestructureResult, } from './restructure';
8
14
  export { orderStatements, restructureSql } from './restructure';
9
- export type { ExtensionDefinition, ExtensionRoute, ExtensionRouteSpec, ExtensionRouterOptions, ExtensionSymbol, ExtensionSymbolNamespace, SymbolRewrite, } from './extension-router';
10
- export { COMMON_EXTENSIONS, ExtensionRouter } from './extension-router';
11
- export type { ExtensionTransformResult } from './extension-transform';
12
- export { createExtensionResult, createExtensionVisitor, transformExtensions, } from './extension-transform';
13
15
  export type { RoleRouteSpec } from './role-router';
14
16
  export { RoleRouter } from './role-router';
15
17
  export type { RoleTransformResult } from './role-transform';
16
18
  export { createRoleResult, createRoleVisitor, transformRoles, } from './role-transform';
17
- export type { ObjectInventory, QualifyResult, QualifyRoutes, QualifyTargetSelector, QualifyUnqualifiedOptions, } from './qualify';
18
- export { collectCreatedObjects, createQualifyVisitor, mergeInventories, qualifyUnqualified, } from './qualify';
19
- export type { ObjectNamespace, ObjectRoute, ObjectRouteTarget, RouteNamespace, RouteSpec, SchemaRoute, } from './router';
20
- export { SchemaRouter } from './router';
21
19
  export type { CapturedAsts } from './round-trip';
22
20
  export { captureAstsFromSql, captureTransformAsts, firstDifference, normalizeParseTree, validateRoundTrip, } from './round-trip';
23
21
  export type { NormalizeTreeOptions } from './round-trip-core';
24
22
  export { CLEAN_TREE_VOLATILE_KEYS, cleanTree, normalizeTree, trimDefElemBody, } from './round-trip-core';
23
+ export type { ObjectNamespace, ObjectRoute, ObjectRouteTarget, RouteNamespace, RouteSpec, SchemaRoute, } from './router';
24
+ export { SchemaRouter } from './router';
25
25
  export type { SchemaMappingInput, SchemaTransformPass, SchemaTransformResult, TransformSqlOptions, } from './transform';
26
26
  export { createPlpgsqlVisitor, createSqlVisitor, escapeRegexp, extractPgpmHeader, shouldTransformSchema, transformComments, transformJsonStringValues, transformNameList, transformPlpgsqlTypeAst, transformPlpgsqlTypeString, transformRelation, transformSchemaName, transformSql, transformSqlStatement, transformVerifyCalls, validateNoUntransformedSchemas, walkPlpgsqlForSchemas, } from './transform';
package/index.js CHANGED
@@ -1,35 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.walkPlpgsqlForSchemas = exports.validateNoUntransformedSchemas = exports.transformVerifyCalls = exports.transformSqlStatement = exports.transformSql = exports.transformSchemaName = exports.transformRelation = exports.transformPlpgsqlTypeString = exports.transformPlpgsqlTypeAst = exports.transformNameList = exports.transformJsonStringValues = exports.transformComments = exports.shouldTransformSchema = exports.extractPgpmHeader = exports.escapeRegexp = exports.createSqlVisitor = exports.createPlpgsqlVisitor = exports.trimDefElemBody = exports.normalizeTree = exports.cleanTree = exports.CLEAN_TREE_VOLATILE_KEYS = exports.validateRoundTrip = exports.normalizeParseTree = exports.firstDifference = exports.captureTransformAsts = exports.captureAstsFromSql = exports.SchemaRouter = exports.qualifyUnqualified = exports.mergeInventories = exports.createQualifyVisitor = exports.collectCreatedObjects = exports.transformRoles = exports.createRoleVisitor = exports.createRoleResult = exports.RoleRouter = exports.transformExtensions = exports.createExtensionVisitor = exports.createExtensionResult = exports.ExtensionRouter = exports.COMMON_EXTENSIONS = exports.restructureSql = exports.orderStatements = exports.identityOf = exports.buildStatementGraph = exports.classifyStatements = void 0;
3
+ exports.walkPlpgsqlForSchemas = exports.validateNoUntransformedSchemas = exports.transformVerifyCalls = exports.transformSqlStatement = exports.transformSql = exports.transformSchemaName = exports.transformRelation = exports.transformPlpgsqlTypeString = exports.transformPlpgsqlTypeAst = exports.transformNameList = exports.transformJsonStringValues = exports.transformComments = exports.shouldTransformSchema = exports.extractPgpmHeader = exports.escapeRegexp = exports.createSqlVisitor = exports.createPlpgsqlVisitor = exports.SchemaRouter = exports.trimDefElemBody = exports.normalizeTree = exports.cleanTree = exports.CLEAN_TREE_VOLATILE_KEYS = exports.validateRoundTrip = exports.normalizeParseTree = exports.firstDifference = exports.captureTransformAsts = exports.captureAstsFromSql = exports.transformRoles = exports.createRoleVisitor = exports.createRoleResult = exports.RoleRouter = exports.restructureSql = exports.orderStatements = exports.qualifyUnqualified = exports.mergeInventories = exports.createQualifyVisitor = exports.collectCreatedObjects = exports.identityOf = exports.buildStatementGraph = exports.classifyStatements = exports.transformExtensions = exports.createExtensionVisitor = exports.createExtensionResult = exports.ExtensionRouter = exports.COMMON_EXTENSIONS = void 0;
4
+ var extension_router_1 = require("./extension-router");
5
+ Object.defineProperty(exports, "COMMON_EXTENSIONS", { enumerable: true, get: function () { return extension_router_1.COMMON_EXTENSIONS; } });
6
+ Object.defineProperty(exports, "ExtensionRouter", { enumerable: true, get: function () { return extension_router_1.ExtensionRouter; } });
7
+ var extension_transform_1 = require("./extension-transform");
8
+ Object.defineProperty(exports, "createExtensionResult", { enumerable: true, get: function () { return extension_transform_1.createExtensionResult; } });
9
+ Object.defineProperty(exports, "createExtensionVisitor", { enumerable: true, get: function () { return extension_transform_1.createExtensionVisitor; } });
10
+ Object.defineProperty(exports, "transformExtensions", { enumerable: true, get: function () { return extension_transform_1.transformExtensions; } });
4
11
  var facts_1 = require("./facts");
5
12
  Object.defineProperty(exports, "classifyStatements", { enumerable: true, get: function () { return facts_1.classifyStatements; } });
6
13
  var graph_1 = require("./graph");
7
14
  Object.defineProperty(exports, "buildStatementGraph", { enumerable: true, get: function () { return graph_1.buildStatementGraph; } });
8
15
  var naming_1 = require("./naming");
9
16
  Object.defineProperty(exports, "identityOf", { enumerable: true, get: function () { return naming_1.identityOf; } });
17
+ var qualify_1 = require("./qualify");
18
+ Object.defineProperty(exports, "collectCreatedObjects", { enumerable: true, get: function () { return qualify_1.collectCreatedObjects; } });
19
+ Object.defineProperty(exports, "createQualifyVisitor", { enumerable: true, get: function () { return qualify_1.createQualifyVisitor; } });
20
+ Object.defineProperty(exports, "mergeInventories", { enumerable: true, get: function () { return qualify_1.mergeInventories; } });
21
+ Object.defineProperty(exports, "qualifyUnqualified", { enumerable: true, get: function () { return qualify_1.qualifyUnqualified; } });
10
22
  var restructure_1 = require("./restructure");
11
23
  Object.defineProperty(exports, "orderStatements", { enumerable: true, get: function () { return restructure_1.orderStatements; } });
12
24
  Object.defineProperty(exports, "restructureSql", { enumerable: true, get: function () { return restructure_1.restructureSql; } });
13
- var extension_router_1 = require("./extension-router");
14
- Object.defineProperty(exports, "COMMON_EXTENSIONS", { enumerable: true, get: function () { return extension_router_1.COMMON_EXTENSIONS; } });
15
- Object.defineProperty(exports, "ExtensionRouter", { enumerable: true, get: function () { return extension_router_1.ExtensionRouter; } });
16
- var extension_transform_1 = require("./extension-transform");
17
- Object.defineProperty(exports, "createExtensionResult", { enumerable: true, get: function () { return extension_transform_1.createExtensionResult; } });
18
- Object.defineProperty(exports, "createExtensionVisitor", { enumerable: true, get: function () { return extension_transform_1.createExtensionVisitor; } });
19
- Object.defineProperty(exports, "transformExtensions", { enumerable: true, get: function () { return extension_transform_1.transformExtensions; } });
20
25
  var role_router_1 = require("./role-router");
21
26
  Object.defineProperty(exports, "RoleRouter", { enumerable: true, get: function () { return role_router_1.RoleRouter; } });
22
27
  var role_transform_1 = require("./role-transform");
23
28
  Object.defineProperty(exports, "createRoleResult", { enumerable: true, get: function () { return role_transform_1.createRoleResult; } });
24
29
  Object.defineProperty(exports, "createRoleVisitor", { enumerable: true, get: function () { return role_transform_1.createRoleVisitor; } });
25
30
  Object.defineProperty(exports, "transformRoles", { enumerable: true, get: function () { return role_transform_1.transformRoles; } });
26
- var qualify_1 = require("./qualify");
27
- Object.defineProperty(exports, "collectCreatedObjects", { enumerable: true, get: function () { return qualify_1.collectCreatedObjects; } });
28
- Object.defineProperty(exports, "createQualifyVisitor", { enumerable: true, get: function () { return qualify_1.createQualifyVisitor; } });
29
- Object.defineProperty(exports, "mergeInventories", { enumerable: true, get: function () { return qualify_1.mergeInventories; } });
30
- Object.defineProperty(exports, "qualifyUnqualified", { enumerable: true, get: function () { return qualify_1.qualifyUnqualified; } });
31
- var router_1 = require("./router");
32
- Object.defineProperty(exports, "SchemaRouter", { enumerable: true, get: function () { return router_1.SchemaRouter; } });
33
31
  var round_trip_1 = require("./round-trip");
34
32
  Object.defineProperty(exports, "captureAstsFromSql", { enumerable: true, get: function () { return round_trip_1.captureAstsFromSql; } });
35
33
  Object.defineProperty(exports, "captureTransformAsts", { enumerable: true, get: function () { return round_trip_1.captureTransformAsts; } });
@@ -41,6 +39,8 @@ Object.defineProperty(exports, "CLEAN_TREE_VOLATILE_KEYS", { enumerable: true, g
41
39
  Object.defineProperty(exports, "cleanTree", { enumerable: true, get: function () { return round_trip_core_1.cleanTree; } });
42
40
  Object.defineProperty(exports, "normalizeTree", { enumerable: true, get: function () { return round_trip_core_1.normalizeTree; } });
43
41
  Object.defineProperty(exports, "trimDefElemBody", { enumerable: true, get: function () { return round_trip_core_1.trimDefElemBody; } });
42
+ var router_1 = require("./router");
43
+ Object.defineProperty(exports, "SchemaRouter", { enumerable: true, get: function () { return router_1.SchemaRouter; } });
44
44
  var transform_1 = require("./transform");
45
45
  Object.defineProperty(exports, "createPlpgsqlVisitor", { enumerable: true, get: function () { return transform_1.createPlpgsqlVisitor; } });
46
46
  Object.defineProperty(exports, "createSqlVisitor", { enumerable: true, get: function () { return transform_1.createSqlVisitor; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgsql/transform",
3
- "version": "18.11.1",
3
+ "version": "18.14.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "AST-based SQL transformation, qualification, classification and closure analysis for PostgreSQL",
6
6
  "main": "index.js",
@@ -32,9 +32,9 @@
32
32
  "scan-corpus": "node scripts/scan-corpus.js"
33
33
  },
34
34
  "dependencies": {
35
- "@pgsql/quotes": "18.1.0",
36
- "@pgsql/traverse": "18.4.0",
37
- "plpgsql-parser": "18.2.3"
35
+ "@pgsql/quotes": "18.2.0",
36
+ "@pgsql/traverse": "18.7.0",
37
+ "plpgsql-parser": "18.5.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "makage": "^0.1.8"
@@ -49,5 +49,5 @@
49
49
  "classify",
50
50
  "plpgsql"
51
51
  ],
52
- "gitHead": "884ba252cf8b074c6790b3722477acc3d6f92539"
52
+ "gitHead": "a60df59baaf11fbfe516b0ed25280d5854ad00e9"
53
53
  }
package/qualify.js CHANGED
@@ -125,7 +125,7 @@ function resolveRoutes(sql, options) {
125
125
  }
126
126
  function collectCteNames(stmt) {
127
127
  const names = new Set();
128
- (0, traverse_1.walk)(stmt, {
128
+ (0, traverse_1.walkSqlAst)(stmt, {
129
129
  CommonTableExpr: (path) => {
130
130
  if (path.node?.ctename)
131
131
  names.add(path.node.ctename);
@@ -148,7 +148,7 @@ function qualifySqlBodyString(body, routes, result) {
148
148
  if (!stmt?.stmt)
149
149
  continue;
150
150
  const visitor = createQualifyVisitor({ routes, cteNames: collectCteNames(stmt.stmt) }, result);
151
- (0, traverse_1.walk)(stmt.stmt, visitor);
151
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
152
152
  pieces.push(plpgsql_parser_1.Deparser.deparse(stmt.stmt));
153
153
  }
154
154
  return pieces.join(';\n');
@@ -265,13 +265,13 @@ function qualifyUnqualified(sql, options) {
265
265
  for (const stmt of stmts) {
266
266
  if (stmt?.stmt) {
267
267
  const visitor = createQualifyVisitor({ routes, cteNames: collectCteNames(stmt.stmt) }, result);
268
- (0, traverse_1.walk)(stmt.stmt, visitor);
268
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
269
269
  }
270
270
  }
271
271
  const bodyVisitor = createQualifyVisitor({ routes }, result);
272
272
  for (const fn of ctx.functions) {
273
273
  if (fn.plpgsql?.hydrated) {
274
- (0, plpgsql_parser_1.walk)(fn.plpgsql.hydrated, {}, {
274
+ (0, plpgsql_parser_1.walkPlpgsqlAst)(fn.plpgsql.hydrated, {}, {
275
275
  walkSqlExpressions: true,
276
276
  sqlVisitor: bodyVisitor
277
277
  });
package/role-transform.js CHANGED
@@ -127,7 +127,7 @@ function transformRoles(sql, router) {
127
127
  const visitor = createRoleVisitor(resolved, result);
128
128
  for (const stmt of stmts) {
129
129
  if (stmt?.stmt)
130
- (0, traverse_1.walk)(stmt.stmt, visitor);
130
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
131
131
  }
132
132
  }, { hydrate: true, pretty: true });
133
133
  return { sql: out, result };
package/transform.js CHANGED
@@ -742,7 +742,7 @@ function transformPlpgsqlTypeAst(typname, schemaMapping, result) {
742
742
  }
743
743
  }
744
744
  };
745
- (0, traverse_1.walk)(parseResult.stmts[0].stmt, sqlVisitor);
745
+ (0, traverse_1.walkSqlAst)(parseResult.stmts[0].stmt, sqlVisitor);
746
746
  const deparsed = plpgsql_parser_1.Deparser.deparse(parseResult.stmts[0].stmt);
747
747
  const match = deparsed.match(/SELECT\s+NULL::(.+)/i);
748
748
  if (match) {
@@ -807,7 +807,7 @@ function walkPlpgsqlForSchemas(node, schemaMapping, result) {
807
807
  if (Array.isArray(expr.query.parseResult?.stmts)) {
808
808
  for (const stmt of expr.query.parseResult.stmts) {
809
809
  if (stmt?.stmt) {
810
- (0, traverse_1.walk)(stmt.stmt, sqlVisitor);
810
+ (0, traverse_1.walkSqlAst)(stmt.stmt, sqlVisitor);
811
811
  }
812
812
  }
813
813
  }
@@ -828,7 +828,7 @@ function walkPlpgsqlForSchemas(node, schemaMapping, result) {
828
828
  if (plType.typname) {
829
829
  if (typeof plType.typname === 'object' && plType.typname.kind === 'type-name') {
830
830
  // Transform schema names directly in the typeNameNode.names array.
831
- // We cannot use walkSql here because the raw typeNameNode is not
831
+ // We cannot use walkSqlAst here because the raw typeNameNode is not
832
832
  // wrapped in the expected AST envelope that the traverse walker needs.
833
833
  if (plType.typname.typeNameNode?.names) {
834
834
  transformNameList(plType.typname.typeNameNode.names, schemaMapping, result, 'type');
@@ -870,7 +870,7 @@ function transformSqlBodyString(body, router, result) {
870
870
  for (const stmt of stmts) {
871
871
  if (!stmt?.stmt)
872
872
  continue;
873
- (0, traverse_1.walk)(stmt.stmt, visitor);
873
+ (0, traverse_1.walkSqlAst)(stmt.stmt, visitor);
874
874
  pieces.push(plpgsql_parser_1.Deparser.deparse(stmt.stmt));
875
875
  }
876
876
  return pieces.join(';\n');
@@ -879,6 +879,20 @@ function transformSqlBodyString(body, router, result) {
879
879
  return body.includes('.') ? transformSchemaRefsInString(body, router, result) : body;
880
880
  }
881
881
  }
882
+ /**
883
+ * Apply schema renaming to a hydrated parse context: every top-level statement
884
+ * and every hydrated PL/pgSQL body in one walk. `walkPlpgsqlForSchemas` handles
885
+ * what the SQL visitor cannot see — PL/pgSQL-only nodes such as declared
886
+ * variable types, which carry schema-qualified names as plain strings.
887
+ */
888
+ function transformSchemasInContext(ctx, schemaMapping, result, visitorOptions) {
889
+ (0, traverse_1.walk)(ctx, createSqlVisitor(schemaMapping, result, visitorOptions));
890
+ for (const fn of ctx.functions ?? []) {
891
+ if (fn.plpgsql?.hydrated) {
892
+ walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, result);
893
+ }
894
+ }
895
+ }
882
896
  /**
883
897
  * Escape a string for use in a regular expression
884
898
  */
@@ -1058,23 +1072,7 @@ function transformSqlStatement(sql, schemaMapping, result) {
1058
1072
  }
1059
1073
  try {
1060
1074
  const transformed = (0, plpgsql_parser_1.transformSync)(sql, (ctx) => {
1061
- const sqlVisitor = createSqlVisitor(schemaMapping, r);
1062
- if (ctx.sql?.stmts) {
1063
- for (const stmt of ctx.sql.stmts) {
1064
- if (stmt?.stmt) {
1065
- (0, traverse_1.walk)(stmt.stmt, sqlVisitor);
1066
- }
1067
- }
1068
- }
1069
- for (const fn of ctx.functions) {
1070
- if (fn.plpgsql?.hydrated) {
1071
- (0, plpgsql_parser_1.walk)(fn.plpgsql.hydrated, {}, {
1072
- walkSqlExpressions: true,
1073
- sqlVisitor: sqlVisitor
1074
- });
1075
- walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, r);
1076
- }
1077
- }
1075
+ transformSchemasInContext(ctx, schemaMapping, r);
1078
1076
  }, { hydrate: true, pretty: true });
1079
1077
  return { sql: transformed, result: r };
1080
1078
  }
@@ -1116,23 +1114,7 @@ function transformSqlContentAst(content, schemaMapping, result, options) {
1116
1114
  let before;
1117
1115
  try {
1118
1116
  transformedBody = (0, plpgsql_parser_1.transformSync)(transformedBody, (ctx) => {
1119
- const sqlVisitor = createSqlVisitor(schemaMapping, result, { assumeSchemasExist });
1120
- if (ctx.sql?.stmts) {
1121
- for (const stmt of ctx.sql.stmts) {
1122
- if (stmt?.stmt) {
1123
- (0, traverse_1.walk)(stmt.stmt, sqlVisitor);
1124
- }
1125
- }
1126
- }
1127
- for (const fn of ctx.functions) {
1128
- if (fn.plpgsql?.hydrated) {
1129
- (0, plpgsql_parser_1.walk)(fn.plpgsql.hydrated, {}, {
1130
- walkSqlExpressions: true,
1131
- sqlVisitor: sqlVisitor
1132
- });
1133
- walkPlpgsqlForSchemas(fn.plpgsql.hydrated, schemaMapping, result);
1134
- }
1135
- }
1117
+ transformSchemasInContext(ctx, schemaMapping, result, { assumeSchemasExist });
1136
1118
  if (roundTrip) {
1137
1119
  before = (0, round_trip_1.captureTransformAsts)(ctx);
1138
1120
  }