Skip to content

StackOverflow while parsing CNF expression #1576

Closed

Description

@deepak-ti

Following expression throws StackOverfow exception :

Expression expr = CCJSqlParserUtil.parseCondExpression(
"((3.0 >= 4.0 AND 5.0 <= 6.0) OR "
+ "(7.0 < 8.0 AND 9.0 > 10.0) OR "
+ "(11.0 = 11.0 AND 19.0 > 20.0) OR "
+ "(17.0 = 14.0 AND 19.0 > 17.0) OR "
+ "(17.0 = 18.0 AND 20.0 > 20.0) OR "
+ "(17.0 = 16.0 AND 19.0 > 20.0) OR "
+ "(17.0 = 18.0 AND 19.0 > 20.0) OR "
+ "(17.0 = 18.0 AND 19.0 > 20.0) OR "
+ "(17.0 = 22.0 AND 19.0 > 20.0) OR "
+ "(18.0 = 18.0 AND 22.0 > 20.0) OR "
+ "(17.0 = 18.0 AND 19.0 > 20.0) OR "
+ "(18.0 = 18.0 AND 22.0 > 20.0) OR "
+ "(17.0 = 18.0 AND 19.0 > 20.0))"
);

java.lang.StackOverflowError
at net.sf.jsqlparser.expression.BinaryExpression.toString(BinaryExpression.java)
at java.base/java.lang.String.valueOf(String.java:2951)
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168)
at net.sf.jsqlparser.expression.BinaryExpression.toString(BinaryExpression.java:67)

Beyond 12 expressions, CNF parser is getting into infinite recursion. If we just remove one expression from the above mentioned expression, then it survives.

Activity

  1. manticore-projects commented on Jun 28, 2022

    @manticore-projects
    Contributor

    Greetings.

    Please use latest JSQLParser 4.5 Snapshot. The following SQL parsed without any problem.
    You can try it online here.

    SELECT ( ( 3.0 >= 4.0
                        AND 5.0 <= 6.0 )
                    OR ( 7.0 < 8.0
                            AND 9.0 > 10.0 )
                    OR ( 11.0 = 11.0
                            AND 19.0 > 20.0 )
                    OR ( 17.0 = 14.0
                            AND 19.0 > 17.0 )
                    OR ( 17.0 = 18.0
                            AND 20.0 > 20.0 )
                    OR ( 17.0 = 16.0
                            AND 19.0 > 20.0 )
                    OR ( 17.0 = 18.0
                            AND 19.0 > 20.0 )
                    OR ( 17.0 = 18.0
                            AND 19.0 > 20.0 )
                    OR ( 17.0 = 22.0
                            AND 19.0 > 20.0 )
                    OR ( 18.0 = 18.0
                            AND 22.0 > 20.0 )
                    OR ( 17.0 = 18.0
                            AND 19.0 > 20.0 )
                    OR ( 18.0 = 18.0
                            AND 22.0 > 20.0 )
                    OR ( 17.0 = 18.0
                            AND 19.0 > 20.0 ) )
    ;
  2. deepak-ti commented on Jun 28, 2022

    @deepak-ti
    Author

    Thanks, but actually I am unable to find 4.5-snapshot dependency in maven central. Can you please point me to that ?

  3. manticore-projects commented on Jun 28, 2022

    @manticore-projects
    Contributor
    <repositories>
         <repository>
             <id>jsqlparser-snapshots</id>
             <snapshots>
                 <enabled>true</enabled>
             </snapshots>
             <url>https://oss.sonatype.org/content/groups/public/</url>
         </repository>
    </repositories>
  4. deepak-ti commented on Jun 28, 2022

    @deepak-ti
    Author

    I have added the 4.5-SNAPSHOT version dependency in a sample and could see that API - CCJSqlParserUtil.parseCondExpression() is surviving but feeding the generated expression to -
    Expression result = CNFConverter.convertToCNF( expr ); and then calling result.toString() is throwing the StackOverfow error :

    java.lang.StackOverflowError
    at java.base/java.lang.StringBuilder.(StringBuilder.java:99)
    at net.sf.jsqlparser.expression.BinaryExpression.toString(BinaryExpression.java:66)

    PS: Following class i have used to reproduce the issue -

    package org.example;
    
    import static org.junit.Assert.assertTrue;
    import net.sf.jsqlparser.JSQLParserException;
    import net.sf.jsqlparser.expression.Expression;
    import net.sf.jsqlparser.parser.CCJSqlParserUtil;
    import net.sf.jsqlparser.util.cnfexpression.CNFConverter;
    import org.junit.Test;
    
    /**
     * Unit test for CNF.
     */
    public class CNFTest
    {
        /**
         * Test the CNF conversion and print it.
         */
        @Test
        public void testStackOverflow()
        {
            try{
            Expression expr = CCJSqlParserUtil.parseCondExpression(
                    "((3.0 >= 4.0 AND 5.0 <= 6.0) OR "
                            + "(7.0 < 8.0 AND 9.0 > 10.0) OR "
                            + "(11.0 = 11.0 AND 19.0 > 20.0) OR "
                            + "(17.0 = 14.0 AND 19.0 > 17.0) OR "
                            + "(17.0 = 18.0 AND 20.0 > 20.0) OR "
                            + "(17.0 = 16.0 AND 19.0 > 20.0) OR "
                            + "(17.0 = 18.0 AND 19.0 > 20.0) OR "
                            + "(17.0 = 18.0 AND 19.0 > 20.0) OR "
                            + "(17.0 = 22.0 AND 19.0 > 20.0) OR "
                            + "(18.0 = 18.0 AND 22.0 > 20.0) OR "
                            + "(17.0 = 18.0 AND 19.0 > 20.0) OR "
                            + "(18.0 = 18.0 AND 22.0 > 20.0) OR "
                            + "(18.0 = 19.0 AND 22.0 > 20.0) OR "
                            + "(17.0 = 18.0 AND 19.0 > 20.0))"
            );
    
            Expression result = CNFConverter.convertToCNF( expr );
    
            System.out.println(result);
            }catch (JSQLParserException e){
                e.printStackTrace();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    
  5. wumpz commented on Jul 7, 2022

    @wumpz
    Member

    Btw the length of the result, since there are 2 ^ 14 * (14 * 14 (estimated item length)), should be near 3 200 200 characters. However, at the moment I look into this Stackoverflow problem. Maybe you could increase the stack size to make it work. Nevertheless, the CNFConverter uses too much memory.

  6. pinned this issue on Jul 7, 2022
  7. wumpz commented on Jul 9, 2022

    @wumpz
    Member

    So the problem here was, how or ' ed expressions were build up in the end. This was a complete unbalanced tree like

    or 
      or 
         or 
           or 
              a
              b
           c
        d
      e
    

    So in the right expression was only a normal expression. Therefore to reach the first expression "a" we 2 ^14 stack calls and a bit deeper :). That broke the stack. I rewrote this expression building to form a balanced binary tree like

    or 
      or
        or 
            a
            b
        or
            c
            d
      e
    

    so now the tree log 2 ^ 14 (base 2) deep, means 14. So now you are facing heap issues since the result is very large.

    Your example results in a sql string with the length of 3448827 characters. I added (and disabled afterwards) a test with three more OR items, which results in 33685499 characters. You get the idea.

  8. unpinned this issue on Jul 9, 2022
  9. added a commit that references this issue on May 11, 2024
    48ea0e2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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