DDL Statements in DBMS_JOB - oracle

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).

Related

Submitted Oracle job using dbms_job.submit and it failed but I don't know where to look for an error message

We are initiating the rebuilding of many materialized views by using dbms_job.submit to execute a stored procedure that perform the rebuilding. However, I am having trouble trying to figure out how to determine if a submitted job failed. The issue that I am having is that the job is failing but I cannot identify what the issue is. So, I am trying to start out with a simple example, which is probably failing on a permission issue but I don't know where to look for the error message.
I have the following test procedure that I want to initiate using dbms_job.submit.
CREATE OR REPLACE PROCEDURE MYLANID.JUNKPROC
AS
lv_msg varchar2(3000);
BEGIN
INSERT INTO MYLANID.junk_log ( msg ) VALUES ('Hello World' );
commit;
EXCEPTION
WHEN OTHERS THEN
lv_msg := SUBSTR(sqlerrm, 1, 3000);
INSERT INTO MYLANID.junk_log ( msg ) VALUES (lv_msg);
END;
/
Note that this table is used above:
CREATE TABLE MYLANID.JUNK_LOG (
EVENT_TIME TIMESTAMP(6) DEFAULT systimestamp,
MSG VARCHAR2(3000 BYTE))
To submit the above procedure as a job, I execute the following anonymous block.
declare l_jobid binary_integer;
BEGIN
dbms_job.submit(job => l_jobid, what => 'BEGIN MYLANID.JUNKPROC; END;');
DBMS_OUTPUT.PUT_LINE('l_jobid:' || l_jobid);
commit;
END;
I then execute the following SQL...
select * from all_jobs;
...to see one record that represents my submitted job. When I re-query the all_jobs view, I see that this record quickly disappears from the view within a few seconds, presumably when the job completes. All is happy so far. I would like to use the presence of a record in the all_jobs view to determine whether a submitted job is running or has failed. I expect to be able to tell if it failed by looking at the ALL_JOBS.FAILURES column having a non null value > 0.
The problem, probably a permission issue, begins when I switch to another schema and I switch all of the occurrences of the above SQL and replace "MYSCHEMA" with "ANOTHERSCHEMA" that I also have access to. For example, I create the following
Table: ANOTHERSCHEMA.JUNK_LOG
Procedure: ANOTHERSCHEMA.JUNKPROC
I am even able to execute the stored procedure successfully in a query window while logged in as MYSCHEMA:
EXEC ANOTHERSCHEMA.JUNKPROC
However, if I execute the following code to submit a job that involves running the same ANOTHERSCHEMA procedure but by submitting it as a JOB...
declare l_jobid binary_integer;
BEGIN
dbms_job.submit(job => l_jobid, what => 'BEGIN ANOTHERSCHEMA.JUNKPROC; END;');
DBMS_OUTPUT.PUT_LINE('l_jobid:' || l_jobid);
commit;
END;
...then, when I query the jobs ALL_JOBS view...
select * from all_jobs;
...I see that the job has a positive value for the column FAILURE and I have no record of what the error was. This FAILURE count value continues to gradually increment over time as Oracle presumably retries up to 16? times until the job is marked BROKEN in the ALL_JOBS view.
But this is just a simple example and I don't know where to look for the error message that would tell me why the job using ANOTEHRSCHEMA references failed.
Where Do I look for the error log of failed jobs? I'm wondering if this will be somewhere only the DBA can see...
Update:
The above is just a simple test example. In my actual real world situation, my log shows that the job was submitted but I never see anything in USER_JOBS or even DBA_JOBS, which should show everything. I don't understand why the dbms_job.submit procedure would return the job number of the submitted job indicating that it was submitted but no job is visible in the DBA_JOBS view! The job that I did submit should have taken a long time to run, so I don't expect that it completed faster than I could notice.
First off, you probably shouldn't be using dbms_job. That package has been superseded for some time by the dbms_scheduler package which is significantly more powerful and flexible. If you are using Oracle 19c or later, Oracle automatically migrates dbms_job jobs to dbms_scheduler.
If you are using an Oracle version prior to 19c and a dbms_job job fails, the error information is written to the database alert log. That tends to be a bit of a pain to query from SQL particularly if you're not a DBA. You can define an external table that reads the alert log to make it queryable. Assuming you're on 11g, there is a view, x$dbgalertext, that presents the alert log information in a way that you can query it but DBAs generally aren't going to rush to give users permission on x$ tables.
If you use dbms_scheduler instead (or if you are on 19c or later and your dbms_job jobs get converted to dbms_scheduler jobs), errors are written to dba_scheduler_job_run_details. dbms_scheduler in general gives you a lot more logging information than dbms_job does so you can see things like the history of successful runs without needing to add a bunch of instrumentation code to your procedures.

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.

Oracle 'after create' trigger to grant privileges

I have an 'after create on database' trigger to provide select access on newly created tables within specific schemas to different Oracle roles.
If I execute a create table ... as select statement and then query the new table in the same block of code within TOAD or a different UI I encounter an error, but it works if I run the commands separately:
create table schema1.table1 as select * from schema2.table2 where rownum < 2;
select count(*) from schema1.table1;
If I execute them as one block of code I get:
ORA-01031: insufficient privileges
If I execute them individually, I don't get an error and am able to obtain the correct count.
Sample snippet of AFTER CREATE trigger
CREATE OR REPLACE TRIGGER TGR_DATABASE_AUDIT AFTER
CREATE OR DROP OR ALTER ON Database
DECLARE
vOS_User VARCHAR2(30);
vTerminal VARCHAR2(30);
vMachine VARCHAR2(30);
vSession_User VARCHAR2(30);
vSession_Id INTEGER;
l_jobno NUMBER;
BEGIN
SELECT sys_context('USERENV', 'SESSIONID'),
sys_context('USERENV', 'OS_USER'),
sys_context('USERENV', 'TERMINAL'),
sys_context('USERENV', 'HOST'),
sys_context('USERENV', 'SESSION_USER')
INTO vSession_Id,
vOS_User,
vTerminal,
vMachine,
vSession_User
FROM Dual;
insert into schema3.event_table VALUES (vSession_Id, SYSDATE,
vSession_User, vOS_User, vMachine, vTerminal, ora_sysevent,
ora_dict_obj_type,ora_dict_obj_owner,ora_dict_obj_name);
IF ora_sysevent = 'CREATE' THEN
IF (ora_dict_obj_owner = 'SCHEMA1') THEN
IF DICTIONARY_OBJ_TYPE = 'TABLE' THEN
dbms_job.submit(l_jobno,'sys.execute_app_ddl(''GRANT SELECT
ON '||ora_dict_obj_owner||'.'||ora_dict_obj_name||' TO
Role1,Role2'');');
END IF;
END IF;
END IF;
END;
Jobs are asynchronous. Your code is not.
Ignoring for the moment the fact that if you're dynamically granting privileges that something in the world is creating new tables live in production without going through a change control process (at which point a human reviewer would ensure that appropriate grants were included) which implies that you have a much bigger problem...
When you run the CREATE TABLE statement, the trigger fires and a job is scheduled to run. That job runs in a separate session and can't start until your CREATE TABLE statement issues its final implicit commit and returns control to the first session. Best case, that job runs a second or two after the CREATE TABLE statement completes. But it could be longer depending on how many background jobs are allowed to run simultaneously, what other jobs are running, how busy Oracle is, etc.
The simplest approach would be to add a dbms_lock.sleep call between the CREATE TABLE and the SELECT that waits a reasonable amount of time to give the background job time to run. That's trivial to code (and useful to validate that this is, in fact, the only problem you have) but it's not foolproof. Even if you put in a delay that's "long enough" for testing, you might encounter a longer delay in the future. The more complicated approach would be to query dba_jobs, look to see if there is a job there related to the table you just created, and sleep if there is in a loop.

Scheduler Oracle Stored Procedure

I have a simple stored procedure I wanted Oracle to execute weekly and I have put it in the Oracle Scheduler. It was created fine and the scheduled task seems to execute (no error) but my stored procedure does not execute. I have admin right on the database and I do not get any error. Just do not get the result I wanted.
Below is the details of the job:
The creation was successful
The stored procedure is a simple one
create or replace
PROCEDURE DELETEBOGUSLETTERRECORDS AS
BEGIN
DELETE FROM BOGUSLETTERS;
COMMIT;
END DELETEBOGUSLETTERRECORDS;
The procedure was tested outside the scheduler, executed fine and all records in the specified tables were deleted.
However, that same procedure was not executed properly when it was scheduled. I even had the job run immediately, but after it was run, the records were not deleted. No error whatsoever.
What is the issue? Thanks!
I am not sure, but we execute the procedure like this:
[ Type of Job: PL/SQL Block ]
BEGIN
SCHEMA_NAME.STORED_PROC_NAME;
END;
And they run fine like this.

run windows command from oracle database trigger

i have oracle database (11g) trigger run after inserting on table, i need to run external program by this trigger through windows command like this:
c:\my_external_apps\app1.exe arg1 arg2 arg3
i am trying this code but it doesn't work:
create or replace TRIGGER GE_MAIN_NOTIFICATION_SEND AFTER INSERT ON TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW BEGIN
SYS.DBMS_SCHEDULER.create_program(program_name => 'UPLOADNC', program_type => 'EXECUTABLE',
program_action => 'C:\WINDOWS\SYSTEM32\CMD.exe /C c:\my_external_apps\app1.exe arg1 arg2 arg3 ',
enabled => TRUE);
END;
and this is the error
ORA-04088: error during execution of trigger 'DURRA.GE_MAIN_NOTIFICATION_SEND'
27486. 00000 - "insufficient privileges"
*Cause: An attempt was made to perform a scheduler operation without the
required privileges.
*Action: Ask a sufficiently privileged user to perform the requested
operation, or grant the required privileges to the proper user(s).
how i can do that?? i am beginner with oracle database
The error is telling you that you haven't been granted the privileges necessary to call dbms_scheduler.create_program. I expect that you are missing the create job privilege.
However, if you resolve that problem, your next problem will be that dbms_scheduler.create_program does an implicit commit and commits are not allowed inside triggers. That means that you cannot call dbms_scheduler.create_program from a trigger (unless you made the trigger an autonomous transaction which would create a separate set of issues). The right way to solve the problem would almost certainly be to use the older dbms_job package. Since that package doesn't implicitly commit, you can submit a job as part of a larger transaction.
Of course, if you're using the dbms_job package to do your job scheduling, you lose out on the ability of dbms_scheduler to call out to the operating system. Instead, you'd need to do something like creating a Java stored procedure that calls out to the operating system. There are multiple examples of this on the web, I linked to one from Tom Kyte.
So, at a high level, your trigger would call dbms_job.submit to submit the job. The job would then call your Java stored procedure. Your Java stored procedure would make the actual call out to the operating system of the database server.

Resources