数据库

位置:IT落伍者 >> 数据库 >> 浏览文章

使用动态SQL克隆数据库对象


发布日期:2019年10月20日
 
使用动态SQL克隆数据库对象

首先给出一个警告下面的技巧提示如果使用不小心会造成严重的安全漏洞当你在一个非测试环境下使用这种方法之前一定要完完全全地理解这样做的后果

如果你需要复制一个表并在(另一个用户名下的)另一个方案(schema)制作它的一个副本那么你可以使用 SQL*Plus 的 COPY 命令或者使用简单的语句create table foo as select * from otherfoo;只要你在另一个用户的表上有足够的 select 权限

然而如果你需要对任何其它数据库对象做同样的事情比如包过程函数或视图就没有这么简单的命令了你需要手工找出其代码并在新实例中运行它如果能够简单克隆对象并让另一个用户的对象出现在自己的实例中那将是一件非常值得高兴的事对于为开发过程创建测试方案来说这一点非常有用在这个例子中我将创建一个包使用该包可以对大多数对象进行克隆(具有一些限制)

要处理的主要问题是获得源代码对象所有者通过视图USER_SOURCE 可以访问源代码而对于其它用户如果对象被授予了EXECUTE 权限给一个用户那么这个用户就只能看到通过视图ALL_SOURCE 选出的源代码我们可以通过一个过程来封装give me the source for your object(给我你的对象的源代码)请求

create or replace procedure get_source

(

p_typevarchar

p_namevarchar

p_cursor outsys_refcursor

)

as

begin

open p_cursor for

select text from user_source

where type = upper(p_type) and name = p_name

order by line;

end get_source;

/

show errors;

注意我没有用UPPER(name)这就意味着你必须匹配这个存储过程的字母大小写Java 存储过程使用很多大小写混合的名字

如果这个过程是由对象所有者所有的那么那个对象的源代码就可以通过一个 REF CURSOR 变量导出如果这个过程被授予了其他用户 EXECUTE 权限那么这个用户将能够调用这个过程并查看任何数据库对象的源代码——即使是那些没有授权给他们的对象和那些在 ALL_SOURCE 中不给出的对象比如 TYPE 声明为了说明这种方法可行请尝试在 SQL*Plus 中输入以下代码

SQL> connect scott/tiger

SQL> create function foo return varchar as begin return hello world; end;

SQL> /

SQL> @get_sourcesql

SQL> variable c refcursor;

SQL> exec get_source(FUNCTIONFOO:c);

SQL> print c

有了从对象所有者手中得到的源代码调用者就可以在创建自己的方案中创建对象了我们需要动态 SQL 来从文本字符串构建对象另外一个需要处理的问题是一些数据库对象的源代码的长度可能会超过个字符即超过 VARCHAR 字符串的最大长度限制这样就不能使用简单的 VARCHAR 字符串来保存 SQL在 Oracle 中有一个很少使用的变量DBMS_SQLPARSE可以使用它将源代码存储为一个由 VARCHAR 字符行所组成的表中这样的表可以用来存储超过长度限制的 SQL(在实际的应用中你可能还需要将任何大于个字符的代码行包装起来因为USER_SOURCE 最多只能存储行字符)下面将其实现为一个带有命令行参数的 SQL*Plus 脚本的代码

declare

ipls_integer := ;

l_source dbms_sqlvarchars;

l_line varchar();

l_cursorsys_refcursor;

c pls_integer;

r pls_integer;

begin

&get_source(&&l_cursor);

l_source(i) := create or replace;

loop

fetch l_cursor into l_line;

exit when l_cursor%notfound;

i := i + ;

l_source(i) := l_linetext;

end loop;

close l_cursor;

if i = then

raise_application_error(object does not exist);

end if;

c := dbms_sqlopen_cursor;

dbms_sqlparse(cl_sourceunttruedbms_sqlnative);

dbms_sqlclose_cursor(c);

end;

/

show errors;

举个例子假设一个方案需要克隆 SCOTT 的方案中的FOO的函数SCOTT 将拥有 CLONER 的一个副本并将 EXECUTE 权限授予允许克隆 SCOTT 的对象的用户其它的用户可以发出以下 SQL*Plus 命令

SQL> connect ANOTHER USER

SQL> @clone SCOTT FUNCTION FOO

这种做法可行但是依然需要 SQL*Plus 会话和脚本我想将所有东西都放在 SQL 中以使得任何应用程序都可以执行这一功能为了实现这一想法我们需要将前面的 SQL*Plus 脚本包装成另外一个动态 SQL 语句在这个语句中我们可以加上所有者的名称并将所有者和类型参数组合可以用以下过程来实现

create or replace procedure clone_obj

(

p_owner varchar

p_typevarchar

p_namevarchar

)

authidcurrent_user

is

lfchar := chr();

begin

execute immediate

declare || lf

|| ipls_integer := ; || lf

|| l_source dbms_sqlvarchars; || lf

|| l_line varchar(); || lf

|| l_cursorsys_refcursor; || lf

|| c pls_integer; || lf

|| r pls_integer; || lf

|| begin || lf

|| ||p_owner||get_source(::l_cursor); || lf

|| l_source(i) := create or replace; || lf

|| loop || lf

|| fetch l_cursor into l_line; || lf

|| exit when l_cursor%notfound; || lf

|| i := i + ; || lf

|| l_source(i) := l_line; || lf

|| end loop; || lf

|| close l_cursor; || lf

|| if i = then || lf

|| raise_application_error(

|| object does not exist); || lf

|| end if; || lf

|| c := dbms_sqlopen_cursor; || lf

|| dbms_sqlparse(cl_sourceunt

|| truedbms_sqlnative); || lf

|| dbms_sqlclose_cursor(c); || lf

|| end; || lf

using p_typep_name;

end clone_obj;

/

show errors;

注意使过程具有足够的权限来创建数据库对象我必须添加AUTHID CURRENT_USER现在你可以用任何能够调用 Oracle 存储过程的产品来调用这个过程下面这个例子与前面的例子相同只不过这个例子是写在 SQL*Plus 中的

SQL> @clone_obj

SQL> exec clone_obj(SCOTTFUNCTIONFOO);

在这里会有一些安全问题但是不多只有被授予对 GET_SOURCE 有 EXECUTE 权限的用户才能读取他们常规情况下无法看到的对象的源代码在理想情况下你可以创建一个只包含GET_SOURCE和一组模板对象的用户

上面的程序还不完整但是还是可以作为一个例子来用的除了需要将个字符的源代码包装成个字符长的目标行之外可能还需要对其进行扩展以扫描对象的名称并插入一个所有者名称以使得 DBA 所有者能够将对象从一个用户克隆到其他用户

上一篇:如何修复SQLSERVER 数据库置疑问题

下一篇:如何减少SQLServer死锁发生