您可以使用EXECUTE IMMEDIATE ... INTO ... USING ... 获取返回值,使用DBMS_ASSERT 在SQL 注入尝试的情况下引发错误:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := 'select sign(count(1))'
|| ' from ' || DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)
|| ' where ' || DBMS_ASSERT.SIMPLE_SQL_NAME(code_name)
|| ' = :code_value';
execute immediate l_query INTO l_res USING code_value;
return l_res;
end;
/
其中,对于样本数据:
CREATE TABLE abc (a, b, c) AS
SELECT 1, 42, 3.14159 FROM DUAL;
然后:
SELECT CHECK_REF_VALUE('abc', 42, 'b') AS chk FROM DUAL;
输出:
还有:
SELECT CHECK_REF_VALUE('abc', 42, '1 = 1 OR b') AS chk FROM DUAL;
引发异常:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 160
ORA-06512: at "FIDDLE_UVOFONEFDEHGDQJELQJL.CHECK_REF_VALUE", line 10
至于你的问题:
这段代码有什么问题?
使用SELECT ... INTO 仅在PL/SQL 块中的SQL 语句中有效,并且当您通过EXECUTE IMMEDIATE 运行该语句时,它在SQL 范围内而不是PL/SQL 范围内执行。
您可以通过将动态代码包装在 BEGIN .. END PL/SQL 匿名块中来修复它(并颠倒 USING 子句中绑定参数的顺序):
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
BEGIN
select sign(count(1))
into :l_res
from '|| DBMS_ASSERT.SIMPLE_SQL_NAME(table_name) ||'
where '|| DBMS_ASSERT.SIMPLE_SQL_NAME(code_name) ||' = :code_value;
END;
';
execute immediate l_query
using out l_res, in code_value;
return l_res;
end;
/
(但是,这比使用EXECUTE IMMEDIATE ... INTO ... USING ... 更复杂一些。)
db小提琴here