JSqlParser: [BUG] JSQLParser 4.6 : PostgreSQL : CPU burn when parsing certain queries

Always check against the Latest SNAPSHOT of JSQLParser and the Syntax Diagram

Failing SQL Feature:

  • Parenthesis in a query lead to CPU burn - my example has ((( ... ))), add more parenthesis to see significant increase in the time it takes to fail

SQL Example:

  • Valid PostgreSQL query (heavily reduced from a large query with many nested cases):
    -- Simple query which fails fast
    SELECT ('{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT)
    
    -- Same query nested in a case statement, takes the CPU for a spin
    SELECT
     CASE
        WHEN true
        THEN (SELECT ((('{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT))))
     END
    

My notes:

I was trying to narrow down the issue by testing the case statement query like this:

CCJSqlParserUtil.newParser(sql).withAllowComplexParsing(allowComplexParsing).statements();
JSql allowComplexParsing outcome
4.5 false Fails fast
4.5 true Fails after ~10 seconds of 100% CPU
4.6 false Fails after ~20 seconds of 100% CPU
4.6 true Doesn’t finish in 3 minutes, CPU stays at 100%

Software Information:

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 29 (19 by maintainers)

Commits related to this issue

Most upvoted comments

Now you can also pretty print and format your statement.

Or print the AST:

SQL Text
 └─Statements: statement.select.PlainSelect
    ├─selectItems: statement.select.SelectItem
    │  └─Column: table_a.b_e_t
    ├─selectItems: statement.select.SelectItem
    │  ├─expression: expression.CaseExpression
    │  │  ├─whenClauses: expression.WhenClause
    │  │  │  ├─whenExpression: expression.operators.relational.IsNullExpression
    │  │  │  │  └─Column: table_a.g_o_a_c
    │  │  │  └─StringValue: 'a'
    │  │  └─Column: table_a.g_o_a_c
    │  └─Alias:  AS e_cd
    ├─selectItems: statement.select.SelectItem
    │  ├─expression: expression.CaseExpression
    │  │  ├─whenClauses: expression.WhenClause
    │  │  │  ├─whenExpression: expression.operators.relational.IsNullExpression
    │  │  │  │  └─Column: table_a.a_f_t
    │  │  │  └─StringValue: 'b'
    │  │  └─Column: table_a.a_f_t
    │  └─Alias:  AS a_f_t
    ├─selectItems: statement.select.SelectItem
    │  ├─expression: expression.Function
    │  │  └─ExpressionList: 1
    │  └─Alias:  AS count
    ├─selectItems: statement.select.SelectItem
    │  ├─expression: expression.Function
    │  │  └─ExpressionList: ABS(SUM(table_a.gb_eq))::NUMERIC, 2
    │  └─Alias:  AS total_x
    ├─fromItem: statement.select.ParenthesedSelect
    │  ├─Alias:  table_a
    │  └─select: statement.select.PlainSelect
    │     ├─selectItems: statement.select.SelectItem
    │     │  └─Column: table_x.b_e_t
    │     ├─selectItems: statement.select.SelectItem
    │     │  └─Column: table_x.b_e_a
    │     ├─selectItems: statement.select.SelectItem
    │     │  └─Column: table_y.g_o_a_c
    │     ├─selectItems: statement.select.SelectItem
    │     │  └─Column: table_z.a_f_t
    │     ├─selectItems: statement.select.SelectItem
    │     │  ├─expression: expression.CaseExpression
    │     │  │  ├─whenClauses: expression.WhenClause
    │     │  │  │  ├─whenExpression: expression.operators.relational.IsNullExpression
    │     │  │  │  │  └─Column: table_x.b_e_a
    │     │  │  │  └─thenExpression: expression.operators.arithmetic.Division
    │     │  │  │     ├─leftExpression: expression.CastExpression
    │     │  │  │     │  ├─Column: table_x.b_e_a
    │     │  │  │     │  └─ColDataType: DOUBLE PRECISION
    │     │  │  │     └─rightExpression: expression.Function
    │     │  │  │        └─ExpressionList: table_x.c_c, 'x'::CHARACTER VARYING, table_x.r_ts::DATE
    │     │  │  └─elseExpression: expression.CaseExpression
    │     │  │     ├─whenClauses: expression.WhenClause
    │     │  │     │  ├─whenExpression: expression.operators.relational.EqualsTo
    │     │  │     │  │  ├─leftExpression: expression.CastExpression
    │     │  │     │  │  │  ├─Column: table_x.b_e_t
    │     │  │     │  │  │  └─ColDataType: TEXT
    │     │  │     │  │  └─rightExpression: expression.CastExpression
    │     │  │     │  │     ├─StringValue: 'p_e'
    │     │  │     │  │     └─ColDataType: TEXT
    │     │  │     │  └─thenExpression: statement.select.ParenthesedSelect
    │     │  │     │     └─select: statement.select.PlainSelect
    │     │  │     │        ├─selectItems: statement.select.SelectItem
    │     │  │     │        │  └─expression: expression.operators.arithmetic.Division
    │     │  │     │        │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │  └─expression: expression.CastExpression
    │     │  │     │        │     │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │     │  └─expression: expression.JsonExpression
    │     │  │     │        │     │     │     └─expr: expression.CastExpression
    │     │  │     │        │     │     │        ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │  └─expr: expression.Parenthesis
    │     │  │     │        │     │     │        │     └─expression: expression.JsonExpression
    │     │  │     │        │     │     │        │        └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │           │  └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           │     ├─Column: table_x.pld
    │     │  │     │        │     │     │        │           │     └─ColDataType: JSON
    │     │  │     │        │     │     │        │           └─ColDataType: TEXT
    │     │  │     │        │     │     │        └─ColDataType: TEXT
    │     │  │     │        │     │     └─ColDataType: DOUBLE PRECISION
    │     │  │     │        │     └─rightExpression: expression.Function
    │     │  │     │        │        └─ExpressionList: fba.s_c_c, 'x'::CHARACTER VARYING, table_x.r_ts::DATE
    │     │  │     │        ├─Table: schema_z.f_b_a fba
    │     │  │     │        │  └─Alias:  fba
    │     │  │     │        ├─joins: statement.select.Join
    │     │  │     │        │  ├─Table: schema_z.t_b_a_n_i table_y
    │     │  │     │        │  │  └─Alias:  table_y
    │     │  │     │        │  └─onExpressions: expression.operators.relational.EqualsTo
    │     │  │     │        │     ├─Column: fba.b_a_i
    │     │  │     │        │     └─Column: table_y.f_b_a_id
    │     │  │     │        └─where: expression.operators.relational.EqualsTo
    │     │  │     │           ├─Column: table_y.t_ngn_id
    │     │  │     │           └─rightExpression: expression.Parenthesis
    │     │  │     │              └─expression: expression.CastExpression
    │     │  │     │                 ├─leftExpression: expression.Parenthesis
    │     │  │     │                 │  └─expression: expression.JsonExpression
    │     │  │     │                 │     └─expr: expression.CastExpression
    │     │  │     │                 │        ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │  └─expr: expression.Parenthesis
    │     │  │     │                 │        │     └─expression: expression.JsonExpression
    │     │  │     │                 │        │        └─expr: expression.CastExpression
    │     │  │     │                 │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │           │  └─expr: expression.CastExpression
    │     │  │     │                 │        │           │     ├─Column: table_x.pld
    │     │  │     │                 │        │           │     └─ColDataType: JSON
    │     │  │     │                 │        │           └─ColDataType: TEXT
    │     │  │     │                 │        └─ColDataType: TEXT
    │     │  │     │                 └─ColDataType: BIGINT
    │     │  │     ├─whenClauses: expression.WhenClause
    │     │  │     │  ├─whenExpression: expression.operators.relational.EqualsTo
    │     │  │     │  │  ├─leftExpression: expression.CastExpression
    │     │  │     │  │  │  ├─Column: table_x.b_e_t
    │     │  │     │  │  │  └─ColDataType: TEXT
    │     │  │     │  │  └─rightExpression: expression.CastExpression
    │     │  │     │  │     ├─StringValue: 'i_e'
    │     │  │     │  │     └─ColDataType: TEXT
    │     │  │     │  └─thenExpression: statement.select.ParenthesedSelect
    │     │  │     │     └─select: statement.select.PlainSelect
    │     │  │     │        ├─selectItems: statement.select.SelectItem
    │     │  │     │        │  └─expression: expression.operators.arithmetic.Division
    │     │  │     │        │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │  └─expression: expression.CastExpression
    │     │  │     │        │     │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │     │  └─expression: expression.JsonExpression
    │     │  │     │        │     │     │     └─expr: expression.CastExpression
    │     │  │     │        │     │     │        ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │  └─expr: expression.Parenthesis
    │     │  │     │        │     │     │        │     └─expression: expression.JsonExpression
    │     │  │     │        │     │     │        │        └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │           │  └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           │     ├─Column: table_x.pld
    │     │  │     │        │     │     │        │           │     └─ColDataType: JSON
    │     │  │     │        │     │     │        │           └─ColDataType: TEXT
    │     │  │     │        │     │     │        └─ColDataType: TEXT
    │     │  │     │        │     │     └─ColDataType: DOUBLE PRECISION
    │     │  │     │        │     └─rightExpression: expression.Function
    │     │  │     │        │        └─ExpressionList: fba.s_c_c, 'x'::CHARACTER VARYING, table_x.r_ts::DATE
    │     │  │     │        ├─Table: schema_z.f_b_a fba
    │     │  │     │        │  └─Alias:  fba
    │     │  │     │        ├─joins: statement.select.Join
    │     │  │     │        │  ├─Table: schema_z.t_b_a_n_i table_y
    │     │  │     │        │  │  └─Alias:  table_y
    │     │  │     │        │  └─onExpressions: expression.operators.relational.EqualsTo
    │     │  │     │        │     ├─Column: fba.b_a_i
    │     │  │     │        │     └─Column: table_y.f_b_a_id
    │     │  │     │        └─where: expression.operators.relational.EqualsTo
    │     │  │     │           ├─Column: table_y.t_ngn_id
    │     │  │     │           └─rightExpression: expression.Parenthesis
    │     │  │     │              └─expression: expression.CastExpression
    │     │  │     │                 ├─leftExpression: expression.Parenthesis
    │     │  │     │                 │  └─expression: expression.JsonExpression
    │     │  │     │                 │     └─expr: expression.CastExpression
    │     │  │     │                 │        ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │  └─expr: expression.Parenthesis
    │     │  │     │                 │        │     └─expression: expression.JsonExpression
    │     │  │     │                 │        │        └─expr: expression.CastExpression
    │     │  │     │                 │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │           │  └─expr: expression.CastExpression
    │     │  │     │                 │        │           │     ├─Column: table_x.pld
    │     │  │     │                 │        │           │     └─ColDataType: JSON
    │     │  │     │                 │        │           └─ColDataType: TEXT
    │     │  │     │                 │        └─ColDataType: TEXT
    │     │  │     │                 └─ColDataType: BIGINT
    │     │  │     ├─whenClauses: expression.WhenClause
    │     │  │     │  ├─whenExpression: expression.operators.relational.EqualsTo
    │     │  │     │  │  ├─leftExpression: expression.CastExpression
    │     │  │     │  │  │  ├─Column: table_x.b_e_t
    │     │  │     │  │  │  └─ColDataType: TEXT
    │     │  │     │  │  └─rightExpression: expression.CastExpression
    │     │  │     │  │     ├─StringValue: 'i_e_2'
    │     │  │     │  │     └─ColDataType: TEXT
    │     │  │     │  └─thenExpression: statement.select.ParenthesedSelect
    │     │  │     │     └─select: statement.select.PlainSelect
    │     │  │     │        ├─selectItems: statement.select.SelectItem
    │     │  │     │        │  └─expression: expression.operators.arithmetic.Division
    │     │  │     │        │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │  └─expression: expression.CastExpression
    │     │  │     │        │     │     ├─leftExpression: expression.Parenthesis
    │     │  │     │        │     │     │  └─expression: expression.JsonExpression
    │     │  │     │        │     │     │     └─expr: expression.CastExpression
    │     │  │     │        │     │     │        ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │  └─expr: expression.Parenthesis
    │     │  │     │        │     │     │        │     └─expression: expression.JsonExpression
    │     │  │     │        │     │     │        │        └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │        │     │     │        │           │  └─expr: expression.CastExpression
    │     │  │     │        │     │     │        │           │     ├─Column: table_x.pld
    │     │  │     │        │     │     │        │           │     └─ColDataType: JSON
    │     │  │     │        │     │     │        │           └─ColDataType: TEXT
    │     │  │     │        │     │     │        └─ColDataType: TEXT
    │     │  │     │        │     │     └─ColDataType: DOUBLE PRECISION
    │     │  │     │        │     └─rightExpression: expression.Function
    │     │  │     │        │        └─ExpressionList: fba.s_c_c, 'x'::CHARACTER VARYING, table_x.r_ts::DATE
    │     │  │     │        ├─Table: schema_z.f_b_a fba
    │     │  │     │        │  └─Alias:  fba
    │     │  │     │        ├─joins: statement.select.Join
    │     │  │     │        │  ├─Table: schema_z.t_b_a_n_i table_y
    │     │  │     │        │  │  └─Alias:  table_y
    │     │  │     │        │  └─onExpressions: expression.operators.relational.EqualsTo
    │     │  │     │        │     ├─Column: fba.b_a_i
    │     │  │     │        │     └─Column: table_y.f_b_a_id
    │     │  │     │        └─where: expression.operators.relational.EqualsTo
    │     │  │     │           ├─Column: table_y.t_ngn_id
    │     │  │     │           └─rightExpression: expression.Parenthesis
    │     │  │     │              └─expression: expression.CastExpression
    │     │  │     │                 ├─leftExpression: expression.Parenthesis
    │     │  │     │                 │  └─expression: expression.JsonExpression
    │     │  │     │                 │     └─expr: expression.CastExpression
    │     │  │     │                 │        ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │  └─expr: expression.Parenthesis
    │     │  │     │                 │        │     └─expression: expression.JsonExpression
    │     │  │     │                 │        │        └─expr: expression.CastExpression
    │     │  │     │                 │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │                 │        │           │  └─expr: expression.CastExpression
    │     │  │     │                 │        │           │     ├─Column: table_x.pld
    │     │  │     │                 │        │           │     └─ColDataType: JSON
    │     │  │     │                 │        │           └─ColDataType: TEXT
    │     │  │     │                 │        └─ColDataType: TEXT
    │     │  │     │                 └─ColDataType: BIGINT
    │     │  │     ├─whenClauses: expression.WhenClause
    │     │  │     │  ├─whenExpression: expression.operators.relational.EqualsTo
    │     │  │     │  │  ├─leftExpression: expression.CastExpression
    │     │  │     │  │  │  ├─Column: table_x.b_e_t
    │     │  │     │  │  │  └─ColDataType: TEXT
    │     │  │     │  │  └─rightExpression: expression.CastExpression
    │     │  │     │  │     ├─StringValue: 'm_e'
    │     │  │     │  │     └─ColDataType: TEXT
    │     │  │     │  └─thenExpression: statement.select.ParenthesedSelect
    │     │  │     │     └─select: statement.select.PlainSelect
    │     │  │     │        └─selectItems: statement.select.SelectItem
    │     │  │     │           └─expression: expression.operators.arithmetic.Division
    │     │  │     │              ├─leftExpression: expression.Parenthesis
    │     │  │     │              │  └─expression: expression.CastExpression
    │     │  │     │              │     ├─leftExpression: expression.Parenthesis
    │     │  │     │              │     │  └─expression: expression.JsonExpression
    │     │  │     │              │     │     └─expr: expression.CastExpression
    │     │  │     │              │     │        ├─leftExpression: expression.JsonExpression
    │     │  │     │              │     │        │  └─expr: expression.Parenthesis
    │     │  │     │              │     │        │     └─expression: expression.JsonExpression
    │     │  │     │              │     │        │        └─expr: expression.CastExpression
    │     │  │     │              │     │        │           ├─leftExpression: expression.JsonExpression
    │     │  │     │              │     │        │           │  └─expr: expression.CastExpression
    │     │  │     │              │     │        │           │     ├─Column: table_x.pld
    │     │  │     │              │     │        │           │     └─ColDataType: JSON
    │     │  │     │              │     │        │           └─ColDataType: TEXT
    │     │  │     │              │     │        └─ColDataType: TEXT
    │     │  │     │              │     └─ColDataType: DOUBLE PRECISION
    │     │  │     │              └─rightExpression: expression.Function
    │     │  │     │                 └─ExpressionList: ((table_x.pld::JSON->'o'::TEXT)->>'dc'::TEXT)::CHARACTER VARYING, 'x'::CHARACTER VARYING, table_x.r_ts::DATE
    │     │  │     └─elseExpression: expression.CastExpression
    │     │  │        ├─NullValue: NULL
    │     │  │        └─ColDataType: DOUBLE PRECISION
    │     │  └─Alias:  AS gb_eq
    │     ├─Table: schema_z.baz
    │     ├─joins: statement.select.Join
    │     │  ├─Table: f_ctl.g_o_f_e_t_a_m table_y
    │     │  │  └─Alias:  table_y
    │     │  └─onExpressions: expression.operators.relational.LikeExpression
    │     │     ├─Column: table_x.p_e_m
    │     │     └─Column: table_y.f_e_m_p
    │     ├─joins: statement.select.Join
    │     │  ├─Table: f_ctl.g_o_c_a_t table_z
    │     │  │  └─Alias:  table_z
    │     │  └─onExpressions: expression.operators.relational.EqualsTo
    │     │     ├─Column: table_z.c_a_t_c
    │     │     └─Column: table_y.g_o_a_c
    │     └─where: expression.operators.relational.EqualsTo
    │        ├─Column: table_x.p_st
    │        └─StringValue: 'E'
    └─groupBy: statement.select.GroupByElement
       └─ExpressionList: 1, 2, 3

I think providing a way for the client to control the thread pool is a good thing. Whether or not we’ll use it, early to say. I think we’ll need to have a think about the potential risk of a runaway parser, and whether we want to handle it somehow differently.

I understand the challenge, but unfortunately I came to the conclusion that for SQL based on JavaCC with a RDBMS agnostic scope, we won’t be able to prevent runaway situations. At least, I am not capable of achieving this and I have fixed many and most of such runaway conditions.

So mitigating via a Time Out Observer seems the most robust approach to me, although it’s brute force. Anyone with better ideas is certainly welcome.

Oopps, that has been an oversight. The second attempt shall only happen when Complex Parsing was allowed. I will correct that tomorrow. Thank you for pointing it out.