Expanded Supplied Packages with 10g – Part 3

In Part 1 I mentioned how pl/sql is empowered by new supplied packages and the importance of using them.

In Part 2 I mentioned;

1- Monitoring batch processes “kindly” – DBMS_APPLICATION_INFO package
2- Lempel-Ziv compression of RAW and BLOB data – UTL_COMPRESS package
3- Which Oracle version I am developing on – DBMS_DB_VERSION package and PL/SQL Conditional Compilation

So we continue with some more new packages;

4- Handling Data Errors with an Error Logging Table

DML error logging extends existing DML functionality by enabling you to specify the name of an error logging table into which Oracle should record errors encountered during DML operations. This enables you to complete the DML operation in spite of any errors, and to take corrective action on the erroneous rows at a later time.

Lets look at this simple example first, here I want to take the data of all_tables into T table. But there is a check constraint, blocks<10. Because of the automicity rule below insert statement fails and T destination table has no rows;


DROP TABLE t PURGE ;
CREATE TABLE t AS SELECT *
FROM all_tables WHERE 1=2;

ALTER TABLE t ADD CONSTRAINT pk_t
PRIMARY KEY (owner, table_name);

ALTER TABLE t ADD CONSTRAINT cc_t
CHECK (blocks < 10);

INSERT /*+ APPEND */ INTO t SELECT * FROM all_tables;

ORA-02290: check constraint (CLON.CC_T) violated

SELECT COUNT(*) FROM t;

COUNT(*)
----------
0

Prior to 10g we could handle this with PL/SQL exceptions, but this would have some performance cost which we will discuss in the second part. But with 10g we can create an error log table on table T and the exceptional rows automatically inserted into this table;


exec dbms_errlog.create_error_log('T');

desc err$_t

Name                      Type           Nullable Default Comments
------------------------- -------------- -------- ------- --------
ORA_ERR_NUMBER$           NUMBER         Y
ORA_ERR_MESG$             VARCHAR2(2000) Y
ORA_ERR_ROWID$            UROWID(4000)   Y
ORA_ERR_OPTYP$            VARCHAR2(2)    Y
ORA_ERR_TAG$              VARCHAR2(2000) Y
OWNER                     VARCHAR2(4000) Y
TABLE_NAME                VARCHAR2(4000) Y
TABLESPACE_NAME           VARCHAR2(4000) Y
CLUSTER_NAME              VARCHAR2(4000) Y
...

INSERT /*+ APPEND */ INTO t
SELECT * FROM all_tables
 LOG ERRORS REJECT LIMIT UNLIMITED;

COMMIT;

SELECT COUNT(*) FROM t;

COUNT(*)
----------
2386

SELECT COUNT(*) FROM err$_t;

COUNT(*)
----------
3238

Mandatory columns of the error log table are described as follows;


Column Name    Data Type    Description

ORA_ERR_NUMBER$    NUMBER    Oracle error number
ORA_ERR_MESG$    VARCHAR2(2000)    Oracle error message text
ORA_ERR_ROWID$    ROWID    Rowid of the row in error update and delete only)
ORA_ERR_OPTYP$    VARCHAR2(2)    Type of operation I = insert, U = update, D = delete
ORA_ERR_TAG$    VARCHAR2(2000)    User supplied tag

So we are always trying to do it with a single SQL if possible, so lets compare this scenerio with PL/SQL bulk operations;


drop table sales_src purge ;
drop table sales_target purge ;
drop table err$_sales_target purge ;
create table sales_src nologging parallel 2 as select * from all_source ;
create table sales_target nologging parallel 2 as select * from all_source where 1 = 2 ;

ALTER TABLE sales_target ADD CONSTRAINT cc_sales_target CHECK (line < 5000);

set serveroutput on SIZE UNLIMITED
set timing on

exec runstats_pkg.rs_start;

-- step 1 - pl/sql bulk operastions and exception handling
DECLARE
TYPE ARRAY IS TABLE OF sales_target%ROWTYPE INDEX BY BINARY_INTEGER;
sales_src_arr ARRAY;
errors        NUMBER;
l_cnt NUMBER := 0;
l_bulk_limit NUMBER := 1000;
bulk_error EXCEPTION;
PRAGMA EXCEPTION_INIT(bulk_error, -24381);
CURSOR c IS
SELECT * FROM sales_src;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT
INTO sales_src_arr LIMIT l_bulk_limit ;
BEGIN
FORALL i IN 1 .. sales_src_arr.COUNT SAVE EXCEPTIONS
INSERT /*+ APPEND */ INTO sales_target VALUES sales_src_arr (i);
EXCEPTION
WHEN bulk_error THEN
errors := SQL%BULK_EXCEPTIONS.COUNT;
l_cnt  := l_cnt + errors;
FOR i IN 1 .. errors LOOP
dbms_output.put_line('error iteration ' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX || ' oracle error IS ' || SQL%BULK_EXCEPTIONS(i).ERROR_CODE);
END LOOP;
END;
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
dbms_output.put_line(l_cnt || ' total errors');
END;
/
..
error iteration 877 oracle error IS 2290
error iteration 878 oracle error IS 2290
error iteration 879 oracle error IS 2290
1441 total errors

PL/SQL procedure successfully completed.

Elapsed: 00:00:18.39

ROLLBACK;

Rollback complete.

Elapsed: 00:00:13.40

exec runstats_pkg.rs_middle;

-- step 2 - Single Insert and Error Log Table Solution with 10g
exec dbms_errlog.create_error_log(upper('sales_target'));
INSERT /*+ APPEND */
INTO sales_target
SELECT * FROM sales_src log
LOG ERRORS
REJECT LIMIT UNLIMITED
/
153136 rows created.

Elapsed: 00:00:13.81

ROLLBACK;

Rollback complete.

Elapsed: 00:00:00.00

exec runstats_pkg.rs_stop(1000);

Run1 ran in 3183 hsecs
Run2 ran in 1437 hsecs
run 1 ran in 221,5% of the time

select count(*) from err$_sales_target ;

COUNT(*)
----------
1441

SELECT * FROM err$_sales_target WHERE rownum < 2;

ORA_ERR_NUMBER$ ORA_ERR_MESG$                                                                    ORA_ERR_ROWID$                                                                   ORA_ERR_OPTYP$ ORA_ERR_TAG$                                                                     OWNER                                                                            NAME                                                                             TYPE                                                                             LINE                                                                             TEXT

2290 ORA-02290: check constraint (HR.CC_SALES_TARGET) violated                                                                                                         I                                                                                               SYS                                                                              DBMS_BACKUP_RESTORE                                                              PACKAGE                                                                          5190                                                                               --     fname - Datafile copy name

5- Automatic SQL Tuning

Automatic SQL Tuning is a new capability of the query optimizer that automates the entire SQL tuning process. Using the newly enhanced query optimizer to tune SQL statements, the automatic process replaces manual SQL tuning, which is a complex, repetitive, and time-consuming function. The Automatic SQL Tuning features are exposed to the user with the SQL Tuning Advisor.

While the recommended interface for the SQL Tuning Advisor is the Oracle Enterprise Manager Database Control, the advisor can be administered with procedures in the DBMS_SQLTUNE package.

Running SQL Tuning Advisor using DBMS_SQLTUNE package is a multi-step process:

1. Create a SQL Tuning Set (if tuning multiple SQL statements)
2. Create a SQL tuning task
3. Execute a SQL tuning task
4. Display the results of a SQL tuning task
5. Implement recommendations as appropriate


set serveroutput on

– Creating a SQL Tuning Task
DECLARE
my_task_name VARCHAR2(30);
my_sqltext   CLOB;
BEGIN
my_sqltext := ‘SELECT /*+ ORDERED */ * ‘                      ||
‘FROM employees e, locations l, departments d ‘ ||
‘WHERE e.department_id = d.department_id AND ‘  ||
‘l.location_id = d.location_id AND ‘      ||
‘e.employee_id < :bnd';

-- uncomment to re-run
-- DBMS_SQLTUNE.drop_tuning_task(task_name   => ‘my_sql_tuning_task’);

my_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
sql_text    => my_sqltext,
bind_list   => sql_binds(anydata.ConvertNumber(100)),
user_name   => ‘HR’,
scope       => ‘COMPREHENSIVE’,
time_limit  => 60,
task_name   => ‘my_sql_tuning_task’,
description => ‘Task to tune a query on a specified employee’);
END;
/

– Executing a SQL Tuning Task
BEGIN
DBMS_SQLTUNE.EXECUTE_TUNING_TASK( task_name => ‘my_sql_tuning_task’ );
END;
/

– Checking the Progress of the SQL Tuning Advisor
SELECT sofar, totalwork FROM V$ADVISOR_PROGRESS WHERE username = ‘HR’ AND task_id =
(SELECT TASK_ID FROM USER_ADVISOR_TASKS WHERE task_name = ‘my_sql_tuning_task’);

– Checking the Status of a SQL Tuning Task
SELECT TASK_ID, status FROM USER_ADVISOR_TASKS WHERE task_name = ‘my_sql_tuning_task’;

– Displaying the Results of a SQL Tuning Task
SET LONG 10000
SET LONGCHUNKSIZE 10000
SET LINESIZE 100
SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK( ‘my_sql_tuning_task’)
FROM DUAL;

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK')
--------------------------------------------------------------------------------
--------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name                  : my_sql_tuning_task
Tuning Task Owner                 : HR
Scope                             : COMPREHENSIVE
Time Limit(seconds)               : 60
Completion Status                 : COMPLETED
Started at                        : 01/05/2007 14:33:38
Completed at                      : 01/05/2007 14:33:43
Number of SQL Profile Findings    : 1
Number of SQL Restructure Findings: 1

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK')
--------------------------------------------------------------------------------
--------------------

-------------------------------------------------------------------------------
Schema Name: HR
SQL ID     : dg7nfaj0bdcvk
SQL Text   : SELECT /*+ ORDERED */ * FROM employees e, locations l,
departments d WHERE e.department_id = d.department_id AND
l.location_id = d.location_id AND e.employee_id < :bnd

-------------------------------------------------------------------------------
FINDINGS SECTION (2 findings)
-------------------------------------------------------------------------------

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK')
----------------------------------------------------------------------------------------------------

1- SQL Profile Finding (see explain plans section below)
--------------------------------------------------------
A potentially better execution plan was found for this statement.

Recommendation (estimated benefit: 33,55%)
------------------------------------------
- Consider accepting the recommended SQL profile.
execute dbms_sqltune.accept_sql_profile(task_name =>
'my_sql_tuning_task', replace => TRUE);

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK')
----------------------------------------------------------------------------------------------------
2- Restructure SQL finding (see plan 1 in explain plans section)
----------------------------------------------------------------
An expensive cartesian product operation was found at line ID 3 of the
execution plan.

Recommendation
--------------
- Consider removing the "ORDERED" hint.

Rationale
---------

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK')
----------------------------------------------------------------------------------------------------
The "ORDERED" hint might force the optimizer to generate a cartesian
product. A cartesian product should be avoided whenever possible because
it is an expensive operation and might produce a large amount of data.

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------
...

6- Hashing and Encryption Capabilities

DBMS_CRYPTO provides an interface to encrypt and decrypt stored data, and can be used in conjunction with PL/SQL programs running network communications. It provides support for several industry-standard encryption and hashing algorithms, including the Advanced Encryption Standard (AES) encryption algorithm. AES has been approved by the National Institute of Standards and Technology (NIST) to replace the Data Encryption Standard (DES).

While encryption is not the ideal solution for addressing a number of security threats, it is clear that selectively encrypting sensitive data before storage in the database does improve security. Examples of such data could include:

* Credit card numbers
* National identity numbers


conn / as sysdba
grant execute on dbms_crypto to hr;

conn hr/hr
set serveroutput on
DECLARE
l_credit_card_no VARCHAR2(19) := '1234-5678-9012-3456';
l_ccn_raw        RAW(128) := utl_raw.cast_to_raw(l_credit_card_no);
l_key            RAW(128) := utl_raw.cast_to_raw('abcdefgh');
l_encrypted_raw RAW(2048);
l_decrypted_raw RAW(2048);
BEGIN
dbms_output.put_line('Original : ' || l_credit_card_no);
l_encrypted_raw := dbms_crypto.encrypt(l_ccn_raw,
dbms_crypto.des_cbc_pkcs5,
l_key);
dbms_output.put_line('Encrypted : ' ||
RAWTOHEX(utl_raw.cast_to_raw(l_encrypted_raw)));
l_decrypted_raw := dbms_crypto.decrypt(src => l_encrypted_raw,
typ => dbms_crypto.des_cbc_pkcs5,
key => l_key);
dbms_output.put_line('Decrypted : ' ||
utl_raw.cast_to_varchar2(l_decrypted_raw));
END;
/

Original : 1234-5678-9012-3456
Encrypted :
38303843354144343732323245303145433338393341423337343543464345393641364335414434
4245454345424136
Decrypted : 1234-5678-9012-3456

PL/SQL procedure successfully completed.

DECLARE
enc_val   RAW(2000);
l_key     RAW(2000);
l_key_len NUMBER := 128 / 8; -- convert bits to bytes
l_mod     NUMBER := dbms_crypto.ENCRYPT_AES128 + dbms_crypto.CHAIN_CBC +
dbms_crypto.PAD_PKCS5;
BEGIN
l_key := dbms_crypto.randombytes(l_key_len);
enc_val := dbms_crypto.encrypt(utl_i18n.string_to_raw('1234-5678-9012-3456',
'AL32UTF8'),
l_mod,
l_key);
dbms_output.put_line(enc_val);
END;
/

D2743700936E23C1CA7CC802022BA7C7954ED0B2DAE274958B56ED6FF4A0DAE5

PL/SQL procedure successfully completed.

Another simple example to demostrate hashing and storing in database;


CREATE TABLE security (    data RAW(2000) );
/* Hash calculating function.
* Takes a string of data as its argument.
* Returns a hash in raw format.
*/
CREATE OR REPLACE FUNCTION return_hash(data IN VARCHAR) RETURN RAW IS
BEGIN
/* Call HASH() function, which takes data in RAW format.
* Must use STRING_TO_RAW() to convert data.
* HASH_SH1 or HASH_MD5 are constants representing types of hashes to calculate.
*/
RETURN DBMS_CRYPTO.HASH(UTL_I18N.STRING_TO_RAW (data, 'AL32UTF8'), DBMS_CRYPTO.HASH_SH1);

END;
/
INSERT INTO security (data) VALUES (return_hash('TONGUC'))    ;
SELECT * FROM security ;

DATA
--------------------------------------------------------------------------------
D8A3068DCFBC27341808B206F6CFFB674F7200EE

7- PL/SQL Compile-Time Warnings

To make your programs more robust and avoid problems at run time, you can turn on checking for certain warning conditions. These conditions are not serious enough to produce an error and keep you from compiling a subprogram. They might point out something in the subprogram that produces an undefined result or might create a performance problem.

To work with PL/SQL warning messages, you use the PLSQL_WARNINGS initialization parameter, the DBMS_WARNING package, and the USER/DBA/ALL_PLSQL_OBJECT_SETTINGS views.


set linesize 120
col name format a40
col value format a40

-- current warning state
-- PLSQL_WARNINGS enables or disables the reporting of warning messages by the PL/SQL compiler, and specifies which warning messages to show as errors.
-- Default value     'DISABLE:ALL'
SELECT name, value FROM gv$parameter
WHERE name LIKE 'plsql%warn%';

NAME                                     VALUE
---------------------------------------- ----------------------------------------
plsql_warnings                           DISABLE:ALL

-- current optimizer level for the instance
-- OPTIMIZER_DYNAMIC_SAMPLING controls the level of dynamic sampling performed by the optimizer.
-- Default value     If OPTIMIZER_FEATURES_ENABLE is set to 10.0.0 or higher, then 2
SELECT name, value FROM gv$parameter
WHERE name = 'optimizer_dynamic_sampling';

NAME                                     VALUE
---------------------------------------- ----------------------------------------
optimizer_dynamic_sampling               2

col plsql_code_type format a30
col plsql_debug format a30
col plsql_warnings format a50
col nls_length_semantics format a30

-- USER_PLSQL_OBJECT_SETTINGS displays compiler settings for all stored objects in the database.
SELECT DISTINCT TYPE,
plsql_optimize_level,
plsql_code_type,
plsql_debug,
plsql_warnings,
nls_length_semantics
FROM user_plsql_object_settings;

TYPE         PLSQL_OPTIMIZE_LEVEL PLSQL_CODE_TYPE                PLSQL_DEBUG                    PLSQL_WARNINGS                                     NLS_LENGTH_SEMANTICS
------------ -------------------- ------------------------------ ------------------------------ -------------------------------------------------- ------------------------------
PACKAGE BODY                    2 INTERPRETED                    FALSE                          DISABLE:ALL                                        BYTE
PROCEDURE                       2 INTERPRETED                    FALSE                          ENABLE:INFORMATIONAL,DISABLE:PERFORMANCE,DISABLE:S BYTE
...

First example with DBMS_WARNING package;


-- Check the current warning setting
SELECT DBMS_WARNING.get_warning_setting_string() FROM DUAL;

DBMS_WARNING.GET_WARNING_SETTING_STRING()
-----------------------------------------
DISABLE:ALL

-- When warnings disabled, the following procedure compiles with no warnings
CREATE OR REPLACE PROCEDURE unreachable_code AS
l_dummy  VARCHAR2(10) := '1';
BEGIN
IF 1=1 THEN
SELECT '2'
INTO   l_dummy
FROM   dual;
ELSE
RAISE_APPLICATION_ERROR(-20000, ‘l_dummy != 1!’);
END IF;
END unreachable_code;
/

Procedure created.

-- enable all warning messages for this session
CALL DBMS_WARNING.set_warning_setting_string('ENABLE:ALL' ,'SESSION');

Call completed.

-- Recompile the procedure and a warning about unreachable code displays
ALTER PROCEDURE unreachable_code COMPILE;

SP2-0805: Procedure altered with compi

SHOW ERRORS;

Errors for PROCEDURE UNREACHABLE_CODE:

LINE/COL ERROR
-------- -----------------------------
9/9      PLW-06002: Unreachable code

Another example with “alter session” this time;


ALTER SESSION SET PLSQL_WARNINGS='DISABLE:ALL';

CREATE OR REPLACE PROCEDURE nocopy_warn(sa IN OUT dbms_sql.varchar2a) IS
BEGIN
dbms_output.put_line('This is a test');
END nocopy_warn;
/

Procedure created.

ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL';

CREATE OR REPLACE PROCEDURE nocopy_warn(sa IN OUT dbms_sql.varchar2a) IS
BEGIN
dbms_output.put_line('This is a test');
END nocopy_warn;
/

SP2-0804: Procedure created with compilation warnings

SQL> sho err
Errors for PROCEDURE NOCOPY_WARN:

LINE/COL ERROR
-------- ------------------------------------------------------------
1/23     PLW-07203: parameter 'SA' may benefit from use of the NOCOPY
compiler hint

CREATE OR REPLACE PROCEDURE nocopy_warn(sa IN OUT NOCOPY dbms_sql.varchar2a) IS
BEGIN
dbms_output.put_line('This is a test');
END nocopy_warn;
/

Procedure created.

– Instance or session level.
ALTER SYSTEM SET PLSQL_WARNINGS=’ENABLE:ALL’;
ALTER SESSION SET PLSQL_WARNINGS=’ENABLE:ALL’;
ALTER SESSION SET PLSQL_WARNINGS=’DISABLE:PERFORMANCE’;

– Recompile with extra checking.
ALTER PROCEDURE hello COMPILE PLSQL_WARNINGS=’ENABLE:PERFORMANCE’;

– Set mutiple values.
ALTER SESSION SET PLSQL_WARNINGS=’ENABLE:SEVERE’,'DISABLE:PERFORMANCE’,'DISABLE:INFORMATIONAL’;
Testing Information : the scripts mentioned are tested on Oracle Database 10g Express Edition Release 10.2.0.1.0

Refences Used :
http://www.psoug.org/reference/dbms_errlog.html
Oracle® Database Performance Tuning Guide
10g Release 2 (10.2) Chapter 12 Automatic SQL Tuning

http://www.psoug.org/reference/dbms_sqltune.html
Oracle® Database Security Guide
10g Release 2 (10.2) Chapter 17 Developing Applications Using Data Encryption

http://www.psoug.org/reference/dbms_crypto.html
How To Encrypt Data in Oracle Using PHP by Larry Ullma
Oracle® Database PL/SQL User’s Guide and Reference 10g Release 2 (10.2) Chapter 10 Handling PL/SQL Errors
http://www.psoug.org/reference/dbms_warning.html

2 Comments

Leave a Comment