Skip to content

Commit 7580c70

Browse files
committed
fix: ROWNUM = N only optimized for N=1
ROWNUM = N predicates can only be transformed to LIMIT when N=1. For N>1, the predicate must remain in WHERE clause to correctly return 0 rows per Oracle semantics. Added test cases for ROWNUM = 2 and ROWNUM = 3 to verify correct behavior.
1 parent 933d3e5 commit 7580c70

3 files changed

Lines changed: 40 additions & 3 deletions

File tree

src/backend/optimizer/plan/planner.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -738,9 +738,16 @@ transform_rownum_to_limit(Query *parse)
738738
}
739739
else if (strcmp(opname, "=") == 0)
740740
{
741-
/* ROWNUM = N -> LIMIT N (only makes sense for N=1) */
742-
limit_value = n;
743-
rownum_qual = qual;
741+
/*
742+
* ROWNUM = N can only be optimized for N=1.
743+
* For N>1, the predicate must remain in WHERE clause,
744+
* which will correctly return 0 rows (Oracle semantics).
745+
*/
746+
if (n == 1)
747+
{
748+
limit_value = n;
749+
rownum_qual = qual;
750+
}
744751
pfree(opname);
745752
break;
746753
}

src/oracle_test/regress/expected/rownum.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,18 @@ SELECT id, name FROM rownum_test WHERE ROWNUM = 0;
203203
10 | Jack
204204
(10 rows)
205205

206+
-- ROWNUM = 2 (not optimizable, returns empty - Oracle semantics)
207+
SELECT id, name FROM rownum_test WHERE ROWNUM = 2;
208+
id | name
209+
----+------
210+
(0 rows)
211+
212+
-- ROWNUM = 3 (not optimizable, returns empty - Oracle semantics)
213+
SELECT id, name FROM rownum_test WHERE ROWNUM = 3;
214+
id | name
215+
----+------
216+
(0 rows)
217+
206218
-- ROWNUM with negative number
207219
SELECT id, name FROM rownum_test WHERE ROWNUM <= -1;
208220
id | name
@@ -308,6 +320,15 @@ EXPLAIN (COSTS OFF) SELECT id, name FROM rownum_test WHERE ROWNUM > 5;
308320
-> Seq Scan on rownum_test
309321
(3 rows)
310322

323+
-- ROWNUM = 2 should NOT be optimized to LIMIT (keep ROWNUM in WHERE)
324+
EXPLAIN (COSTS OFF) SELECT id, name FROM rownum_test WHERE ROWNUM = 2;
325+
QUERY PLAN
326+
---------------------------------
327+
Result
328+
One-Time Filter: (ROWNUM = 2)
329+
-> Seq Scan on rownum_test
330+
(3 rows)
331+
311332
--
312333
-- ROWNUM with other clauses
313334
--

src/oracle_test/regress/sql/rownum.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ SELECT id, name FROM rownum_test WHERE ROWNUM >= 2;
125125
-- ROWNUM = 0 (always false)
126126
SELECT id, name FROM rownum_test WHERE ROWNUM = 0;
127127

128+
-- ROWNUM = 2 (not optimizable, returns empty - Oracle semantics)
129+
SELECT id, name FROM rownum_test WHERE ROWNUM = 2;
130+
131+
-- ROWNUM = 3 (not optimizable, returns empty - Oracle semantics)
132+
SELECT id, name FROM rownum_test WHERE ROWNUM = 3;
133+
128134
-- ROWNUM with negative number
129135
SELECT id, name FROM rownum_test WHERE ROWNUM <= -1;
130136

@@ -174,6 +180,9 @@ SELECT * FROM (
174180
-- Non-optimizable pattern (no Limit)
175181
EXPLAIN (COSTS OFF) SELECT id, name FROM rownum_test WHERE ROWNUM > 5;
176182

183+
-- ROWNUM = 2 should NOT be optimized to LIMIT (keep ROWNUM in WHERE)
184+
EXPLAIN (COSTS OFF) SELECT id, name FROM rownum_test WHERE ROWNUM = 2;
185+
177186
--
178187
-- ROWNUM with other clauses
179188
--

0 commit comments

Comments
 (0)