@pgsql/transform 18.3.1 → 18.4.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.
- package/esm/index.js +1 -0
- package/esm/router.js +121 -0
- package/esm/transform.js +178 -121
- package/index.d.ts +3 -1
- package/index.js +3 -1
- package/package.json +2 -2
- package/router.d.ts +94 -0
- package/router.js +125 -0
- package/transform.d.ts +21 -12
- package/transform.js +178 -121
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgsql/transform",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.4.1",
|
|
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",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"classify",
|
|
50
50
|
"plpgsql"
|
|
51
51
|
],
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "df667707ab725d762cac0daab26a07d1b202d1e1"
|
|
53
53
|
}
|
package/router.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema routing for the core transform.
|
|
3
|
+
*
|
|
4
|
+
* The schema transform historically rewrote every reference to a source
|
|
5
|
+
* schema to a single target schema (`Map<oldSchema, newSchema>`). That is the
|
|
6
|
+
* degenerate, whole-schema case of a more general question asked at every
|
|
7
|
+
* schema-qualified occurrence:
|
|
8
|
+
*
|
|
9
|
+
* given a reference to `(schema, name)` in namespace `ns`, what schema
|
|
10
|
+
* should it live in now?
|
|
11
|
+
*
|
|
12
|
+
* A {@link SchemaRouter} answers exactly that. It unifies two dimensions:
|
|
13
|
+
*
|
|
14
|
+
* - **schema-level** default: move everything in a source schema to one target
|
|
15
|
+
* (the classic `Map` behaviour), and
|
|
16
|
+
* - **object-level** routes: send a specific object — a table, a function, a
|
|
17
|
+
* type — to its own target schema, independent of its siblings.
|
|
18
|
+
*
|
|
19
|
+
* Object routes are bucketed by PostgreSQL namespace (`relations`, `functions`,
|
|
20
|
+
* `types` — matching `pg_class` / `pg_proc` / `pg_type`), mirroring the routing
|
|
21
|
+
* model already used by {@link qualifyUnqualified}. Resolution is
|
|
22
|
+
* object-route-first, then the schema-level default, then "leave unchanged".
|
|
23
|
+
*/
|
|
24
|
+
/** PostgreSQL object namespaces relevant to schema routing. */
|
|
25
|
+
export type ObjectNamespace = 'relation' | 'function' | 'type';
|
|
26
|
+
/**
|
|
27
|
+
* A namespace hint for a schema-qualified occurrence. `schema` marks an
|
|
28
|
+
* operation on the schema itself (CREATE/DROP/GRANT ON SCHEMA, search_path);
|
|
29
|
+
* `unknown` marks a site whose namespace cannot be determined statically, in
|
|
30
|
+
* which case only the schema-level default applies.
|
|
31
|
+
*/
|
|
32
|
+
export type RouteNamespace = ObjectNamespace | 'schema' | 'unknown';
|
|
33
|
+
/** Per-source-schema routing: a schema-level default plus per-object routes. */
|
|
34
|
+
export interface SchemaRoute {
|
|
35
|
+
/**
|
|
36
|
+
* Schema-level default: every object in this source schema that has no more
|
|
37
|
+
* specific object route moves here. Omit to route *only* the named objects
|
|
38
|
+
* and leave the rest (and the schema itself) untouched.
|
|
39
|
+
*/
|
|
40
|
+
schema?: string;
|
|
41
|
+
/** Relation name (table/view/sequence/matview) → target schema. */
|
|
42
|
+
relations?: Record<string, string>;
|
|
43
|
+
/** Function/procedure/aggregate name → target schema. */
|
|
44
|
+
functions?: Record<string, string>;
|
|
45
|
+
/** Type/domain name → target schema. */
|
|
46
|
+
types?: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
/** The full routing specification: one {@link SchemaRoute} per source schema. */
|
|
49
|
+
export type RouteSpec = Record<string, SchemaRoute>;
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the target schema for any schema-qualified occurrence, unifying the
|
|
52
|
+
* whole-schema `Map` behaviour and per-object routing behind one `resolve`.
|
|
53
|
+
*/
|
|
54
|
+
export declare class SchemaRouter {
|
|
55
|
+
private readonly routes;
|
|
56
|
+
constructor(routes?: RouteSpec | Map<string, SchemaRoute>);
|
|
57
|
+
/** Build a router from the classic whole-schema `Map<oldSchema,newSchema>`. */
|
|
58
|
+
static fromSchemaMap(mapping: Map<string, string> | Record<string, string>): SchemaRouter;
|
|
59
|
+
/** Coerce a `Map`, plain mapping, or existing router into a router. */
|
|
60
|
+
static from(source: SchemaRouter | Map<string, string> | Record<string, string>): SchemaRouter;
|
|
61
|
+
/** True when this router might rewrite something in `sourceSchema`. */
|
|
62
|
+
has(sourceSchema: string | undefined | null): boolean;
|
|
63
|
+
/** True when the router carries no routes at all. */
|
|
64
|
+
get size(): number;
|
|
65
|
+
/**
|
|
66
|
+
* True when any route targets individual objects (as opposed to whole
|
|
67
|
+
* schemas). Object routes require AST-precise rewriting of opaque function
|
|
68
|
+
* bodies; whole-schema routes are handled by the cheaper string passes.
|
|
69
|
+
*/
|
|
70
|
+
hasObjectRoutes(): boolean;
|
|
71
|
+
/** Every source schema this router may touch. */
|
|
72
|
+
sourceSchemas(): string[];
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the target schema for `(sourceSchema, name)` in namespace `ns`,
|
|
75
|
+
* or `undefined` to leave it unchanged. Object routes win over the
|
|
76
|
+
* schema-level default; the default applies to schema-level operations and
|
|
77
|
+
* to any object without a specific route.
|
|
78
|
+
*/
|
|
79
|
+
resolve(sourceSchema: string | undefined | null, name?: string, ns?: RouteNamespace): string | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Source schemas whose *every* object is guaranteed to move — i.e. those
|
|
82
|
+
* with a schema-level default. After a transform, none of these should
|
|
83
|
+
* survive as a qualifier; partially-routed schemas may legitimately remain,
|
|
84
|
+
* so they are excluded from the strict leftover check.
|
|
85
|
+
*/
|
|
86
|
+
fullyMovedSchemas(): Map<string, string>;
|
|
87
|
+
/**
|
|
88
|
+
* A flat schema-level view (`oldSchema → newSchema`) for legacy string-level
|
|
89
|
+
* passes and comment/verify/JSON rewrites that operate per source schema.
|
|
90
|
+
* Only schema-level defaults are included; object-only routes carry no single
|
|
91
|
+
* schema answer and are omitted.
|
|
92
|
+
*/
|
|
93
|
+
schemaLevelMap(): Map<string, string>;
|
|
94
|
+
}
|
package/router.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Schema routing for the core transform.
|
|
4
|
+
*
|
|
5
|
+
* The schema transform historically rewrote every reference to a source
|
|
6
|
+
* schema to a single target schema (`Map<oldSchema, newSchema>`). That is the
|
|
7
|
+
* degenerate, whole-schema case of a more general question asked at every
|
|
8
|
+
* schema-qualified occurrence:
|
|
9
|
+
*
|
|
10
|
+
* given a reference to `(schema, name)` in namespace `ns`, what schema
|
|
11
|
+
* should it live in now?
|
|
12
|
+
*
|
|
13
|
+
* A {@link SchemaRouter} answers exactly that. It unifies two dimensions:
|
|
14
|
+
*
|
|
15
|
+
* - **schema-level** default: move everything in a source schema to one target
|
|
16
|
+
* (the classic `Map` behaviour), and
|
|
17
|
+
* - **object-level** routes: send a specific object — a table, a function, a
|
|
18
|
+
* type — to its own target schema, independent of its siblings.
|
|
19
|
+
*
|
|
20
|
+
* Object routes are bucketed by PostgreSQL namespace (`relations`, `functions`,
|
|
21
|
+
* `types` — matching `pg_class` / `pg_proc` / `pg_type`), mirroring the routing
|
|
22
|
+
* model already used by {@link qualifyUnqualified}. Resolution is
|
|
23
|
+
* object-route-first, then the schema-level default, then "leave unchanged".
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.SchemaRouter = void 0;
|
|
27
|
+
const NS_BUCKET = {
|
|
28
|
+
relation: 'relations',
|
|
29
|
+
function: 'functions',
|
|
30
|
+
type: 'types'
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Resolves the target schema for any schema-qualified occurrence, unifying the
|
|
34
|
+
* whole-schema `Map` behaviour and per-object routing behind one `resolve`.
|
|
35
|
+
*/
|
|
36
|
+
class SchemaRouter {
|
|
37
|
+
routes;
|
|
38
|
+
constructor(routes = {}) {
|
|
39
|
+
this.routes = routes instanceof Map ? new Map(routes) : new Map(Object.entries(routes));
|
|
40
|
+
}
|
|
41
|
+
/** Build a router from the classic whole-schema `Map<oldSchema,newSchema>`. */
|
|
42
|
+
static fromSchemaMap(mapping) {
|
|
43
|
+
const entries = mapping instanceof Map ? [...mapping.entries()] : Object.entries(mapping);
|
|
44
|
+
const spec = {};
|
|
45
|
+
for (const [from, to] of entries)
|
|
46
|
+
spec[from] = { schema: to };
|
|
47
|
+
return new SchemaRouter(spec);
|
|
48
|
+
}
|
|
49
|
+
/** Coerce a `Map`, plain mapping, or existing router into a router. */
|
|
50
|
+
static from(source) {
|
|
51
|
+
if (source instanceof SchemaRouter)
|
|
52
|
+
return source;
|
|
53
|
+
return SchemaRouter.fromSchemaMap(source);
|
|
54
|
+
}
|
|
55
|
+
/** True when this router might rewrite something in `sourceSchema`. */
|
|
56
|
+
has(sourceSchema) {
|
|
57
|
+
if (!sourceSchema)
|
|
58
|
+
return false;
|
|
59
|
+
return this.routes.has(sourceSchema);
|
|
60
|
+
}
|
|
61
|
+
/** True when the router carries no routes at all. */
|
|
62
|
+
get size() {
|
|
63
|
+
return this.routes.size;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* True when any route targets individual objects (as opposed to whole
|
|
67
|
+
* schemas). Object routes require AST-precise rewriting of opaque function
|
|
68
|
+
* bodies; whole-schema routes are handled by the cheaper string passes.
|
|
69
|
+
*/
|
|
70
|
+
hasObjectRoutes() {
|
|
71
|
+
for (const route of this.routes.values()) {
|
|
72
|
+
if (route.relations || route.functions || route.types)
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/** Every source schema this router may touch. */
|
|
78
|
+
sourceSchemas() {
|
|
79
|
+
return [...this.routes.keys()];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the target schema for `(sourceSchema, name)` in namespace `ns`,
|
|
83
|
+
* or `undefined` to leave it unchanged. Object routes win over the
|
|
84
|
+
* schema-level default; the default applies to schema-level operations and
|
|
85
|
+
* to any object without a specific route.
|
|
86
|
+
*/
|
|
87
|
+
resolve(sourceSchema, name, ns = 'unknown') {
|
|
88
|
+
if (!sourceSchema)
|
|
89
|
+
return undefined;
|
|
90
|
+
const route = this.routes.get(sourceSchema);
|
|
91
|
+
if (!route)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (name && (ns === 'relation' || ns === 'function' || ns === 'type')) {
|
|
94
|
+
const bucket = route[NS_BUCKET[ns]];
|
|
95
|
+
const mapped = bucket?.[name];
|
|
96
|
+
if (mapped !== undefined)
|
|
97
|
+
return mapped;
|
|
98
|
+
}
|
|
99
|
+
return route.schema;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Source schemas whose *every* object is guaranteed to move — i.e. those
|
|
103
|
+
* with a schema-level default. After a transform, none of these should
|
|
104
|
+
* survive as a qualifier; partially-routed schemas may legitimately remain,
|
|
105
|
+
* so they are excluded from the strict leftover check.
|
|
106
|
+
*/
|
|
107
|
+
fullyMovedSchemas() {
|
|
108
|
+
const out = new Map();
|
|
109
|
+
for (const [from, route] of this.routes) {
|
|
110
|
+
if (route.schema !== undefined)
|
|
111
|
+
out.set(from, route.schema);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A flat schema-level view (`oldSchema → newSchema`) for legacy string-level
|
|
117
|
+
* passes and comment/verify/JSON rewrites that operate per source schema.
|
|
118
|
+
* Only schema-level defaults are included; object-only routes carry no single
|
|
119
|
+
* schema answer and are omitted.
|
|
120
|
+
*/
|
|
121
|
+
schemaLevelMap() {
|
|
122
|
+
return this.fullyMovedSchemas();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.SchemaRouter = SchemaRouter;
|
package/transform.d.ts
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
* - Comment headers, verify function calls, JSON string values (regex fallback)
|
|
25
25
|
*/
|
|
26
26
|
import type { QualifyUnqualifiedOptions } from './qualify';
|
|
27
|
+
import type { RouteNamespace } from './router';
|
|
28
|
+
import { SchemaRouter } from './router';
|
|
29
|
+
/** A schema mapping accepted by the transform: the classic whole-schema map or a router. */
|
|
30
|
+
export type SchemaMappingInput = Map<string, string> | SchemaRouter;
|
|
27
31
|
export interface SchemaTransformResult {
|
|
28
32
|
schemasFound: Set<string>;
|
|
29
33
|
schemasTransformed: Map<string, string>;
|
|
@@ -91,26 +95,31 @@ export declare function shouldTransformSchema(schemaName: string | undefined, sc
|
|
|
91
95
|
/**
|
|
92
96
|
* Transform schema names in a String node array (used for funcname, names, etc.)
|
|
93
97
|
* These arrays contain String nodes like { String: { sval: 'schema_name' } }
|
|
98
|
+
*
|
|
99
|
+
* `ns` names the namespace of the referenced object so object-level routes can
|
|
100
|
+
* apply; the object's own name is the last element of the list. When `ns` is
|
|
101
|
+
* `unknown` (the default) only the schema-level default applies — identical to
|
|
102
|
+
* the historic whole-schema behaviour.
|
|
94
103
|
*/
|
|
95
|
-
export declare function transformNameList(names: any[] | undefined, schemaMapping:
|
|
104
|
+
export declare function transformNameList(names: any[] | undefined, schemaMapping: SchemaMappingInput, result: SchemaTransformResult, ns?: RouteNamespace): void;
|
|
96
105
|
/**
|
|
97
106
|
* Transform a bare schema-name field on a node (e.g. { String: { sval } }
|
|
98
107
|
* entries in GrantStmt.objects, RenameStmt.subname, CommentStmt.object).
|
|
99
108
|
* Returns the (possibly new) schema name.
|
|
100
109
|
*/
|
|
101
|
-
export declare function transformSchemaNameField(container: any, field: string, schemaMapping:
|
|
110
|
+
export declare function transformSchemaNameField(container: any, field: string, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): void;
|
|
102
111
|
/**
|
|
103
112
|
* Transform a RangeVar-like relation object (has schemaname/relname fields).
|
|
104
113
|
* Many statement nodes embed a relation that the walker does NOT auto-recurse into.
|
|
105
114
|
*/
|
|
106
|
-
export declare function transformRelation(relation: any, schemaMapping:
|
|
115
|
+
export declare function transformRelation(relation: any, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): void;
|
|
107
116
|
/**
|
|
108
117
|
* Transform schema-qualified references embedded in a plain string
|
|
109
118
|
* (e.g. advisory-lock keys like 'schema-name.fn_name', COMMENT text,
|
|
110
119
|
* RAISE messages). Only occurrences of a mapped schema name immediately
|
|
111
120
|
* followed by a dot are rewritten.
|
|
112
121
|
*/
|
|
113
|
-
export declare function transformSchemaRefsInString(str: string, schemaMapping:
|
|
122
|
+
export declare function transformSchemaRefsInString(str: string, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): string;
|
|
114
123
|
/**
|
|
115
124
|
* Create a SQL AST visitor that transforms schema names.
|
|
116
125
|
*
|
|
@@ -119,7 +128,7 @@ export declare function transformSchemaRefsInString(str: string, schemaMapping:
|
|
|
119
128
|
* However, some node types carry schema names as plain strings or name lists
|
|
120
129
|
* that require explicit handlers.
|
|
121
130
|
*/
|
|
122
|
-
export declare function createSqlVisitor(schemaMapping:
|
|
131
|
+
export declare function createSqlVisitor(schemaMapping: SchemaMappingInput, result: SchemaTransformResult, visitorOptions?: {
|
|
123
132
|
assumeSchemasExist?: Set<string>;
|
|
124
133
|
}): {
|
|
125
134
|
RangeVar: (path: any) => void;
|
|
@@ -182,27 +191,27 @@ export declare function createSqlVisitor(schemaMapping: Map<string, string>, res
|
|
|
182
191
|
* Throws an error if any schema names from the mapping are found in the output,
|
|
183
192
|
* indicating that the AST visitor is missing a handler for that node type.
|
|
184
193
|
*/
|
|
185
|
-
export declare function validateNoUntransformedSchemas(content: string, schemaMapping:
|
|
194
|
+
export declare function validateNoUntransformedSchemas(content: string, schemaMapping: SchemaMappingInput): void;
|
|
186
195
|
/**
|
|
187
196
|
* Create a PL/pgSQL visitor that transforms schema names in PL/pgSQL-specific nodes.
|
|
188
197
|
*/
|
|
189
|
-
export declare function createPlpgsqlVisitor(schemaMapping:
|
|
198
|
+
export declare function createPlpgsqlVisitor(schemaMapping: SchemaMappingInput, result: SchemaTransformResult): {
|
|
190
199
|
PLpgSQL_type: (path: any) => void;
|
|
191
200
|
PLpgSQL_var: (path: any) => void;
|
|
192
201
|
};
|
|
193
202
|
/**
|
|
194
203
|
* Transform a PLpgSQL_type typname using proper AST parsing.
|
|
195
204
|
*/
|
|
196
|
-
export declare function transformPlpgsqlTypeAst(typname: string, schemaMapping:
|
|
205
|
+
export declare function transformPlpgsqlTypeAst(typname: string, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): string;
|
|
197
206
|
/**
|
|
198
207
|
* Fallback string-based transformation for PLpgSQL_type typname.
|
|
199
208
|
* Uses @pgsql/quotes QuoteUtils for proper identifier quoting.
|
|
200
209
|
*/
|
|
201
|
-
export declare function transformPlpgsqlTypeString(typname: string, schemaMapping:
|
|
210
|
+
export declare function transformPlpgsqlTypeString(typname: string, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): string;
|
|
202
211
|
/**
|
|
203
212
|
* Recursively walk the PL/pgSQL AST to transform schema names.
|
|
204
213
|
*/
|
|
205
|
-
export declare function walkPlpgsqlForSchemas(node: any, schemaMapping:
|
|
214
|
+
export declare function walkPlpgsqlForSchemas(node: any, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): void;
|
|
206
215
|
/**
|
|
207
216
|
* Escape a string for use in a regular expression
|
|
208
217
|
*/
|
|
@@ -244,7 +253,7 @@ export declare function transformJsonStringValues(content: string, schemaMapping
|
|
|
244
253
|
* `options.post_passes`. The built-in passes `transform_verify_calls`
|
|
245
254
|
* and `transform_json_string_values` are exported for convenience.
|
|
246
255
|
*/
|
|
247
|
-
export declare function transformSql(content: string, schemaMapping:
|
|
256
|
+
export declare function transformSql(content: string, schemaMapping: SchemaMappingInput, options?: TransformSqlOptions | SchemaTransformResult, result?: SchemaTransformResult): {
|
|
248
257
|
content: string;
|
|
249
258
|
result: SchemaTransformResult;
|
|
250
259
|
};
|
|
@@ -253,7 +262,7 @@ export declare function transformSql(content: string, schemaMapping: Map<string,
|
|
|
253
262
|
* Unlike transform_sql, this does NOT handle headers, verify calls, or JSON values.
|
|
254
263
|
* Use this for testing individual SQL statements.
|
|
255
264
|
*/
|
|
256
|
-
export declare function transformSqlStatement(sql: string, schemaMapping:
|
|
265
|
+
export declare function transformSqlStatement(sql: string, schemaMapping: SchemaMappingInput, result?: SchemaTransformResult): {
|
|
257
266
|
sql: string;
|
|
258
267
|
result: SchemaTransformResult;
|
|
259
268
|
};
|