问题描述
有一个到远程数据库的 DBLink 和一个包 MY_PACKAGE
(在本地架构中)通过 MY_VIEW
间接依赖它。
当 DBLink 无效时(无论出于何种原因,即连接丢失或远程凭据更改),程序包就会无效。但是让它工作会很棒,因为除了通过这个 DBLink 进行谈判之外,这个包还有其他的职责。总体而言,我认为根据网络连接等不稳定部分来确定模式对象有效性并不是一种理想的情况。
我在旧代码中发现了这种情况(请参阅 MY_PACKAGE.doSomething()
),我什至不确定这是否是正确的设计。您能谈谈您对此的看法吗?
即使某些 DBLink 不可用,保持包处于 VALID 状态的正确方法是什么?
例如,使用引用 MY_VIEW
的动态游标是否正确,它仅在 DBLink 不正常(即 MY_PACKAGE.doSomethingAgain()
)时才无法打开。当然,也许有一个更自然的方法来解决这个问题。我会很高兴听到所有适合在 Oracle 中安全使用 DBLink 的选项。
代码:
-- DBLink --------------------------------------------------------------
CREATE DATABASE LINK "MY_LINK"
CONNECT TO "MY_USER" IDENTIFIED BY VALUES ':1' USING 'WHATEVER';
------------------------------------------------------------------------
-- Synonym -------------------------------------------------------------
CREATE OR REPLACE EDITIONABLE SYNONYM "MY_SYNONYM"
FOR "MY_REMOTE_SCHEMA"."MY_REMOTE_VIEW"@"MY_LINK";
------------------------------------------------------------------------
-- View ----------------------------------------------------------------
CREATE OR REPLACE VIEW "MY_VIEW" AS
SELECT a,b,c FROM "MY_SYNONYM";
> Compiler Error
> ORA-02063: preceding 2 lines from MY_LINK
> ORA-01017: invalid username/password; logon denied
> [Oracle][ODBC sql Server Wire Protocol driver][sql Server]
> Login Failed for user 'MY_USER'. {28000,NativeErr = 18456}
------------------------------------------------------------------------
.
-- Package body --------------------------------------------------------
CREATE OR REPLACE PACKAGE BODY MY_PACKAGE AS
PROCEDURE doSomething()
IS
i NUMBER := 0;
BEGIN
FOR rec IN (SELECT * FROM "MY_VIEW")
LOOP
i := i + 1;
END LOOP;
END;
PROCEDURE doSomethingAgain()
IS
i NUMBER := 0;
my_cur SYS_REFCURSOR;
BEGIN
OPEN my_cur FOR 'SELECT * FROM MY_VIEW';
LOOP
FETCH my_cur -- ...
-- etc ...
i := i + 1;
END LOOP;
END;
END MY_PACKAGE;
> Compiler Error
> 7/4 PL/sql: sql Statement ignored
> 7/35 PL/sql: ORA-04063: view "MY_VIEW" has errors
------------------------------------------------------------------------
解决方法
我认为,您拥有的最佳选择是保持数据库链接有效并有效。
另一种选择是将使用那些可疑数据库链接的过程从该包中移出,并使它们成为独立的存储过程,这样如果它们变得无效,只有它们 无效(而不是整个程序包),或者 - 可能 - 将这些程序(具有相同命运)放入一个单独的程序包中。
另一种选择是使用动态 SQL,因为在您实际尝试使用它们之前,Oracle 并不关心所涉及的任何对象是否有效。像这样:
SQL> create database link my_link
2 connect to whoever
3 identified by whatever
4 using 'database_that_does_not_exist';
Database link created.
SQL> create or replace view my_view as
2 select * from some_table@my_link;
select * from some_table@my_link
*
ERROR at line 2:
ORA-12154: TNS:could not resolve the connect identifier specified
SQL> create or replace procedure my_proc as
2 l_id number;
3 begin
4 select id into l_id
5 from my_view;
6 end;
7 /
Warning: Procedure created with compilation errors.
SQL> show err
Errors for PROCEDURE MY_PROC:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/3 PL/SQL: SQL Statement ignored
5/8 PL/SQL: ORA-00942: table or view does not exist
但是,使用动态 SQL:
SQL> create or replace procedure my_proc as
2 l_id number;
3 begin
4 execute immediate 'select id from my_view' into l_id;
5 end;
6 /
Procedure created.
SQL>
看起来不错,但用不了多久:
SQL> exec my_proc;
BEGIN my_proc; END;
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SCOTT.MY_PROC",line 4
ORA-06512: at line 1
SQL>
是的,我知道 - 并非所有代码都足够简单,可以将其放入动态 SQL 中。它越大,您必须维护它的问题就越严重,所以这只是我真的不想使用的可能性。