How to perform checkpoint from PL/SQL - oracle

I'm performing cleanup operations from PL/SQL which tend to fill up my redo log, causing the database to freeze on the "Cannot allocate new log" condition.
Splitting up the work into smaller chunks didn't solve the problem, because the redo-log files stay at status=ACTIVE and ARCHIVED=YES for too long. Only after "alter system checkpoint" the dirty logs are written to disk and become available again for the next chunk.
Now, how can I do this from PL/SQL. I tried
create procedure cp as begin execute immediate 'alter system checkpoint'; end;
but it gave me ORA-01031: insufficient privileges
I've got the DBA role but that's not effective in PL/SQL procedures. What is the privilege I need to grant?
I know there are parameters to control checkpointing, but I don't want to change these. I only want to manually checkpoint during the cleanup.

Make sure the procedure is owned by a privileged user, and alter its permissions to run with owner privileges.
That's kind-of like a setuid program in Unix.
The syntax is CREATE PROCEDURE ... AUTHID DEFINER ....
The alternative, as commented above by #a_horse_with_no_name and #tbone is to grant ALTER SYSTEM privilege to the user running the procedure.

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!!

Getting Insufficient Privilege while creating Oracle sequence using procedure but not using plsql block

Begin
Execute immediate 'CREATE SEQUENCE seq1 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
Sequence creating successfully.
Create or replace procedure proc_seq as
Begin
Execute immediate 'CREATE SEQUENCE seq2 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
Exec proc_seq();
Error: Insufficient Privilege...
This is a invokers/definers rights issue. By default a stored procedure has definer's right (DR - emphasis added):
During a server call, when a DR unit is pushed onto the call stack, the database stores the currently enabled roles and the current values of CURRENT_USER and CURRENT_SCHEMA. It then changes both CURRENT_USER and CURRENT_SCHEMA to the owner of the DR unit, and enables only the role PUBLIC.
If your privilege to create a sequence was granted to you via a role, then as that role will be disabled, the privilege isn't active. You can verify that is the case by running set role none, which will cause your anonymous block to fail with the same error.
As #Littlefoot said you can have the privilege granted directly to you instead of (or as well as) via the role.
Alternatively, you can specify that the procedure should have invoker's rights:
Create or replace procedure proc_seq
authid current_user
as
Begin
Execute immediate 'CREATE SEQUENCE seq2 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
/
exec proc_seq;
PL/SQL procedure successfully completed.
select seq2.nextval from dual;
NEXTVAL
----------
1
Hopefully this is a contrived example; you wouldn't normally create database objects at runtime anyway...
The caveat to this approach is that it might not be suitable for how you intend the procedure to be invoked. If you're running it as yourself then that's fine, but if you will be granting execute permission to other users then they would have to have the relevant privileges (granted either directly or via a role); and would be creating objects in their own schema. That may be exactly what you do want. But if it isn't, and you don't want to grant create sequence to the callers or to have them create their own objects, then you can keep the procedure with definer's rights and grant the privilege to your user directly.
If you acquired CREATE SEQUENCE privilege via role (apparently, you did), it will work in SQL layer as well is in anonymous PL/SQL blocks (your first code).
However, it won't work in named PL/SQL procedures (your second code) - to do so, you (which means: user that runs that code) will have to be granted that privilege directly, not via role.

DDL Statements in DBMS_JOB

I am trying to schedule a job using DBMS_JOB (I can't use DBMS_SCHEDULER for security reasons), which uses a DDL statement.
DECLARE
job_num NUMBER;
BEGIN
DBMS_JOB.SUBMIT(job => job_num,
what => 'BEGIN EXECUTE IMMEDIATE ''CREATE TABLE temp1 (ID NUMBER)''; END;'
);
DBMS_OUTPUT.PUT_LINE('JobID'||job_num);
DBMS_JOB.RUN(job_num);
END;
/
It fails to execute giving me an error message :
ORA-12011: execution of 1 jobs failed
ORA-06512: at "SYS.DBMS_IJOB", line 548
ORA-06512: at "SYS.DBMS_JOB", line 278
ORA-06512: at line 8
On removing the DBMS_JOB.RUN() statement from inside the anonymous block, I am able to at least create (and save) the job. When I check the job, it has saved this as the code to execute
BEGIN EXECUTE IMMEDIATE 'CREATE TABLE temp1 (id NUMBER) '; END;
If I execute it standalone, it obviously executes. The only time it fails it when I try to execute the entire thing through the call to DBMS_JOB.RUN().
Is there a restriction on using DDL statements as a parameter in DBMS_JOB? I can't find any pointer in documentation for this.
While echoing the sentiments of the other commenters-- creating tables on the fly is a red flag that often indicates that you really ought to be using global temporary tables-- a couple of questions.
Is there a reason that you need the DBMS_JOB.RUN call? Your call to DBMS_JOB.SUBMIT is telling Oracle to run the job asynchronously as soon as the parent transaction commits. So, normally, you'd call DBMS_JOB.SUBMIT and then just `COMMIT'.
Does the user that is submitting job have the CREATE TABLE privilege granted directly? My guess is that the user only has the CREATE TABLE privilege granted via a role. That would allow you to run the anonymous PL/SQL block interactively but not in a job. If so, you'll need the DBA to grant you the CREATE TABLE privilege directly, not via a role.
When a job fails, an entry is written to the alert log with the error message. Can you (or, more likely, the DBA) get the error message and the error stack from the alert log and post it here (assuming it is something other than the privileges issue from #2).

oracle identifier 'ctx_ddl' must be declared - adding a dbms_job

I've added an index to my table with the command:
CREATE INDEX patient_index ON radiology_record(patient_name) INDEXTYPE IS CTXSYS.CONTEXT;
And I'm trying to add a DBMS_JOB which will keep it up to date.
The way I've been running this script is by calling "#myscript.sql" from SQLPLUS
set serveroutput on
declare
job number;
begin
dbms_job.submit(job, 'ctx_ddl.sync_index(''patient_index'');',
interval=>'SYSDATE+1/1440');
commit;
dbms_output.put_line('job '||job||'has been submitted.');
end;
/
The resulting error is PLS-00201: identifier 'CTX_DDL' must be declared
Through my searching I've found someone with a similar problem and his solution was
I spent enough time
debugging this that I thought it
merited sharing what i learned. It
turns out that dbms_jobs only inherit
your schema user's default privileges,
not any privileges it might inherit
from roles granted to that user. This
means that the job will not run with
the ctxsys privilege that you should
have granted to your schema user. So
what does this mean? It means that you
have to run the job as the ctxsys user
I unfortunately cannot use this to grant myself privileges since yes, this is homework, and I don't have permissions to grant execute on ctx_ddl to myself.
Does anyone have clues as to how I can resolve this issue? Otherwise I will wait until later this week and consult the TA's.
Thanks
I don't think there's a workaround since the documentation of DBMS_JOB explicitly specifies this restriction:
You can execute procedures that are owned by the user or for which the user is explicitly granted EXECUTE. However, procedures for which the user is granted the execute privilege through roles cannot be executed.

Can GRANT be used inside an Oracle Store Procedure?

When trying to place a GRANT statement in an Oracle 11 stored procedure, it reports that GRANT is an unexpected symbol. Does GRANT need to be prefaced by something, or does Oracle simply disallow running GRANTS inside SPs?
It's a bad idea to use DDL (like GRANT) inside stored procedures.
You will have to use dynamic SQL (EXECUTE IMMEDIATE) to do this, but, honestly, I don't see why would you want to do this inside a stored proc.
Here's a PL/SQL stored procedure that grants object privileges (SELECT, UPDATE, etc.) on all tables owned by the current user to another user, e.g. - "appuser".
CREATE OR REPLACE PROCEDURE grant_privs
IS
CURSOR ut_cur IS SELECT table_name from user_tables;
ut_rec ut_cur%rowtype;
BEGIN
FOR ut_rec IN ut_cur
LOOP
EXECUTE IMMEDIATE 'GRANT ALL ON ' || ut_rec.table_name || ' TO appuser';
END LOOP;
END;
/
It was executed automatically after deploying database changes for a web application. The current user owns the database tables. After deploying any database changes, the stored procedure was executed to ensure that "appuser" could run SQL statements on all of the tables. The web application connected as "appuser", which had only limited system privileges, for security reasons.
This is both a solution to the problem and a solid use case for the solution.

Resources