Sayantan's Blog On Oracle

Sayantan's blogs on Oracle

Learn Oracle with Sayantan

The Ultimate Guide to PLSQL Compiler

PLSQL COMPILER

Before oracle 11g, the PLSQL Compiler translated our source code to system code without applying many changes to improve the performance. But now Oracle PLSQL uses a new optimizing compiler that can rearrange our code for better performance.
The default optimization level improves performance in a broad range of PLSQL operation.

The list of changes happened in PLSQL Compiler:

  • Elimination of compiler-generated temporary operands
  • Computation of some operations during compilation
  • Reuse of expression values
  • Simplification or elimination of some branches and dead code elimination
  • Avoidance of library calls by direct execution in the PLSQL virtual machine of some operations
  • All cursors correctly finalized upon exit from a cursor loop or a declared block
  • Elimination of computations which causes an exception

PLSQL compiler optimization techniques

INITIALIZATION PARAMETERS USED IN PLSQL COMPILATION

  • PLSQL_CODE_TYPE
  • PLSQL_OPTIMIZE_LEVEL
  • PLSQL_WARNINGS
  • PLSQL_CCFLAGS

PLSQL_CODE_TYPE

It specifies the compilation mode for PLSQL library units. It has two values:

  • INTERPRETED (Default)
  • NATIVE

INTERPRETED

PLSQL library units will be compiled to the PLSQL bytecode format.
Such modules are executed by the PLSQL interpreter engine.
The code is parsed and changed to source code one line at a time. Also only one error occurs at a time.

NATIVE

PLSQL library units (with the possible exception of top-level anonymous PLSQL blocks) will be compiled to native (machine code).
Such modules will be executed natively without incurring any interpreter overhead.

  • When the value of this parameter (PLSQL_CODE_TYPE) is changed, it has no effect on PLSQL library units that have already been compiled.
  • The value of this parameter is stored persistently with each library unit.
  • If a PLSQL library unit is compiled native, all subsequent automatic recompilations of that library unit will use native compilation

PLSQL_OPTIMIZE_LEVEL

PLSQL_OPTIMIZE_LEVEL specifies the optimization level to be used to optimize the PLSQL library units. The higher the value of this parameter, the greater the effort the compiler will put to compile the PLSQL library unit.

PLSQL_OPTIMIZE_LEVEL = {0 | 1 | 2 | 3}

Default value is 2.

0

For 0 value oracle doesn't do any changes or do little changes to make the performance better.

1

Applies a wide range of optimizations to PLSQL programs including the elimination of unnecessary computations and exceptions, but generally doesn't move source code out of its original source order.

2

Applies a wide range of optimization techniques beyond those of level 1 including changes which may move the source code relatively far from its original location.

3

Applies a wide range of optimization techniques beyond those of level 2, automatically including techniques which are not specifically requested.

Now we will check the plsql_optimize_level set in our database.

CONN SYS AS SYSDBA;
PASSWORD: SYS

GRANT SELECT ON V_$PARAMETER TO HR;

CONN HR/HR;

CREATE OR REPLACE SYNONYM V$PARAMETER FOR SYS.V_$PARAMETER;
SELECT NAME, VALUE
FROM V$PARAMETER
WHERE NAME = 'plsql_optimize_level';
PLSQL COMPILER : output

Changing the parameter value from 2 to 0.

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 0;
SELECT NAME, VALUE
FROM V$PARAMETER
WHERE NAME = 'plsql_optimize_level';
SET SERVEROUTPUT ON;
DECLARE
  NUM1 NUMBER := 0;
  NUM2 NUMBER := 0;
  NUM3 NUMBER := 0;
BEGIN
  DBMS_OUTPUT.PUT_LINE('START TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
  FOR I IN 1..100000000
  LOOP
    NUM1 := 1;
    NUM2 := NUM1 + 1;
    NUM3 := NUM1 + NUM2;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('END TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
END;
/

In the above example we have executed a PLSQL block to check the performance. It took 20 sec to complete.

Now changing the PLSQL optimize level to 2 to execute the same task.

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 2;
SELECT NAME, VALUE
FROM V$PARAMETER
WHERE NAME = 'plsql_optimize_level';
DECLARE
  NUM1 NUMBER := 0;
  NUM2 NUMBER := 0;
  NUM3 NUMBER := 0;
BEGIN
  DBMS_OUTPUT.PUT_LINE('START TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
  FOR I IN 1..100000000
  LOOP
    NUM1 := 1;
    NUM2 := NUM1 + 1;
    NUM3 := NUM1 + NUM2;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('END TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
END;
/

So in the above output we can see that under optimize level 2 the PLSQL block took almost no time to complete the operation.

Now we will change this parameter to 3.

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 3;
SELECT NAME, VALUE
FROM V$PARAMETER
WHERE NAME = 'plsql_optimize_level';
SET SERVEROUTPUT ON;
DECLARE
  NUM1 NUMBER := 0;
  NUM2 NUMBER := 0;
  NUM3 NUMBER := 0;
BEGIN
  DBMS_OUTPUT.PUT_LINE('START TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
  FOR I IN 1..100000000
  LOOP
    NUM1 := 1;
    NUM2 := NUM1 + 1;
    NUM3 := NUM1 + NUM2;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('END TIME : '||TO_CHAR(SYSDATE, 'HH24:MI:SS'));
END;
/

It is also taking almost no time to complete.

PLSQL_WARNINGS

Prior 10g if we compile any sub program like Procedure, Function, Trigger, Package etc. then result would be either Success or Failure.

But 10g onwards, Oracle introduced a new feature called PLSQL_WARNINGS. It could be Success with warning or Failure with warning. Warning can produce undefined result or performance issue.

PLSQL warning with Success should be treated as warning that indicates that your program might not perform optimally. Then you should rectify your code so that the warning messages are removed while compile your program. On the other hand, PLSQL warning with Failure is simply a failure. First you should correct failure and then rectify the warning part.

How PLSQL_WARNINGS work

From 10g onwards PLSQL compiler has been enhanced to produce warnings for subprograms.

PLSQL_WARNINGS enables or disables the reporting of warning messages by the PLSQL compiler and specifies which warning message to show as errors.

Syntax of PLSQL_WARNINGS:

PLSQL_WARNINGS = 'value_clause' [, 'value_clause' ] ...

Values_Clause:

  • Multiple values_clause can be specified, enclosed by quotes, separated by commas.
  • Each value_clause is composed of
    • a qualifier
    • colon (:)
    • a modifier

Qualifier Values:

  • ENABLE
    • Enable a specific warning or a set of warnings
  • DISABLE
    • Disable a specific warning or a set of warnings
  • ERROR
    • Treat a specific warnings or a set of warnings as errors

Modifier Values:

  • ALL
    • Apply the qualifier to all warning messages
  • SEVERE
    • Apply the qualifier to only those warning messages which are in SEVERE category. Messages for condition that may cause unexpected behavior or wrong result.
  • INFORMATIONAL
    • Apply the qualifier to only those warning messages which are in INFORMATIONAL category. Messages for condition that do not have an effect on performance or correctness. But you may want to change the code more maintainable, such as unreachable code that never be executed.
  • PERFORMANCE
    • Apply the qualifier to only those warning messages which are in PERFORMANCE category. Messages for condition that may cause performance problem such as passing a VARCHAR2 value in a number column in an insert statement.

Default value for values_clause: 'DISABLE:ALL'

We can modify PLSQL_WARNINGS through ALTER SESSION or ALTER SYSTEM commands.

PLSQL_WARNINGS can be set through two levels:

  • Declaratively
    • Using the PLSQL_WARNINGS initialization parameter
  • Programmatically
    • Using DBMS_WARNING package

Enable PLSQL_WARNINGS:

Using PLSQL_WARNINGS Parameter:

ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:ALL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

Creating a sample procedure to check the PLSQL_WARNING:

SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE SP_EMP_DTLS(P_EMPNO EMPLOYEES.EMPLOYEE_ID%TYPE)
IS
  V_EMP_DTLS VARCHAR2(4000) := NULL;
BEGIN
  SELECT EMPLOYEE_ID||' - '||FIRST_NAME||' '||LAST_NAME
  INTO V_EMP_DTLS
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = P_EMPNO;
  --
  DBMS_OUTPUT.PUT_LINE('EMPLOYEE DETAILS - >'||V_EMP_DTLS);
END;
/

Warnings are identified by PLW identifier. Below is the list of modifier range.

ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:SEVERE';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

When one qualifier value is enabled the others are disabled by default as we can see in the above output.

To change the default behavior we can enable multiple qualifier.

ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:SEVERE', 'ENABLE:PERFORMANCE', 'DISABLE:INFORMATIONAL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';
ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:INFORMATIONAL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';
ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:PERFORMANCE';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

Using DBMS_WARNING Package:

We can set compiler warning messages by using DBMS_WARNING package. Following are the settings we can do by using this package.

List of DBMS_WARNINGS package subprograms:

OPERATIONS SUBPROGRAM NAMESUBPROGRAM TYPE DESCRIPTION
SET WARNINGSADD_WARNING_SETTING_CATPROCEDUREModifies the current session or system warning setting of the
warning_category previously set
ADD_WARNING_SETTING_NUMPROCEDUREModifies the current session or system warning setting of the
warning_num previously set
GET WARNINGSGET_WARNING_SETTING_CATFUNCTIONReturns the specific warning category in the session
GET_WARNING_SETTING_NUMFUNCTIONReturns the specific warning number in the session
GET_WARNING_SETTING_STRINGFUNCTIONReturns the entire warning string in the current session
REPLACE WARNINGSSET_WARNING_SETTING_STRINGPROCEDUREReplace the previous settings with a new value
GET WARNING CATEGORY
NAME
GET_CATEGORYFUNCTIONReturns the category name
ADD_WARNING_SETTING_CAT:

This procedure takes three input parameters:

PARAMETER DESCRIPTION
WARNING_CATEGORYName of the category. Allowed values are:
ALL, INFORMATIONAL, SEVERE, PERFORMANCE
WARNING_VALUEValue of the category. Allowed values are:
ENABLE, DISABLE, ERROR
SCOPESpecifies if the changes are being performed in the session context
or the system context. Allowed values are:
SESSION, SYSTEM

Syntax:

DBMS_WARNING.ADD_WARNING_SETTING_CAT 
(
 warning_category    IN    VARCHAR2,
 warning_value       IN    VARCHAR2,
 scope               IN    VARCHAR2
);

Checking the current warning setup:

SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';
EXEC DBMS_WARNING.ADD_WARNING_SETTING_CAT('SEVERE', 'ENABLE', 'SESSION');
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

We can also use the function DBMS_WARNING.GET_WARNING_SETTING_STRING to get the above result.

SELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;
ADD_WARNING_SETTING_NUM:

We can modify the current session or system setting with the value provided in this procedure.

This procedure takes three input parameters:

PARAMETER DESCRIPTION
WARNING_CATEGORYName of the category. Allowed values are:
ALL, INFORMATIONAL, SEVERE, PERFORMANCE
WARNING_VALUEValue of the category. Allowed values are:
ENABLE, DISABLE, ERROR
SCOPESpecifies if the changes are being performed in the session context
or the system context. Allowed values are:
SESSION, SYSTEM
DBMS_WARNING.ADD_WARNING_SETTING_NUM 
(
 warning_category    IN    VARCHAR2,
 warning_value       IN    VARCHAR2,
 scope               IN    VARCHAR2
);
EXEC DBMS_WARNING.ADD_WARNING_SETTING_NUM (6019, 'ENABLE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;

So we can see that the INFORMATIONAL warnings are disable except 6019. So this procedure has override the session or system settings.

To disable the above warning setting number:

EXEC DBMS_WARNING.ADD_WARNING_SETTING_NUM (6019, 'DISABLE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SSELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;
GET_WARNING_SETTING_CAT:

This function takes one parameter.

PARAMETER DESCRIPTION
WARNING_CATEGORYName of the category. Allowed values are:
ALL, INFORMATIONAL, SEVERE, PERFORMANCE

Syntax:

DBMS_WARNING.GET_WARNING_SETTING_CAT 
(
 warning_category    IN    VARCHAR2
)
RETURN warning_value;
EXEC DBMS_WARNING.ADD_WARNING_SETTING_CAT('SEVERE', 'ENABLE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;
SELECT DBMS_WARNING.GET_WARNING_SETTING_CAT('SEVERE') FROM DUAL;
SELECT DBMS_WARNING.GET_WARNING_SETTING_CAT('INFORMATIONAL') FROM DUAL;
SELECT DBMS_WARNING.GET_WARNING_SETTING_CAT('PERFORMANCE') FROM DUAL;
GET_WARNING_SETTING_NUM:

This function takes one parameter.

PARAMETER DESCRIPTION
WARNING_NUMBERWarning numbers. Allowed values are
all valid warning numbers.

Syntax:

DBMS_WARNING.GET_WARNING_SETTING_NUM 
(
 warning_number      IN    NUMBER
)
RETURN warning_value;
EXEC DBMS_WARNING.ADD_WARNING_SETTING_NUM (6019, 'ENABLE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_NUM(6019) FROM DUAL;
EXEC DBMS_WARNING.ADD_WARNING_SETTING_NUM (6019, 'DISABLE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_NUM(6019) FROM DUAL;
GET_WARNING_SETTING_STRING:

Syntax:

DBMS_WARNING.GET_WARNING_SETTING_STRING
RETURN VARCHAR2;
SET_WARNING_SETTING_STRING:

This procedure takes two input parameters:

PARAMETER DESCRIPTION
WARNING_VALUEThe new string that will constitute the new value
SCOPESpecifies if the changes are being performed in the session context
or the system context. Allowed values are:
SESSION, SYSTEM

Syntax:

DBMS_WARNING.SET_WARNING_SETTING_STRING 
(
 warning_value   IN   VARCHAR2,
 scope           IN   VARCHAR2
);
EXEC DBMS_WARNING.SET_WARNING_SETTING_STRING ('ENABLE:ALL', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;
EXEC DBMS_WARNING.SET_WARNING_SETTING_STRING ('DISABLE:INFORMATIONAL, ENABLE:SEVERE, ENABLE:PERFORMANCE', 'SESSION');
SELECT DBMS_WARNING.GET_WARNING_SETTING_STRING FROM DUAL;
GET_CATEGORY:

This function takes one parameter as input.

PARAMETER DESCRIPTION
WARNING_NUMBERWarning numbers. Allowed values are
all valid warning numbers.

Syntax:

DBMS_WARNING.GET_CATEGORY 
(
 warning_number  IN   pls_integer
) 
RETURN VARCHAR2;
SELECT DBMS_WARNING.GET_CATEGORY(5018) FROM DUAL;
SELECT DBMS_WARNING.GET_CATEGORY(6018) FROM DUAL;
SELECT DBMS_WARNING.GET_CATEGORY(7018) FROM DUAL;

SET SERVEROUTPUT ON;
DECLARE
  WMSG VARCHAR2(1000);
BEGIN

  DBMS_WARNING.SET_WARNING_SETTING_STRING('ENABLE:ALL', 'SESSION');
  WMSG := DBMS_WARNING.GET_WARNING_SETTING_STRING();
  DBMS_OUTPUT.PUT_LINE(WMSG);
END;
/
SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE SP_EMP_DTLS(P_EMPNO EMPLOYEES.EMPLOYEE_ID%TYPE,
                                        P_EMP_DTLS OUT VARCHAR2)
IS
  V_EMP_DTLS VARCHAR2(4000) := NULL;
BEGIN
  SELECT EMPLOYEE_ID||' - '||FIRST_NAME||' '||LAST_NAME
  INTO V_EMP_DTLS
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = P_EMPNO;
  --
  P_EMP_DTLS := V_EMP_DTLS;
  --
  DBMS_OUTPUT.PUT_LINE('EMPLOYEE DETAILS - >'||P_EMP_DTLS);
END;
/

So in the above example, two warnings are captured by PLSQL compiler.

To check the warning messages we will query the USER_ERRORS and USER_PLSQL_OBJECT_SETTINGS data dictionary view.

SELECT * FROM USER_ERRORS
WHERE NAME = 'SP_EMP_DTLS';
SELECT * FROM USER_PLSQL_OBJECT_SETTINGS
WHERE NAME = 'SP_EMP_DTLS';

Unreachable Code:

Unreachable code are the code which are written in a sub program but they are never executed.

SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE SP_EMP_DTLS(P_EMPNO EMPLOYEES.EMPLOYEE_ID%TYPE,
                                        P_EMP_DTLS OUT VARCHAR2)
IS
  V_EMP_DTLS VARCHAR2(4000) := NULL;
  NUM1 NUMBER := 0;
BEGIN
  SELECT EMPLOYEE_ID||' - '||FIRST_NAME||' '||LAST_NAME
  INTO V_EMP_DTLS
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = P_EMPNO;
  --
  P_EMP_DTLS := V_EMP_DTLS;
  --
  DBMS_OUTPUT.PUT_LINE('EMPLOYEE DETAILS - >'||P_EMP_DTLS);
  RETURN;
  NUM1 := NUM1 + 1;
END;
/

In the above example, we can see that the warning PLW-06002 is an Informational warning. It has the range of 6000 – 6249. Other warnings are PLW-07203 and PLW-0518. PLW is a performance warning and its range is 7000 – 7249. PLW-05018 is a Severe warning and its range is 5000 – 5999.

Disable PLSQL_WARNINGS:

ALTER SESSION SET PLSQL_WARNINGS = 'DISABLE:ALL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

There is another way we can set plsql_warnings as disable. For that we need enable all the warnings.

ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:ALL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

Now we will disable the INFORMATIONAL qualifier.

ALTER SESSION SET PLSQL_WARNINGS = 'DISABLE:INFORMATIONAL';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';

This is happened as we know when we enable on qualifier then rest all are in disable mode. So if we disable one qualifier then automatically all the qualifiers will be in disable mode.

Now we will check the Unreachable code example to see how disabling the INFORMATIONAL qualifier affect the compilation result. We will enable rest of the qualifier.

ALTER SESSION SET PLSQL_WARNINGS = 'DISABLE:INFORMATIONAL, ENABLE:SEVERE, ENABLE:PERFORMANCE';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';
CREATE OR REPLACE PROCEDURE SP_EMP_DTLS(P_EMPNO EMPLOYEES.EMPLOYEE_ID%TYPE,
                                        P_EMP_DTLS OUT VARCHAR2)
IS
  V_EMP_DTLS VARCHAR2(4000) := NULL;
  NUM1 NUMBER := 0;
BEGIN
  SELECT EMPLOYEE_ID||' - '||FIRST_NAME||' '||LAST_NAME
  INTO V_EMP_DTLS
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = P_EMPNO;
  --
  P_EMP_DTLS := V_EMP_DTLS;
  --
  DBMS_OUTPUT.PUT_LINE('EMPLOYEE DETAILS - >'||P_EMP_DTLS);
  RETURN;
  NUM1 := NUM1 + 1;
END;
/

So we can see that the unreachable message is not appeared in the compilation output.

Now in the above example, we can also see that the message PLW-05018 as warning. We will convert this to an error. To do this we will do the below settings:

ALTER SESSION SET PLSQL_WARNINGS = 'DISABLE:INFORMATIONAL, ENABLE:SEVERE, ENABLE:PERFORMANCE, ERROR:05018';
SELECT NAME, VALUE 
FROM V$PARAMETER
WHERE NAME = 'plsql_warnings';
CREATE OR REPLACE PROCEDURE SP_EMP_DTLS(P_EMPNO EMPLOYEES.EMPLOYEE_ID%TYPE,
                                        P_EMP_DTLS OUT VARCHAR2)
IS
  V_EMP_DTLS VARCHAR2(4000) := NULL;
  NUM1 NUMBER := 0;
BEGIN
  SELECT EMPLOYEE_ID||' - '||FIRST_NAME||' '||LAST_NAME
  INTO V_EMP_DTLS
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = P_EMPNO;
  --
  P_EMP_DTLS := V_EMP_DTLS;
  --
  DBMS_OUTPUT.PUT_LINE('EMPLOYEE DETAILS - >'||P_EMP_DTLS);
  RETURN;
  NUM1 := NUM1 + 1;
END;
/

Now we can see that the warning PLS-05018 has been converted into an error. We will check the status of procedure SP_EMP_DTLS.

SELECT * FROM USER_ERRORS
WHERE NAME = 'SP_EMP_DTLS';

RELATED TOPICS:

Leave a Comment

Your email address will not be published.