I have a requirement to call a function on a remote database (defined by a public database link) within a query. Everything works as expected except that when selecting "Disconnect" from the local database connection in SQL Developer it brings up the dialog:
Connection "localDB" has uncommited changes....
even though the remote function only reads from tables, and the invoking query isn't an update / insert / delete. I'm concerned that using this architecture may cause problems in the future where uncompleted transactions on the localDB diminish resources.
Have tried using the "PRAGMA AUTONOMOUS_TRANSACTION;" instruction in the remote db but to no avail. Any suggestions?
The following demonstrates the behaviour I'm experiencing:
RemoteDB function:
create or replace
FUNCTION FN_test (inempid in number) RETURN varchar2 is PRAGMA AUTONOMOUS_TRANSACTION;
sReturn varchar2(20);
BEGIN
sReturn:=to_char(sysdate,'dd-Mon-yyyy HH24:mi');
return sReturn;
END FN_test;
Invoke from localDB:
select fn_test#RemoteDB.World(123) from Dual;
Thanks!
Any remote call opens transaction - even this executes just query. Oracle can't predict what exactly you do on remote side and tries to prevent uncommitted distributed transaction.
In example below I mere created local database link connecting to native host and just requested data from DUAL table using db link. Oracle has opened the local transaction in respond:
SQL> create database link locallink connect to scott identified by tiger
2 using '127.0.0.1:1521/test';
Database link created.
SQL> select dbms_transaction.local_transaction_id from dual;
LOCAL_TRANSACTION_ID
--------------------------------------------------------------------------------
SQL> select * from dual#locallink;
D
-
X
SQL> select dbms_transaction.local_transaction_id from dual;
LOCAL_TRANSACTION_ID
--------------------------------------------------------------------------------
17.13.10520
Autonomous transaction is useless because it opens and closes the transaction what is differ from the main one. So you need to commit changes in main transaction if you use remote calls.
Related
I have many procedures for which i am using the same db link and this procedure can be executed at different time or some of the procedure runs at the same time depending on the scheduler job. This procedure used to fetch the data from remote database and insert the data in my local database. As i dont want to left open the db connection after execution of every procedure because it produces the load on the database connection which might impact on query cost and query execution time. Thats why i am using dbms_session to close the database link at the end of each and every procedure.
DBMS_SESSION.CLOSE_DATABASE_LINK
But i dont know whether this results any issue while fetching the data for some of the procedure which executes at the same time. As the dbms session close the db link connection for some of the procedures and at the same time if any of the procedure try to acess the db link and it might not fetch the data or results in any error. Will this situation arises while using
DBMS_SESSION.CLOSE_DATABASE_LINK ?
Following are the ways links could be closed:
A. Only session which opened the database link can close it..
Database link are closed when the session is closed...
select * from dba_DB_LINKS will show database links created
V$DBLINK will lists all open/active database links in your session,..
For an indication on how long the dblink has been open, run:
select t.addr, s.sid, s.username, s.machine, s.status,
(sysdate - to_date(t.start_time, 'MM/DD/YY HH24:MI:SS')) * 24 as hours_active
from v$transaction t, v$session s
where t.addr = s.taddr;
How to know if a transaction is local or distributed?
Check v$global_transaction
B. Using ALTER SESSION or explicitly using command:
alter session close database link <name>;
or use the following package:
dbms_session.close_database_link(<name>);
C. It is also posible to set idle_time limit to user under which connects dblink.
On the server side of dblink (target of dblink) issue:
create profile pidle limit idle_time 5; -- 5 minutes
alter user test profile pidle; -- user under which connects dblink
alter system set resource_limit=true; -- must be set to work idle_time limit
(or add resource_limit=true to init.ora or both)
I've got a synonym on a remote Oracle database that I can access in SQL over a database link, eg,
insert into my_table select * from my_synonym#my_database_link;
If I put the above statement into a PLSQL block, it won't compile, giving the error message "ORA-00980: synonym translation is no longer valid". The standard explanation is the table that the synonym points to has been dropped, etc, but this is not the case because the statement works in SQL.
If something works in SQL but not in PL/SQL then in most cases this is a problem with privileges.
Any privilege that a user received through a role is not active when you enter a PL/SQL block. So most probably the SELECT privilege on the underlying table was granted through a role and thus is not "active" in the PL/SQL block.
The usual cure for this is to grant the privileges directly to the user, not through a role.
Thank you to everyone who tried to help. This turned out to be an Oracle limitation:
https://support.oracle.com/rs?type=doc&id=453754.1
APPLIES TO:
PL/SQL - Version 9.2.0.8 and later Information in this document
applies to any platform.
Checked for relevance on 01-Apr-2015
SYMPTOMS
A PL/SQL block fails with error: ORA-00980: synonym translation is no
longer valid, when selecting data from a remote database. The
following code demonstrates this issue:
On DB3 (create the table)
CONNECT u3/u3 DROP TABLE tab; CREATE TABLE tab(c1 number); INSERT
INTO tab VALUES (1); COMMIT;
On DB2 (create a synonym to the table on DB3)
CONNECT u2/u2 DROP DATABASE LINK dblink2; CREATE DATABASE LINK
dblink2 CONNECT TO u3 IDENTIFIED BY u3 USING 'EMT102U6'; SELECT *
FROM global_name#dblink2; DROP SYNONYM syn2; CREATE SYNONYM syn2
FOR tab#dblink2; SELECT * FROM syn2;
On DB1 (create a synonym to the synonym on DB2)
CONNECT u1/u1 DROP DATABASE LINK dblink1; CREATE DATABASE LINK
dblink1 CONNECT TO u2 IDENTIFIED BY u2 USING 'EMT102W6'; SELECT *
FROM global_name#dblink1; DROP SYNONYM syn1; CREATE SYNONYM syn1
FOR syn2#dblink1; SELECT c1 from syn1;
This works in SQL but fails when called from PL/SQL
DECLARE num NUMBER; BEGIN SELECT c1 INTO num FROM syn1; END;
/
ERROR at line 4: ORA-06550: line 4, column 3: PL/SQL: ORA-00980:
synonym translation is no longer valid ORA-06550: line 4, column 3:
PL/SQL: SQL Statement ignored
CAUSE
This issue was reported in Bug 2829591 QUERING FROM A PL/SQL
PROCEDURE IN 9I -> 8I-> 7.3.4, GETTING ORA-980. This bug was closed
as 'NOT A BUG' for the following reasons
PL/SQL cannot instruct middle database (DB2) to follow the database
link during the compilation phase. Therefore in order for this PL/SQL
block to compile and run, both database links dblink1 and dblink2
should be defined on the front end database - DB1. During runtime
database link dblink2 will be looked up in DB2 as expected.
SOLUTION
To implement the solution, please execute the following steps:
Create a database link dblink2 on DB1 pointing to DB3
SQL> create database link dblink2 connect to u3 identified by u3 using
'EMT102U6';
Create and compile the PL/SQL block on DB1.
CREATE DATABASE LINK dblink2 CONNECT TO u3 IDENTIFIED BY u3 USING
'EMT102U6';
SELECT * FROM global_name#dblink2; DECLARE num NUMBER; BEGIN
SELECT c1 INTO num FROM syn1; END; / PL/SQL procedure successfully
completed.
TIP: Another option is to use dyanmic SQL in the PL/SQL block as a
work around. When using dynamic SQL the database link is not resolved
at compile time but at runtime.
Workaround solution is to use an Oracle view instead.
CREATE VIEW v_my_synomym as (select * from my_synonym#my_database_link);
Then reference the view in your package or procedure i.e.:
insert into my_table select * from v_my_synonym;
Check in remote database grants for "my_synonym" must be almost "select" for the user you use in connect string, check also the object which this synonym points at (maybe someone deleted the table).
I found this issue when owner of the table/view/procedure are not match with owner mentioned in SYNONYM.
Example : If owner of table TABLE_BRACH is ownerA and in Synonym mentioned table owner is something else (Not ownerA).
Solution:
1. Drop the SYNONYM
2. Create that with same name with correct owner.
CREATE PUBLIC SYNONYM BRANCH FOR ownerA.TABLE_BRACH ;
I have the function in which I need to drop and create tables. In the example below I try to create the table but it fails
CREATE OR REPLACE FUNCTION DEVTEST
RETURN NUMBER
IS
COMMAND VARCHAR2(256);
ID VARCHAR2(128);
NAME VARCHAR2(128);
TMP_LIST VARCHAR2(128);
BEGIN
ID := '12345';
NAME := 'ABCdef';
TMP_LIST := 'tmpTest';
command := 'create table ' || TMP_LIST || ' ( USER_ID VARCHAR2(11), USER_NAME VARCHAR2(36))';
DBMS_OUTPUT.PUT_LINE('command = ' || command);
EXECUTE IMMEDIATE command;
return 0;
END;
I call the function:
select NSB_DEVTEST() from dual
And get the error:
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML ORA-06512: at "DEV1_SERVER.DEVTEST", line 15
How do I correct this to create/drop a table inside a function?
My server details:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for Solaris: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
The problem is not with the function but with it being called from a SQL statement rather than from a pl/sql block.
A SELECT statement in SQL is equivalent to a READ operation that comes with read consistency. It cannot make any changes to the database. The database should always be the same before and after the "READ" operation completed, otherwise it would be a WRITE operation and the entire database consistency would go havoc.
Also, like the error says, DDL operations do a COMMIT behind the scenes before they start. Any read consistent operation should never do any COMMITS and write to the database without the user knowing.
You can instead call the function from pl/sql like this -
DECLARE
l_result NUBMER;
BEGIN
l_result := DEVTEST;
DBMS_OUTPUT.PUT_LINE(l_number);
END;
Still I would prefer writing a procedure for this, so others don't get confused by why this can't be called from SQL. The general rule that I follow for myself is that - Functions "get" things and Procedures "do" things (like DML).
The answer to your question is: don't. Production code, on the whole, shouldn't be creating tables on the fly. If you need a table to hold data temporarily, then create a Global Temporary Table (GTT) once and have your code refer to it.
The reason why you're getting that error (apart from it being self-evident from the error message) is because you're calling the function from within a SQL statement. You can't do that; you'd have to call it directly in PL/SQL.
I'm curious as to why you think this approach is a good, feasible approach, and also what you're going to be doing with the table once you've created it.
Your code is perfect no problem in the code . the problem is while you try to execute
you can only execute a pure function in select statement, which means a function without ddl & dml . ( if you use pragma autonomous_transaction while performing dml inside a function then you can use it in select statement ). When function has DDL command you can never ever execute it in select statement , but instead you can only execute it in PLSQL block like this
declare
a number;
begin
a:= devtest;
end;
/
and you can check your table
select * from tmptest;
Is there any table or log that shows the connection attempts to an oracle database (I don't mean the active sessions, but attempts, even though they've failed or been denied)?
The information I need is the IP from which has been done the attempt, the user used, the executable used (sqlplus, toad, sqldeveloper, java ...), etc.
I guess you need Oracle Listener logging.
You can use a DATABASE TRIGGER, see CREATE TRIGGER
Example:
CREATE OR REPLACE TRIGGER MY_TRIGGER
AFTER LOGON ON DATABASE
DECLARE
BEGIN
INSERT INTO LOG_TABLE
SELECT
USERNAME, OSUSER, MACHINE, PROGRAM, --> columns in V$SESSION
ora_client_ip_address, ora_login_user, ora_sysevent --> Event Attributes from Trigger
FROM V$SESSION
WHERE SID = SYS_CONTEXT('USERENV', 'SID')
COMMIT;
END;
/
Here you see a list of all System-Defined Event Attributes: Coding Triggers
However, as stated this is a AFTER LOGON trigger, so failed attempts due to wrong password are not covered!
I would like to create database link inside of script, and want to receive all table names from the linked database. If I am correct, I need to create database link in order to use, but Oracle does not allow me to create such thing neither inside of my_fn or DECLARE section. Any suggestion?
DECLARE
TYPE tp_col_array IS TABLE OF varchar2(1000);
FUNCTION my_fn(
p_in_dblink_name IN VARCHAR2,
p_in_schema_name IN VARCHAR2)
RETURN varchar2 AS
vr_coll_table tp_col_array;
vr_coll_owner tp_col_array;
BEGIN
create database link "database1"
connect to my_name
identified by "my_password"
using 'database1';
SELECT owner, table_name
bulk collect into vr_coll_owner, vr_coll_table
FROM all_tables#database1
WHERE OWNER NOT IN ('SYS');
RETURN TO_CHAR(vr_coll_owner(1)); //just for temporary
END my_fn;
BEGIN
DBMS_OUTPUT.PUT_LINE(my_fn('link1','schema1'));
END;
EDIT
I also tried the following, but no luck :(
Execute immediate q'[create database link "database1"
connect to my_name
identified by "my_password"
using 'database1']';
If you create a database link dynamically in a PL/SQL block, every reference to that database link would also need to use dynamic SQL otherwise your block won't compile. Your SELECT statement would need to use EXECUTE IMMEDIATE as well. Stepping back, creating database links at runtime is generally a poor practice-- I'd seriously question why you're going down that path.
According to Justin Cave's comment
Make sure the definer-schema is granted the "create database link" privilege.
This one is working:
me#XE> execute execute immediate 'create database link superlink connect to a identified by b using ''TNSALIAS''';
PL/SQL procedure successfully completed.
me#XE> #mylinks
DB_LINK USERNAME PASSWORD HOST CREATED
--------------- --------------- --------------- ------------------------- --------------------
SUPERLINK A TNSALIAS 22.10.2014 22:42:19