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

上一篇 / 下一篇  2012-03-31 09:43:21 / 个人分类:数据库

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

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

f-F'``y0  要处理的主要问题是获得源代码。对象所有者通过视图USER_SOURCE 可以访问源代码。而对于其它用户,如果对象被授予了EXECUTE 权限给一个用户,那么这个用户就只能看到通过视图ALL_SOURCE 选出的源代码。我们可以通过一个过程来封装“give me the source for your object(给我你的对象的源代码)”请求:51Testing软件测试网NW m jG/T6c_

t/ml'`%[+k5e0

[X/B/Yc#l$vV!q2n0
create or replace procedure get_source51Testing软件测试网%E;kh#zM2j(EaoK
(
m o&Dre2{0p_type          varchar2,51Testing软件测试网1N'p0r,nc
p_name          varchar2,51Testing软件测试网\8Dt.s6E"T+G)_PL1T
p_cursor out    sys_refcursor
;gz%n3lc w*r)Q0)
T;Oc2Bah0as51Testing软件测试网 O0ip.E!U7yS
begin
%DN;{wc0zY0open p_cursor for51Testing软件测试网!Vz~9e7E
select text from user_source51Testing软件测试网#xb;rnwo
where type = upper(p_type) and name = p_name
T9BK8i {(XfSQ0order by line;51Testing软件测试网&F.T;l(X8P^s;c
end get_source;
9s?:~%ipRix0/
U|}$H O2pG a0show errors;
51Testing软件测试网o4Qr-Z5tR"Xq

l^P/{.UU051Testing软件测试网6a:G3~#g!C`L|-zFe*s

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

5a(E5j"O;B'b051Testing软件测试网T&~)l/QZ5o P

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

"\%_R'a5\ z k0

~t!jPkw I0

hm!KPm!{1x(tox0
SQL> connect scott/tiger51Testing软件测试网5n9N;p7m9w IOQ
SQL> create function foo return varchar2 as begin return 'hello world'; end;
*d$K$O*b!s(c0SQL> /51Testing软件测试网(j.z!X:scs
SQL> @get_source.sql51Testing软件测试网W-Uz |k` q5id
SQL> variable c refcursor;
6PE+f]+F-SOW%wyU0SQL> exec get_source('FUNCTION','FOO',:c);51Testing软件测试网LgE3iYLz
SQL> print c
51Testing软件测试网xJ-]u^

Uhr F x@}0

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

7q`k)`Y/I#J+p#^0

)|;{p:|ZC7G+T3e6a0

V5n-e G9g&khQ0
declare
N,? `Gh m a.Q0ipls_integer := 1;
2t;Fq r-db!S0l_source dbms_sql.varchar2s;51Testing软件测试网0X] k2\^D#MN(o
l_line varchar2(256);51Testing软件测试网8gx^d V,?d
l_cursorsys_refcursor;51Testing软件测试网1M)E.YR+J"o.z7W
c pls_integer;
~MG lL0r pls_integer;51Testing软件测试网B:nB;O aq%tj
begin
\C8IbWw0&1..get_source('&2','&3',l_cursor);
RX;zdo;oq1Qk/[0l_source(i) := 'create or replace';51Testing软件测试网DW)eUp5PO*SNk
loop51Testing软件测试网u/sV[ A@ {
fetch l_cursor into l_line;51Testing软件测试网0@0be~1kNP
exit when l_cursor%notfound;
}&{!\;J4je0i := i + 1;51Testing软件测试网M:U}3I)o8lI\+a`[
l_source(i) := l_line.text;
H$_2cc0Kh0end loop;51Testing软件测试网~#B*P5HpSW
close l_cursor;
V.R)~k3S0if i = 1 then51Testing软件测试网L\!O,e)B!kZ2CDa
raise_application_error(-20000,'object does not exist');51Testing软件测试网!A8m]N9_;l2M?H
end if;51Testing软件测试网AK joB1y
c := dbms_sql.open_cursor;51Testing软件测试网!m;}}Gc^_S
dbms_sql.parse(c,l_source,1,l_source.count,true,dbms_sql.native);51Testing软件测试网s5k*H)z `5sN!p
dbms_sql.close_cursor(c);
-zdY!QB zlI2a0end;
g{"P#Ls(_PdQQ0/51Testing软件测试网+rz%SquY3R+BT
举个例子,假设一个方案需要克隆 SCOTT 的方案中的“FOO”的函数。SCOTT 将拥有 CLONER 的一个副本并将 EXECUTE 权限授予允许克隆 SCOTT 的对象的用户。其它的用户可以发出以下 SQL*Plus 命令:

d@-M0_c7je{6S0

vbAu_Ba0
SQL> connect ANOTHER USER
w*KO"@ `:]f0SQL> @clone SCOTT FUNCTION FOO

)kdM1oJ g1]^"tf0

vQ8f_Q5W6[&X*L0

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

P wY5Kk"O:`RRk0

4li({(Y*X-C051Testing软件测试网 raTB l!i8Ni!R&m

create or replace procedure clone_obj
x&K2O?9t/P} t4G0(
-~fs ^,h-zX0p_owner         varchar2,51Testing软件测试网/Y0G ? h4k_B l
p_type          varchar2,
d0~il1w+k"} b"r0p_name          varchar251Testing软件测试网 rwv!X3vi6k:T
)
)|&_7ff}? Y0authidcurrent_user
,yG9fC&Q2]#T0is
P4e,m'| b{gyM0lf          char := chr(10);51Testing软件测试网@]li1J"NH
begin51Testing软件测试网1Od,O#XfR/QQ
execute immediate51Testing软件测试网l8u1PtI ` o$jA
'declare' || lf
gD5h%P _4G0|| '    ipls_integer := 1;' || lf
/r1`B0dy;Oi'w0|| '    l_source dbms_sql.varchar2s;' || lf51Testing软件测试网KM4Y+H"QRT Q y
|| '    l_line varchar2(4000);' || lf
UO KP"n?8Ng7{*n0|| '    l_cursorsys_refcursor;' || lf51Testing软件测试网E(ieS4U Ta
|| '    c pls_integer;' || lf
(?U { WSF}0|| '    r pls_integer;' || lf
f6o}@$B+{k#tD0|| 'begin' || lf
l/iE'MR9S;[pO0|| '    '||p_owner||'.get_source(:1,:2,l_cursor);' || lf
C!B3eL+g"_0|| '    l_source(i) := ''create or replace'';' || lf
N5KL]hyN:X'U0|| '    loop' || lf51Testing软件测试网~7a3o:lGYI
|| '        fetch l_cursor into l_line;' || lf
!p4RY6?DtjN0|| '        exit when l_cursor%notfound;' || lf51Testing软件测试网-t:P'_oO4~nd
|| '        i := i + 1;' || lf51Testing软件测试网q`x-|m"H
|| '        l_source(i) := l_line;' || lf
8WGbtU}0|| '    end loop;' || lf
,[nm-mVw2C0|| '    close l_cursor;' || lf
k4A'm pwV oS8k0|| '    if i = 1 then' || lf
n,gYPE;sxY*l0|| '        raise_application_error(-20000,'51Testing软件测试网-a9ey#n~;V,N\
|| '''object does not exist'');' || lf51Testing软件测试网\ W9C r!i)nG8[ w
|| '    end if;' || lf
"bQV~'~G{(r0|| '    c := dbms_sql.open_cursor;' || lf
r |^Br#R+\8`+y0|| '    dbms_sql.parse(c,l_source,1,l_source.count,'
ek`C:u;e8_NI0|| 'true,dbms_sql.native);' || lf
Oo1[t#Ty0|| '    dbms_sql.close_cursor(c);' || lf51Testing软件测试网*RsSE2Rev$} s7F
|| 'end;' || lf51Testing软件测试网9]av&X8b,H Z
using p_type,p_name;
[9Q!{h|2Q%m$@3Eg0end clone_obj;51Testing软件测试网+c:~R;j S+E9Ef
show errors;

1[*uAF9o051Testing软件测试网(g-Y*C)L4g Zf'J.@

51Testing软件测试网.ey;QuE6xZ

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

K7we"abg051Testing软件测试网8\ux:~Vb l9f Kg

'X^4U$~ s NS0
SQL> @clone_obj
WVZ%^]^t[m0SQL> exec clone_obj('SCOTT','FUNCTION','FOO');
51Testing软件测试网 ^tN0SFI [

51Testing软件测试网3P,NYH8h7PN*W

51Testing软件测试网$pukw+`4f-p3w

  在这里会有一些安全问题,但是不多。只有被授予对 GET_SOURCE 有 EXECUTE 权限的用户才能读取他们常规情况下无法看到的对象的源代码。在理想情况下,你可以创建一个只包含“GET_SOURCE”和一组模板对象的用户。51Testing软件测试网J{Ru4?7G

51Testing软件测试网(Clc,t;E

  上面的程序还不完整,但是还是可以作为一个例子来用的。除了需要将4000个字符的源代码包装成256个字符长的目标行之外,可能还需要对其进行扩展以扫描对象的名称并插入一个所有者名称,以使得 DBA 所有者能够将对象从一个用户克隆到其他用户。51Testing软件测试网 sGWQ b q~5tT_


TAG:

 

评分:0

我来说两句

Open Toolbar