Oracle动态SQL:UDF中的立即执行

时间:2019-06-12 13:34:41

标签: sql oracle user-defined-functions execute-immediate

我正在尝试使我的某些代码动态化。在输入问题如何在动态SQL中使用UDF时,我找出了答案:

一个人可以从外面呼叫UDF!

这有效:

Update my_table
Set col1 = get_some_value(col2,col2)
Where 1 = 1;

这不起作用:

Execute Immediate '
Update my_table
Set col1 = get_some_value(col2,col3)
Where 1 = 1
';

但这可行:

Execute Immediate '
Update my_table
Set col1 = my_package_name.get_some_value(col2,col3)
Where 1 = 1
';

我正在使用Oracle Database 12c企业版12.1.0.2.0

如果您有想法,如何从外面跳过电话,请随时告诉我。

许多问候, 彼得

1 个答案:

答案 0 :(得分:0)

检查您的拨款,并确保在加引号的调用中包括明确的架构所有者,使用同义词或直接作为架构所有者进行连接。

请记住,存储过程可以使用代码创建者的特权正常执行,因此应确保用于运行立即执行的用户名具有执行功能的直接访问权限(而不是通过角色)。

以架构所有者身份登录时,在Oracle 12c中工作正常:

create function myfunc(p_text in varchar2) return varchar2 is
begin
  return initcap(p_text);
end;
/

begin
  execute immediate 'update emp set ename = myfunc(ename)';
end;
/
select ename from emp;

返回:

King
Blake
Clark
...

编辑:

根据功能和调用过程在同一程序包中的其他信息,问题可能仅仅是命名和作用域。

使用execute immediate时,该语句由Oracle的SQL引擎在运行时使用有限的周围环境来解析和执行。 简而言之,execute immediate的有效负载不知道它在程序包中运行。

这里是一个演示,应该可以使事情更加清晰。

create or replace package mytest as
  function public_func(p_text in varchar2) return varchar2;
  procedure demo;
end;
/
create or replace package body mytest as
-------------------------------------------------------------------------------
  function public_func(p_text in varchar2) return varchar2 is
  begin
    return initcap(p_text);
  end;
-------------------------------------------------------------------------------
  function private_func(p_text in varchar2) return varchar2 is
  begin
    return lower(p_text);
  end;
-------------------------------------------------------------------------------
  procedure demo is
  begin
    -- Test 1 should fail because the function name is not fully qualified
    begin
      execute immediate 'update emp set ename = public_func(ename)';
    exception when others then
      dbms_output.put_line('Test1: ' || SQLERRM);
    end;
    -- Test 2 should pass
    begin
      execute immediate 'update emp set ename = mytest.public_func(ename)';
    exception when others then
      dbms_output.put_line('Test2: ' || SQLERRM);
    end;
    -- Test 3 should fail because the private function is not visible
    begin
      execute immediate 'update emp set ename = mytest.private_func(ename)';
    exception when others then
      dbms_output.put_line('Test3: ' || SQLERRM);
    end;
  end;
end;
/

结果如下:

SQL> set serveroutput on;
SQL> begin
  2    mytest.demo;
  3  end;
  4  /
Test1: ORA-00904: "PUBLIC_FUNC": invalid identifier
Test3: ORA-00904: "MYTEST"."PRIVATE_FUNC": invalid identifier

PL/SQL procedure successfully completed.

SQL> 

对于测试1,SQL引擎正在寻找名为“ public_func”的东西,但找不到它。这是有道理的,因为您可能有两个软件包,每个软件包中都有一个名为“ public_func”的文件。 SQL引擎不知道它是从包中调用的。

测试2是您所做的,并且按预期运行。

对于测试3,仅在包体内存在的函数被调用。通常,程序包中的其他过程都可以看到私有函数,但是由于这是在运行时解释的,并且SQL引擎不知道在程序包范围内正在调用它,因此该调用也会失败。