Skip to content

Commit 78569b1

Browse files
Fix walk function to traverse WithClause and union larg/rarg nodes
- Modified walk function to check if field type is any node type (not just 'Node') - Added logic to use schemaMap to determine if a field type is a node - Added test cases for WithClause traversal - Added test cases for union larg/rarg traversal - Fixes #216 Co-Authored-By: Dan Lynch <[email protected]>
1 parent 1a0bf71 commit 78569b1

File tree

4 files changed

+111
-12
lines changed

4 files changed

+111
-12
lines changed

packages/traverse/__test__/traverse.test.ts

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { visit, walk, NodePath } from '../src';
21
import type { Visitor, Walker } from '../src';
2+
import { NodePath,visit, walk } from '../src';
33

44
describe('traverse', () => {
55
it('should visit SelectStmt nodes with new walk API', () => {
@@ -122,13 +122,13 @@ describe('traverse', () => {
122122
const visitedNodes: string[] = [];
123123

124124
const visitor: Visitor = {
125-
A_Expr: (path: NodePath) => {
125+
A_Expr: (_path: NodePath) => {
126126
visitedNodes.push('A_Expr');
127127
},
128-
ColumnRef: (path: NodePath) => {
128+
ColumnRef: (_path: NodePath) => {
129129
visitedNodes.push('ColumnRef');
130130
},
131-
A_Const: (path: NodePath) => {
131+
A_Const: (_path: NodePath) => {
132132
visitedNodes.push('A_Const');
133133
}
134134
};
@@ -390,4 +390,100 @@ describe('traverse', () => {
390390
expect(targetListVisited[0].ctx.path).toEqual(['targetList', 0]);
391391
expect(targetListVisited[1].ctx.path).toEqual(['targetList', 1]);
392392
});
393+
394+
it('should traverse WithClause nodes', () => {
395+
const visitedNodes: string[] = [];
396+
397+
const walker: Walker = (path: NodePath) => {
398+
visitedNodes.push(path.tag);
399+
};
400+
401+
const ast = {
402+
SelectStmt: {
403+
withClause: {
404+
WithClause: {
405+
ctes: [
406+
{
407+
CommonTableExpr: {
408+
ctename: 'cte1',
409+
ctequery: {
410+
SelectStmt: {
411+
targetList: [] as any[],
412+
limitOption: 'LIMIT_OPTION_DEFAULT',
413+
op: 'SETOP_NONE'
414+
}
415+
}
416+
}
417+
}
418+
]
419+
}
420+
},
421+
targetList: [] as any[],
422+
limitOption: 'LIMIT_OPTION_DEFAULT',
423+
op: 'SETOP_NONE'
424+
}
425+
};
426+
427+
walk(ast, walker);
428+
429+
expect(visitedNodes).toContain('SelectStmt');
430+
expect(visitedNodes).toContain('WithClause');
431+
expect(visitedNodes).toContain('CommonTableExpr');
432+
expect(visitedNodes.filter(n => n === 'SelectStmt')).toHaveLength(2);
433+
});
434+
435+
it('should traverse union larg and rarg nodes', () => {
436+
const visitedNodes: string[] = [];
437+
438+
const walker: Walker = (path: NodePath) => {
439+
visitedNodes.push(path.tag);
440+
};
441+
442+
const ast = {
443+
SelectStmt: {
444+
larg: {
445+
SelectStmt: {
446+
targetList: [
447+
{
448+
ResTarget: {
449+
val: {
450+
A_Const: {
451+
ival: { Integer: { ival: 1 } }
452+
}
453+
}
454+
}
455+
}
456+
],
457+
limitOption: 'LIMIT_OPTION_DEFAULT',
458+
op: 'SETOP_NONE'
459+
}
460+
},
461+
op: 'SETOP_UNION',
462+
rarg: {
463+
SelectStmt: {
464+
targetList: [
465+
{
466+
ResTarget: {
467+
val: {
468+
A_Const: {
469+
ival: { Integer: { ival: 2 } }
470+
}
471+
}
472+
}
473+
}
474+
],
475+
limitOption: 'LIMIT_OPTION_DEFAULT',
476+
op: 'SETOP_NONE'
477+
}
478+
}
479+
}
480+
};
481+
482+
walk(ast, walker);
483+
484+
expect(visitedNodes).toContain('SelectStmt');
485+
expect(visitedNodes.filter(n => n === 'SelectStmt')).toHaveLength(3);
486+
expect(visitedNodes).toContain('ResTarget');
487+
expect(visitedNodes).toContain('A_Const');
488+
});
393489
});

packages/traverse/scripts/pg-proto-parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { join,resolve } from 'path';
12
import { PgProtoParser, PgProtoParserOptions } from 'pg-proto-parser';
2-
import { resolve, join } from 'path';
33

44
const versions = ['17'];
55
const baseDir = resolve(join(__dirname, '../../../__fixtures__/proto'));

packages/traverse/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { walk, visit, NodePath } from './traverse';
2-
export type { Visitor, VisitorContext, Walker, NodeTag } from './traverse';
1+
export type { NodeTag,Visitor, VisitorContext, Walker } from './traverse';
2+
export { NodePath,visit, walk } from './traverse';

packages/traverse/src/traverse.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import type { Node } from '@pgsql/types';
3+
34
import type { NodeSpec } from './17/runtime-schema';
45
import { runtimeSchema } from './17/runtime-schema';
56

@@ -47,10 +48,10 @@ export function walk(
4748
const actualCallback: Walker = typeof callback === 'function'
4849
? callback
4950
: (path: NodePath) => {
50-
const visitor = callback as Visitor;
51-
const visitFn = visitor[path.tag];
52-
return visitFn ? visitFn(path) : undefined;
53-
};
51+
const visitor = callback as Visitor;
52+
const visitFn = visitor[path.tag];
53+
return visitFn ? visitFn(path) : undefined;
54+
};
5455

5556
if (Array.isArray(root)) {
5657
root.forEach((node, index) => {
@@ -70,7 +71,9 @@ export function walk(
7071
const nodeSpec = schemaMap.get(tag);
7172
if (nodeSpec) {
7273
for (const field of nodeSpec.fields) {
73-
if (field.type === 'Node' && nodeData[field.name] != null) {
74+
// Check if field type is 'Node' or any other node type (e.g., 'WithClause', 'SelectStmt', etc.)
75+
const isNodeType = field.type === 'Node' || schemaMap.has(field.type);
76+
if (isNodeType && nodeData[field.name] != null) {
7477
const value = nodeData[field.name];
7578
if (field.isArray && Array.isArray(value)) {
7679
value.forEach((item, index) => {

0 commit comments

Comments
 (0)