JSqlParser icon indicating copy to clipboard operation
JSqlParser copied to clipboard

JSQLParser 5.1: Incorrect Parent node

Open davyboyhayes opened this issue 8 months ago • 2 comments

I have a query: select A from B where (A ~ 'fish') which when tree walking with an ExpressionVisitor, I end up in void visit(RegExpMatchOperator regExpMatchOperator) {...}, the parent of the regExpMatchOperator is the PlainSelect, and not the ParenthesedExpressionList

Test Case (which fails on the final assertion):

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class RegexpMatchesTest {
    @Test
    void parentIsExpected() throws JSQLParserException {
        var parsed = CCJSqlParserUtil.parse("select A from B where (A ~ 'fish')");
        assertThat(parsed).isInstanceOf(PlainSelect.class);
        var select = (PlainSelect) parsed;
        assertThat(select.getWhere()).isInstanceOf(ParenthesedExpressionList.class);
        var parenthesis = (ParenthesedExpressionList<?>) select.getWhere();
        assertThat(parenthesis.getFirst()).isInstanceOf(RegExpMatchOperator.class);
        var regExpMatchOperator = (RegExpMatchOperator) parenthesis.getFirst();
        assertThat(regExpMatchOperator.getParent()).isSameAs(parenthesis);
    }
}

My expectation is that the parent node of the regex operator should be the parenthesis object, not the plain select

davyboyhayes avatar Mar 19 '25 10:03 davyboyhayes

Although I note now, that ParenthesedExpressionList does not contain a getParent() method, so you couldn't tree walk up to the PlainSelect from the ParenthesedExpressionList. I guess that because getParent() is a member of ASTNodeAccessImpl, and you can't have ExpressionList extend ArrayList and ASTNodeAccessImpl.

davyboyhayes avatar Mar 19 '25 10:03 davyboyhayes

An extension of this problem, which I think is more problematic: SELECT A FROM B WHERE (A ~ 'fish' AND A !~ 'banana')

The RegExpMatchOperator of A ~ 'fish' has a parent of the PlainSelect and not AndExpression (as does the A !~ 'banana'), even though the structure is PlainSelect.where => ParenthesedExpressionList(AndExpression(RegExpMatchOperator, RegExpMatchOperator)) where AndExpression does extend ASTNodeAccessImpl

davyboyhayes avatar Mar 19 '25 14:03 davyboyhayes

Although I still think this is an issue, we worked around this issue and so I'm happy for this to be closed.

davyboyhayes avatar Aug 05 '25 20:08 davyboyhayes