Sample Header Ad - 728x90

Oracle Violates Short-circuit in CASE Statement

-1 votes
1 answer
76 views
**Problem** I am well aware that SQL is a declarative language and does not enforce short-circuiting. Oracle, on the other hand, explicitly states that short-circuiting takes place for CASE statements. I have the following example:
-- Query 1
SELECT 
    CASE 
        WHEN ASCII(SUBSTR(test_texts, 5, 1)) < 110 THEN 1 
        ELSE 0 
    END 
FROM test_data_types ORDER BY ID OFFSET 1 ROWS FETCH FIRST 1 ROWS ONLY;

-- Query 2
SELECT 
    CASE 
        WHEN ASCII(SUBSTR(test_texts, 5, 1)) < 110 THEN 1
        ELSE 0/0    -- zero division
    END 
FROM test_data_types ORDER BY ID OFFSET 1 ROWS FETCH FIRST 1 ROWS ONLY;
Query1 returns 1 because the 5th character of the row ('world') is 'd' with ASCII value 100 but Query2 raises a zero division error, meaning the short-circuiting did not take place. I assumed this was because the expression 0/0 is constant and optimization kicked is, however, expression ln(ora_hash(test_texts) - 10), which is equivalent to ln(0), throws an error unconditionally as well. Hence, eager evaluation of constant expressions is not the (sole) reason. Surprisingly enough, short-circuiting does take place when I increase the upper bound to 120, i.e., ASCII(...) < 120. Again, the selected letter is 'd' with ASCII value of 100, which is less than both 110 and 120, but only 120 works for some reason. I really can't wrap my mind around this. **Additional Context** I am doing research on blind SQL injection optimization so I'm limited to SELECT queries. Also, the error forcing is intentional, I'm just looking for a way to make it conditional. Lastly, I'm using Oracle Database 23ai Free. **Setup for Reproduction**
CREATE TABLE test_data_types (
  id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  test_texts CLOB
);

INSERT INTO test_data_types (test_texts) VALUES ('hello');
INSERT INTO test_data_types (test_texts) VALUES ('world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello world');
INSERT INTO test_data_types (test_texts) VALUES ('hello');

COMMIT;
**Question(s)** 1) Why is short-circuiting not respected here? 2) If it's is due to optimization, are there some expressions (e.g., heavy computation) that are evaluated last? 3) As a last resort, are there some other SELECT constructs that produce the desired behavior (conditional errors)? **TL;DR of the Bellow Discussion** The issue is not that CASE short-circuiting is violated, but that the SELECT part has precedence over the OFFSET and FETCH parts, leading to evaluation of the first row ("hello") which triggers the zero division.
Asked by Pruzo (3 rep)
May 31, 2025, 11:09 AM
Last activity: Jun 2, 2025, 02:07 PM