Oracle - using synonym as variable - oracle

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.

Related

Oracle: my DML for Index recreation puzzle

Problem anamnesis:
In order to automate index recreation for a PVJOURNAL table in oracle I wrote a procedure in a package.
The table PVJOURNAL locates in PROVIEW schema. My SPACEMAN user has enough grants in that schema. Here below is a DDL of the package:
CREATE OR REPLACE
PACKAGE spaceman.tmp_itcm4052 is
-- This proc rebuilds ALL indexes of a specific.
PROCEDURE idx_rebuild;
END;
/
CREATE OR REPLACE
PACKAGE BODY spaceman.tmp_itcm4052 is
-- ===========================================================================
PROCEDURE idx_rebuild as
-- This proc rebuilds ALL indexes of a specific table. Run by Job
v_sql_str VARCHAR2(200);
begin
FOR rec in (
select owner ||'.'|| index_name as IDX_NAME,
tablespace_name as TblSpace
from sys.all_indexes
where upper(index_type) = 'NORMAL'
and upper(table_owner) = 'PROVIEW'
and upper(table_name) in ('PVJOURNAL')
)
LOOP
v_sql_str := 'ALTER INDEX '||rec.IDX_NAME||' rebuild tablespace '
||rec.TblSpace||' online';
-- ALTER INDEX ... SHRINK SPACE COMPACT
-- ALTER INDEX ... DEALLOCATE UNUSED SHRINK SPACE COMPACT
dbms_output.put_line('v_sql_str = '||v_sql_str);
execute immediate v_sql_str;
commit;
END LOOP;
end idx_rebuild;
END tmp_ITCM4052;
/
Problem itself:
The problem is that a call of the procedure under my SPACEMAN user
begin
spaceman.tmp_itcm4052.idx_rebuild;
end;
ends up with error
ORA-01031: insufficient privileges - full view of the error
But the direct run of the line, produced by the procedure, saved into v_sql_str variable and visualized by this piece of the procedure:
dbms_output.put_line('v_sql_str = '||v_sql_str);, done under the same SPACEMAN user works like a magic. proof that direct alter does work
Questions:
What wrong have I done to the gods of pl/sql? Why the call to the procedure fails whilst the same command, execurted directly - is not?
UPD: Not only GRANT INDEX ON PROVIEW.PVJOURNAL TO SPACEMAN; didn't help. Moreover, for whatewer reason not yet kown to me, GRANT ALTER ANY INDEX to SPACEMAN; didn't help either.
Had doublechecked it with
SELECT PRIVILEGE
FROM sys.dba_sys_privs
WHERE upper(grantee) = 'SPACEMAN'
UNION
SELECT PRIVILEGE
FROM dba_role_privs rp JOIN role_sys_privs rsp ON (rp.granted_role = rsp.role)
WHERE upper(rp.grantee) = 'SPACEMAN'
ORDER BY 1;
the privilege ALTER ANY INDEX (as well as ALTER ANY INDEXTYPE ) is in the list.
UPD 1: as it turned out - the ALTER ANY INDEX privilege is not sufficient in this case. What did helped me is that link, kindly provided by #Wernfried Domscheit. REspect, man!
The procedure created as
CREATE OR REPLACE
PACKAGE spaceman.tmp_itcm4052 authid CURRENT_USER is
works like a spell.
Inside a PL/SQL block you have only privileges which are granted to the user directly. Privileges which are granted by ROLE (e.g. DBA) do not apply inside a PL/SQL block (except role PUBLIC).
Grant CREATE ANY INDEX directly to the user, then it should work.
The behavior described above is the default, it is called "definer's rights unit". You can change it by adding AUTHID CURRENT_USER to your procedure/package. For more information see Invoker's Rights and Definer's Rights (AUTHID Property)
N.b. COMMIT is not required after a DDL statement. Oracle performs an implicit commit.
Different grants are used when you invoke direct SQL and when it is used in the procedure.
Privileges granted through a role (such as DBA) are not available to
definer's rights stored procedures which is the default.
Only privileges that are granted directly to the user are available in a
definer's rights stored procedure.
You must grant the ALTER ANY INDEX privileges directly to your user.
Cheers!!

Strange ORA-942 when using DBMS_CRYPTO but table exists

This is very strange, and not easy to explain, so please bear with me.
Oracle 12.2.0.1 on Linux x86_64.
We have a user called BATCH who has minimal privileges.
CREATE USER batch IDENTIFIED BY batch
DEFAULT TABLESPACE users
QUOTA UNLIMITED ON users;
GRANT CREATE SESSION TO batch;
GRANT EXECUTE ON DBMS_CRYPTO TO batch;
The is a PLSQL package in a schema called ATLED which is :
CREATE OR REPLACE PACKAGE ALTED.the_pkh AUTHID current_user AS
PROCEDURE crttab;
END;
/
CREATE OR REPLACE PACKAGE BODY ALTED.the_pkh AS
PROCEDURE crttab IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b';
END crttab;
END;
/
We are using Code Based Access Control (CBAC - 12c feature) to restrict/control/allow certain canned actions to an otherwise toothless user, so we create a wrapper procedure, grant that a high priv role, and grant execute on that to the batch user:
CREATE OR REPLACE PROCEDURE ALTED.wrapper_crttab AS
PROCEDURE p1 IS
CURSOR c1 is SELECT * FROM SESSION_PRIVS;
BEGIN
FOR r1 IN c1 LOOP
DBMS_OUTPUT.PUT_LINE( r1.privilege );
END LOOP;
END;
BEGIN
p1;
ALTED.the_pkh.crttab;
END;
/
GRANT IMP_FULL_DATABASE TO ALTED;
GRANT IMP_FULL_DATABASE ALTED.wrapper_crttab;
GRANT EXECUTE ON ALTED.wrapper_crttab TO batch;
Now let's run it:
CONN batch/batch
SET SERVEROUTPUT ON
EXEC ALTED.wrapper_crttab;
This causes the error:
Error at line 1:
ORA-00942: table or view does not exist
The tables referenced do exist.
The call to the p1 proc confirms that all the privileges bundled with IMP_FULL_DATABASE are present, including CREATE ANY TABLE, DROP ANY TABLE, EXECUTE ANY PROCEDURE.
If I do this:
GRANT CREATE ANY TABLE TO batch;
GRANT SELECT ANY TABLE TO batch;
CONN batch/batch
EXEC EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b;
This works.
If I change the CREATE TABLE stmt to remove the DBMS_CRYPTO call, it works as well.
The actual package/proc that is called creates a number of tables fine when run as above, but fails on the case when DBMS_CRYPTO is called in any CREATE TABLE stmt.
If I grant the batch user the CREATE ANY TABLE, SELECT ANY TABLE and EXECUTE ANY PROCEDURE privs directly and run the CREATE TABLE command as batch directly then that works too.
So this is not (I think) a straight ORA-942 error, but something related to a chain of privileges to DBMS_CRYPTO, and only when executed in a package stored procedure, but what exactly I do not know.
UPDATE 1
If I create a wrapper for DBMS_CRYPTO.HASH as follows:
CREATE OR REPLACE FUNCTION batch.crypto_hash ( pcc IN CLOB ) RETURN VARCHAR2 IS
BEGIN
RETURN DBMS_CRYPTO.HASH(pcc,2);
END;
/
Then replace the DBMS_CRYPTO.HASH(cc,2) in the CREATE TABLE stmt with batch.crypto_hash(cc) then it works!!!
So, DEFINITELY not a issue with grants on teh tables being referenced, but more likely something internal with the way DBMS_CRYPTO works. Perhaps it reads a look up table somewhere. I tried GRANT EXECUTE ON UTL_I18N to batch as well before this but that didn't work.
So I have a workaround, but woudl still like to know why this happens.
You're doing this:
... FROM ARCHIVE.table_b
User, who is supposed to select from that table, has to have SELECT privileges on it. It can be granted
via role
directly
If you granted the privilege via role, it works - but not in named PL/SQL procedures. It will work in anonymous PL/SQL, but not in procedures, function, packages, so - check that and, possibly, grant SELECT on table_b directly to that user.

Best process strategy for maintaining table access when dropping and re-creating tables

Is it possible to have permissions script all of the permissions in an Oracle 12c database without also simultaneously having the rights to modify either objects or data in the schema?
I need the ability to be able to script the existing permissions on a table before a table is dropped and recreated in order to re-apply the permissions after the table is recreated. I will have to submit the scripts to a DBA to run and need to include these permisions when dropping and re-creating a table. If I cannot see the existing permissions, I cannot include them. The DBA will not allow me to have rights to do this myself
but he will only run scripts that I write 100% myself.
When I try to view the DDL for a table while logged in using an ID that does not match the schema name, I get the following error:
To extract audit options, you must either have SELECT privilege on
DBA_OBJ_AUDIT_OPTS or log into the schema that you are extracting.
Will granting SELECT rights on DBA_OBJ_AUDIT_OPTS give me the ability to see all grants made on a table without also providing me additional rights to modify the schema or data?
Don't do a DROP TABLE/CREATE TABLE. Use DBMS_REDEFINITION instead. Here's a modified version of the sample code I keep around for this feature:
CREATE TABLE my_preexisting_table
( a number,
constraint my_preexisting_table_pk primary key (a) );
GRANT SELECT, UPDATE ON my_preexisting_table TO ont;
-- Start the online redefinition process...
-- First, check whether your table is a candidate for the process
BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE('apps','my_preexisting_table',
DBMS_REDEFINITION.CONS_USE_ROWID);
END;
/
-- Create your new table with a new name. This will eventually replace the pre-existing one
--DROP TABLE apps.my_preexisting_table_redef;
CREATE TABLE apps.my_preexisting_table_redef
(
new_column1 NUMBER,
a NUMBER,
new_column2 DATE,
-- Let's change the primary key while we're at it
-- Unfortunately, we have to rename our constraints because they share a global namespace
constraint my_preexisting_table_pk_r primary key (new_column1, a)
)
-- Let's partition the table while we're at it...
PARTITION BY RANGE (new_column2)
INTERVAL (NUMTODSINTERVAL (1,'DAY')) ( partition my_preexisting_table_old values less than (to_date('01-JAN-2000','DD-MON-YYYY') ));
;
-- Takes long if your table is big.
BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE('apps', 'my_preexisting_table','my_preexisting_table_redef',
-- Map columns from the existing table to the new table here
'a new_column1, a a, sysdate new_column2',
dbms_redefinition.cons_use_rowid);
END;
/
DECLARE
num_errors PLS_INTEGER;
BEGIN
DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS('apps', 'my_preexisting_table','my_preexisting_table_redef',
DBMS_REDEFINITION.CONS_ORIG_PARAMS, TRUE, TRUE, TRUE, TRUE, num_errors);
DBMS_OUTPUT.PUT_LINE('Copy depenedent objects: num_errors = ' || num_errors);
END;
-- Make sure there were no problems... or, if there were problems then they were expected. For example,
-- there will be an error listed because it cannot copy the PK constraint from the original table (because we made a new one already)
-- and that's OK.
select * from DBA_REDEFINITION_ERRORS where base_table_name = 'MY_PREEXISTING_TABLE';
BEGIN
DBMS_REDEFINITION.FINISH_REDEF_TABLE('apps', 'my_preexisting_table', 'my_preexisting_table_redef');
END;
/
-- Check out the results.
select * from my_preexisting_table;
-- Verify the grants are still in place...
select * from DBA_TAB_PRIVS where table_Name = 'MY_PREEXISTING_TABLE';
-- Drop our redef table when ready...
DROP TABLE apps.my_preexisting_table_redef;
Create a function on the application schema that returns object privileges on tables owned by that schema, then grant yourself the privilege to execute that function.
That's the simplest solution to the problem. Looking at the big picture, there are better methods but these might require significant changes to the process.
User ALTER instead of DROP and CREATE. There are a lot of dependent object types, it's impossible to think of them all. For example, do the tables have any
Virtual Private Database predicates, histograms built based on column usage, etc. In an environment where the code "lives" on the database, DROPs are the enemy.
Store the "one true version" of the database in version controlled text files. This is the only way you can safely DROP tables and know exactly how to rebuild them.
Only after the schemas have been dropped and recreated a few hundred times on local databases will your organization truly understand how things work.
Here's the easiest way to get this working:
Sample Schema
drop table test1;
create table test1(a number);
grant select on test1 to system;
grant references on test1 to system with grant option;
Create Function to Generate Script
Create this function on the application schema.
create or replace function get_table_grants(p_table_name in varchar2) return clob is
--Purpose: Return the object grants for a table.
v_ddl clob;
begin
--Enable the SQL terminator, ";" or "/".
dbms_metadata.set_transform_param(
dbms_metadata.session_transform,
'SQLTERMINATOR',
true);
--Get the DDL.
select dbms_metadata.get_dependent_ddl(
object_type => 'OBJECT_GRANT',
base_object_name => upper(trim(p_table_name)),
base_object_schema => user)
into v_ddl
from dual;
--Return the DDL.
return v_ddl;
end get_table_grants;
/
--Grant access to yourself.
grant execute on get_table_grants to YOUR_USERNAME;
Sample Output
select get_table_grants('TEST1') from dual;
GRANT REFERENCES ON "JHELLER"."TEST1" TO "SYSTEM" WITH GRANT OPTION;
GRANT SELECT ON "JHELLER"."TEST1" TO "SYSTEM";

ORA-00980 synonym translation no longer valid in PLSQL

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 ;

Oracle 9i: Synonymed Table Does Not Exist?

I've created a package that contains a stored procedure that I plan to invoke from a separate application. The stored procedure will return a sorted list of all the views and tables in the schema. To do that, it performs a simple select on the DBA_TABLES and DBA_VIEWS synonyms, as shown below:
CREATE OR REPLACE
PACKAGE BODY TITAN_ENTITY AS
PROCEDURE GETSCHEMAOBJECTS (RESULTS IN OUT T_CURSOR)
IS
V_CURSOR T_CURSOR;
BEGIN
OPEN V_CURSOR FOR
SELECT 'T' OBJECTTYPE, TABLE_NAME OBJECTNAME
FROM DBA_TABLES
WHERE OWNER = 'SONAR5'
UNION ALL
SELECT 'V' OBJECTTYPE, VIEW_NAME OBJECTNAME
FROM DBA_VIEWS
WHERE OWNER = 'SONAR5'
ORDER BY OBJECTNAME;
RESULTS := V_CURSOR;
END GETSCHEMAOBJECTS;
END TITAN_ENTITY;
I have verified that the synonyms in question exist, and are public:
CREATE PUBLIC SYNONYM "DBA_TABLES" FOR "SYS"."DBA_TABLES"
CREATE PUBLIC SYNONYM "DBA_VIEWS" FOR "SYS"."DBA_VIEWS"
My understanding is that, because they are public, I don't need any further permissions to get to them. If that understanding is incorrect, I wish someone would disabuse me of the notion and point me to more accurate data.
Now here's my problem: I can open a worksheet in Oracle SQL Developer and select from these tables just fine. I get meaningful data just fine (567 rows, as a matter of fact). But when I try to execute the stored procedure, Oracle complains with a compilation error, as shown below:
Error(9,8): PL/SQL: SQL Statement ignored
Error(10,16): PL/SQL: ORA-00942: table or view does not exist
When I double-click on that second error message, SQL Developer takes me to the first FROM clause ("FROM DBA_TABLES").
So I'm fairly stumped. I know SQL Server pretty well, and I'm new to Oracle, so please bear with me. If you could provide some clues, or point me in the right direction, I'd really appreciate it.
Thanks in advance!
Use ALL_TABLES and ALL_VIEWS instead of DBA_TABLES and DBA_VIEWS. ALL_% views should be accessible to all users.
If you select from a table or a view in a stored PL/SQL-procedure or a stored PL/SQL-function you need a direct grant. A grant via a database role isn't enough.
You probably need a direct grant on view dba_tables. (public) synonyms are just (public) synonyms. You need directly granted select rights.
See here: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:48704116042682#48798240264807
Edit: Sorry, I glossed over the part where you seem to say that you can select from DBA_TABLES directly. Most likely the issue is that your privileges are granted through a role as someone else answered. But it's still worth explaining that your understanding of PUBLIC synonyms is incomplete, and that using ALL_TABLES would be better if it accomplishes what you need.
The synonym being PUBLIC only means that all users can reference the synonym; it does not grant them any access to the object that the synonym refers to.
What you would do to most directly solve this error is grant SELECT privilege on the SYS views to the user(s) that will run this procedure. However, I think that is a very bad idea.
As suggested by Raimonds, consider whether you can get what you need from USER_TABLES or ALL_TABLES instead. What user is calling this procedure, and what access does that user have to SONAR5's tables?
Generally, if your application is interested in a table, presumably it has some privileges on it, in which case is should be listed in ALL_TABLES.

Resources