Skip to content

Commit 61d2053

Browse files
committed
fix: allow ROWNUM/ROWID as PL/iSQL variable names
In PL/iSQL's pre-columnref hook, check if ROWNUM or ROWID identifiers resolve to declared variables. This prevents the SQL parser from converting them to pseudocolumn references, matching Oracle behavior where these can be used as variable/parameter names in PL/SQL. The fix adds special handling in plisql_pre_column_ref() to check ROWNUM/ROWID names even when resolve_option is not PLISQL_RESOLVE_VARIABLE, since the SQL parser would otherwise intercept them first.
1 parent 42cb0d3 commit 61d2053

3 files changed

Lines changed: 31 additions & 16 deletions

File tree

src/oracle_test/regress/expected/ora_plisql.out

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,12 +1830,8 @@ drop function ff_test;
18301830
-- Oracle allows ROWNUM as parameter/variable name in PL/SQL.
18311831
-- https://github.com/IvorySQL/IvorySQL/pull/1000#issuecomment-3717690863
18321832
--
1833-
-- Current IvorySQL behavior: Fails because ROWNUM is treated as keyword
1834-
-- TODO: These should work to match Oracle behavior
1835-
--
18361833
-- Test 1: Function with 'rownum' as parameter name
18371834
-- Oracle: Creates successfully
1838-
-- IvorySQL: Currently fails with syntax error
18391835
CREATE OR REPLACE FUNCTION test_rownum_param(rownum IN VARCHAR2) RETURN INTEGER IS
18401836
BEGIN
18411837
RAISE NOTICE 'Parameter value: %', rownum;
@@ -1844,7 +1840,6 @@ END;
18441840
/
18451841
-- Test 2: Call the function with variable named 'rownum'
18461842
-- Oracle: Runs successfully
1847-
-- IvorySQL: Currently fails
18481843
DECLARE
18491844
rownum VARCHAR2(256) := 'hello';
18501845
ret INTEGER;
@@ -1853,19 +1848,18 @@ BEGIN
18531848
RAISE NOTICE 'Return value: %', ret;
18541849
END;
18551850
/
1856-
NOTICE: Parameter value: 1
1851+
NOTICE: Parameter value: hello
18571852
NOTICE: Return value: 23
18581853
-- Test 3: Named parameter call using 'rownum =>'
18591854
-- Oracle: Runs successfully
1860-
-- IvorySQL: Currently fails
18611855
DECLARE
18621856
ret INTEGER;
18631857
BEGIN
18641858
ret := test_rownum_param(rownum => 'world');
18651859
RAISE NOTICE 'Named param return: %', ret;
18661860
END;
18671861
/
1868-
NOTICE: Parameter value: 1
1862+
NOTICE: Parameter value: world
18691863
NOTICE: Named param return: 23
18701864
-- Cleanup (may fail if function wasn't created)
18711865
DROP FUNCTION IF EXISTS test_rownum_param;

src/oracle_test/regress/sql/ora_plisql.sql

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,13 +1462,9 @@ drop function ff_test;
14621462
-- Oracle allows ROWNUM as parameter/variable name in PL/SQL.
14631463
-- https://github.com/IvorySQL/IvorySQL/pull/1000#issuecomment-3717690863
14641464
--
1465-
-- Current IvorySQL behavior: Fails because ROWNUM is treated as keyword
1466-
-- TODO: These should work to match Oracle behavior
1467-
--
14681465

14691466
-- Test 1: Function with 'rownum' as parameter name
14701467
-- Oracle: Creates successfully
1471-
-- IvorySQL: Currently fails with syntax error
14721468
CREATE OR REPLACE FUNCTION test_rownum_param(rownum IN VARCHAR2) RETURN INTEGER IS
14731469
BEGIN
14741470
RAISE NOTICE 'Parameter value: %', rownum;
@@ -1478,7 +1474,6 @@ END;
14781474

14791475
-- Test 2: Call the function with variable named 'rownum'
14801476
-- Oracle: Runs successfully
1481-
-- IvorySQL: Currently fails
14821477
DECLARE
14831478
rownum VARCHAR2(256) := 'hello';
14841479
ret INTEGER;
@@ -1490,7 +1485,6 @@ END;
14901485

14911486
-- Test 3: Named parameter call using 'rownum =>'
14921487
-- Oracle: Runs successfully
1493-
-- IvorySQL: Currently fails
14941488
DECLARE
14951489
ret INTEGER;
14961490
BEGIN

src/pl/plisql/src/pl_comp.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "utils/fmgroids.h"
3535
#include "utils/guc.h"
3636
#include "utils/lsyscache.h"
37+
#include "utils/ora_compatible.h"
3738
#include "utils/memutils.h"
3839
#include "utils/regproc.h"
3940
#include "utils/syscache.h"
@@ -1610,8 +1611,34 @@ plisql_pre_column_ref(ParseState *pstate, ColumnRef *cref)
16101611

16111612
if (expr->func->resolve_option == PLISQL_RESOLVE_VARIABLE)
16121613
return resolve_column_ref(pstate, expr, cref, false);
1613-
else
1614-
return NULL;
1614+
1615+
/*
1616+
* Even when resolve_option is not PLISQL_RESOLVE_VARIABLE, we need to
1617+
* check if the identifier is ROWNUM or ROWID and exists as a PL/iSQL
1618+
* variable. This is necessary because the SQL parser would otherwise
1619+
* convert these to pseudocolumn references before the post-columnref
1620+
* hook has a chance to resolve them as variables.
1621+
*
1622+
* This matches Oracle behavior where ROWNUM/ROWID can be used as
1623+
* variable names in PL/SQL.
1624+
*/
1625+
if (compatible_db == ORA_PARSER && list_length(cref->fields) == 1)
1626+
{
1627+
Node *field1 = (Node *) linitial(cref->fields);
1628+
const char *name1 = strVal(field1);
1629+
1630+
if (pg_strcasecmp(name1, "rownum") == 0 ||
1631+
pg_strcasecmp(name1, "rowid") == 0)
1632+
{
1633+
Node *result;
1634+
1635+
result = resolve_column_ref(pstate, expr, cref, false);
1636+
if (result != NULL)
1637+
return result;
1638+
}
1639+
}
1640+
1641+
return NULL;
16151642
}
16161643

16171644
/*

0 commit comments

Comments
 (0)