为什么会出现 "ORA-00933: SQL command not properly ended" 错误(立即执行)?

Why do I get "ORA-00933: SQL command not properly ended" error ( execute immediate )?

我创建了一个函数,它使用动态 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))
        into :l_res
        from '|| table_name ||'
        where '|| code_name ||' = :code_value
    ';

    execute immediate l_query
    using in code_value, out l_res;

    return l_res;
end;

但是当我尝试使用它时出现异常“ORA-00933:SQL 命令未正确结束” 这段代码有什么问题?

您可以使用 EXECUTE IMMEDIATE ... INTO ... USING ... 获取 return 值并使用 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;

输出:

CHK
1

并且:

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

关于你的问题:

What is wrong with this code?

使用 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<>fiddle here