views:

749

answers:

3

I'm running the following query in Hypersonic DB (HSQLDB):

SELECT (CASE foo WHEN 'a' THEN 'bar' WHEN 'b' THEN 'biz' ....
        ELSE 'fin' END ) FROM MyTable LIMIT 1

When the number of "WHEN" clauses exceeds about 1000, I get a Java StackOverflowError thrown by the JDBC driver in org.hsqldb.jdbc.Util.sqlException().

Here's the really weird part: I tried breaking up my CASE statement into pieces with e.g. 100 WHEN clauses followed by ELSE ( CASE foo WHEN ... ) END. But even with this rewrite I get exactly the same behavior!

I don't see any reference to a limit of 1000 or anything else in the HSQLDB manual. Help!

+1  A: 

You should never get anywhere near 1000 terms in a CASE statement. Long before that, you should put the other values into a separate table and pick them by joining.

INSERT INTO MappingTable (foo, string) VALUES
  ('a', 'bar'), ('b', 'biz'), ...

SELECT COALESCE(m.string, 'fin')
FROM MyTable t LEFT OUTER JOIN MappingTable m USING (foo)
LIMIT 1;


Java API says about StackOverflowError:

Thrown when a stack overflow occurs because an application recurses too deeply.

So I would guess that when HSQLDB parses a CASE expression, each WHEN term adds another layer to the runtime stack (actually probably several layers per WHEN).

You'd probably get a similar StackOverflowError if you had an arithmetic expression with 1,000 levels of nested parentheses.

The limit of 1,000 is probably variable, depending on the implementation of the Java VM, the version of Java, the platform you're running on, the amount of memory available, etc. They may not document it in the HSQLDB documentation because it's a platform-specific limit, not something built into HSQLDB.

Bill Karwin
Yes, I understand that this is bad design. However I would still like an answer to the original question.
Jason Cohen
You didn't ask a question.
Bill Karwin
Yes, I did. The question is how to get more than 1000 items in a CASE statement. I agree with your assessment about why it's happening... perhaps that implies it's impossible!
Jason Cohen
It's also sad and surprising that they use recursion at that level, but it's the most likely reason. I was hoping someone would have run into this before and might have a trick -- e.g. a different statement to use? -- to get around this.
Jason Cohen
If not I'll of course mark your answer as "correct" -- that it's impossible because of their design and the correction you give seems the most logical (except INNER doesn't work as described elsewhere) -- you might want to fix that too.
Jason Cohen
LEFT JOIN is an outer join. The OUTER keyword is optional.
Bill Karwin
You can just increase your thread stack size
Seun Osewa
+1  A: 

Eliminate the CASE statement entirely.

Make a table using those 1000 values, then just do an inner join to that table.

BradC
He can't get the ELSE value using an inner join.
Bill Karwin
True. I could OUTER and ifnull() it for the ELSE value. That does work but I'd like an answer to the original question instead of a schema change. Yes I know the schema change is smarter.
Jason Cohen
+1  A: 

As Bill said, it's impossible to remove the limit given the apparent HSQL parser design.

In terms of alleviating the limit (i.e. letting yourself get to 1000 switches by just pushing the limit up to... somewhere over 1000), you have two options.

  1. Increase the stack size in the VM when running your app. If you're using Sun's Hotspot VM, you should be able to pass e.g. -XX:ThreadStackSize=1024 to use a 1MB stack per thread, instead of the default 512K. That may let you get to a greater recursion depth.
  2. You can run your work in a Thread created by the constructor Thread(ThreadGroup, Runnable, String, long), where the last parameter is a requested stack size. This may or may not work; if you read the Javadoc, it's a suggestion -- VMs are more than welcome to ignore this request. Not sure what Hotspot does specifically, but it might help.
Cowan