DDL, DML, DCL and TCL Commands

DDL

Data Definition Language (DDL) statements are used to define the database structure or schema. Some examples:
  • CREATE - to create objects in the database
  • ALTER - alters the structure of the database
  • DROP - delete objects from the database
  • TRUNCATE - remove all records from a table, including all spaces allocated for the records are removed
  • COMMENT - add comments to the data dictionary
  • RENAME - rename an object

DML

Data Manipulation Language (DML) statements are used for managing data within schema objects. Some examples:
  • SELECT - retrieve data from the a database
  • INSERT - insert data into a table
  • UPDATE - updates existing data within a table
  • DELETE - deletes all records from a table, the space for the records remain
  • MERGE - UPSERT operation (insert or update)
  • CALL - call a PL/SQL or Java subprogram
  • EXPLAIN PLAN - explain access path to data
  • LOCK TABLE - control concurrency

DCL

Data Control Language (DCL) statements. Some examples:
  • GRANT - gives user's access privileges to database
  • REVOKE - withdraw access privileges given with the GRANT command

TCL

Transaction Control (TCL) statements are used to manage the changes made by DML statements. It allows statements to be grouped together into logical transactions.
  • COMMIT - save work done
  • SAVEPOINT - identify a point in a transaction to which you can later roll back
  • ROLLBACK - restore database to original since the last COMMIT
  • SET TRANSACTION - Change transaction options like isolation level and what rollback segment to use

AUTHID DEFINER V/S AUTHID CURRENT_USER

There are lot of times we get error in Oracle Apps while trying to execute the API's at development time, due to the AUTHID DEFINER and AUTHID CURRENT_USER. This article gives you good understanding about the AUTHID DEFINER and AUTHID CURRENT_USER.

A stored procedure runs either with the rights of the caller (AUTHID CURRENT_USER) or with the rights of the procedure's owner (AUTHID DEFINER). This behaviour is specified with the AUTHID clause. This authid clause immediatly follows the create procedure, create function, create package or create type statement. It can be ommited, in which case the default authid definer is taken.


AUTHID DEFINER and AUTHID CURRENT_USER
-----------------------------------------------------------
AUTHID DEFINER:-
--------------------
Example:-
---------
The following are done in APPS scheme.

create table a (a_a number);

CREATE OR REPLACE PACKAGE XYZ AUTHID DEFINER
AS
PROCEDURE XYZ_1;
END XYZ;

CREATE OR REPLACE PACKAGE body XYZ
AS
PROCEDURE XYZ_1
IS
BEGIN
INSERT INTO A VALUES (1);
END XYZ_1;
END XYZ;

begin
xyz.XYZ_1;
end;

select * from a;

Provide grants for this package to other schema (scott) and create the synonym for the xyz package in scott.

grant all on to

Example:-
---------

grant all on xyz to scott

Above command is run from the apps schema.

Now for the other schema SCOTT try to run the same query.

begin
xyz.XYZ_1;
end;

It have inserted new record in the 'A' table. Note there is no synonym for the table A in SCOTT schema.

Running this program from anywhere, it is as good as running from APPS schema in case of AUTHID DEFINER.

10.2) CURRENT_USER:-
----------------------
Example:-
-----------
The following are done in the APPS schema.

create table a (a_a number);

CREATE OR REPLACE PACKAGE XYZ AUTHID CURRENT_USER
AS
PROCEDURE XYZ_1;
END XYZ;

CREATE OR REPLACE PACKAGE body XYZ
AS
PROCEDURE XYZ_1
IS
BEGIN
INSERT INTO A VALUES (1);
END XYZ_1;
END XYZ;

begin
xyz.XYZ_1;
end;

select * from a;

Provide grants for this package to other schema (scott) and create the synonym for the xyz package in scott.

grant all on to

Example:-
-----------
grant all on xyz to scott

Above command is run from the apps schema.

Now for the other schema (scott) try to run the same query.

begin
xyz.XYZ_1;
end;

Got the error message table or view doesn't exist for the A table.

Create view for the a table and run the same program again.

create synonym 'A' for table 'A'

begin
xyz.XYZ_1;
end;

select * from a;

Now there is no error. It is inserting the record with no issue.

WITH NO AUTHID DEFINER and AUTHID CURRENT_USER :-
-----------------------------------------------------------------------
Example:-
---------
The following are done in the APPS schema.

create table a (a_a number);

CREATE OR REPLACE PACKAGE XYZ
AS
PROCEDURE XYZ_1;
END XYZ;

CREATE OR REPLACE PACKAGE body XYZ
AS
PROCEDURE XYZ_1
IS
BEGIN
INSERT INTO A VALUES (1);
END XYZ_1;
END XYZ;

begin
xyz.XYZ_1;
end;

select * from a;

Provide grants for this package to other schema (scott) and create the synonym for the xyz package in scott.

grant all on to

Example:-
---------
grant all on xyz to scott

Above command is run from the apps schema.

Now for the other schema SCOTT try to run the same query.


begin
xyz.XYZ_1;
end;

It is working in same way as it have done for the AUTHID DEFINER.

Q) Is it possible to know from the select statement if it is INVOKER(CURRENT_USER) or DEFINER
A) Yes, It is possible to get this information from the select statement. Use
SELECT dbo.object_name,
(DECODE(SIGN(bitand(options,16)),1,'INVOKER','DEFINER')) "authid"
FROM dba_objects dbo,
sys.PROCEDURE$ p
WHERE p.obj# = dbo.object_id
AND dbo.object_name = "Your Package Name"
AND dbo.object_type = 'PACKAGE'
AND dbo.owner = 'APPS'

Example:-
-----------
SELECT dbo.object_name,
(DECODE(SIGN(bitand(options,16)),1,'INVOKER','DEFINER')) "authid"
FROM dba_objects dbo,
sys.PROCEDURE$ p
WHERE p.obj# = dbo.object_id
AND dbo.object_name = 'ASO_APR_WF_INT'
AND dbo.object_type = 'PACKAGE'
AND dbo.owner = 'APPS'

PRAGMA

Signifies that the statement is a pragma (compiler directive). Pragmas are processed at compile time, not at run time. They do not affect the meaning of a program; they simply convey information to the compiler.

1) Autonomous Transaction

Autonomous Transactions is the child transaction, which are Independent of Parent transactions. In Our Example, p1 is child transaction, which is used in the Parent transaction.

Example
: -
CREATE or REPLACE Procedure p1 IS
Pragma Autonomous_transaction;
BEGIN
INSERT INTO TEST_T VALUES (1111,’PHANI1’);
COMMIT;
END;
In the Declaration section, you will declare this Transaction as the Autonomous Transaction.
DECLARE
A NUMBER;
BEGIN
INSERT INTO TEST_T VALUES (2222,’JACK’);
P1;
ROLLBACK;
END;
NOW Table has (1111,’PHANI’) Record. COMMIT in the PROCEDURE P1 have not commit the Outside (p1) DML operations. It will just commit p1 transactions.
The ROLLBACK will not rollback PHANI record, it will just rollback the JACK record.
CREATE or REPLACE Procedure p1 IS
BEGIN
INSERT INTO TEST_T VALUES (1111,’PHANI1’);
COMMIT;
END;

If I remove the Pragma Autonomous_transaction From the declaration section, then this transaction will become the normal transaction. Now if you try to use the same parent transaction as given below.
>> delete from TEST_T;
DECLARE
A NUMBER;
BEGIN
INSERT INTO TEST_T VALUES (2222,’JACK’);
P1; -- This transaction has ended with the COMMIT;
ROLLBACK;
END;

After executing the above transaction, you can see BOTH records got Inserted (PHANI and JACK records). Here COMMIT in P1 will commit both transactions (PHANI and JACK Records Insert) And then Rollback. Since, there are no transactions happening between COMMIT and ROLLBACK. Our ROLLBACK is not doing any ROLLBACK.
Note: - IF COMMIT is not given in P1 then, the ROLLBACK will do the ROLLBACK both the INSERT transaction (PHANI Record which is in p1 procedure and JACK Record).
2) Pragma Restrict_references

It gives the Purity Level of the Function in the package.
CREATE OR REPLACE PACKAGE PKG12 AS
FUNCTION F1 RETURN NUMBER;
PRAGMA RESTRICT_REFERENCES (F1, WNDS,
RNDS,
WNPS,
RNPS);
END PKG12;
CREATE OR REPLACE PACKAGE BODY PKG12 AS
FUNCTION F1 RETURN NUMBER IS
X NUMBER;
BEGIN
SELECT EMPNO INTO X FROM SCOTT.EMP
WHERE ENAME LIKE ‘SCOTT’;
DBMS_OUTPUT.PUT_LINE (X);
RETURN (X);
END F1;
END PKG12;
You will get the Violate It’s Associated Pragma Error. This in Purity Level, we said
It cannot read from the database. RNDS (In Our Function F1, we have SELECT STATEMENT which is reading the data from the database).
3) Pragma SERIALLY_REUSABLE


In my 5 Years of Experience in the Oracle Applications, I have never found the requirement to use this feature :). But, I found this feature is used in some standard Oracle Packages. We may use this feature for improving the performance or to meet certain requirements.
This pragma is appropriate for packages that declare large temporary work areas that are used once and not needed during subsequent database calls in the same session.
You can mark a bodiless package as serially reusable. If a package has a spec and body, you must mark both. You cannot mark only the body.
The global memory for serially reusable packages is pooled in the System Global Area (SGA), not allocated to individual users in the User Global Area (UGA). That way, the package work area can be reused. When the call to the server ends, the memory is returned to the pool. Each time the package is reused, its public variables are initialized to their default values or to NULL.
Serially reusable packages cannot be accessed from database triggers or other PL/SQL subprograms that are called from SQL statements. If you try, Oracle generates an error.

Examples
WITH PRAGMA SERIALLY_REUSABLE
The following example creates a serially reusable package:
CREATE PACKAGE pkg1 IS
PRAGMA SERIALLY_REUSABLE;
num NUMBER := 0;
PROCEDURE init_pkg_state(n NUMBER);
PROCEDURE print_pkg_state;
END pkg1;
/
CREATE PACKAGE BODY pkg1 IS
PRAGMA SERIALLY_REUSABLE;
PROCEDURE init_pkg_state (n NUMBER) IS
BEGIN
pkg1.num := n;
END;
PROCEDURE print_pkg_state IS
BEGIN
dbms_output.put_line('Num: ' || pkg1.num);
END;
END pkg1;
/
begin
pkg1.init_pkg_state(10);
pkg1.PRINT_PKG_STATE;
end;

Num: 10

begin
pkg1.PRINT_PKG_STATE;
end;

Num: 0

Note: - The first block is changing the value of the variable (num) to 10 and if I check the value in same block then it is showing the changed value that is 10. But, if I try to check the value of the (num) variable then it should the default value given to it (i.e.) “0”
WITHOUT PRAGMA SERIALLY_REUSABLE
CREATE OR REPLACE PACKAGE pkg1 IS
num NUMBER := 0;
PROCEDURE init_pkg_state(n NUMBER);
PROCEDURE print_pkg_state;
END pkg1;
CREATE PACKAGE BODY pkg1 IS
PROCEDURE init_pkg_state (n NUMBER) IS
BEGIN
pkg1.num := n;
END;
PROCEDURE print_pkg_state IS
BEGIN
dbms_output.put_line('Num: ' || pkg1.num);
END;
END pkg1;
begin
pkg1.init_pkg_state(10);
pkg1.PRINT_PKG_STATE;
end;
>>Num: 10
begin
pkg1.PRINT_PKG_STATE;
end;
>>Num: 10
Note: - Now, you may noticed the difference. The second block is giving us the changed value.
DROP PACKAGE pkg1;
(There are many other pragma's like Pragma Exception_init etc. I have not convered these concepts in this article. I will cover them in Exception concept article)

NOCOPY

The NOCOPY hint tells the PL/SQL compiler to pass OUT and IN OUT parameters by reference, rather than by value.

When parameters are passed by value, the contents of the OUT and IN OUT parameters are copied to temporary variables, which are then used by the subprogram being called. On successful completion of the subprogram the values are copied back to the actual parameters, but unhandled exceptions result in the original parameter values being left unchanged. The process of copying large parameters, such as records, collections, and objects requires both time and memory which affects performance.

With the NOCOPY hint the parameters are passed by reference and on successful completion the outcome is the same, but unhandled exceptions may leave the parameters in an altered state, so programs must handle errors or cope with the suspect values.

The nocopy.sql script compares the performance of both methods by passing a populated collection as a parameter.

nocopy.sql

SET SERVEROUTPUT ON
DECLARE

TYPE t_tab IS TABLE OF VARCHAR2(32767);
l_tab t_tab := t_tab();
l_start NUMBER;

PROCEDURE in_out (p_tab IN OUT t_tab) IS
BEGIN
NULL;
END;

PROCEDURE in_out_nocopy (p_tab IN OUT NOCOPY t_tab) IS
BEGIN
NULL;
END;

BEGIN
l_tab.extend;
l_tab(1) := '1234567890123456789012345678901234567890';
l_tab.extend(999999, 1); -- Copy element 1 into 2..1000000


-- Time normal IN OUT
l_start := DBMS_UTILITY.get_time;

in_out(l_tab);

DBMS_OUTPUT.put_line('IN OUT : ' ||
(DBMS_UTILITY.get_time - l_start));

-- Time IN OUT NOCOPY
l_start := DBMS_UTILITY.get_time;

in_out_nocopy(l_tab); -- pass IN OUT NOCOPY parameter

DBMS_OUTPUT.put_line('IN OUT NOCOPY: ' ||
(DBMS_UTILITY.get_time - l_start));
END;
/

The output of the script clearly demonstrates the performance improvements possible when using the NOCOPY hint.

SQL> @nocopy.sql

IN OUT : 122
IN OUT NOCOPY: 0

PL/SQL procedure successfully completed.

Dynamic SQL

We got two ways for the Dynamic SQL.
1. EXECUTE IMMEDIATE
2.DBMS_SQL

1. EXECUTE IMMEDIATE
Note:- This type of Dynamic SQL would not work in the 10.7 version Database.

For the Select statement
Note:- Sample Examples code based on the EMP and DEPT table in the scott schema.
Example:-
----------
DECLARE
L_DEPTNO NUMBER DEFAULT 10;
L_SAL NUMBER;
BEGIN
EXECUTE IMMEDIATE 'select max(sal) from emp
where deptno = :l_deptno'
INTO L_SAL
USING L_DEPTNO;
DBMS_OUTPUT.PUT_LINE(L_SAL);
END;

For the Insert statement
Note:- Sample Examples code based on the EMP and DEPT table in the scott schema.
Example:-
-----------
DECLARE
L_ENAME VARCHAR2(20) DEFAULT 'PHANI';
L_EMPNO NUMBER DEFAULT 2;
L_DEPTNO NUMBER DEFAULT 10;
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO EMP(ENAME,EMPNO,DEPTNO) VALUES
(:L_ENAME,:L_EMPNO,:L_DEPTNO)'
USING L_ENAME,
L_EMPNO,
L_DEPTNO;
END;

For the Update Statement
Note
:- Sample Examples code based on the EMP and DEPT table in the scott schema.
Example:-
-----------
DECLARE
L_ENAME VARCHAR2(20) DEFAULT 'PHANI';
L_EMPNO NUMBER DEFAULT 2;
L_DEPTNO NUMBER DEFAULT 10;
BEGIN
EXECUTE IMMEDIATE 'UPDATE EMP
SET ENAME = ''RAHUL''
WHERE ENAME = :l_ENAME'
USING L_ENAME;
END;

2.DBMS_SQL
For the Select statement
Note
:- Sample Examples code based on the EMP and DEPT table in the scott schema.
Example:-
DECLARE
L_DEPTNO NUMBER DEFAULT 10;
L_SAL NUMBER;
L_SQL VARCHAR2( 3000 );
L_CUR NUMBER;
L_RC NUMBER;
BEGIN
--- Code converted in dynamic SQL start(phani).
L_SQL := 'select max(sal) from emp where deptno = :l_deptno';
L_CUR := dbms_sql.OPEN_CURSOR;
dbms_sql.PARSE( L_CUR, L_SQL, dbms_sql.NATIVE );
dbms_sql.BIND_VARIABLE( L_CUR, ':l_deptno', L_DEPTNO );
-- describe defines
dbms_sql.DEFINE_COLUMN( L_CUR, 1, L_SAL );
-- execute
L_RC := dbms_sql.EXECUTE( L_CUR );
LOOP
-- fetch a row
IF dbms_sql.FETCH_ROWS( L_CUR ) > 0 THEN
-- fetch columns from the row
dbms_sql.COLUMN_VALUE( L_CUR, 1, L_SAL );
ELSE
EXIT;
END IF;
END LOOP;
dbms_sql.CLOSE_CURSOR( L_CUR );
dbms_output.PUT_LINE( L_SAL );
--- Code converted in dynamic SQL end (phani).
END;

For the Insert statementNote:- Smple Examples code based on the EMP and DEPT table in the scott schema.
Example:-
DECLARE
L_SQL VARCHAR2( 3000 );
L_CUR NUMBER;
L_RC NUMBER;
g_empno NUMBER := 1;
g_ename VARCHAR2(30) := 'REDDY';
g_deptno NUMBER := 10;
BEGIN
--- Code converted in dynamic SQL start.
l_sql:= 'insert into emp
(EMPNO,
ENAME,
DEPTNO)
VALUES
( :p_empno,
:p_ename,
:p_deptno)';

l_cur := dbms_sql.open_cursor;
dbms_sql.parse(l_cur, l_sql, dbms_sql.native);
dbms_sql.bind_variable(l_cur, ':p_empno', g_empno);
dbms_sql.bind_variable(l_cur, ':p_ename', g_ename);
dbms_sql.bind_variable(l_cur, ':p_deptno',g_deptno);
-- execute
l_rc := dbms_sql.execute(l_cur);
DBMS_SQL.CLOSE_CURSOR (l_cur);
END;

For the Update StatementNote:- Sample Examples code based on the EMP and DEPT table in the scott schema.
Example:-
DECLARE
L_SQL VARCHAR2( 3000 );
L_CUR NUMBER;
L_RC NUMBER;
g_new_ename VARCHAR2(20) := 'REDDY01';
g_old_ename VARCHAR2(20) := 'REDDY';
BEGIN
--- Code converted in dynamic SQL start.

l_sql:= 'update emp
set ename = :p_new_ename
where ename = :p_old_ename';

l_cur := dbms_sql.open_cursor;
dbms_sql.parse(l_cur, l_sql, dbms_sql.native);

dbms_sql.bind_variable(l_cur, ':p_new_ename',g_new_ename);
dbms_sql.bind_variable(l_cur, ':p_old_ename',g_old_ename);

--- execute
l_rc := dbms_sql.execute(l_cur);
DBMS_SQL.CLOSE_CURSOR (l_cur);

--- Code converted in dynamic SQL end.
END;

BULK COLLECT

Bulk Collect concept help us reducing the Iterations between SQL Engine and PL/SQL Block.

Note:- When ever, we excute any DML (INSERT or UPDATE or DELETE) Operations in the PL/SQL block then It will move from PL/SQL Block to SQL Engine to excute the DML Operation in SQL Engine. And if we have some SELECT statement in the PL/SQL Block, then it will move from SQL Enginer to get the data from SQL Enginer to the PL/SQL Block. For the SELECT statement, we use the BULK COLLECT to record the Iteration from the SQL Enginer to the PL/SQL Block.

If I have to put my self in other word then, I should say, BULK COLLECT will process group of SELECT statements at one short.

Similarly for the DML Operations, we use FORALL Concept, please refer the FORALL Concept for more details.

Note:- This Example is based on the EMP table in the SCOTT schema. Here say, EMP table have 90 Records and if we do it in the Normal way (Without BULK COLLECT concept, then It will iterate SQL enginer to the PL/SQL Block for 90 Times). By using BULK COLLECT, we can completly reduce the Iterations.

In the following Example, I have limited to 10, so for 90 records, this will iterate for 9 times.

Example:-
-----------

declare
type array is table of number index by binary_integer;
l_data array;
cursor c is select empno from emp;
begin
open c;
loop
fetch c bulk collect into l_data limit 10;
if ( c%notfound )
then
dbms_output.put_line
( 'Cursor returned NOT FOUND but array has ' || l_data.count
|| ' left to process' );
else
dbms_output.put_line
( 'We have ' || l_data.count
|| ' to process' );
end if;
exit when c%notfound;
end loop;
close c;
end;

Returning clause with the Bulk collect with the DML opeartions:-
---------------------------------------------------------------------------

DELETE FROM emp WHERE num = 30
RETURNING empno, ename BULK COLLECT INTO p_num, p_ename;

FORALL

FORALL concept help us reducing the Iterations between PL/SQL Block and SQL Engine.

Note:- When ever, we excute any DML (INSERT or UPDATE or DELETE) Operations in the PL/SQL block then, It will move from PL/SQL Block to SQL Engine to excute the DML Operation in SQL Engine.

If we have some LOOP and it is looping for 100 times then, this will iterate from 100 times from PL/SQL block to SQL Engine.

Example:-

CREATE TABLE emp_by_dept AS SELECT employee_id, department_id
FROM employees WHERE 1 = 0;
DECLARE
TYPE dept_tab IS TABLE OF departments.department_id%TYPE;
deptnums dept_tab;
TYPE NumList IS TABLE OF NUMBER;
-- The zeros in this list will cause divide-by-zero errors.
num_tab NumList := NumList(10,0,11,12,30,0,20,199,2,0,9,1);
errors NUMBER;
dml_errors EXCEPTION;
PRAGMA exception_init(dml_errors, -24381);
BEGIN
SELECT department_id BULK COLLECT INTO deptnums FROM departments;
-- SAVE EXCEPTIONS means don't stop if some INSERT fail.
FORALL i IN 1..deptnums.COUNT SAVE EXCEPTIONS
INSERT INTO emp_by_dept
SELECT employee_id, department_id FROM employees
WHERE department_id = deptnums(i);
FOR i IN 1..deptnums.COUNT LOOP
-- Count how many rows were inserted for each department; that is,
-- how many employees are in each department.
dbms_output.put_line('Dept '||deptnums(i)||': inserted '||
SQL%BULK_ROWCOUNT(i)||' records');
END LOOP;
dbms_output.put_line('Total records inserted =' || SQL%ROWCOUNT);
-- If any errors occurred during the FORALL SAVE EXCEPTIONS,
-- a single exception is raised when the statement completes.
EXCEPTION
WHEN dml_errors THEN -- Now we figure out what failed and why.
errors := SQL%BULK_EXCEPTIONS.COUNT;
dbms_output.put_line('Number of INSERT statements that failed: ' || errors);
FOR i IN 1..errors LOOP
dbms_output.put_line('Error #' || i || ' occurred during '||
'iteration #' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);
dbms_output.put_line('Error message is ' ||
SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;