PL/SQL insufficient privileges within a query manually possible - oracle

I need to do this task , and update this table that has a lot of rows.
This table has 2 columns :
FOO and BAR
I have FOO as PK and i know those values, they are both numbers but i don't have any value at bar.
I can manually run every query without any problems, but i made this PL/SQL so it automatically run without any problem, once i need to find the BAR value within another query.
create or replace procedure FxB_pro
IS
tmpFIELD NUMBER := 0;
i NUMBER := 0;
cursor c1 is
SELECT * FROM FooXBar WHERE BAR IS NULL;
BEGIN
FOR CTUpdate IN c1
LOOP
BEGIN
SELECT t5.bar INTO tmpFIELD FROM table_1 t1, table_2 t2, table_3 t3, table_4 t4, table_5 t5, table_6 t6
where t1.fielda_id = t2.fielda_id
and t2.fielda_id = t3.fielda_id
and t3.fieldb_id = t4.fieldb_id
and t3.fieldb_id = t6.fieldb_id
and t4.fieldd_id = t5.fieldc_id
and t1.fieldc = CTUpdate.FOO
and rownum = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
tmpFIELD :=null;
END;
UPDATE FooXBar set BAR = tmpFIELD where FOO=CTUpdate.FOO;
i := i+1;
IF mod(i, 1000) = 0 THEN -- Commit every 1000 records
COMMIT;
END IF;
END LOOP;
COMMIT;
END;
I've tested this in my properly Test Environment the PL/SQL Is created and runs, but when i'm going to run it in Production , i have this error in the Select wich put the value in tmpFIELD :
Erro(12,11): PL/SQL: SQL Statement ignored
Erro(12,143): PL/SQL: ORA-01031: insufficient privileges
I can't figure why this is happening, can someone please help me?

Your privileges are assigned via ROLE.
This is fine with direct SQL, but don't work with PL/SQL.
You need to acquire the privileges direct to you user.
While testing the PL/SQL queries set in advance
set role none;
this will deactivate the priviledges acquired via ROLE and show possible problems running in PL/SQL.

Related

Error when running stored procedure : maximum number of object durations exceeded seems when variable not pass it works

I'm newbie in stored procedures and I create a stored procedure, but when I run it by user input, I get an error; but when get value to variable daynumber, it is working.
Suggetions from SQL Developer are:
*Cause: This typically happens if there is infinite recursion in the PL/SQLfunction that is being executed.
*Action: User should alter the recursion condition in order to prevent infinite recursion.
How can I solve it?
create or replace procedure P_SiteNumber_Range_D(Sitenum NUMBER) is
daynumber number;
begin
p_sitenumber_range_d(Sitenum => daynumber);
-- daynumber := 2;
for l in (select PROVINCE from v_sitenumber_D_province_range)
loop
update PM4h_db.IND_D_3102
set IND_D_3102_029 =
(select countsite from some table where l1.province=province );
end loop;
end P_SiteNumber_Range_D;
Run procedure as :
DECLARE
SITENUM NUMBER;
BEGIN
SITENUM := 3;
P_SITENUMBER_RANGE_D(
SITENUM => SITENUM
);
END;
This procedure doesn't make much sense (at least, to me).
you are passing sitenum and never do anything with it; should it be used in where clause in cursor for loop and/or update statement?
this is a procedure, and then - in line #4 of your original code - you are calling itself (which then calls itself which calls itself etc., until Oracle stops it and returns an error)
the most obvious "solution" is to remove that statement:
p_sitenumber_range_d(Sitenum => daynumber);
but that probably won't be all, because of my first objection
Furthermore, maybe you don't need the loop at all, as the whole code can be rewritten as
create or replace procedure p_sitenumber_range_d (par_sitenum in number)
is
begin
update pm4h_db.ind_d_3102 set
ind_d_3102_029 = (select countsite
from some_table
where province = (select province
from v_sitenumber_d_province_range
where sitenum = par_sitenum
)
);
end;
It might, or might not work - there's a possibility of TOO_MANY_ROWS if select returns more than a single value. I don't know, as I don't have your tables, so - that might need to be fixed.
If you insist on the loop, then consider such a code:
create or replace procedure p_sitenumber_range_d (par_sitenum in number)
is
begin
for cur_r in (select province
from v_sitenumber_d_province_range
where sitenum = par_sitenum
)
loop
update pm4h_db.ind_d_3102 set
ind_d_3102_029 = (select countsite
from some_table
where province = cur_r.province
);
end loop;
end;
Are you aware that you've built in a recursion?
The first thing you do during the procedure is to call up the procedure itself!

Trigger created with compilation errors. Unable to find out the error

I tried to create trigger but it has compilation errors. Below is my plsql code
Create trigger t1 before delete on dept for each row
Declare x number;
Begin
Select count(*) into x from emp where deptno
:=old.deptno;
If x>0 then
Raise_application_error(5000, 'cannot delete row');
End if;
End;
it has compilation errors.
What environment are you using to create your trigger? Most clients should tell you the error message as soon as you run the statement, although some do make it harder than seems reasonable.
However we can always find compilation errors by querying the data dictionary:
select * from user_errors
where name = 'T1';
The only obvious error I can spot in your code is this:
where deptno := old.deptno;
:= is the PL/SQL assignment operator. The test for equality is just =. Also, the old namespace is indicated with a colon. So your WHERE clause should be
where deptno = :old.deptno
Also this is wrong:
Raise_application_error(5000, 'cannot delete row');
Raise_application_error() must have a number in the range Oracle allocates for user-defined exceptions, which is -20999 to -20000. All Oracle error numbers are negative, by the way.
I presume this trigger is part of a homework assignment, because it implements very bad practice. The relationship between EMP and DEPT should be enforced by a foreign key on EMP referencing DEPT. Triggers like this do not scale well and - crucially - do not work in multi-user environments.
CREATE TRIGGER t1
BEFORE DELETE
ON dept
FOR EACH ROW
DECLARE
x NUMBER;
BEGIN
SELECT COUNT (*)
INTO x
FROM emp
WHERE deptno = old.deptno;
IF x > 0
THEN
Raise_application_error (-20000, 'cannot delete row');
END IF;
END;

PL/SQL cannot delete multiple rows

I have written simple code using PL/SQL to delete multiple rows from a table, but below code only deletes one row every i trigger it.
DECLARE
i number(2);
BEGIN
FOR i IN 1..4 LOOP
DELETE FROM table_name WHERE rownum = i;
dbms_output.put_line('i is: '|| i);
END LOOP;
END;
Can someone please suggest what is wrong with code?
ROWNUM is the nth row read.
select * from table_name where rownum = 1;
gets you the first row.
select * from table_name where rownum <= 2;
gets you the first two rows.
select * from table_name where rownum = 2;
gets you no rows, because you cannot read a second row without having read a first one.
This said, you'd have to replace
DELETE FROM table_name WHERE rownum = i;
with
DELETE FROM table_name WHERE rownum = 1;
But why would you do this anyway? Why delete arbitrarily picked records? Why use PL/SQL at all, rather than a mere DELETE FROM table_name WHERE rownum <= 4;?
What you need to understand is how Oracle processes ROWNUM. When assigning ROWNUM to a row, Oracle starts at 1 and only only increments the value when a row is selected; that is, when all conditions in the WHERE clause are met. Since our condition requires that ROWNUM is greater than 2 or equal to nth value, no rows are selected and ROWNUM is never incremented beyond 1.
If you really do wanna achieve it using PLSQL anot using SQL query as my friend Throsten has stated then please find a work around below.
I Created a dummy table test_c which holds 1 column (ID with number as its type).
set serveroutput on ;
DECLARE
i number(2);
j number(2);
counter number(10):=0;
BEGIN
FOR i IN 5..11 LOOP
if counter = 0 then
j:=i;
end if;
DELETE FROM test_c WHERE ID = (select id from (select id,rownum as ro from test_c order by id) where ro =j);
dbms_output.put_line('i is: '|| i);
counter:=counter+1;
END LOOP;
END;
Please note that this is not the right way to do it, but will work for your requirement.

Select from a table which might not exist

We have an issue that needs a bad hack to get around. Let me give you some context:
We have an app the overrides a customers configuration settings upon uninstalling/reinstalling it. It gets installed with default values, overriding any settings the customer put in.
The solution by management would be to create two scripts, one for each step:
Create a temporary table and copy over the configuration settings into it before uninstalling the app.
Once the app is re-installed, copy over the values from the temporary table back into the original table to retain their settings.
I'm not very fond of their solution, but I have to go with it.
I have step 1 down, but I'm having trouble dealing with the situation of running the second script (step 2) without the first script (step 1) being run before.
In essence, the temporary table would not be there when the second script compiles if someone else in a different department forgets to run the first one.
This is the code I'm currently using for the second script.
DECLARE
lvnTableExists NUMBER(1);
lvbTempTableCopied BOOLEAN;
lvsTempTable VARCHAR2(21) := 'TEMP_TABLE';
BEGIN
-- CalcTypVarValue Table Copy
SELECT COUNT(*)
INTO lvnTableExists
FROM ALL_TABLES x
WHERE x.Table_Name = lvsTempTable ;
IF lvnTableExists = 1 THEN
FOR CalcRow IN (SELECT * FROM Temp_Table) LOOP -- Temp_Table will not exist if first script didn't run, causing a compile error
UPDATE SomeOtherTable c
SET c.foo= CalcRow.foo,
c.bar= CalcRow.bar,
c.DateLastMaint = SYSDATE
WHERE c.bob= CalcRow.bob
AND c.bill= CalcRow .bill;
END LOOP;
lvbTempTableCopied := TRUE;
ELSE
lvbTempTableCopied := FALSE;
END IF;
EXCEPTION
WHEN OTHERS THEN
...
...
My problem is that if Temp_Table doesn't exist at all, then I'll get a compile time error, so the script won't run at all. I need it to run so I can take action on whether to do something else if the table doesn't exist based on lvbTempTableCopied.
I've heard of bypassing it with something like FOR CalcRow IN (EXECUTE IMMEDIATE 'SELECT * FROM ' || lvsTempTable), but I can't use it within a FOR IN LOOP like that.
How would I use EXECUTE IMMEDIATE to bypass the compile time error?
You can do it dynamically using REF CURSOR, see sample code below,
DECLARE
TYPE cur_typ IS REF CURSOR;
c cur_typ;
v_table_exists VARCHAR2(1);
type temp1_rec is record (col1 VARCHAR2(100), col2 VARCHAR2(100));
v_temp temp1_rec;
BEGIN
SELECT 'Y'
INTO v_table_exists
FROM all_tables
WHERE table_name = 'TEMP1';
--dynamic query with parameters
OPEN c FOR 'SELECT col1, col2 FROM temp1 WHERE :param1=:param2' USING 'PARAM1', 'PARAM1' ;
LOOP
FETCH c INTO v_temp;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_temp.col1);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
/
CREATE TABLE TEMP1
(COL1 VARCHAR2(100),
col2 VARCHAR2(100));
INSERT INTO temp1
VALUES('123123123asdfasdfsfa', 'JHASDKLFJLASDFLAS');

Find out last executed query in oracle

I am working with an web based application, Where i have to do some customizations. But i dont have access to Java Source code and even JSP's. I can access only database.
Is there a any way to find out last executed query.
Simply there are many triggers on tables(They fires when i do something from frontend), I want to know which query is causing the trigger to fire. Is there a way to find them. Either inside a trigger or some other oracle views or table.
You can use the below example in the trigger to find the statement executed
create or replace TRIGGER tbuTAR_TSM
BEFORE UPDATE ON TAR_TSM
FOR EACH ROW
declare
v_test varchar2(10000);
cursor c1 is
select ltrim(sq.sql_text)
into v_test
from v$sql sq, v$session se, v$open_cursor oc
where sq.sql_id = oc.sql_id
and se.saddr = oc.saddr
and se.sid = oc.sid
and se.audsid = SYS_CONTEXT('userenv', 'sessionid')
order by oc.LAST_SQL_ACTIVE_TIME desc;
begin
open c1;
loop
fetch c1
into v_test;
exit when c1%notfound;
if substr(upper(v_test), 1, 6) = 'UPDATE' then
if instr(upper(v_test), 'TAR_TSM') != 0 then
dbms_output.put_line(v_test);
exit;
end if;
end if;
end loop;
close c1;
end;

Resources