ORA-00980 synonym translation no longer valid in PLSQL - oracle

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 ;

Related

Oracle - using synonym as variable

i need to use synonym as variable in a block. I have 2 different schemas with same tables on them and job that switches between schemas making one active. Now I want to write a block checking which schema is active with ALL_SYNONYMS and using result as part of a query.
Here is example:
DECLARE
OWNER VARCHAR2(15);
BEGIN
SELECT TABLE_OWNER
INTO OWNER
FROM ALL_SYNONYMS
WHERE TABLE_NAME = 'MY_TABLE1';
SELECT *
FROM OWNER.MY_TABLE2 ;
END;
But I’m getting ORA-06550 table or view does not exist, and when i run query itself where i put value from ALL_SYNONYMS it returns result.
Any idea how to fix this?
Thanks
You are attempting using symptoms incorrectly. Synonyms are used so you do not need to know which is active. According to the documentation:
Synonyms provide both data independence and location transparency.
Synonyms permit applications to function without modification
regardless of which user owns the table or view and regardless of
which database holds the table or view.
You just use the synonym instead of the object itself.
create table evens( id integer generated always as identity
, val integer
) ;
create table odds( id integer generated always as identity
, val integer
) ;
insert all
when mod(val,2) = 0 then into evens(val)
when mod(val,2) = 1 then into odds(val)
select level val
from dual connect by level <= 10;
-- create the synonym then use it in Select;
create or replace synonym current_even_odd for evens;
select * from current_even_odd;
-- now change the synonym, then run the EXACT same query.
create or replace synonym current_even_odd for odds;
select * from current_even_odd;
In this case it is not quite without modification, you need to change the synonym, But it seems you are trying that already.
Note: You cannot create a synonym for a schema but must point it to a specific object.
I attempted a db<>fiddle for the above, but it appears it is having problems at the moment.
I agree with Belayer that the synonym should provide a layer of abstraction on your tables and your procedure shouldn't need to know what the schema is. But the "table or view does not exist" error is likely an issue related to privileges and definer's rights versus invoker's rights.
To directly reference an object in a procedure, the procedure's schema must have a direct grant to the table. However, an ad hoc query only needs a role with privileges on the object. This is why the SQL will work in your IDE but not in the procedure. Ensure the code that modifies objects and switches synonyms is granting privileges to both roles and directly to schemas.
If direct grants are not possible, you will need to modify the procedure to use AUTHID CURRENT_USER and change the SQL statements to use dynamic SQL - which can be a huge pain. For example:
create or replace procedure test_procedure authid current_user is
v_count number;
begin
execute immediate
q'[
select count(*)
from some_table
]'
into v_count;
end test_procedure;
/
If you really do need to manually switch between schemas, then you may want to consider using something like execute immediate 'alter session set current_schema=schema1'; in the procedure and using dynamic SQL for all of the querying.

PL/SQL Impossible to select a table into a trigger

As system user, I created a new user and grant him dba privileges : GRANT dba TO user_bdda_adminProjet
I'm trying to look into the DBA_ROLE_PRIVS table into a trigger (I didn't paste all my code, feel free to tell me if it's necessary) :
CREATE OR REPLACE TRIGGER my_trigger
INSTEAD OF
INSERT ON vueEnquete
FOR EACH ROW
DECLARE
tmp int;
BEGIN
SELECT COUNT(*) INTO tmp FROM DBA_ROLE_PRIVS;
...
...
END;
/
But when I try to execute this script, I got the following errors :
Erreur(6,3): PL/SQL: SQL Statement ignored
Erreur(6,39): PL/SQL: ORA-00942: table or view does not exist
Looking at different forums, I found that the problem is in general that the user don't have rights to the table.
But when I execute, as my user user_bdda_adminProjet the following line alone (out of a trigger), it works perfectly
SELECT COUNT(*) FROM DBA_ROLE_PRIVS;
Moreover, if I just comment the line in my trigger, it executes without any errors, so I guess the error is specifically at this line.
Thanks in advance for your help and feel free to tell me if you need any further info.
dba is a role, not a privilege.
Privileges obtained through a role are not "active" in PL/SQL. You need to grant the select privilege on the DBA_ROLE_PRIVS directly to the user.

PLSQL - Create DBLink inside function

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

truncate and insert within procedure don't work together

I need to do truncate table and then insert data in that table using procedure.
However, one does like dinamic sql but the other one doesn't:
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
TRUNCATE TABLE AGGREGATE;
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(6,14): PLS-00103: Encountered the symbol "TABLE" when expecting one of the following: := . ( # % ; The symbol ":= was inserted before "TABLE" to continue.
If I add execute immediate around TRUNCATE statement, it works fine, but insert is erroring out.
If I remove it, TRUNCATE TABLE complains...
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
execute immediate 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(7,5): PL/SQL: SQL Statement ignored
Error(7,84): PL/SQL: ORA-00942: table or view does not exist
Can some shed some light here?
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
will work assuming that you have appropriate privileges.
Based on your edit (and echoing #OracleUser's comment), you're likely getting an error running your INSERT statement because old_aggregate is owned by another user and you only have SELECT privileges on that table via a role. If you want to do something in a definer's rights stored procedure, you'll need to have those privileges granted directly to you (or be using 12c which lets you grant privileges to blocks of code rather than to users).
Assuming you want to use a definer's rights stored procedure, you'd need the owner of old_aggregate (or the DBA) to
GRANT SELECT
ON old_user.old_aggregate
TO new_user;
You can verify that you only have the privilege via a role by disabling roles for the session. I'll wager that if you do
SQL> set role none;
SQL> SELECT * FROM old_aggregate
that you'll get an ORA-00942 error as well. This is a good way of simulating what privileges will be available to the user inside a definer's rights stored procedure.
"TRUNCATE is DDL (data definition language). You cannot perform DDL from within PL/SQL. "
http://www.orafaq.com/forum/t/119427

Create stored procedure with table from another schema throws PLS-00201

Oracle 10g. I'm new to procedures, so maybe I'm missing something obvious.
Schema owner ABC has table T2001_WRITEOFF. First I had granted SIUD to some_update_role, and granted that role to developer user IJK. User IJK then created synonym T2001_WRITEOFF for ABC.T2001_WRITEOFF; This worked with normal SQL DML commands.
However, I read elsewhere on here that grants via a role do not work in stored procedures. I dropped the synonym from IJK; then from ABC, granted SIUD directly to IJK. From IJK, normal SQL DML works.
When I try to create a simple procedure as follows, it throws PLS-00201 identifier 'T2001_WRITEOFF' must be declared, and points to the 2nd line. This error is the same whether I use the role grants or not.
create or replace procedure woof1(
fooname in T2001_WRITEOFF.territory%TYPE, <=== error points here
bardesc IN T2001_WRITEOFF.ind_batch_submit%TYPE) IS
BEGIN
INSERT into T2001_WRITEOFF
VALUES ( fooname, bardesc);
END woof1;
/
Thanks in advance for help
JimR
In order to make role right applicable in stored procedures you might want to look at authid current_user in the oracle documentation. Also helpful: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/subprograms.htm#LNPLS682

Resources