Skip to content

StackOverflowException when using multiple SELECT statements in one query (2.3.230) #4097

@jacquipre

Description

@jacquipre

After we updated H2 from version 2.2.224 to 2.3.230 a StackOverflowException occurs when having multiple SELECT statements in one query, like this:

SELECT c1 FROM table1;
SELECT c2 FROM table1;

We are executing the query like this:

 String sqlStatement = "SELECT c1 FROM table1;\r\nSELECT c2 FROM table1;"
 java.sql.Connection con = // .. get DB connection

try (final Statement testStatement = con.createStatement()) {
    testStatement.execute(sqlStatement);
} catch (SQLException e) {
    // org.h2.jdbc.JdbcSQLNonTransientException: GeneralError; 
} finally {
    //... free DB connection
}

With debugging we found out that the cause is a StackOverflowException:
image

The reason for that is this change 925a188 (Issue: #4012).

Looking into the code the following happens here:

  1. a Command is build in org.h2.command.Parser#prepareCommandList
    with "SELECT c1 FROM table1" as "command" (CommandContainer) and
    "commands" (ArrayList<Prepared>) containing a Select "SELECT c2 FROM table1"
  2. calling CommandList.query(int)
  3. calls query(int) on the CommandContainer "command"
  4. calls org.h2.command.CommandList#executeRemaining()
  5. this calls org.h2.command.Command#executeQuery(int, boolean) for each Prepared in ArrayList<Prepared> "commands"
  6. this calls org.h2.command.Command#query(int) in line 196
  7. since we called executeQuery(int, boolean) of CommandList in step 5 the implementation of CommandList (and not CommandContainer) is used here and we are starting at step 2 again --> endless loop.

I guess the executeQuery(int, boolean) in line 48 of org.h2.command.CommandList#executeRemaining should be called on the created CommandContainer of line 45, but I'm not sure about that.

for (Prepared prepared : commands) {
    CommandContainer commandContainer = new CommandContainer(session, prepared.getSQL(), prepared);
    prepared.prepare();
     if (prepared.isQuery()) {
        executeQuery(0, false); // --> instead use commandContainer.executeQuery(0, false);
    } else {
        commandContainer.executeUpdate(null);
     }
}

Hope this helps!

Regards, jacquipre.

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