Descend into UDF and procedure bodies inside cursor plans - #493
Merged
Conversation
#456 taught the parser to read StoredProc/UDF sub-plan bodies, but that descent lives in ParseStatement - and a StmtCursor's operation statements never pass through ParseStatement. They are built in the cursor branch of ParseStatementAndChildren straight from CursorPlan > Operation > QueryPlan, so a function called by the cursor's query carried its whole body in the XML (the Operation element holds the UDF sub-plan beside its QueryPlan) and the parser dropped every statement of it: not enumerated, not analyzed, not counted. Same failure mode as #455 - well-formed, plausible, and silently incomplete output. Fix (#491): extract ParseStatement's UDF/StoredProc reads into a shared ParseSubPlans helper and call it from the cursor branch on each Operation element, attaching the bodies to that operation's statement. That is the whole integration: PlanStatements.EnumerateAll already walks UdfPlans/StoredProcPlan on every statement it yields, and since #486 every consumer (analyzer, scorer, result mapper, statements grid, web viewer) reads that traversal, so the bodies flow through analysis and counts with no consumer changes. The caller's depth carries into the new descent unchanged, preserving #484: a generated bomb alternating cursor and procedure shapes past MaxParseDepth throws the catchable depth error on the big-stack thread, and was verified to fail cleanly (parse completes, assert reports the miss) against a simulated depth reset at the cursor boundary. Tests use generated XML on the depth-limit tests' precedent - a cursor wrapping a UDF sub-plan is three nested element shapes, stated more clearly by a minimal document than a captured fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PvAv72Pwb8czsjDWsCCk7n
|
Reviewed. This is a clean, narrowly-scoped fix — no correctness issues found. Verified specifics:
No untrusted-input/SQL-generation concerns (parser-only change, no T-SQL emitted), no version-bump or One pre-existing (not introduced by this PR) limitation worth noting for future work: a cursor |
This was referenced Sep 3, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes #491.
#456 taught the parser to descend into StoredProc/UDF sub-plans, but that descent lived in
ParseStatement— aStmtCursor's operation statements are built throughParseQueryPlanAsStatementand never pass through it, so a function called by a cursor's query carried its whole body in the XML and the parser dropped every statement of it.The two element-reads are extracted into one shared
ParseSubPlans(stmt, containerEl, depth, ct)— byte-identical logic — called fromParseStatement(unchanged semantics) and now from the cursor branch perOperationelement, where the real schema places theUDFsub-plan beside theQueryPlan.PlanStatements.EnumerateAllalready walksUdfPlans/StoredProcPlanon every statement, and since #486 every consumer reads that traversal, so cursor bodies surface everywhere (analyzer, scorer, grid, web) with zero traversal changes — pinned end-to-end by a test running Analyze + Score +ResultMapper.Mapand asserting the counted statements.Depth passes through unreset (#484 preserved): an alternating cursor/procedure depth bomb throws the catchable depth error, and the regression mode was verified honestly — with the descent temporarily reset to
depth: 0, the bomb test fails on its assert without killing the host, then the patch was reverted.Fixtures are generated in-test with justifying comments (the depth-limit tests' precedent): no committed cursor fixture exists at all, and
udf_plan.sqlplancalls a UDF without carrying a sub-plan element.How was this tested?
Six new tests in
CursorSubPlanTestsandShowPlanParserLimitsTests: attach-point, EnumerateAll order + container paths, analyzed-and-counted via ResultMapper, the Operation-level StoredProc symmetric read, the alternating depth bomb, and 50-level legitimate nesting parsing every level. Full suite at dev tip: 451 tests, 450 passed, 1 platform skip, 0 failed — run twice in the worktree and once in the main checkout.🤖 Generated with Claude Code
https://claude.ai/code/session_01PvAv72Pwb8czsjDWsCCk7n