Skip to content

fix: traverse missing expression and query children in visitor adapters - #2761

Merged
manticore-projects merged 1 commit into
JSQLParser:masterfrom
AndreasLanz01:fix/feature-expression-positions
Oct 9, 2026
Merged

manticore-projects merged 1 commit into
JSQLParser:masterfrom
AndreasLanz01:fix/feature-expression-positions

Conversation

@AndreasLanz01

Copy link
Copy Markdown
Contributor

What

ExpressionVisitorAdapter and SelectVisitorAdapter skipped some child expressions and sub-selects. Every visitor built on them, including StatementFeatureVisitor, therefore never saw a function call or subquery in these positions:

Node Child that was not visited
Column array subscript and slice: a[f()], t.a[1:f()]
Function named parameters: substring(x FROM 2 FOR 3), position(x IN y), overlay(x PLACING y FROM 2); an attribute expression
TrimFunction the FROM expression: trim(LEADING 'x' FROM f())
AnyComparisonExpression the sub-select: = ANY (...), > ALL (...), = SOME (...)
LikeExpression ESCAPE expression
MemberOfExpression left expression
TimezoneExpression the time zone: d AT TIME ZONE f()
JsonExpression operands after ->, ->>, #>, #>>
JsonAggregateFunction key and value of json_objectagg(k : v), its ORDER BY, OVER (PARTITION BY .. ORDER BY ..)
Values (select path) all rows: top-level VALUES (...), IN (VALUES (...))
PlainSelect LIMIT, LIMIT .. BY, START WITH .. CONNECT BY
SetOperationList WITH, ORDER BY, LIMIT, OFFSET, FETCH
ParenthesedSelect OFFSET / FETCH got a null context instead of the caller's

Why

StatementFeatureVisitor.analyse(statement, pureFunctions) is meant for read-only guards: a function that is not proven pure leaves MODIFIES_DATA / MODIFIES_SCHEMA possible and is reported in getUnresolvedReferences(). With pg_read_file not declared pure, each of these reported nothing before this change:

SELECT substring(pg_read_file('x') FROM 2 FOR 3)
SELECT trim(LEADING 'x' FROM pg_read_file('x'))
SELECT a FROM t WHERE a = ANY (SELECT pg_read_file('x'))
VALUES (pg_read_file('x'))
SELECT a FROM t LIMIT length(pg_read_file('x'))
SELECT d AT TIME ZONE pg_read_file('x') FROM t
SELECT j -> pg_read_file('x') FROM t
SELECT a[length(pg_read_file('x'))] FROM t
SELECT 1 UNION SELECT 2 LIMIT length(pg_read_file('x'))

The same gap hid row locks and data-modifying CTEs in these positions, e.g. WHERE a = ANY (SELECT b FROM u FOR UPDATE) reported no MODIFIES_TRANSACTION, and VALUES ((WITH c AS (DELETE FROM u RETURNING b) SELECT b FROM c)) reported no MODIFIES_DATA.

The fix is in the adapters, not in StatementFeatureVisitor, so every subclass of the adapters sees these children too.

Testing

  • StatementFeatureVisitorTest: new nested class ExpressionPositions:

    • 29 positions where a function must be reported unresolved (and nothing is reported when it is declared pure);
    • 5 positions where a nested FOR UPDATE must give MODIFIES_TRANSACTION;
    • 2 positions where a nested data-modifying CTE must give MODIFIES_DATA;
    • a control that top-level VALUES (1, 2) still reads nothing and returns rows.

    On base 26d3e02a all 36 parameterized cases fail and the control passes.

  • ./gradlew check on Windows 11 with the JDK 17 toolchain: 9,579 test cases, 0 failures, 0 errors, 25 skipped. Grammar ambiguity, Spotless, Checkstyle, PMD, SpotBugs and JaCoCo verification pass.

  • Not run: mvn verify, JMH (no grammar or parser change), Linux/macOS.

Not changed

Origin

First observed with com.manticore-projects.jsqlformatter:jsqlparser:5.4.104 and Dialect.POSTGRESQL. Reproduced on upstream master 26d3e02a78e061f5fecf2eb2e783bf0482e10840 with the default parser. No existing issue is linked.

AI disclosure: this change was prepared with an AI coding agent (Claude Code) and reviewed by me.

- Visit skipped children: named function parameters, TRIM FROM,
  ANY/ALL subquery, ESCAPE, AT TIME ZONE, JSON operators and aggregates,
  column array subscripts
- Visit VALUES rows, LIMIT, LIMIT BY, CONNECT BY and set-operation
  WITH/ORDER BY/LIMIT/OFFSET/FETCH
- StatementFeatureVisitor now checks functions, locks and writes there

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@manticore-projects
manticore-projects merged commit f7bf370 into JSQLParser:master Oct 9, 2026
10 checks passed
@manticore-projects

Copy link
Copy Markdown
Contributor

Thank you very much for improving this!

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.

2 participants