Skip to content

feat(traverse): dispatch on bare ParseResult/ScanResult roots + payload guards#314

Merged
pyramation merged 1 commit into
mainfrom
feat/traverse-bare-parse-result-roots
Jul 22, 2026
Merged

feat(traverse): dispatch on bare ParseResult/ScanResult roots + payload guards#314
pyramation merged 1 commit into
mainfrom
feat/traverse-bare-parse-result-roots

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

Follow-up robustness pass on #313. libpg-query's parseSync() returns the top level as a bare {version, stmts} object — no {ParseResult: {...}} wrapper — so walk(parseSync(sql), visitor) fell into the generic untagged branch and the ParseResult/RawStmt visitors never fired (visit() was worse: it took the first key version as the tag and traversed nothing).

// walk()/visit(): at the true root only (parent === null, empty keyPath)
detectUntaggedRootTag(root) {
  if (Array.isArray(root.stmts)  && typeof root.version === 'number') return 'ParseResult';
  if (Array.isArray(root.tokens) && typeof root.version === 'number') return 'ScanResult';
  return null;
}

When detected, the root is walked as that node via the runtime schema, so ParseResultstmts: RawStmt[] (untagged elements) → each statement all dispatch correctly:

walk(parseSync('CREATE POLICY ...'), { ParseResult, RawStmt, RangeVar }) // all fire now

Also hardened walkNode to ignore tagged wrappers whose payload isn't an object (e.g. {Weird: 'str'} previously iterated string indices via for..in).

Detection is deliberately root-only — nested {version, stmts} shapes are untouched (covered by a test). Tagged roots and all other behavior are unchanged. This also benefits plpgsql-parser's walkSqlExpressions path, which passes hydrated parseResult objects straight to walkSql.

Tests

New cases in packages/traverse/__tests__/typed-fields.test.ts for both walk and visit: bare parse-result root dispatch, non-root {version, stmts} not misdetected, non-object tagged payload ignored. Full workspace build + tests pass (traverse 29, transform 1437, deparser 714, plpgsql-parser 303, etc.).

Link to Devin session: https://app.devin.ai/sessions/0ce0d0a13c624fb8b8eb22c77d448fb6
Requested by: @pyramation

@pyramation pyramation self-assigned this Jul 22, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit a2b0d2e into main Jul 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant