@pgsql/transform 18.9.0 → 18.11.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/esm/facts.js +9 -2
- package/esm/index.js +1 -0
- package/esm/naming.js +65 -0
- package/facts.d.ts +8 -0
- package/facts.js +9 -2
- package/index.d.ts +2 -0
- package/index.js +3 -1
- package/naming.d.ts +43 -0
- package/naming.js +68 -0
- package/package.json +2 -2
package/esm/facts.js
CHANGED
|
@@ -223,9 +223,15 @@ function classifyOne(nodeTag, node) {
|
|
|
223
223
|
}
|
|
224
224
|
break;
|
|
225
225
|
case 'CreateEnumStmt':
|
|
226
|
-
case 'CreateDomainStmt':
|
|
227
226
|
case 'CreateRangeStmt': {
|
|
228
|
-
const name = nameListToQualified(node.typeName
|
|
227
|
+
const name = nameListToQualified(node.typeName);
|
|
228
|
+
if (name)
|
|
229
|
+
facts.creates.push(name);
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'CreateDomainStmt': {
|
|
233
|
+
// `typeName` on a domain is the base type; the name is `domainname`.
|
|
234
|
+
const name = nameListToQualified(node.domainname);
|
|
229
235
|
if (name)
|
|
230
236
|
facts.creates.push(name);
|
|
231
237
|
break;
|
|
@@ -352,6 +358,7 @@ export function classifyStatements(sql) {
|
|
|
352
358
|
const start = stmt?.stmt_location ?? 0;
|
|
353
359
|
facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
|
|
354
360
|
if (stmtNode) {
|
|
361
|
+
facts.stmt = stmtNode;
|
|
355
362
|
walkSql(stmtNode, createFactsVisitor(facts));
|
|
356
363
|
}
|
|
357
364
|
if (nodeTag === 'CreateFunctionStmt') {
|
package/esm/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { classifyStatements } from './facts';
|
|
2
2
|
export { buildStatementGraph } from './graph';
|
|
3
|
+
export { identityOf } from './naming';
|
|
3
4
|
export { orderStatements, restructureSql } from './restructure';
|
|
4
5
|
export { COMMON_EXTENSIONS, ExtensionRouter } from './extension-router';
|
|
5
6
|
export { createExtensionResult, createExtensionVisitor, transformExtensions, } from './extension-transform';
|
package/esm/naming.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive the identity of the object a statement primarily creates or
|
|
3
|
+
* targets, or `null` when the statement creates nothing (grants, comments —
|
|
4
|
+
* such statements ride with the change of the object they attach to).
|
|
5
|
+
*
|
|
6
|
+
* Table-scoped kinds are recovered from the classifier's table-qualified
|
|
7
|
+
* names (`table.trigger`) and, for indexes and constraints, from the
|
|
8
|
+
* targeted relation.
|
|
9
|
+
*/
|
|
10
|
+
export function identityOf(facts) {
|
|
11
|
+
if (facts.kind === 'extension' && facts.extension) {
|
|
12
|
+
return { kind: 'extension', schema: null, name: facts.extension.name };
|
|
13
|
+
}
|
|
14
|
+
const created = facts.creates[0];
|
|
15
|
+
if (!created)
|
|
16
|
+
return null;
|
|
17
|
+
switch (facts.kind) {
|
|
18
|
+
case 'schema':
|
|
19
|
+
return { kind: 'schema', schema: null, name: created.name };
|
|
20
|
+
case 'trigger':
|
|
21
|
+
case 'policy': {
|
|
22
|
+
const dot = created.name.indexOf('.');
|
|
23
|
+
if (dot > 0) {
|
|
24
|
+
return {
|
|
25
|
+
kind: facts.kind,
|
|
26
|
+
schema: created.schema,
|
|
27
|
+
name: created.name.slice(dot + 1),
|
|
28
|
+
table: created.name.slice(0, dot)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { kind: facts.kind, schema: created.schema, name: created.name };
|
|
32
|
+
}
|
|
33
|
+
case 'index': {
|
|
34
|
+
// IndexStmt records the index name in creates and the indexed relation
|
|
35
|
+
// in references (same-schema RangeVar).
|
|
36
|
+
const rel = facts.references.find(r => r.schema === created.schema) ?? facts.references[0];
|
|
37
|
+
return {
|
|
38
|
+
kind: 'index',
|
|
39
|
+
schema: created.schema,
|
|
40
|
+
name: created.name,
|
|
41
|
+
table: rel?.name
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
case 'fk_constraint':
|
|
45
|
+
case 'constraint':
|
|
46
|
+
case 'rls_enable':
|
|
47
|
+
// ALTER TABLE statements target their table.
|
|
48
|
+
return { kind: 'constraint', schema: created.schema, name: created.name, table: created.name };
|
|
49
|
+
case 'seed_dml':
|
|
50
|
+
return { kind: 'seed_dml', schema: created.schema, name: created.name, table: created.name };
|
|
51
|
+
case 'table':
|
|
52
|
+
// AlterTableStmt facts also classify as `table`-targeting; the created
|
|
53
|
+
// name is the table either way.
|
|
54
|
+
return { kind: 'table', schema: created.schema, name: created.name };
|
|
55
|
+
case 'view':
|
|
56
|
+
case 'function':
|
|
57
|
+
case 'type':
|
|
58
|
+
return { kind: facts.kind, schema: created.schema, name: created.name };
|
|
59
|
+
default:
|
|
60
|
+
if (facts.nodeTag === 'CreateSeqStmt') {
|
|
61
|
+
return { kind: 'sequence', schema: created.schema, name: created.name };
|
|
62
|
+
}
|
|
63
|
+
return { kind: 'other', schema: created.schema, name: created.name };
|
|
64
|
+
}
|
|
65
|
+
}
|
package/facts.d.ts
CHANGED
|
@@ -104,6 +104,14 @@ export interface StatementFacts {
|
|
|
104
104
|
* facts without a second parse.
|
|
105
105
|
*/
|
|
106
106
|
span: StatementSpan;
|
|
107
|
+
/**
|
|
108
|
+
* The raw parsed statement node (`{ CreateStmt: {...} }` etc.), exactly as
|
|
109
|
+
* the parser produced it. Facts are still read-only — the node is carried
|
|
110
|
+
* so consumers like `revertFor`/`verifyFor` can derive inverse or
|
|
111
|
+
* existence-check statements without a second parse. Absent only for
|
|
112
|
+
* facts constructed by hand.
|
|
113
|
+
*/
|
|
114
|
+
stmt?: Record<string, any>;
|
|
107
115
|
}
|
|
108
116
|
/** A statement's location in the source script (byte offsets). */
|
|
109
117
|
export interface StatementSpan {
|
package/facts.js
CHANGED
|
@@ -226,9 +226,15 @@ function classifyOne(nodeTag, node) {
|
|
|
226
226
|
}
|
|
227
227
|
break;
|
|
228
228
|
case 'CreateEnumStmt':
|
|
229
|
-
case 'CreateDomainStmt':
|
|
230
229
|
case 'CreateRangeStmt': {
|
|
231
|
-
const name = nameListToQualified(node.typeName
|
|
230
|
+
const name = nameListToQualified(node.typeName);
|
|
231
|
+
if (name)
|
|
232
|
+
facts.creates.push(name);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case 'CreateDomainStmt': {
|
|
236
|
+
// `typeName` on a domain is the base type; the name is `domainname`.
|
|
237
|
+
const name = nameListToQualified(node.domainname);
|
|
232
238
|
if (name)
|
|
233
239
|
facts.creates.push(name);
|
|
234
240
|
break;
|
|
@@ -355,6 +361,7 @@ function classifyStatements(sql) {
|
|
|
355
361
|
const start = stmt?.stmt_location ?? 0;
|
|
356
362
|
facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
|
|
357
363
|
if (stmtNode) {
|
|
364
|
+
facts.stmt = stmtNode;
|
|
358
365
|
(0, traverse_1.walk)(stmtNode, createFactsVisitor(facts));
|
|
359
366
|
}
|
|
360
367
|
if (nodeTag === 'CreateFunctionStmt') {
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export type { ExtensionAction, ExtensionFact, QualifiedName, StatementFacts, Sta
|
|
|
2
2
|
export { classifyStatements } from './facts';
|
|
3
3
|
export type { EdgeKind, StatementEdge, StatementGraph, StatementNode, } from './graph';
|
|
4
4
|
export { buildStatementGraph } from './graph';
|
|
5
|
+
export type { ObjectIdentity, ObjectIdentityKind, } from './naming';
|
|
6
|
+
export { identityOf } from './naming';
|
|
5
7
|
export type { Granularity, RestructureOptions, RestructureResult, } from './restructure';
|
|
6
8
|
export { orderStatements, restructureSql } from './restructure';
|
|
7
9
|
export type { ExtensionDefinition, ExtensionRoute, ExtensionRouteSpec, ExtensionRouterOptions, ExtensionSymbol, ExtensionSymbolNamespace, SymbolRewrite, } from './extension-router';
|
package/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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.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.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;
|
|
4
4
|
var facts_1 = require("./facts");
|
|
5
5
|
Object.defineProperty(exports, "classifyStatements", { enumerable: true, get: function () { return facts_1.classifyStatements; } });
|
|
6
6
|
var graph_1 = require("./graph");
|
|
7
7
|
Object.defineProperty(exports, "buildStatementGraph", { enumerable: true, get: function () { return graph_1.buildStatementGraph; } });
|
|
8
|
+
var naming_1 = require("./naming");
|
|
9
|
+
Object.defineProperty(exports, "identityOf", { enumerable: true, get: function () { return naming_1.identityOf; } });
|
|
8
10
|
var restructure_1 = require("./restructure");
|
|
9
11
|
Object.defineProperty(exports, "orderStatements", { enumerable: true, get: function () { return restructure_1.orderStatements; } });
|
|
10
12
|
Object.defineProperty(exports, "restructureSql", { enumerable: true, get: function () { return restructure_1.restructureSql; } });
|
package/naming.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object identity — the canonical, Postgres-native answer to "what object is
|
|
3
|
+
* this statement about?".
|
|
4
|
+
*
|
|
5
|
+
* Identity is the key used by dependency graphs, semantic diffing, and any
|
|
6
|
+
* downstream naming scheme. It is a pure function of classifier facts —
|
|
7
|
+
* grounded in the parser's node taxonomy (`CreateStmt`, `CreateTrigStmt`,
|
|
8
|
+
* `IndexStmt`, ...), never in surface syntax like RangeVars. Rendering an
|
|
9
|
+
* identity to a change path (e.g. a pgpm module layout) is deliberately NOT
|
|
10
|
+
* defined here: paths are derived projections that belong to whichever
|
|
11
|
+
* packaging layer consumes the identity, so nothing is ever attached to them.
|
|
12
|
+
*
|
|
13
|
+
* Identity tuple: `(kind, schema, name, table?)` — `table` scopes objects
|
|
14
|
+
* that are only unique per table (triggers, policies, indexes, constraints,
|
|
15
|
+
* seed data). Function overloads share an identity for now (signature
|
|
16
|
+
* disambiguation is a planned refinement).
|
|
17
|
+
*/
|
|
18
|
+
import { StatementFacts } from './facts';
|
|
19
|
+
/** The kinds of objects an identity can describe. */
|
|
20
|
+
export type ObjectIdentityKind = 'schema' | 'extension' | 'role' | 'table' | 'view' | 'sequence' | 'type' | 'function' | 'index' | 'trigger' | 'policy' | 'constraint' | 'seed_dml' | 'other';
|
|
21
|
+
/**
|
|
22
|
+
* The identity of a database object. Identity is the diff/dependency key;
|
|
23
|
+
* any path or name is only a downstream rendering of it.
|
|
24
|
+
*/
|
|
25
|
+
export interface ObjectIdentity {
|
|
26
|
+
kind: ObjectIdentityKind;
|
|
27
|
+
/** Owning schema (`null` for non-schema objects: roles, extensions). */
|
|
28
|
+
schema: string | null;
|
|
29
|
+
/** Object name, unqualified (for table-scoped kinds: without the table). */
|
|
30
|
+
name: string;
|
|
31
|
+
/** Owning table, for objects only unique per table (trigger/policy/index/constraint/seed). */
|
|
32
|
+
table?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Derive the identity of the object a statement primarily creates or
|
|
36
|
+
* targets, or `null` when the statement creates nothing (grants, comments —
|
|
37
|
+
* such statements ride with the change of the object they attach to).
|
|
38
|
+
*
|
|
39
|
+
* Table-scoped kinds are recovered from the classifier's table-qualified
|
|
40
|
+
* names (`table.trigger`) and, for indexes and constraints, from the
|
|
41
|
+
* targeted relation.
|
|
42
|
+
*/
|
|
43
|
+
export declare function identityOf(facts: StatementFacts): ObjectIdentity | null;
|
package/naming.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.identityOf = identityOf;
|
|
4
|
+
/**
|
|
5
|
+
* Derive the identity of the object a statement primarily creates or
|
|
6
|
+
* targets, or `null` when the statement creates nothing (grants, comments —
|
|
7
|
+
* such statements ride with the change of the object they attach to).
|
|
8
|
+
*
|
|
9
|
+
* Table-scoped kinds are recovered from the classifier's table-qualified
|
|
10
|
+
* names (`table.trigger`) and, for indexes and constraints, from the
|
|
11
|
+
* targeted relation.
|
|
12
|
+
*/
|
|
13
|
+
function identityOf(facts) {
|
|
14
|
+
if (facts.kind === 'extension' && facts.extension) {
|
|
15
|
+
return { kind: 'extension', schema: null, name: facts.extension.name };
|
|
16
|
+
}
|
|
17
|
+
const created = facts.creates[0];
|
|
18
|
+
if (!created)
|
|
19
|
+
return null;
|
|
20
|
+
switch (facts.kind) {
|
|
21
|
+
case 'schema':
|
|
22
|
+
return { kind: 'schema', schema: null, name: created.name };
|
|
23
|
+
case 'trigger':
|
|
24
|
+
case 'policy': {
|
|
25
|
+
const dot = created.name.indexOf('.');
|
|
26
|
+
if (dot > 0) {
|
|
27
|
+
return {
|
|
28
|
+
kind: facts.kind,
|
|
29
|
+
schema: created.schema,
|
|
30
|
+
name: created.name.slice(dot + 1),
|
|
31
|
+
table: created.name.slice(0, dot)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { kind: facts.kind, schema: created.schema, name: created.name };
|
|
35
|
+
}
|
|
36
|
+
case 'index': {
|
|
37
|
+
// IndexStmt records the index name in creates and the indexed relation
|
|
38
|
+
// in references (same-schema RangeVar).
|
|
39
|
+
const rel = facts.references.find(r => r.schema === created.schema) ?? facts.references[0];
|
|
40
|
+
return {
|
|
41
|
+
kind: 'index',
|
|
42
|
+
schema: created.schema,
|
|
43
|
+
name: created.name,
|
|
44
|
+
table: rel?.name
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
case 'fk_constraint':
|
|
48
|
+
case 'constraint':
|
|
49
|
+
case 'rls_enable':
|
|
50
|
+
// ALTER TABLE statements target their table.
|
|
51
|
+
return { kind: 'constraint', schema: created.schema, name: created.name, table: created.name };
|
|
52
|
+
case 'seed_dml':
|
|
53
|
+
return { kind: 'seed_dml', schema: created.schema, name: created.name, table: created.name };
|
|
54
|
+
case 'table':
|
|
55
|
+
// AlterTableStmt facts also classify as `table`-targeting; the created
|
|
56
|
+
// name is the table either way.
|
|
57
|
+
return { kind: 'table', schema: created.schema, name: created.name };
|
|
58
|
+
case 'view':
|
|
59
|
+
case 'function':
|
|
60
|
+
case 'type':
|
|
61
|
+
return { kind: facts.kind, schema: created.schema, name: created.name };
|
|
62
|
+
default:
|
|
63
|
+
if (facts.nodeTag === 'CreateSeqStmt') {
|
|
64
|
+
return { kind: 'sequence', schema: created.schema, name: created.name };
|
|
65
|
+
}
|
|
66
|
+
return { kind: 'other', schema: created.schema, name: created.name };
|
|
67
|
+
}
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgsql/transform",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.11.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",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"classify",
|
|
50
50
|
"plpgsql"
|
|
51
51
|
],
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "f888a7701dc5abf309c0dfd04e0069375d4e7550"
|
|
53
53
|
}
|