Skip to content

[BUG] JSQLParser Version : 4.0 : ON condition not parsed correctly when modifying SQL with natural join #2016

Closed as not planned
@Createsequence

Description

@Createsequence

Description

Hi, I am trying to dynamically add condition statements to SQL using JSqlparser. However, it seems there is an issue when the SQL contains a natural join:

public static void main(String[] args) throws JSQLParserException {
    String sql = "SELECT * FROM A a, B b where a.id = b.id";
    Statement statement = CCJSqlParserUtil.parse(sql, null);
    PlainSelect plainSelect = (PlainSelect) ((Select) statement).getSelectBody();
    Join join = CollUtil.getFirst(plainSelect.getJoins());
    // append a condition "a.id = '100'"
    join.setOnExpression(new EqualsTo(new Column("a.id"), new StringValue("100")));

    // = "SELECT * FROM A a, B b WHERE a.id = b.id"
    System.out.println(statement.toString());
}

I am not sure how JSqlparser handles this situation, but SELECT * FROM A a, B b WHERE a.id = b.id is clearly not the expected result.

Analysis

I tried debugging to find the root cause of this issue. I noticed that in the above code, the query of table B is parsed into a net.sf.jsqlparser.statement.select.Join object, and at this point, Join.simple is true.

After setting the ON condition with join.setOnExpression(new EqualsTo(new Column("a.id"), new StringValue("100"))); and attempting to convert it back to SQL, the ON condition is not correctly parsed into SQL because Join.simple is true:

@Override
public String toString() {
    if (isSimple() && isOuter()) {
        return "OUTER " + rightItem;
    } else if (isSimple()) {
        return "" + rightItem;
    } else {

        // isSimple() returns true, so this branch is ignored

        String type = "";

        if (isRight()) {
            type += "RIGHT ";
        } else if (isNatural()) {
            type += "NATURAL ";
        } else if (isFull()) {
            type += "FULL ";
        } else if (isLeft()) {
            type += "LEFT ";
        } else if (isCross()) {
            type += "CROSS ";
        }

        if (isOuter()) {
            type += "OUTER ";
        } else if (isInner()) {
            type += "INNER ";
        } else if (isSemi()) {
            type += "SEMI ";
        }

        if (isStraight()) {
            type = "STRAIGHT_JOIN ";
        } else if (isApply()) {
            type += "APPLY ";
        } else {
            type += "JOIN ";
        }

        return type + rightItem + ((joinWindow != null) ? " WITHIN " + joinWindow : "")
                + ((onExpression != null) ? " ON " + onExpression + "" : "")
                + PlainSelect.getFormatedList(usingColumns, "USING", true, true);
    }
}

Fix

Calling join.setSimple(false) after setOnExpression fixes the issue:

public static void main(String[] args) throws JSQLParserException {
    String sql = "SELECT * FROM A a, B b where a.id = b.id";
    Statement statement = CCJSqlParserUtil.parse(sql, null);
    PlainSelect plainSelect = (PlainSelect) ((Select) statement).getSelectBody();
    Join join = CollUtil.getFirst(plainSelect.getJoins());
    join.setOnExpression(new EqualsTo(new Column("a.id"), new StringValue("100")));
    join.setSimple(false); // set Join.simple to false

    // SELECT * FROM A a JOIN B b ON a.id = '100' WHERE a.id = b.id
    System.out.println(statement.toString());
}

In fact, if the ON condition is not null, isSimple should return false. Should we make the following changes to fix this issue?

  • When calling the setOnExpression method, if the parameter is not null, set simple to false.
  • When calling the isSimple method, check if onExpression is null.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions