首先給出一個警告
下面的技巧提示如果使用不小心會造成嚴重的安全漏洞
當你在一個非測試環境下使用這種方法之前
一定要完完全全地理解這樣做的後果
如果你需要復制一個表並在(另一個用戶名下的)另一個方案(schema)制作它的一個副本
那麼你可以使用 SQL*Plus 的 COPY 命令
或者使用簡單的語句
create table foo as select * from other
foo;
只要你在另一個用戶的表上有足夠的 select 權限
然而
如果你需要對任何其它數據庫對象做同樣的事情
比如包
過程
函數或視圖
就沒有這麼簡單的命令了
你需要手工找出其代碼並在新實例中運行它
如果能夠簡單
克隆對象
並讓另一個用戶的對象出現在自己的實例中
那將是一件非常值得高興的事
對於為開發過程創建測試方案來說
這一點非常有用
在這個例子中
我將創建一個包
使用該包可以對大多數對象進行克隆(具有一些限制)
要處理的主要問題是獲得源代碼
對象所有者通過視圖USER_SOURCE 可以訪問源代碼
而對於其它用戶
如果對象被授予了EXECUTE 權限給一個用戶
那麼這個用戶就只能看到通過視圖ALL_SOURCE 選出的源代碼
我們可以通過一個過程來封裝
give me the source for your object(給我你的對象的源代碼)
請求
create or replace procedure get_source
(
p_type varchar
p_name varchar
p_cursor out sys_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_source
sql
SQL> variable c refcursor;
SQL> exec get_source(
FUNCTION
FOO
:c);
SQL> print c
有了從對象所有者手中得到的源代碼
調用者就可以在創建自己的方案中創建對象了
我們需要動態 SQL 來從文本字符串構建對象
另外一個需要處理的問題是一些數據庫對象的源代碼的長度可能會超過
個字符
即超過 VARCHAR
字符串的最大長度限制
這樣就不能使用簡單的 VARCHAR
字符串來保存 SQL
在 Oracle 中有一個很少使用的變量DBMS_SQL
PARSE
可以使用它將源代碼存儲為一個由 VARCHAR
字符行所組成的表中
這樣的表可以用來存儲超過
長度限制的 SQL
(在實際的應用中
你可能還需要將任何大於
個字符的代碼行包裝起來
因為USER_SOURCE 最多只能存儲
行字符)
下面將其實現為一個帶有命令行參數的 SQL*Plus 腳本的代碼
declare
ipls_integer :=
;
l_source dbms_sql
varchar
s;
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_line
text;
end loop;
close l_cursor;
if i =
then
raise_application_error(
object does not exist
);
end if;
c := dbms_sql
open_cursor;
dbms_sql
parse(c
l_source
unt
true
dbms_sql
native);
dbms_sql
close_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_type varchar
p_name varchar
)
authidcurrent_user
is
lf char := chr(
);
begin
execute immediate
declare
|| lf
||
ipls_integer :=
;
|| lf
||
l_source dbms_sql
varchar
s;
|| 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_sql
open_cursor;
|| lf
||
dbms_sql
parse(c
l_source
unt
||
true
dbms_sql
native);
|| lf
||
dbms_sql
close_cursor(c);
|| lf
||
end;
|| lf
using p_type
p_name;
end clone_obj;
/
show errors;
注意
使過程具有足夠的權限來創建數據庫對象
我必須添加AUTHID CURRENT_USER
現在你可以用任何能夠調用 Oracle 存儲過程的產品來調用這個過程
下面這個例子與前面的例子相同
只不過這個例子是寫在 SQL*Plus 中的
SQL> @clone_obj
SQL> exec clone_obj(
SCOTT
FUNCTION
FOO
);
在這裡會有一些安全問題
但是不多
只有被授予對 GET_SOURCE 有 EXECUTE 權限的用戶才能讀取他們常規情況下無法看到的對象的源代碼
在理想情況下
你可以創建一個只包含
GET_SOURCE
和一組模板對象的用戶
上面的程序還不完整
但是還是可以作為一個例子來用的
除了需要將
個字符的源代碼包裝成
個字符長的目標行之外
可能還需要對其進行擴展以掃描對象的名稱並插入一個所有者名稱
以使得 DBA 所有者能夠將對象從一個用戶克隆到其他用戶
From:http://tw.wingwit.com/Article/program/SQLServer/201311/22241.html