GRANT SELECT doesn't work in altered session - oracle

I have the following situation. I need to write a procedure to give one schema access to object of the other one. The thing is that this procedure is being executed by administrative account via flyway.
I tried numerous options, but face the following:
Error starting at line : 3 in command - (my begin...end procedure)
Error report -
ORA-00942: table or view does not exist
ORA-06512: at line 3
00942. 00000 - "table or view does not exist"
My code:
ALTER SESSION SET CURRENT_SCHEMA = AppUser;
BEGIN
FOR R IN (SELECT owner, table_name FROM dba_tables WHERE owner='AppUser') LOOP
EXECUTE IMMEDIATE 'GRANT SELECT ON '||R.owner||'.'||R.table_name||' TO QAUser';
END LOOP;
END;
Neither it works w/o altering schema.

You owner is AppUser in mixed case. As such, you will need to quote it when using it in statements, otherwise Oracle will convert it to uppercase.
So you could try this:
ALTER SESSION SET CURRENT_SCHEMA = AppUser;
BEGIN
FOR R IN (SELECT owner, table_name FROM dba_tables WHERE owner='AppUser') LOOP
EXECUTE IMMEDIATE 'GRANT SELECT ON "'||R.owner||'"."'||R.table_name||'" TO "QAUser"';
END LOOP;
END;
See Oracle: What exactly do quotation marks around the table name do?

Related

ORA-00942 error when referencing table after EXECUTE IMMEDIATE create table

I try to run the following snippet in PL/SQL Developer, but the last command throws an ORA-00942: table or view does not exist error message.
DECLARE
sqlCommandABC varchar2(30000) := 'create table ABC_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (SELECT ABC_ID from ABC where ID <=400000)';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ABC_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandABC;
COMMIT;
END;
/
DECLARE
sqlCommandDEF varchar2(30000) := 'create table DEF_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (SELECT DEF_ID from DEF where ID <=15000)';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE DEF_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandDEF;
COMMIT;
END;
/
DECLARE
sqlCommandXYZ varchar2(30000) := 'create table XYZ_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (select ID from XYZ where ABC_1 in (SELECT ABC_ID from ABC_TMP) or DEF_1 in (SELECT DEF_ID from DEF_TMP))';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE XYZ_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandXYZ;
COMMIT;
END;
/
I want to create two temporary tables by selecting data from the original table, with some condition according to the logic.
After the ABC_TMP and the DEF_TMP created, I need to use them in a condition for the select of the third temporary table.
The XYZ_TMP table has two conditions, one for the ABC_TMP records and another for the DEF_TMP records.
This third create table clause throws the error.
I know i could use the original selects here, but those are pretty complex and has a relatively high cost, so if it is possible i want to use the filtered tables. I also know that if I open a new session after the first two tables created it will work, but I want to execute these in one script.
according to the provided info, I understood that DEF_TMP and ABC_TMP is successfully created and thus available. in that case, there are two possibilities here.
First is XYZ table is not exists
Second is the user trying to execute this command does not have select grant.

Table exists in stored procedure while used in select but not when Used in Insert statement

My stored procedure is like this:
create or replace procedure tpk.sp_Test_proc
IS
err_code NUMBER;
err_msg VARCHAR (500);
v_tbl_cnt NUMBER;
v_tbl_valid NUMBER;
Begin
SELECT COUNT(*) INTO v_tbl_cnt FROM USER_TABLES
WHERE TABLE_NAME IN (UPPER('Tbl1'),UPPER('tbl2'),UPPER('tbl3'));
IF(v_tbl_cnt =3) THEN
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl1';
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl2';
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl3';
EXECUTE IMMEDIATE 'DROP TABLE Tbl1';
EXECUTE IMMEDIATE 'DROP TABLE Tbl2';
EXECUTE IMMEDIATE 'DROP TABLE Tbl3';
EXECUTE IMMEDIATE
'CREATE global temporary TABLE tbl1
( Id Integer... )'
Insert into tbl1
Select * from another_schema.Dw_table /* In this line it throws error Table does not exist */
end if;
end;
I tired same table with store procedure only to fetch the data its working there but when I used in Insert statement it throws an error
PL/SQL: ORA-00942 table or view does not exist.
I am totally confused - what's wrong here?
Select * from another_schema.Dw_table
You don't have a privilege to select from that table. Even if you think you do (granted via a role), it won't work in stored procedures - you have to grant it directly to user you're connected to.
Besides, there's no point in truncating tables first, and dropping them next. Just drop them.
Furthermore, there's rarely need to create tables dynamically (the way you do it), especially global temporary tables. Create them once, use them many times. No dropping. No (re)creating them in PL/SQL.

Invalid table name error, when execute alter stored procedure in oracle

-- Disable constaint, good
CREATE OR REPLACE PROCEDURE cpl_disable_constraint(table_name IN varchar2, constraint_name IN varchar2)
AS
BEGIN
execute immediate 'ALTER TABLE :1 DISABLE CONSTRAINT :2' using table_name, constraint_name;
END;
/
-- Bug
declare
table_name varchar2(100) := 'ADV_TEST_COURSE_CREDIT';
column_name varchar2(100) := 'SEQUENCE_NUMBER';
begin
cpl_disable_constraint(table_name, column_name);
end;
/
I'm getting these errors:
ORA-00903: invalid table name
ORA-06512: at "SISD_OWNER.CPL_DISABLE_CONSTRAINT", line 5
ORA-06512: at line 5
00000 - "invalid table name"
Any idea?
As a_horse_with_no_name mentioned, you can't pass an identifier to execute immediate as a parameter. You have to put it in the SQL statement.
execute immediate 'ALTER TABLE ' || table_name || ' DISABLE CONSTRAINT :1' using constraint_name;
Note that this will open you up to SQL Injection if you don't carefully validate the table_name variable. I usually do something like this before calling execute immediate, to make sure the table_name variable is the valid and correct table name for the constraint, and not some malicious string like null; DROP TABLE ADV_TEST_COURSE_CREDIT;:
select c.table_name into table_name from user_constraints c where c.constraint_name = constraint_name;
(By the way, this is part of why people often choose a prefix for local PL/SQL variable names, like "v_table_name", to keep them separate from column names. You can see it's a little confusing in the query above.)
Also I'd like to point out that in your function definition, you're calling the second parameter "constraint_name", but in the anonymous block you're calling it "column_name".

Why EXECUTE IMMEDIATE is needed here?

I am a SQL Server user and I have a small project to do using Oracle, so I’m trying to understand some of the particularities of Oracle and I reckon that I need some help to better understand the following situation:
I want to test if a temporary table exists before creating it so I had this code here:
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
SELECT COUNT(*) INTO table_count FROM all_tables WHERE table_name = 'TEST';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
END IF;
END;
It works normally, so after I executed it once, I added an else statement on my IF:
ELSE
insert into test (hello) values ('hi');
Executed it again and a line was added to my test table.
Ok, my code was ready and working, so I dropped the temp table and tried to run the entire statement again, however when I do that I get the following error:
ORA-06550: line 11, column 19:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 11, column 7:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Then I changed my else statement to this and now it works again:
ELSE
EXECUTE IMMEDIATE 'insert into test (hello) values (''hi'')';
My question is why running individually I can simply use the insert instead of the EXECUTE IMMEDIATE and also why my SELECT statement right after BEGIN still works when all the rest appears to need EXECUTE IMMEDIATE to run properly?
The whole PL/SQL block is parsed at compile time, but the text within a dynamic statement isn't evaluated until runtime. (They're close to the same thing for an anonymous block, but still distinct steps).
Your if/else isn't evaluated until runtime either. The compiler doesn't know that the table will always exist by the time you do your insert, it can only check whether or not it exists at the point it parses the whole block.
If the table does already exist then it's OK; the compiler can see it, the block executes, your select gets 1, and you go into the else to do the insert. But if it does not exist then the parsing of the insert correctly fails with ORA-00942 at compile time and nothing in the block is executed.
Since the table creation is dynamic, all references to the table have to be dynamic too - your insert as you've seen, but also if you then query it. Basically it makes your code much harder to read and can hide syntax errors - since the dynamic code isn't parsed until run-time, and it's possible you could have a mistake in a dynamic statement in a branch that isn't hit for a long time.
Global temporary tables should not be created on-the-fly anyway. They are permanent objects with temporary data, specific to each session, and should not be created/dropped as part of your application code. (No schema changes should be made by your application generally; they should be confined to upgrade/maintenance changes and be controlled, to avoid errors, data loss and unexpected side effects; GTTs are no different).
Unlike temporary tables in some other relational databases, when you create a temporary table in an Oracle database, you create a static table definition. The temporary table is a persistent object described in the data dictionary, but appears empty until your session inserts data into the table. You create a temporary table for the database itself, not for every PL/SQL stored procedure.
Create the GTT once and make all your PL/SQL code static. If you want something closer to SQL Server's local temporary tables then look into PL/SQL collections.
PL/SQL: ORA-00942: table or view does not exist
It is compile time error, i.e. when the static SQL is parsed before even the GTT is created.
Let's see the difference between compile time and run time error:
Static SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 select empno into v from a;
5 end;
6 /
select empno into v from a;
*
ERROR at line 4:
ORA-06550: line 4, column 26:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 4, column 1:
PL/SQL: SQL Statement ignored
Dynamic SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 execute immediate 'select empno from a' into v;
5 end;
6 /
DECLARE
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 4
In the 1st PL/SQL block, there was a semantic check at compile time, and you could see the PL/SQL: ORA-00942: table or view does not exist. In the 2nd PL/SQL block, you do not see the PL/SQL error.
Bottomline,
At compile time it is not known if the table exists, as it is
only created at run time.
In your case, to avoid this behaviour, you need to make the INSERT also dynamic and use EXECUTE IMMEDIATE. In that way, you can escape the compile time error and get the table created dynamically and also do an insert into it dynamically at run time.
Having said that, the basic problem is that you are trying to create GTT on the fly which is not a good idea. You should create it once, and use it the way you want.
I have modified your code a litle bit and it works as far as logic is concerned. But as exp[lained in earlier posts creating GTT on the fly at run time is not at all is a goood idea.
--- Firstly by dropping the table i.e NO TABLE EXISTS in the DB in AVROY
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST'; --Added the line just to drop the table as per your comments
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
END IF;
END;
--------------------OUTPUT-----------------------------------------------
anonymous block completed
table created
SELECT COUNT(*)
-- INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
COUNT(*)
------
1
--------
-- Second option is without DROPPING TABLE
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
--EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST';
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
dbms_output.put_line(SQL%ROWCOUNT||' Rows inserted into the table');
END IF;
END;
-------------------------------OUTPUT-------------------------------------
anonymous block completed
1 Rows inserted into the table
---------------------------------------------------------------------------

Check whether a tables exist or not on another schema - ORACLE DB

I've a stored procedure created by EMP_DBA and part of the query will check whether the existing tables exist or not, if exist then drop table. This query works fine if I connect as EMP_DBA, now I want to run this stored procedure with other account let say USER1 and I've grant all the necessary rights to USER1. How to rewrite below statement in order count return 1 if the table MARKET_DATA exist in schema EMP_DBA ?
BEGIN
SELECT COUNT(*) INTO c
FROM all_tables
WHERE
table_name = 'MARKET_DATA' AND OWNER = 'EMP_DBA';
IF C = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MARKET_DATA';
--exception when others then null;
END IF;
"I've grant all the necessary rights to USER1"
This is a slightly worrying statement. What do you mean by all the necessary rights ? The only appropriate right is execute on a stored procedure owned by EMP_DBA. That procedure should encapsulate everything. EMP_DBA doesn't (or shouldn't) want USER1to drop their tables independently. Besides it isn't possible to grant DDL statements on specific objects, or even specific schemas. And DROP ANY is a powerful privilege to hand out.
The best way to write the stored procedure is to use definer's rights (which is the default). This ensures that the code is executed with the privileges of the stored procedure's owner, not those of the executing user. That your code doesn't work - presumably because you haven't specified the table owner - suggests you haven't got your security model quite right.
In my version I've used ALL_TABLES just like you did, to show the difference between CURRENT_USER and SESSION_USER, but actually USER_TABLES would work just as well.
create or replace procedure recreate_tab
(p_tab_name in all_tables.table_name%type)
authid definer
is
n pls_integer;
begin
select count(*)
into n
from all_tables
where owner = (sys_context('userenv','current_user'))
and table_name = p_tab_name;
if n = 1
then
-- no need to specify schema because it's the procedure owner
execute immediate 'drop table '|| p_tab_name;
end if;
execute immediate 'create table '||p_tab_name
||' ( id number, descr varchar2(30))';
-- grant rights on the new table to the user executing the procedure
execute immediate 'grant select on '||p_tab_name||' to '
|| sys_context('userenv','session_user');
end recreate_tab;
/
grant execute on recreate_tab to user1
/
So. Nothing up my sleeve ...
SQL> conn user1/user1
Connected.
SQL> select count(*) from t42
2 /
select count(*) from t42
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> select count(*) from emp_dba.t42
2 /
COUNT(*)
----------
56179
SQL> exec emp_dba.recreate_tab('T42')
PL/SQL procedure successfully completed.
SQL> select count(*) from emp_dba.t42
2 /
COUNT(*)
----------
0
SQL>
Your select is correct. You should rewrite the EXECUTE IMMEDIATE to do
DROP TABLE EMP_DBA.MARKET_DATA

Resources