@pgsql/transform 18.7.0 → 18.8.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 +4 -1
- package/esm/transform.js +74 -185
- package/facts.d.ts +15 -0
- package/facts.js +4 -1
- package/package.json +4 -4
- package/transform.d.ts +4 -22
- package/transform.js +74 -185
package/esm/facts.js
CHANGED
|
@@ -154,7 +154,8 @@ function classifyOne(nodeTag, node) {
|
|
|
154
154
|
bodyReferences: [],
|
|
155
155
|
securityRelevant: SECURITY_TAGS.has(nodeTag),
|
|
156
156
|
securityDefiner: false,
|
|
157
|
-
dynamicSql: false
|
|
157
|
+
dynamicSql: false,
|
|
158
|
+
span: { start: 0, len: 0 }
|
|
158
159
|
};
|
|
159
160
|
switch (nodeTag) {
|
|
160
161
|
case 'CreateSchemaStmt':
|
|
@@ -348,6 +349,8 @@ export function classifyStatements(sql) {
|
|
|
348
349
|
const nodeTag = stmtNode ? Object.keys(stmtNode)[0] : 'other';
|
|
349
350
|
const node = stmtNode?.[nodeTag] ?? {};
|
|
350
351
|
const facts = classifyOne(nodeTag, node);
|
|
352
|
+
const start = stmt?.stmt_location ?? 0;
|
|
353
|
+
facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
|
|
351
354
|
if (stmtNode) {
|
|
352
355
|
walkSql(stmtNode, createFactsVisitor(facts));
|
|
353
356
|
}
|
package/esm/transform.js
CHANGED
|
@@ -47,6 +47,36 @@ function createResult() {
|
|
|
47
47
|
errors: [],
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Routing claims: each routable site (a RangeVar, a qualified name list, a
|
|
52
|
+
* bare schema-name field) is resolved through the router at most once per
|
|
53
|
+
* transform result.
|
|
54
|
+
*
|
|
55
|
+
* The walker visits parents before children, so a statement-level handler
|
|
56
|
+
* with namespace context (e.g. `AlterFunctionStmt` routing its
|
|
57
|
+
* `ObjectWithArgs` as a `function`) claims the site before a generic child
|
|
58
|
+
* visitor (`ObjectWithArgs`, namespace `unknown`) reaches it — first (most
|
|
59
|
+
* contextual) visitor wins. Without claims a site is routed twice, which
|
|
60
|
+
* corrupts cyclic mappings (a→b, b→a swaps back) and applies the less precise
|
|
61
|
+
* namespace second.
|
|
62
|
+
*/
|
|
63
|
+
const routingClaims = new WeakMap();
|
|
64
|
+
function claimSite(result, site, field = '*') {
|
|
65
|
+
let byNode = routingClaims.get(result);
|
|
66
|
+
if (!byNode) {
|
|
67
|
+
byNode = new WeakMap();
|
|
68
|
+
routingClaims.set(result, byNode);
|
|
69
|
+
}
|
|
70
|
+
let fields = byNode.get(site);
|
|
71
|
+
if (!fields) {
|
|
72
|
+
fields = new Set();
|
|
73
|
+
byNode.set(site, fields);
|
|
74
|
+
}
|
|
75
|
+
if (fields.has(field))
|
|
76
|
+
return false;
|
|
77
|
+
fields.add(field);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
50
80
|
/**
|
|
51
81
|
* Transform a schema name if it exists in the mapping
|
|
52
82
|
*/
|
|
@@ -75,6 +105,8 @@ export function shouldTransformSchema(schemaName, schemaMapping) {
|
|
|
75
105
|
export function transformNameList(names, schemaMapping, result, ns = 'unknown') {
|
|
76
106
|
if (!names || names.length < 2)
|
|
77
107
|
return;
|
|
108
|
+
if (!claimSite(result, names))
|
|
109
|
+
return;
|
|
78
110
|
const router = asRouter(schemaMapping);
|
|
79
111
|
// For schema-qualified names, the first element is the schema.
|
|
80
112
|
const first = names[0];
|
|
@@ -110,6 +142,8 @@ export function transformSchemaNameField(container, field, schemaMapping, result
|
|
|
110
142
|
const schemaName = container?.[field];
|
|
111
143
|
if (typeof schemaName !== 'string')
|
|
112
144
|
return;
|
|
145
|
+
if (!claimSite(result, container, field))
|
|
146
|
+
return;
|
|
113
147
|
// A bare schema name is an operation on the schema itself: only the
|
|
114
148
|
// schema-level default applies.
|
|
115
149
|
const newName = asRouter(schemaMapping).resolve(schemaName, undefined, 'schema');
|
|
@@ -121,11 +155,16 @@ export function transformSchemaNameField(container, field, schemaMapping, result
|
|
|
121
155
|
}
|
|
122
156
|
/**
|
|
123
157
|
* Transform a RangeVar-like relation object (has schemaname/relname fields).
|
|
124
|
-
*
|
|
158
|
+
* The walker auto-recurses into embedded relations (concrete `RangeVar`
|
|
159
|
+
* fields are tag-synthesized), so the generic `RangeVar` visitor covers
|
|
160
|
+
* every occurrence; statement handlers call this directly only when they
|
|
161
|
+
* carry extra context, and the claim guard keeps each relation routed once.
|
|
125
162
|
*/
|
|
126
163
|
export function transformRelation(relation, schemaMapping, result) {
|
|
127
164
|
if (!relation?.schemaname)
|
|
128
165
|
return;
|
|
166
|
+
if (!claimSite(result, relation))
|
|
167
|
+
return;
|
|
129
168
|
const oldName = relation.schemaname;
|
|
130
169
|
// A RangeVar names a relation (table/view/sequence/matview); route by the
|
|
131
170
|
// relation name so object-level routes can send it to its own schema.
|
|
@@ -185,6 +224,8 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
185
224
|
const schemaName = container?.[key];
|
|
186
225
|
if (typeof schemaName !== 'string')
|
|
187
226
|
return;
|
|
227
|
+
if (!claimSite(result, container, key))
|
|
228
|
+
return;
|
|
188
229
|
const newName = router.resolve(schemaName, undefined, 'schema');
|
|
189
230
|
if (newName && newName !== schemaName) {
|
|
190
231
|
result.schemasFound.add(schemaName);
|
|
@@ -242,15 +283,6 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
242
283
|
const node = path.node;
|
|
243
284
|
transformNameList(node.funcname, router, result, 'function');
|
|
244
285
|
},
|
|
245
|
-
// Transform CallStmt (CALL schema.procedure(...)).
|
|
246
|
-
// Note: The walker does NOT auto-recurse into CallStmt.funccall,
|
|
247
|
-
// so the FuncCall visitor above never sees it.
|
|
248
|
-
CallStmt: (path) => {
|
|
249
|
-
const node = path.node;
|
|
250
|
-
if (node.funccall?.funcname) {
|
|
251
|
-
transformNameList(node.funccall.funcname, router, result, 'function');
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
286
|
// Transform TypeName nodes (type references with schema-qualified names)
|
|
255
287
|
TypeName: (path) => {
|
|
256
288
|
const node = path.node;
|
|
@@ -308,8 +340,9 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
308
340
|
}
|
|
309
341
|
},
|
|
310
342
|
// Transform DropStmt (DROP SCHEMA name; DROP TABLE/INDEX/POLICY/TRIGGER/
|
|
311
|
-
// TYPE/FUNCTION schema.obj)
|
|
312
|
-
//
|
|
343
|
+
// TYPE/FUNCTION schema.obj) with the namespace derived from removeType,
|
|
344
|
+
// so object routes apply; claims stop the generic List/ObjectWithArgs
|
|
345
|
+
// visitors from re-routing the same names with namespace `unknown`.
|
|
313
346
|
DropStmt: (path) => {
|
|
314
347
|
const node = path.node;
|
|
315
348
|
if (node.removeType !== 'OBJECT_SCHEMA' && Array.isArray(node.objects)) {
|
|
@@ -343,79 +376,10 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
343
376
|
}
|
|
344
377
|
}
|
|
345
378
|
},
|
|
346
|
-
// Transform
|
|
347
|
-
//
|
|
348
|
-
InsertStmt: (path) => {
|
|
349
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
350
|
-
},
|
|
351
|
-
// Transform CreateStmt (CREATE TABLE schema.table)
|
|
352
|
-
// Note: The walker does NOT auto-recurse into CreateStmt.relation.
|
|
353
|
-
CreateStmt: (path) => {
|
|
354
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
355
|
-
},
|
|
356
|
-
// Transform UpdateStmt (UPDATE schema.table SET ...)
|
|
357
|
-
// Note: The walker does NOT auto-recurse into UpdateStmt.relation.
|
|
358
|
-
UpdateStmt: (path) => {
|
|
359
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
360
|
-
},
|
|
361
|
-
// Transform DeleteStmt (DELETE FROM schema.table)
|
|
362
|
-
// Note: The walker does NOT auto-recurse into DeleteStmt.relation.
|
|
363
|
-
DeleteStmt: (path) => {
|
|
364
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
365
|
-
},
|
|
366
|
-
// Transform MergeStmt (MERGE INTO schema.table USING ...)
|
|
367
|
-
// Note: The walker does NOT auto-recurse into MergeStmt.relation.
|
|
368
|
-
MergeStmt: (path) => {
|
|
369
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
370
|
-
},
|
|
371
|
-
// Transform IndexStmt (CREATE INDEX ... ON schema.table)
|
|
372
|
-
// Note: The walker does NOT auto-recurse into IndexStmt.relation.
|
|
373
|
-
IndexStmt: (path) => {
|
|
374
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
375
|
-
},
|
|
376
|
-
// Transform Constraint (REFERENCES schema.table for foreign keys)
|
|
377
|
-
// Note: The walker does NOT auto-recurse into Constraint.pktable.
|
|
378
|
-
Constraint: (path) => {
|
|
379
|
-
const node = path.node;
|
|
380
|
-
if (node.contype === 'CONSTR_FOREIGN' && node.pktable) {
|
|
381
|
-
transformRelation(node.pktable, schemaMapping, result);
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
// Transform CompositeTypeStmt (CREATE TYPE schema.name AS (...))
|
|
385
|
-
// Note: The walker does NOT auto-recurse into CompositeTypeStmt.typevar.
|
|
386
|
-
CompositeTypeStmt: (path) => {
|
|
387
|
-
transformRelation(path.node.typevar, schemaMapping, result);
|
|
388
|
-
},
|
|
389
|
-
// Transform ColumnDef (column definitions with schema-qualified types)
|
|
390
|
-
// Note: The walker does NOT auto-recurse into ColumnDef.typeName.
|
|
391
|
-
ColumnDef: (path) => {
|
|
392
|
-
const node = path.node;
|
|
393
|
-
if (node.typeName?.names) {
|
|
394
|
-
transformNameList(node.typeName.names, schemaMapping, result, 'type');
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
// Transform ViewStmt (CREATE VIEW schema.viewname AS ...)
|
|
398
|
-
// Note: The walker does NOT auto-recurse into ViewStmt.view.
|
|
399
|
-
ViewStmt: (path) => {
|
|
400
|
-
transformRelation(path.node.view, schemaMapping, result);
|
|
401
|
-
},
|
|
402
|
-
// Transform AlterTableStmt (ALTER TABLE schema.table ...)
|
|
403
|
-
// Note: The walker does NOT auto-recurse into AlterTableStmt.relation.
|
|
404
|
-
AlterTableStmt: (path) => {
|
|
405
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
406
|
-
},
|
|
407
|
-
// Transform CreateSeqStmt (CREATE SEQUENCE schema.seqname)
|
|
408
|
-
// Note: The walker does NOT auto-recurse into CreateSeqStmt.sequence.
|
|
409
|
-
CreateSeqStmt: (path) => {
|
|
410
|
-
transformRelation(path.node.sequence, schemaMapping, result);
|
|
411
|
-
},
|
|
412
|
-
// Transform AlterSeqStmt (ALTER SEQUENCE schema.seqname [OWNED BY schema.table.col])
|
|
413
|
-
// Note: The walker does NOT auto-recurse into AlterSeqStmt.sequence.
|
|
414
|
-
// Also handles OWNED BY clause where the schema name is in DefElem.arg.List.items.
|
|
379
|
+
// Transform AlterSeqStmt OWNED BY (schema name in DefElem.arg.List.items;
|
|
380
|
+
// a bare name list no generic visitor covers)
|
|
415
381
|
AlterSeqStmt: (path) => {
|
|
416
382
|
const node = path.node;
|
|
417
|
-
transformRelation(node.sequence, schemaMapping, result);
|
|
418
|
-
// Handle OWNED BY option: schema name is in the name list
|
|
419
383
|
if (node.options) {
|
|
420
384
|
for (const opt of node.options) {
|
|
421
385
|
if (opt?.DefElem?.defname === 'owned_by' && opt.DefElem?.arg?.List?.items) {
|
|
@@ -424,67 +388,12 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
424
388
|
}
|
|
425
389
|
}
|
|
426
390
|
},
|
|
427
|
-
// Transform
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
transformRelation(path.node.table, schemaMapping, result);
|
|
431
|
-
},
|
|
432
|
-
// Transform RuleStmt (CREATE RULE ... ON schema.table)
|
|
433
|
-
// Note: The walker does NOT auto-recurse into RuleStmt.relation.
|
|
434
|
-
RuleStmt: (path) => {
|
|
435
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
436
|
-
},
|
|
437
|
-
// Transform CopyStmt (COPY schema.table ...)
|
|
438
|
-
// Note: The walker does NOT auto-recurse into CopyStmt.relation.
|
|
439
|
-
CopyStmt: (path) => {
|
|
440
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
441
|
-
},
|
|
442
|
-
// Transform ClusterStmt (CLUSTER schema.table USING index)
|
|
443
|
-
// Note: The walker does NOT auto-recurse into ClusterStmt.relation.
|
|
444
|
-
ClusterStmt: (path) => {
|
|
445
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
446
|
-
},
|
|
447
|
-
// Transform VacuumRelation (VACUUM/ANALYZE schema.table)
|
|
448
|
-
// Note: The walker visits VacuumRelation but does NOT recurse into its relation.
|
|
449
|
-
VacuumRelation: (path) => {
|
|
450
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
451
|
-
},
|
|
452
|
-
// Transform RefreshMatViewStmt (REFRESH MATERIALIZED VIEW schema.view)
|
|
453
|
-
// Note: The walker does NOT auto-recurse into RefreshMatViewStmt.relation.
|
|
454
|
-
RefreshMatViewStmt: (path) => {
|
|
455
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
456
|
-
},
|
|
457
|
-
// Transform CreateTableAsStmt (CREATE MATERIALIZED VIEW schema.viewname AS ...)
|
|
458
|
-
// Note: The walker does NOT auto-recurse into CreateTableAsStmt.into.rel.
|
|
459
|
-
CreateTableAsStmt: (path) => {
|
|
460
|
-
transformRelation(path.node.into?.rel, schemaMapping, result);
|
|
461
|
-
},
|
|
462
|
-
// Transform TypeCast nodes (type casts like NULL::schema.type)
|
|
463
|
-
// Note: The walker visits TypeCast but does NOT recurse into the TypeName node.
|
|
464
|
-
TypeCast: (path) => {
|
|
465
|
-
const node = path.node;
|
|
466
|
-
if (node.typeName?.names) {
|
|
467
|
-
transformNameList(node.typeName.names, schemaMapping, result, 'type');
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
// Transform CreateFunctionStmt (CREATE FUNCTION schema.funcname)
|
|
471
|
-
// Also handles RETURNS [SETOF] schema.type via returnType.names
|
|
391
|
+
// Transform CreateFunctionStmt (CREATE FUNCTION schema.funcname).
|
|
392
|
+
// funcname is a bare name list (no generic visitor); return and parameter
|
|
393
|
+
// types are TypeName nodes the walker reaches on its own.
|
|
472
394
|
CreateFunctionStmt: (path) => {
|
|
473
395
|
const node = path.node;
|
|
474
396
|
transformNameList(node.funcname, schemaMapping, result, 'function');
|
|
475
|
-
// Transform the return type (e.g., RETURNS SETOF schema.tablename)
|
|
476
|
-
if (node.returnType?.names) {
|
|
477
|
-
transformNameList(node.returnType.names, schemaMapping, result, 'type');
|
|
478
|
-
}
|
|
479
|
-
// Transform parameter types (the walker does NOT auto-recurse into
|
|
480
|
-
// FunctionParameter.argType TypeName nodes)
|
|
481
|
-
if (Array.isArray(node.parameters)) {
|
|
482
|
-
for (const param of node.parameters) {
|
|
483
|
-
if (param?.FunctionParameter?.argType?.names) {
|
|
484
|
-
transformNameList(param.FunctionParameter.argType.names, schemaMapping, result, 'type');
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
397
|
// Transform schema-qualified references inside the function body
|
|
489
398
|
// (AS $$...$$ is a String under the 'as' DefElem, which the walker
|
|
490
399
|
// does not parse as SQL). LANGUAGE plpgsql bodies are additionally
|
|
@@ -511,12 +420,11 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
511
420
|
}
|
|
512
421
|
},
|
|
513
422
|
// Transform CreateTrigStmt (CREATE TRIGGER ... EXECUTE PROCEDURE schema.func)
|
|
514
|
-
//
|
|
515
|
-
//
|
|
423
|
+
// funcname is a bare name list; the ON relation is a RangeVar the walker
|
|
424
|
+
// reaches on its own.
|
|
516
425
|
CreateTrigStmt: (path) => {
|
|
517
426
|
const node = path.node;
|
|
518
427
|
transformNameList(node.funcname, schemaMapping, result, 'function');
|
|
519
|
-
transformRelation(node.relation, schemaMapping, result);
|
|
520
428
|
},
|
|
521
429
|
// Transform CommentStmt (COMMENT ON TABLE/COLUMN/FUNCTION schema.obj,
|
|
522
430
|
// COMMENT ON SCHEMA schema)
|
|
@@ -527,13 +435,14 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
527
435
|
// For functions, object is { ObjectWithArgs: { objname: [...] } }
|
|
528
436
|
// For schemas, object is a bare { String: { sval } }
|
|
529
437
|
if (node.object) {
|
|
438
|
+
const ns = namespaceOfObjectType(node.objtype);
|
|
530
439
|
// Handle List-wrapped objects (COMMENT ON TABLE schema.tbl, COMMENT ON COLUMN schema.tbl.col)
|
|
531
440
|
if (node.object?.List?.items) {
|
|
532
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
441
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
533
442
|
}
|
|
534
443
|
// Handle ObjectWithArgs-style objects (COMMENT ON FUNCTION schema.func(args))
|
|
535
444
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
536
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
445
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
537
446
|
}
|
|
538
447
|
// Handle bare schema names (COMMENT ON SCHEMA schema)
|
|
539
448
|
else if (node.object?.String && node.objtype === 'OBJECT_SCHEMA') {
|
|
@@ -590,41 +499,41 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
590
499
|
const node = path.node;
|
|
591
500
|
transformNameList(node.objname, schemaMapping, result);
|
|
592
501
|
},
|
|
593
|
-
// Transform AlterObjectSchemaStmt (ALTER ... SET SCHEMA newschema)
|
|
594
|
-
//
|
|
595
|
-
//
|
|
502
|
+
// Transform AlterObjectSchemaStmt (ALTER ... SET SCHEMA newschema):
|
|
503
|
+
// object name lists and the destination schema. The relation form is a
|
|
504
|
+
// RangeVar the walker reaches on its own.
|
|
596
505
|
AlterObjectSchemaStmt: (path) => {
|
|
597
506
|
const node = path.node;
|
|
598
|
-
|
|
507
|
+
const ns = namespaceOfObjectType(node.objectType);
|
|
599
508
|
// Non-relation objects (ALTER TYPE/FUNCTION ... SET SCHEMA)
|
|
600
509
|
if (node.object?.List?.items) {
|
|
601
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
510
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
602
511
|
}
|
|
603
512
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
604
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
513
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
605
514
|
}
|
|
606
515
|
else if (node.object?.TypeName?.names) {
|
|
607
|
-
transformNameList(node.object.TypeName.names, schemaMapping, result);
|
|
516
|
+
transformNameList(node.object.TypeName.names, schemaMapping, result, ns);
|
|
608
517
|
}
|
|
609
518
|
// Transform the newschema (destination schema)
|
|
610
519
|
transformSchemaNameField(node, 'newschema', schemaMapping, result);
|
|
611
520
|
},
|
|
612
521
|
// Transform RenameStmt (ALTER SCHEMA old RENAME TO new;
|
|
613
|
-
// ALTER TABLE schema.tbl RENAME ...). The
|
|
614
|
-
//
|
|
522
|
+
// ALTER TABLE schema.tbl RENAME ...). The relation form is a RangeVar
|
|
523
|
+
// the walker reaches on its own.
|
|
615
524
|
RenameStmt: (path) => {
|
|
616
525
|
const node = path.node;
|
|
617
|
-
transformRelation(node.relation, schemaMapping, result);
|
|
618
526
|
if (node.renameType === 'OBJECT_SCHEMA') {
|
|
619
527
|
// subname is the old schema name; newname is the target
|
|
620
528
|
transformSchemaNameField(node, 'subname', schemaMapping, result);
|
|
621
529
|
transformSchemaNameField(node, 'newname', schemaMapping, result);
|
|
622
530
|
}
|
|
531
|
+
const ns = namespaceOfObjectType(node.renameType);
|
|
623
532
|
if (node.object?.List?.items) {
|
|
624
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
533
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
625
534
|
}
|
|
626
535
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
627
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
536
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
628
537
|
}
|
|
629
538
|
},
|
|
630
539
|
// Transform AlterFunctionStmt (ALTER FUNCTION schema.fn(...) SET ...)
|
|
@@ -637,46 +546,26 @@ export function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
637
546
|
// Transform AlterOwnerStmt (ALTER ... OWNER TO role)
|
|
638
547
|
AlterOwnerStmt: (path) => {
|
|
639
548
|
const node = path.node;
|
|
640
|
-
|
|
549
|
+
const ns = namespaceOfObjectType(node.objectType);
|
|
641
550
|
if (node.object?.List?.items) {
|
|
642
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
551
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
643
552
|
}
|
|
644
553
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
645
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
554
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
646
555
|
}
|
|
647
556
|
else if (node.object?.String && node.objectType === 'OBJECT_SCHEMA') {
|
|
648
557
|
transformSchemaNameField(node.object.String, 'sval', schemaMapping, result);
|
|
649
558
|
}
|
|
650
559
|
},
|
|
651
|
-
// Transform CreateCastStmt (CREATE CAST (src AS tgt) WITH FUNCTION fn)
|
|
560
|
+
// Transform CreateCastStmt (CREATE CAST (src AS tgt) WITH FUNCTION fn).
|
|
561
|
+
// The function name gets its `function` namespace here; the cast's
|
|
562
|
+
// source/target/argument types are TypeName nodes the walker reaches on
|
|
563
|
+
// its own.
|
|
652
564
|
CreateCastStmt: (path) => {
|
|
653
565
|
const node = path.node;
|
|
654
|
-
if (node.sourcetype?.names) {
|
|
655
|
-
transformNameList(node.sourcetype.names, schemaMapping, result, 'type');
|
|
656
|
-
}
|
|
657
|
-
if (node.targettype?.names) {
|
|
658
|
-
transformNameList(node.targettype.names, schemaMapping, result, 'type');
|
|
659
|
-
}
|
|
660
566
|
if (node.func?.objname) {
|
|
661
567
|
transformNameList(node.func.objname, schemaMapping, result, 'function');
|
|
662
568
|
}
|
|
663
|
-
if (Array.isArray(node.func?.objargs)) {
|
|
664
|
-
for (const arg of node.func.objargs) {
|
|
665
|
-
if (arg?.TypeName?.names) {
|
|
666
|
-
transformNameList(arg.TypeName.names, schemaMapping, result, 'type');
|
|
667
|
-
}
|
|
668
|
-
else if (arg?.names) {
|
|
669
|
-
transformNameList(arg.names, schemaMapping, result, 'type');
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (Array.isArray(node.func?.objfuncargs)) {
|
|
674
|
-
for (const param of node.func.objfuncargs) {
|
|
675
|
-
if (param?.FunctionParameter?.argType?.names) {
|
|
676
|
-
transformNameList(param.FunctionParameter.argType.names, schemaMapping, result, 'type');
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
569
|
},
|
|
681
570
|
// Transform CreateEventTrigStmt (CREATE EVENT TRIGGER ... EXECUTE FUNCTION schema.fn())
|
|
682
571
|
CreateEventTrigStmt: (path) => {
|
package/facts.d.ts
CHANGED
|
@@ -94,6 +94,21 @@ export interface StatementFacts {
|
|
|
94
94
|
* are incomplete and slicing should treat it conservatively.
|
|
95
95
|
*/
|
|
96
96
|
dynamicSql: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* The statement's source span in the classified script, as reported by the
|
|
99
|
+
* parser: `start` is the byte offset of the statement's first token, `len`
|
|
100
|
+
* runs to the end of the statement (the parser excludes the trailing `;`;
|
|
101
|
+
* for the final statement the span extends to the end of the script).
|
|
102
|
+
* `sql.slice(span.start, span.start + span.len)` is the statement's
|
|
103
|
+
* verbatim source, so consumers can carry original text alongside the
|
|
104
|
+
* facts without a second parse.
|
|
105
|
+
*/
|
|
106
|
+
span: StatementSpan;
|
|
107
|
+
}
|
|
108
|
+
/** A statement's location in the source script (byte offsets). */
|
|
109
|
+
export interface StatementSpan {
|
|
110
|
+
start: number;
|
|
111
|
+
len: number;
|
|
97
112
|
}
|
|
98
113
|
/**
|
|
99
114
|
* Classify each top-level statement in a SQL script into {@link StatementFacts}.
|
package/facts.js
CHANGED
|
@@ -157,7 +157,8 @@ function classifyOne(nodeTag, node) {
|
|
|
157
157
|
bodyReferences: [],
|
|
158
158
|
securityRelevant: SECURITY_TAGS.has(nodeTag),
|
|
159
159
|
securityDefiner: false,
|
|
160
|
-
dynamicSql: false
|
|
160
|
+
dynamicSql: false,
|
|
161
|
+
span: { start: 0, len: 0 }
|
|
161
162
|
};
|
|
162
163
|
switch (nodeTag) {
|
|
163
164
|
case 'CreateSchemaStmt':
|
|
@@ -351,6 +352,8 @@ function classifyStatements(sql) {
|
|
|
351
352
|
const nodeTag = stmtNode ? Object.keys(stmtNode)[0] : 'other';
|
|
352
353
|
const node = stmtNode?.[nodeTag] ?? {};
|
|
353
354
|
const facts = classifyOne(nodeTag, node);
|
|
355
|
+
const start = stmt?.stmt_location ?? 0;
|
|
356
|
+
facts.span = { start, len: stmt?.stmt_len ?? Math.max(0, sql.length - start) };
|
|
354
357
|
if (stmtNode) {
|
|
355
358
|
(0, traverse_1.walk)(stmtNode, createFactsVisitor(facts));
|
|
356
359
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgsql/transform",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.8.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",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@pgsql/quotes": "18.1.0",
|
|
36
|
-
"@pgsql/traverse": "18.
|
|
37
|
-
"plpgsql-parser": "18.2.
|
|
36
|
+
"@pgsql/traverse": "18.4.0",
|
|
37
|
+
"plpgsql-parser": "18.2.2"
|
|
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": "
|
|
52
|
+
"gitHead": "979608121bc1e7daf2bd11473b2b894266e21bc7"
|
|
53
53
|
}
|
package/transform.d.ts
CHANGED
|
@@ -110,7 +110,10 @@ export declare function transformNameList(names: any[] | undefined, schemaMappin
|
|
|
110
110
|
export declare function transformSchemaNameField(container: any, field: string, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): void;
|
|
111
111
|
/**
|
|
112
112
|
* Transform a RangeVar-like relation object (has schemaname/relname fields).
|
|
113
|
-
*
|
|
113
|
+
* The walker auto-recurses into embedded relations (concrete `RangeVar`
|
|
114
|
+
* fields are tag-synthesized), so the generic `RangeVar` visitor covers
|
|
115
|
+
* every occurrence; statement handlers call this directly only when they
|
|
116
|
+
* carry extra context, and the claim guard keeps each relation routed once.
|
|
114
117
|
*/
|
|
115
118
|
export declare function transformRelation(relation: any, schemaMapping: SchemaMappingInput, result: SchemaTransformResult): void;
|
|
116
119
|
/**
|
|
@@ -134,34 +137,13 @@ export declare function createSqlVisitor(schemaMapping: SchemaMappingInput, resu
|
|
|
134
137
|
RangeVar: (path: any) => void;
|
|
135
138
|
CreateSchemaStmt: (path: any) => void;
|
|
136
139
|
FuncCall: (path: any) => void;
|
|
137
|
-
CallStmt: (path: any) => void;
|
|
138
140
|
TypeName: (path: any) => void;
|
|
139
141
|
ColumnRef: (path: any) => void;
|
|
140
142
|
GrantStmt: (path: any) => void;
|
|
141
143
|
VariableSetStmt: (path: any) => void;
|
|
142
144
|
AlterDefaultPrivilegesStmt: (path: any) => void;
|
|
143
145
|
DropStmt: (path: any) => void;
|
|
144
|
-
InsertStmt: (path: any) => void;
|
|
145
|
-
CreateStmt: (path: any) => void;
|
|
146
|
-
UpdateStmt: (path: any) => void;
|
|
147
|
-
DeleteStmt: (path: any) => void;
|
|
148
|
-
MergeStmt: (path: any) => void;
|
|
149
|
-
IndexStmt: (path: any) => void;
|
|
150
|
-
Constraint: (path: any) => void;
|
|
151
|
-
CompositeTypeStmt: (path: any) => void;
|
|
152
|
-
ColumnDef: (path: any) => void;
|
|
153
|
-
ViewStmt: (path: any) => void;
|
|
154
|
-
AlterTableStmt: (path: any) => void;
|
|
155
|
-
CreateSeqStmt: (path: any) => void;
|
|
156
146
|
AlterSeqStmt: (path: any) => void;
|
|
157
|
-
CreatePolicyStmt: (path: any) => void;
|
|
158
|
-
RuleStmt: (path: any) => void;
|
|
159
|
-
CopyStmt: (path: any) => void;
|
|
160
|
-
ClusterStmt: (path: any) => void;
|
|
161
|
-
VacuumRelation: (path: any) => void;
|
|
162
|
-
RefreshMatViewStmt: (path: any) => void;
|
|
163
|
-
CreateTableAsStmt: (path: any) => void;
|
|
164
|
-
TypeCast: (path: any) => void;
|
|
165
147
|
CreateFunctionStmt: (path: any) => void;
|
|
166
148
|
CreateTrigStmt: (path: any) => void;
|
|
167
149
|
CommentStmt: (path: any) => void;
|
package/transform.js
CHANGED
|
@@ -68,6 +68,36 @@ function createResult() {
|
|
|
68
68
|
errors: [],
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Routing claims: each routable site (a RangeVar, a qualified name list, a
|
|
73
|
+
* bare schema-name field) is resolved through the router at most once per
|
|
74
|
+
* transform result.
|
|
75
|
+
*
|
|
76
|
+
* The walker visits parents before children, so a statement-level handler
|
|
77
|
+
* with namespace context (e.g. `AlterFunctionStmt` routing its
|
|
78
|
+
* `ObjectWithArgs` as a `function`) claims the site before a generic child
|
|
79
|
+
* visitor (`ObjectWithArgs`, namespace `unknown`) reaches it — first (most
|
|
80
|
+
* contextual) visitor wins. Without claims a site is routed twice, which
|
|
81
|
+
* corrupts cyclic mappings (a→b, b→a swaps back) and applies the less precise
|
|
82
|
+
* namespace second.
|
|
83
|
+
*/
|
|
84
|
+
const routingClaims = new WeakMap();
|
|
85
|
+
function claimSite(result, site, field = '*') {
|
|
86
|
+
let byNode = routingClaims.get(result);
|
|
87
|
+
if (!byNode) {
|
|
88
|
+
byNode = new WeakMap();
|
|
89
|
+
routingClaims.set(result, byNode);
|
|
90
|
+
}
|
|
91
|
+
let fields = byNode.get(site);
|
|
92
|
+
if (!fields) {
|
|
93
|
+
fields = new Set();
|
|
94
|
+
byNode.set(site, fields);
|
|
95
|
+
}
|
|
96
|
+
if (fields.has(field))
|
|
97
|
+
return false;
|
|
98
|
+
fields.add(field);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
71
101
|
/**
|
|
72
102
|
* Transform a schema name if it exists in the mapping
|
|
73
103
|
*/
|
|
@@ -96,6 +126,8 @@ function shouldTransformSchema(schemaName, schemaMapping) {
|
|
|
96
126
|
function transformNameList(names, schemaMapping, result, ns = 'unknown') {
|
|
97
127
|
if (!names || names.length < 2)
|
|
98
128
|
return;
|
|
129
|
+
if (!claimSite(result, names))
|
|
130
|
+
return;
|
|
99
131
|
const router = asRouter(schemaMapping);
|
|
100
132
|
// For schema-qualified names, the first element is the schema.
|
|
101
133
|
const first = names[0];
|
|
@@ -131,6 +163,8 @@ function transformSchemaNameField(container, field, schemaMapping, result) {
|
|
|
131
163
|
const schemaName = container?.[field];
|
|
132
164
|
if (typeof schemaName !== 'string')
|
|
133
165
|
return;
|
|
166
|
+
if (!claimSite(result, container, field))
|
|
167
|
+
return;
|
|
134
168
|
// A bare schema name is an operation on the schema itself: only the
|
|
135
169
|
// schema-level default applies.
|
|
136
170
|
const newName = asRouter(schemaMapping).resolve(schemaName, undefined, 'schema');
|
|
@@ -142,11 +176,16 @@ function transformSchemaNameField(container, field, schemaMapping, result) {
|
|
|
142
176
|
}
|
|
143
177
|
/**
|
|
144
178
|
* Transform a RangeVar-like relation object (has schemaname/relname fields).
|
|
145
|
-
*
|
|
179
|
+
* The walker auto-recurses into embedded relations (concrete `RangeVar`
|
|
180
|
+
* fields are tag-synthesized), so the generic `RangeVar` visitor covers
|
|
181
|
+
* every occurrence; statement handlers call this directly only when they
|
|
182
|
+
* carry extra context, and the claim guard keeps each relation routed once.
|
|
146
183
|
*/
|
|
147
184
|
function transformRelation(relation, schemaMapping, result) {
|
|
148
185
|
if (!relation?.schemaname)
|
|
149
186
|
return;
|
|
187
|
+
if (!claimSite(result, relation))
|
|
188
|
+
return;
|
|
150
189
|
const oldName = relation.schemaname;
|
|
151
190
|
// A RangeVar names a relation (table/view/sequence/matview); route by the
|
|
152
191
|
// relation name so object-level routes can send it to its own schema.
|
|
@@ -206,6 +245,8 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
206
245
|
const schemaName = container?.[key];
|
|
207
246
|
if (typeof schemaName !== 'string')
|
|
208
247
|
return;
|
|
248
|
+
if (!claimSite(result, container, key))
|
|
249
|
+
return;
|
|
209
250
|
const newName = router.resolve(schemaName, undefined, 'schema');
|
|
210
251
|
if (newName && newName !== schemaName) {
|
|
211
252
|
result.schemasFound.add(schemaName);
|
|
@@ -263,15 +304,6 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
263
304
|
const node = path.node;
|
|
264
305
|
transformNameList(node.funcname, router, result, 'function');
|
|
265
306
|
},
|
|
266
|
-
// Transform CallStmt (CALL schema.procedure(...)).
|
|
267
|
-
// Note: The walker does NOT auto-recurse into CallStmt.funccall,
|
|
268
|
-
// so the FuncCall visitor above never sees it.
|
|
269
|
-
CallStmt: (path) => {
|
|
270
|
-
const node = path.node;
|
|
271
|
-
if (node.funccall?.funcname) {
|
|
272
|
-
transformNameList(node.funccall.funcname, router, result, 'function');
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
307
|
// Transform TypeName nodes (type references with schema-qualified names)
|
|
276
308
|
TypeName: (path) => {
|
|
277
309
|
const node = path.node;
|
|
@@ -329,8 +361,9 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
329
361
|
}
|
|
330
362
|
},
|
|
331
363
|
// Transform DropStmt (DROP SCHEMA name; DROP TABLE/INDEX/POLICY/TRIGGER/
|
|
332
|
-
// TYPE/FUNCTION schema.obj)
|
|
333
|
-
//
|
|
364
|
+
// TYPE/FUNCTION schema.obj) with the namespace derived from removeType,
|
|
365
|
+
// so object routes apply; claims stop the generic List/ObjectWithArgs
|
|
366
|
+
// visitors from re-routing the same names with namespace `unknown`.
|
|
334
367
|
DropStmt: (path) => {
|
|
335
368
|
const node = path.node;
|
|
336
369
|
if (node.removeType !== 'OBJECT_SCHEMA' && Array.isArray(node.objects)) {
|
|
@@ -364,79 +397,10 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
364
397
|
}
|
|
365
398
|
}
|
|
366
399
|
},
|
|
367
|
-
// Transform
|
|
368
|
-
//
|
|
369
|
-
InsertStmt: (path) => {
|
|
370
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
371
|
-
},
|
|
372
|
-
// Transform CreateStmt (CREATE TABLE schema.table)
|
|
373
|
-
// Note: The walker does NOT auto-recurse into CreateStmt.relation.
|
|
374
|
-
CreateStmt: (path) => {
|
|
375
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
376
|
-
},
|
|
377
|
-
// Transform UpdateStmt (UPDATE schema.table SET ...)
|
|
378
|
-
// Note: The walker does NOT auto-recurse into UpdateStmt.relation.
|
|
379
|
-
UpdateStmt: (path) => {
|
|
380
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
381
|
-
},
|
|
382
|
-
// Transform DeleteStmt (DELETE FROM schema.table)
|
|
383
|
-
// Note: The walker does NOT auto-recurse into DeleteStmt.relation.
|
|
384
|
-
DeleteStmt: (path) => {
|
|
385
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
386
|
-
},
|
|
387
|
-
// Transform MergeStmt (MERGE INTO schema.table USING ...)
|
|
388
|
-
// Note: The walker does NOT auto-recurse into MergeStmt.relation.
|
|
389
|
-
MergeStmt: (path) => {
|
|
390
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
391
|
-
},
|
|
392
|
-
// Transform IndexStmt (CREATE INDEX ... ON schema.table)
|
|
393
|
-
// Note: The walker does NOT auto-recurse into IndexStmt.relation.
|
|
394
|
-
IndexStmt: (path) => {
|
|
395
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
396
|
-
},
|
|
397
|
-
// Transform Constraint (REFERENCES schema.table for foreign keys)
|
|
398
|
-
// Note: The walker does NOT auto-recurse into Constraint.pktable.
|
|
399
|
-
Constraint: (path) => {
|
|
400
|
-
const node = path.node;
|
|
401
|
-
if (node.contype === 'CONSTR_FOREIGN' && node.pktable) {
|
|
402
|
-
transformRelation(node.pktable, schemaMapping, result);
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
// Transform CompositeTypeStmt (CREATE TYPE schema.name AS (...))
|
|
406
|
-
// Note: The walker does NOT auto-recurse into CompositeTypeStmt.typevar.
|
|
407
|
-
CompositeTypeStmt: (path) => {
|
|
408
|
-
transformRelation(path.node.typevar, schemaMapping, result);
|
|
409
|
-
},
|
|
410
|
-
// Transform ColumnDef (column definitions with schema-qualified types)
|
|
411
|
-
// Note: The walker does NOT auto-recurse into ColumnDef.typeName.
|
|
412
|
-
ColumnDef: (path) => {
|
|
413
|
-
const node = path.node;
|
|
414
|
-
if (node.typeName?.names) {
|
|
415
|
-
transformNameList(node.typeName.names, schemaMapping, result, 'type');
|
|
416
|
-
}
|
|
417
|
-
},
|
|
418
|
-
// Transform ViewStmt (CREATE VIEW schema.viewname AS ...)
|
|
419
|
-
// Note: The walker does NOT auto-recurse into ViewStmt.view.
|
|
420
|
-
ViewStmt: (path) => {
|
|
421
|
-
transformRelation(path.node.view, schemaMapping, result);
|
|
422
|
-
},
|
|
423
|
-
// Transform AlterTableStmt (ALTER TABLE schema.table ...)
|
|
424
|
-
// Note: The walker does NOT auto-recurse into AlterTableStmt.relation.
|
|
425
|
-
AlterTableStmt: (path) => {
|
|
426
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
427
|
-
},
|
|
428
|
-
// Transform CreateSeqStmt (CREATE SEQUENCE schema.seqname)
|
|
429
|
-
// Note: The walker does NOT auto-recurse into CreateSeqStmt.sequence.
|
|
430
|
-
CreateSeqStmt: (path) => {
|
|
431
|
-
transformRelation(path.node.sequence, schemaMapping, result);
|
|
432
|
-
},
|
|
433
|
-
// Transform AlterSeqStmt (ALTER SEQUENCE schema.seqname [OWNED BY schema.table.col])
|
|
434
|
-
// Note: The walker does NOT auto-recurse into AlterSeqStmt.sequence.
|
|
435
|
-
// Also handles OWNED BY clause where the schema name is in DefElem.arg.List.items.
|
|
400
|
+
// Transform AlterSeqStmt OWNED BY (schema name in DefElem.arg.List.items;
|
|
401
|
+
// a bare name list no generic visitor covers)
|
|
436
402
|
AlterSeqStmt: (path) => {
|
|
437
403
|
const node = path.node;
|
|
438
|
-
transformRelation(node.sequence, schemaMapping, result);
|
|
439
|
-
// Handle OWNED BY option: schema name is in the name list
|
|
440
404
|
if (node.options) {
|
|
441
405
|
for (const opt of node.options) {
|
|
442
406
|
if (opt?.DefElem?.defname === 'owned_by' && opt.DefElem?.arg?.List?.items) {
|
|
@@ -445,67 +409,12 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
445
409
|
}
|
|
446
410
|
}
|
|
447
411
|
},
|
|
448
|
-
// Transform
|
|
449
|
-
//
|
|
450
|
-
|
|
451
|
-
transformRelation(path.node.table, schemaMapping, result);
|
|
452
|
-
},
|
|
453
|
-
// Transform RuleStmt (CREATE RULE ... ON schema.table)
|
|
454
|
-
// Note: The walker does NOT auto-recurse into RuleStmt.relation.
|
|
455
|
-
RuleStmt: (path) => {
|
|
456
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
457
|
-
},
|
|
458
|
-
// Transform CopyStmt (COPY schema.table ...)
|
|
459
|
-
// Note: The walker does NOT auto-recurse into CopyStmt.relation.
|
|
460
|
-
CopyStmt: (path) => {
|
|
461
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
462
|
-
},
|
|
463
|
-
// Transform ClusterStmt (CLUSTER schema.table USING index)
|
|
464
|
-
// Note: The walker does NOT auto-recurse into ClusterStmt.relation.
|
|
465
|
-
ClusterStmt: (path) => {
|
|
466
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
467
|
-
},
|
|
468
|
-
// Transform VacuumRelation (VACUUM/ANALYZE schema.table)
|
|
469
|
-
// Note: The walker visits VacuumRelation but does NOT recurse into its relation.
|
|
470
|
-
VacuumRelation: (path) => {
|
|
471
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
472
|
-
},
|
|
473
|
-
// Transform RefreshMatViewStmt (REFRESH MATERIALIZED VIEW schema.view)
|
|
474
|
-
// Note: The walker does NOT auto-recurse into RefreshMatViewStmt.relation.
|
|
475
|
-
RefreshMatViewStmt: (path) => {
|
|
476
|
-
transformRelation(path.node.relation, schemaMapping, result);
|
|
477
|
-
},
|
|
478
|
-
// Transform CreateTableAsStmt (CREATE MATERIALIZED VIEW schema.viewname AS ...)
|
|
479
|
-
// Note: The walker does NOT auto-recurse into CreateTableAsStmt.into.rel.
|
|
480
|
-
CreateTableAsStmt: (path) => {
|
|
481
|
-
transformRelation(path.node.into?.rel, schemaMapping, result);
|
|
482
|
-
},
|
|
483
|
-
// Transform TypeCast nodes (type casts like NULL::schema.type)
|
|
484
|
-
// Note: The walker visits TypeCast but does NOT recurse into the TypeName node.
|
|
485
|
-
TypeCast: (path) => {
|
|
486
|
-
const node = path.node;
|
|
487
|
-
if (node.typeName?.names) {
|
|
488
|
-
transformNameList(node.typeName.names, schemaMapping, result, 'type');
|
|
489
|
-
}
|
|
490
|
-
},
|
|
491
|
-
// Transform CreateFunctionStmt (CREATE FUNCTION schema.funcname)
|
|
492
|
-
// Also handles RETURNS [SETOF] schema.type via returnType.names
|
|
412
|
+
// Transform CreateFunctionStmt (CREATE FUNCTION schema.funcname).
|
|
413
|
+
// funcname is a bare name list (no generic visitor); return and parameter
|
|
414
|
+
// types are TypeName nodes the walker reaches on its own.
|
|
493
415
|
CreateFunctionStmt: (path) => {
|
|
494
416
|
const node = path.node;
|
|
495
417
|
transformNameList(node.funcname, schemaMapping, result, 'function');
|
|
496
|
-
// Transform the return type (e.g., RETURNS SETOF schema.tablename)
|
|
497
|
-
if (node.returnType?.names) {
|
|
498
|
-
transformNameList(node.returnType.names, schemaMapping, result, 'type');
|
|
499
|
-
}
|
|
500
|
-
// Transform parameter types (the walker does NOT auto-recurse into
|
|
501
|
-
// FunctionParameter.argType TypeName nodes)
|
|
502
|
-
if (Array.isArray(node.parameters)) {
|
|
503
|
-
for (const param of node.parameters) {
|
|
504
|
-
if (param?.FunctionParameter?.argType?.names) {
|
|
505
|
-
transformNameList(param.FunctionParameter.argType.names, schemaMapping, result, 'type');
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
418
|
// Transform schema-qualified references inside the function body
|
|
510
419
|
// (AS $$...$$ is a String under the 'as' DefElem, which the walker
|
|
511
420
|
// does not parse as SQL). LANGUAGE plpgsql bodies are additionally
|
|
@@ -532,12 +441,11 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
532
441
|
}
|
|
533
442
|
},
|
|
534
443
|
// Transform CreateTrigStmt (CREATE TRIGGER ... EXECUTE PROCEDURE schema.func)
|
|
535
|
-
//
|
|
536
|
-
//
|
|
444
|
+
// funcname is a bare name list; the ON relation is a RangeVar the walker
|
|
445
|
+
// reaches on its own.
|
|
537
446
|
CreateTrigStmt: (path) => {
|
|
538
447
|
const node = path.node;
|
|
539
448
|
transformNameList(node.funcname, schemaMapping, result, 'function');
|
|
540
|
-
transformRelation(node.relation, schemaMapping, result);
|
|
541
449
|
},
|
|
542
450
|
// Transform CommentStmt (COMMENT ON TABLE/COLUMN/FUNCTION schema.obj,
|
|
543
451
|
// COMMENT ON SCHEMA schema)
|
|
@@ -548,13 +456,14 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
548
456
|
// For functions, object is { ObjectWithArgs: { objname: [...] } }
|
|
549
457
|
// For schemas, object is a bare { String: { sval } }
|
|
550
458
|
if (node.object) {
|
|
459
|
+
const ns = namespaceOfObjectType(node.objtype);
|
|
551
460
|
// Handle List-wrapped objects (COMMENT ON TABLE schema.tbl, COMMENT ON COLUMN schema.tbl.col)
|
|
552
461
|
if (node.object?.List?.items) {
|
|
553
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
462
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
554
463
|
}
|
|
555
464
|
// Handle ObjectWithArgs-style objects (COMMENT ON FUNCTION schema.func(args))
|
|
556
465
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
557
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
466
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
558
467
|
}
|
|
559
468
|
// Handle bare schema names (COMMENT ON SCHEMA schema)
|
|
560
469
|
else if (node.object?.String && node.objtype === 'OBJECT_SCHEMA') {
|
|
@@ -611,41 +520,41 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
611
520
|
const node = path.node;
|
|
612
521
|
transformNameList(node.objname, schemaMapping, result);
|
|
613
522
|
},
|
|
614
|
-
// Transform AlterObjectSchemaStmt (ALTER ... SET SCHEMA newschema)
|
|
615
|
-
//
|
|
616
|
-
//
|
|
523
|
+
// Transform AlterObjectSchemaStmt (ALTER ... SET SCHEMA newschema):
|
|
524
|
+
// object name lists and the destination schema. The relation form is a
|
|
525
|
+
// RangeVar the walker reaches on its own.
|
|
617
526
|
AlterObjectSchemaStmt: (path) => {
|
|
618
527
|
const node = path.node;
|
|
619
|
-
|
|
528
|
+
const ns = namespaceOfObjectType(node.objectType);
|
|
620
529
|
// Non-relation objects (ALTER TYPE/FUNCTION ... SET SCHEMA)
|
|
621
530
|
if (node.object?.List?.items) {
|
|
622
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
531
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
623
532
|
}
|
|
624
533
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
625
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
534
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
626
535
|
}
|
|
627
536
|
else if (node.object?.TypeName?.names) {
|
|
628
|
-
transformNameList(node.object.TypeName.names, schemaMapping, result);
|
|
537
|
+
transformNameList(node.object.TypeName.names, schemaMapping, result, ns);
|
|
629
538
|
}
|
|
630
539
|
// Transform the newschema (destination schema)
|
|
631
540
|
transformSchemaNameField(node, 'newschema', schemaMapping, result);
|
|
632
541
|
},
|
|
633
542
|
// Transform RenameStmt (ALTER SCHEMA old RENAME TO new;
|
|
634
|
-
// ALTER TABLE schema.tbl RENAME ...). The
|
|
635
|
-
//
|
|
543
|
+
// ALTER TABLE schema.tbl RENAME ...). The relation form is a RangeVar
|
|
544
|
+
// the walker reaches on its own.
|
|
636
545
|
RenameStmt: (path) => {
|
|
637
546
|
const node = path.node;
|
|
638
|
-
transformRelation(node.relation, schemaMapping, result);
|
|
639
547
|
if (node.renameType === 'OBJECT_SCHEMA') {
|
|
640
548
|
// subname is the old schema name; newname is the target
|
|
641
549
|
transformSchemaNameField(node, 'subname', schemaMapping, result);
|
|
642
550
|
transformSchemaNameField(node, 'newname', schemaMapping, result);
|
|
643
551
|
}
|
|
552
|
+
const ns = namespaceOfObjectType(node.renameType);
|
|
644
553
|
if (node.object?.List?.items) {
|
|
645
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
554
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
646
555
|
}
|
|
647
556
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
648
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
557
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
649
558
|
}
|
|
650
559
|
},
|
|
651
560
|
// Transform AlterFunctionStmt (ALTER FUNCTION schema.fn(...) SET ...)
|
|
@@ -658,46 +567,26 @@ function createSqlVisitor(schemaMapping, result, visitorOptions) {
|
|
|
658
567
|
// Transform AlterOwnerStmt (ALTER ... OWNER TO role)
|
|
659
568
|
AlterOwnerStmt: (path) => {
|
|
660
569
|
const node = path.node;
|
|
661
|
-
|
|
570
|
+
const ns = namespaceOfObjectType(node.objectType);
|
|
662
571
|
if (node.object?.List?.items) {
|
|
663
|
-
transformNameList(node.object.List.items, schemaMapping, result);
|
|
572
|
+
transformNameList(node.object.List.items, schemaMapping, result, ns);
|
|
664
573
|
}
|
|
665
574
|
else if (node.object?.ObjectWithArgs?.objname) {
|
|
666
|
-
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result);
|
|
575
|
+
transformNameList(node.object.ObjectWithArgs.objname, schemaMapping, result, ns);
|
|
667
576
|
}
|
|
668
577
|
else if (node.object?.String && node.objectType === 'OBJECT_SCHEMA') {
|
|
669
578
|
transformSchemaNameField(node.object.String, 'sval', schemaMapping, result);
|
|
670
579
|
}
|
|
671
580
|
},
|
|
672
|
-
// Transform CreateCastStmt (CREATE CAST (src AS tgt) WITH FUNCTION fn)
|
|
581
|
+
// Transform CreateCastStmt (CREATE CAST (src AS tgt) WITH FUNCTION fn).
|
|
582
|
+
// The function name gets its `function` namespace here; the cast's
|
|
583
|
+
// source/target/argument types are TypeName nodes the walker reaches on
|
|
584
|
+
// its own.
|
|
673
585
|
CreateCastStmt: (path) => {
|
|
674
586
|
const node = path.node;
|
|
675
|
-
if (node.sourcetype?.names) {
|
|
676
|
-
transformNameList(node.sourcetype.names, schemaMapping, result, 'type');
|
|
677
|
-
}
|
|
678
|
-
if (node.targettype?.names) {
|
|
679
|
-
transformNameList(node.targettype.names, schemaMapping, result, 'type');
|
|
680
|
-
}
|
|
681
587
|
if (node.func?.objname) {
|
|
682
588
|
transformNameList(node.func.objname, schemaMapping, result, 'function');
|
|
683
589
|
}
|
|
684
|
-
if (Array.isArray(node.func?.objargs)) {
|
|
685
|
-
for (const arg of node.func.objargs) {
|
|
686
|
-
if (arg?.TypeName?.names) {
|
|
687
|
-
transformNameList(arg.TypeName.names, schemaMapping, result, 'type');
|
|
688
|
-
}
|
|
689
|
-
else if (arg?.names) {
|
|
690
|
-
transformNameList(arg.names, schemaMapping, result, 'type');
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
if (Array.isArray(node.func?.objfuncargs)) {
|
|
695
|
-
for (const param of node.func.objfuncargs) {
|
|
696
|
-
if (param?.FunctionParameter?.argType?.names) {
|
|
697
|
-
transformNameList(param.FunctionParameter.argType.names, schemaMapping, result, 'type');
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
590
|
},
|
|
702
591
|
// Transform CreateEventTrigStmt (CREATE EVENT TRIGGER ... EXECUTE FUNCTION schema.fn())
|
|
703
592
|
CreateEventTrigStmt: (path) => {
|