Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 95

PL SQL Document 1

PL SQL DOCUMENTATION
ON

DYNAMIC SQL,

SEND MAIL,

FOR ALL,

FOR UPDATE

BY

Mahesh M. Vishwekar

Oracle Apps Technical Developer 


Surmani Office Complex,

D.P. Road, Aundh, Pune - 7


O:  001 267.... &  +9120 40... / M: 879.616.4608

www.saturninfotech.com | mahesh@saturninfotech.com

Oracle Gold Partner

www.saturninfotech.com
PL SQL Document 2

INDEX -

SR NO TITLE PAGE_NO.

1 Coding Dynamic SQL Statements - 3

2 Send mail from PL/SQL - 22

 UTL_SMTP - 32

 UTL_TCP - 59

 UTL_URL - 81

3 PL SQL For All Operator - 87

4 Select For Updates in Cursors - 90

www.saturninfotech.com
PL SQL Document 3

Coding Dynamic SQL Statements


Dynamic SQL is a programming technique that enables you to build SQL statements
dynamically at runtime. You can create more general purpose, flexible applications by using
dynamic SQL because the full text of a SQL statement may be unknown at compilation. For
example, dynamic SQL lets you create a procedure that operates on a table whose name is not
known until runtime.

Oracle includes two ways to implement dynamic SQL in a PL/SQL application:

 Native dynamic SQL, where you place dynamic SQL statements directly into PL/SQL blocks.
 Calling procedures in the DBMS_SQL package.

This chapter covers the following topics:

 "What Is Dynamic SQL?"


 "Why Use Dynamic SQL?"
 "A Dynamic SQL Scenario Using Native Dynamic SQL"
 "Choosing Between Native Dynamic SQL and the DBMS_SQL Package"
 "Using Dynamic SQL in Languages Other Than PL/SQL"
 "Using PL/SQL Records in SQL INSERT and UPDATE Statements"

What Is Dynamic SQL?

Dynamic SQL enables you to write programs that reference SQL statements whose full text is
not known until runtime. Before discussing dynamic SQL in detail, a clear definition of static
SQL may provide a good starting point for understanding dynamic SQL. Static SQL statements
do not change from execution to execution. The full text of static SQL statements are known at
compilation, which provides the following benefits:

 Successful compilation verifies that the SQL statements reference valid database objects.
 Successful compilation verifies that the necessary privileges are in place to access the database
objects.
 Performance of static SQL is generally better than dynamic SQL.

Because of these advantages, you should use dynamic SQL only if you cannot use static SQL to
accomplish your goals, or if using static SQL is cumbersome compared to dynamic SQL.
However, static SQL has limitations that can be overcome with dynamic SQL. You may not
always know the full text of the SQL statements that must be executed in a PL/SQL procedure.
Your program may accept user input that defines the SQL statements to execute, or your
program may need to complete some processing work to determine the correct course of action.
In such cases, you should use dynamic SQL.

www.saturninfotech.com
PL SQL Document 4

For example, a reporting application in a data warehouse environment might not know the exact
table name until runtime. These tables might be named according to the starting month and year
of the quarter, for example INV_01_1997, INV_04_1997, INV_07_1997, INV_10_1997,
INV_01_1998, and so on. You can use dynamic SQL in your reporting application to specify the
table name at runtime.

You might also want to run a complex query with a user-selectable sort order. Instead of coding
the query twice, with different ORDER BY clauses, you can construct the query dynamically to
include a specified ORDER BY clause.

Dynamic SQL programs can handle changes in data definitions, without the need to recompile.
This makes dynamic SQL much more flexible than static SQL. Dynamic SQL lets you write
reusable code because the SQL can be easily adapted for different environments..

Dynamic SQL also lets you execute data definition language (DDL) statements and other SQL
statements that are not supported in purely static SQL programs.

Why Use Dynamic SQL?

You should use dynamic SQL in cases where static SQL does not support the operation you want
to perform, or in cases where you do not know the exact SQL statements that must be executed
by a PL/SQL procedure. These SQL statements may depend on user input, or they may depend
on processing work done by the program. The following sections describe typical situations
where you should use dynamic SQL and typical problems that can be solved by using dynamic
SQL

Executing DDL and SCL Statements in PL/SQL

In PL/SQL, you can only execute the following types of statements using dynamic SQL, rather
than static SQL:

 Data definition language (DDL) statements, such as CREATE, DROP, GRANT, and REVOKE
 Session control language (SCL) statements, such as ALTER SESSION and SET ROLE

See Also:

Oracle9i SQL Reference for information about DDL and SCL statements.

Also, you can only use the TABLE clause in the SELECT statement through dynamic SQL. For
example, the following PL/SQL block contains a SELECT statement that uses the TABLE clause
and native dynamic SQL:

CREATE TYPE t_emp AS OBJECT (id NUMBER, name VARCHAR2(20))


/
CREATE TYPE t_emplist AS TABLE OF t_emp
/

www.saturninfotech.com
PL SQL Document 5

CREATE TABLE dept_new (id NUMBER, emps t_emplist)


NESTED TABLE emps STORE AS emp_table;

INSERT INTO dept_new VALUES (


10,
t_emplist(
t_emp(1, 'SCOTT'),
t_emp(2, 'BRUCE')));

DECLARE
deptid NUMBER;
ename VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'SELECT d.id, e.name
FROM dept_new d, TABLE(d.emps) e -- not allowed in static SQL
-- in PL/SQL
WHERE e.id = 1'
INTO deptid, ename;
END;
/

Executing Dynamic Queries

You can use dynamic SQL to create applications that execute dynamic queries, whose full text is
not known until runtime. Many types of applications need to use dynamic queries, including:

 Applications that allow users to input or choose query search or sorting criteria at runtime
 Applications that allow users to input or choose optimizer hints at run time
 Applications that query a database where the data definitions of tables are constantly changing
 Applications that query a database where new tables are created often

For examples, see "Querying Using Dynamic SQL: Example", and see the query examples in "A
Dynamic SQL Scenario Using Native Dynamic SQL".

Referencing Database Objects that Do Not Exist at Compilation

Many types of applications must interact with data that is generated periodically. For example,
you might know the tables definitions at compile time, but not the names of the tables.

Dynamic SQL can solve this problem, because it lets you wait until runtime to specify the table
names. For example, in the sample data warehouse application discussed in "What Is Dynamic
SQL?", new tables are generated every quarter, and these tables always have the same definition.
You might let a user specify the name of the table at runtime with a dynamic SQL query similar
to the following:

CREATE OR REPLACE PROCEDURE query_invoice(


month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;

www.saturninfotech.com
PL SQL Document 6

c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/

Optimizing Execution Dynamically

You can use dynamic SQL to build a SQL statement in a way that optimizes the execution by
concatenating the hints into a SQL statement dynamically. This lets you change the hints based
on your current database statistics, without requiring recompilation.

For example, the following procedure uses a variable called a_hint to allow users to pass a hint
option to the SELECT statement:

CREATE OR REPLACE PROCEDURE query_emp


(a_hint VARCHAR2) AS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
BEGIN
OPEN c FOR 'SELECT ' || a_hint ||
' empno, ename, sal, job FROM emp WHERE empno = 7566';
-- process
END;
/

In this example, the user can pass any of the following values for a_hint:

a_hint = '/*+ ALL_ROWS */'

a_hint = '/*+ FIRST_ROWS */'

a_hint = '/*+ CHOOSE */'

or any other valid hint option.

See Also:

www.saturninfotech.com
PL SQL Document 7

Oracle9i Database Performance Guide and Reference for more information


about using hints.

Executing Dynamic PL/SQL Blocks

You can use the EXECUTE IMMEDIATE statement to execute anonymous PL/SQL blocks. You can
add flexibility by constructing the block contents at runtime.

For example, suppose ythroughthroughou want to write an application that takes an event
number and dispatches to a handler for the event. The name of the handler is in the form
EVENT_HANDLER_event_num, where event_num is the number of the event. One approach is to
implement the dispatcher as a switch statement, where the code handles each event by making a
static call to its appropriate handler. This code is not very extensible because the dispatcher code
must be updated whenever a handler for a new event is added.

CREATE OR REPLACE PROCEDURE event_handler_1(param number) AS BEGIN


-- process event
RETURN;
END;
/

CREATE OR REPLACE PROCEDURE event_handler_2(param number) AS BEGIN


-- process event
RETURN;
END;
/

CREATE OR REPLACE PROCEDURE event_handler_3(param number) AS BEGIN


-- process event
RETURN;
END;
/

CREATE OR REPLACE PROCEDURE event_dispatcher


(event number, param number) IS
BEGIN
IF (event = 1) THEN
EVENT_HANDLER_1(param);
ELSIF (event = 2) THEN
EVENT_HANDLER_2(param);
ELSIF (event = 3) THEN
EVENT_HANDLER_3(param);
END IF;
END;
/

Using native dynamic SQL, you can write a smaller, more flexible event dispatcher similar to the
following:

www.saturninfotech.com
PL SQL Document 8

CREATE OR REPLACE PROCEDURE event_dispatcher


(event NUMBER, param NUMBER) IS
BEGIN
EXECUTE IMMEDIATE
'BEGIN
EVENT_HANDLER_' || to_char(event) || '(:1);
END;'
USING param;
END;
/

Performing Dynamic Operations Using Invoker-Rights

By using the invoker-rights feature with dynamic SQL, you can build applications that issue
dynamic SQL statements under the privileges and schema of the invoker. These two features,
invoker-rights and dynamic SQL, enable you to build reusable application subcomponents that
can operate on and access the invoker's data and modules.

See Also:

PL/SQL User's Guide and Reference for information about using invokers-
rights and native dynamic SQL.

A Dynamic SQL Scenario Using Native Dynamic SQL


This scenario shows you how to perform the following operations using native dynamic SQL:

 Execute DDL and DML operations


 Execute single row and multiple row queries

The database in this scenario is a company's human resources database (named hr) with the
following data model:

A master table named offices contains the list of all company locations. The offices table has
the following definition:

Column Name Null? Type


LOCATION NOT_NULL VARCHAR2(200)

Multiple emp_location tables contain the employee information, where location is the name of
city where the office is located. For example, a table named emp_houston contains employee
information for the company's Houston office, while a table named emp_boston contains
employee information for the company's Boston office.

Each emp_location table has the following definition:

www.saturninfotech.com
PL SQL Document 9

Column Name Null? Type


EMPNO NOT_NULL NUMBER(4)
ENAME NOT_NULL VARCHAR2(10)
JOB NOT_NULL VARCHAR2(9)
SAL NOT_NULL NUMBER(7,2)
DEPTNO NOT_NULL NUMBER(2)

The following sections describe various native dynamic SQL operations that can be performed
on the data in the hr database.

Sample DML Operation Using Native Dynamic SQL

The following native dynamic SQL procedure gives a raise to all employees with a particular job
title:

CREATE OR REPLACE PROCEDURE salary_raise (raise_percent NUMBER, job VARCHAR2)


IS
TYPE loc_array_type IS TABLE OF VARCHAR2(40)
INDEX BY binary_integer;
dml_str VARCHAR2 (200);
loc_array loc_array_type;
BEGIN
-- bulk fetch the list of office locations
SELECT location BULK COLLECT INTO loc_array
FROM offices;
-- for each location, give a raise to employees with the given 'job'
FOR i IN loc_array.first..loc_array.last LOOP
dml_str := 'UPDATE emp_' || loc_array(i)
|| ' SET sal = sal * (1+(:raise_percent/100))'
|| ' WHERE job = :job_title';
EXECUTE IMMEDIATE dml_str USING raise_percent, job;
END LOOP;
END;
/
SHOW ERRORS;

Sample DDL Operation Using Native Dynamic SQL

The EXECUTE IMMEDIATE statement can perform DDL operations. For example, the following
procedure adds an office location:

CREATE OR REPLACE PROCEDURE add_location (loc VARCHAR2) IS


BEGIN
-- insert new location in master table
INSERT INTO offices VALUES (loc);
-- create an employee information table
EXECUTE IMMEDIATE
'CREATE TABLE ' || 'emp_' || loc ||
'(
empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),

www.saturninfotech.com
PL SQL Document 10

job VARCHAR2(9),
sal NUMBER(7,2),
deptno NUMBER(2)
)';
END;
/
SHOW ERRORS;

The following procedure deletes an office location:

CREATE OR REPLACE PROCEDURE drop_location (loc VARCHAR2) IS


BEGIN
-- delete the employee table for location 'loc'
EXECUTE IMMEDIATE 'DROP TABLE ' || 'emp_' || loc;
-- remove location from master table
DELETE FROM offices WHERE location = loc;
END;
/
SHOW ERRORS;

Sample Single-Row Query Using Native Dynamic SQL

The EXECUTE IMMEDIATE statement can perform dynamic single-row queries. You can specify
bind variables in the USING clause and fetch the resulting row into the target specified in the
INTO clause of the statement.

The following function retrieves the number of employees at a particular location performing a
specified job:

CREATE OR REPLACE FUNCTION get_num_of_employees (loc VARCHAR2, job VARCHAR2)


RETURN NUMBER IS
query_str VARCHAR2(1000);
num_of_employees NUMBER;
BEGIN
query_str := 'SELECT COUNT(*) FROM '
|| ' emp_' || loc
|| ' WHERE job = :job_title';
EXECUTE IMMEDIATE query_str
INTO num_of_employees
USING job;
RETURN num_of_employees;
END;
/
SHOW ERRORS;

Sample Multiple-Row Query Using Native Dynamic SQL

www.saturninfotech.com
PL SQL Document 11

The OPEN-FOR, FETCH, and CLOSE statements can perform dynamic multiple-row queries. For
example, the following procedure lists all of the employees with a particular job at a specified
location:

CREATE OR REPLACE PROCEDURE list_employees(loc VARCHAR2, job VARCHAR2) IS


TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(1000);
emp_name VARCHAR2(20);
emp_num NUMBER;
BEGIN
query_str := 'SELECT ename, empno FROM emp_' || loc
|| ' WHERE job = :job_title';
-- find employees who perform the specified job
OPEN c FOR query_str USING job;
LOOP
FETCH c INTO emp_name, emp_num;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
SHOW ERRORS;

Choosing Between Native Dynamic SQL and the DBMS_SQL


Package
Oracle provides two methods for using dynamic SQL within PL/SQL: native dynamic SQL and
the DBMS_SQL package. Native dynamic SQL lets you place dynamic SQL statements directly
into PL/SQL code. These dynamic statements include DML statements (including queries),
PL/SQL anonymous blocks, DDL statements, transaction control statements, and session control
statements.

To process most native dynamic SQL statements, you use the EXECUTE IMMEDIATE statement. To
process a multi-row query (SELECT statement), you use OPEN-FOR, FETCH, and CLOSE statements.

Note:

To use native dynamic SQL, the COMPATIBLE initialization parameter must be


set to 8.1.0 or higher. See Oracle9i Database Migration for more information
about the COMPATIBLE parameter.

www.saturninfotech.com
PL SQL Document 12

The DBMS_SQL package is a PL/SQL library that offers an API to execute SQL statements
dynamically. The DBMS_SQL package has procedures to open a cursor, parse a cursor, supply
binds, and so on. Programs that use the DBMS_SQL package make calls to this package to perform
dynamic SQL operations.

The following sections provide detailed information about the advantages of both methods.

See Also:

The PL/SQL User's Guide and Reference for detailed information about
using native dynamic SQL and the Oracle9i Supplied PL/SQL Packages and
Types Reference for detailed information about using the DBMS_SQL package.
In the PL/SQL User's Guide and Reference, native dynamic SQL is referred
to simply as dynamic SQL.

Advantages of Native Dynamic SQL

Native dynamic SQL provides the following advantages over the DBMS_SQL package:

Native Dynamic SQL is Easy to Use

Because native dynamic SQL is integrated with SQL, you can use it in the same way that you
use static SQL within PL/SQL code. Native dynamic SQL code is typically more compact and
readable than equivalent code that uses the DBMS_SQL package.

With the DBMS_SQL package you must call many procedures and functions in a strict sequence,
making even simple operations require a lot of code. You can avoid this complexity by using
native dynamic SQL instead.

Table 8-1 illustrates the difference in the amount of code required to perform the same operation
using the DBMS_SQL package and native dynamic SQL.

Table 8-1 Code Comparison of DBMS_SQL Package and Native Dynamic SQL
DBMS_SQL Package Native Dynamic SQL

CREATE PROCEDURE insert_into_table ( CREATE PROCEDURE insert_into_table (


table_name VARCHAR2, table_name VARCHAR2,
deptnumber NUMBER, deptnumber NUMBER,
deptname VARCHAR2, deptname VARCHAR2,
location VARCHAR2) IS location VARCHAR2) IS
cur_hdl INTEGER; stmt_str VARCHAR2(200);
stmt_str VARCHAR2(200);
rows_processed BINARY_INTEGER; BEGIN
stmt_str := 'INSERT INTO ' ||
BEGIN table_name || ' values
stmt_str := 'INSERT INTO ' || (:deptno, :dname, :loc)';
table_name || ' VALUES

www.saturninfotech.com
PL SQL Document 13

DBMS_SQL Package Native Dynamic SQL

(:deptno, :dname, :loc)'; EXECUTE IMMEDIATE stmt_str


USING
-- open cursor deptnumber, deptname, location;
cur_hdl := dbms_sql.open_cursor;
END;
-- parse cursor /
dbms_sql.parse(cur_hdl, stmt_str, SHOW ERRORS;
dbms_sql.native);

-- supply binds
dbms_sql.bind_variable
(cur_hdl, ':deptno', deptnumber);
dbms_sql.bind_variable
(cur_hdl, ':dname', deptname);
dbms_sql.bind_variable
(cur_hdl, ':loc', location);

-- execute cursor
rows_processed :=
dbms_sql.execute(cur_hdl);

-- close cursor
dbms_sql.close_cursor(cur_hdl);

END;
/
SHOW ERRORS;

Native Dynamic SQL is Faster than DBMS_SQL

Native dynamic SQL in PL/SQL performs comparably to the performance of static SQL, because
the PL/SQL interpreter has built-in support for it. Programs that use native dynamic SQL are
much faster than programs that use the DBMS_SQL package. Typically, native dynamic SQL
statements perform 1.5 to 3 times better than equivalent DBMS_SQL calls. (Your performance
gains may vary depending on your application.)

Native dynamic SQL bundles the statement preparation, binding, and execution steps into a
single operation, which minimizes the data copying and procedure call overhead and improves
performance.

The DBMS_SQL package is based on a procedural API and incurs high procedure call and data
copy overhead. Each time you bind a variable, the DBMS_SQL package copies the PL/SQL bind
variable into its space for use during execution. Each time you execute a fetch, the data is copied
into the space managed by the DBMS_SQL package and then the fetched data is copied, one
column at a time, into the appropriate PL/SQL variables, resulting in substantial overhead.

www.saturninfotech.com
PL SQL Document 14

Performance Tip: Using Bind Variables

When using either native dynamic SQL or the DBMS_SQL package, you can improve performance
by using bind variables, because bind variables allow Oracle to share a single cursor for multiple
SQL statements.

For example, the following native dynamic SQL code does not use bind variables:

CREATE OR REPLACE PROCEDURE del_dept (


my_deptno dept.deptno%TYPE) IS
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = ' || to_char
(my_deptno);
END;
/
SHOW ERRORS;

For each distinct my_deptno variable, a new cursor is created, causing resource contention and
poor performance. Instead, bind my_deptno as a bind variable:

CREATE OR REPLACE PROCEDURE del_dept (


my_deptno dept.deptno%TYPE) IS
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = :1' USING my_deptno;
END;
/
SHOW ERRORS;

Here, the same cursor is reused for different values of the bind my_deptno, improving
performance and scalabilty.

Native Dynamic SQL Supports User-Defined Types

Native dynamic SQL supports all of the types supported by static SQL in PL/SQL, including
user-defined types such as user-defined objects, collections, and REFs. The DBMS_SQL package
does not support these user-defined types.

Note:

The DBMS_SQL package provides limited support for arrays. See the Oracle9i
Supplied PL/SQL Packages and Types Reference for information.

www.saturninfotech.com
PL SQL Document 15

Native Dynamic SQL Supports Fetching Into Records

Native dynamic SQL and static SQL both support fetching into records, but the DBMS_SQL
package does not. With native dynamic SQL, the rows resulting from a query can be directly
fetched into PL/SQL records.

In the following example, the rows from a query are fetched into the emp_rec record:

DECLARE
TYPE EmpCurTyp IS REF CURSOR;
c EmpCurTyp;
emp_rec emp%ROWTYPE;
stmt_str VARCHAR2(200);
e_job emp.job%TYPE;

BEGIN
stmt_str := 'SELECT * FROM emp WHERE job = :1';
-- in a multi-row query
OPEN c FOR stmt_str USING 'MANAGER';
LOOP
FETCH c INTO emp_rec;
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
-- in a single-row query
EXECUTE IMMEDIATE stmt_str INTO emp_rec USING 'PRESIDENT';

END;
/

Advantages of the DBMS_SQL Package

The DBMS_SQL package provides the following advantages over native dynamic SQL:

DBMS_SQL is Supported in Client-Side Programs

The DBMS_SQL package is supported in client-side programs, but native dynamic SQL is not.
Every call to the DBMS_SQL package from the client-side program translates to a PL/SQL remote
procedure call (RPC); these calls occur when you need to bind a variable, define a variable, or
execute a statement.

DBMS_SQL Supports DESCRIBE

The DESCRIBE_COLUMNS procedure in the DBMS_SQL package can be used to describe the columns
for a cursor opened and parsed through DBMS_SQL. This feature is similar to the DESCRIBE
command in SQL*Plus. Native dynamic SQL does not have a DESCRIBE facility.

www.saturninfotech.com
PL SQL Document 16

DBMS_SQL Supports Multiple Row Updates and Deletes with a RETURNING Clause

The DBMS_SQL package supports statements with a RETURNING clause that update or delete
multiple rows. Native dynamic SQL only supports a RETURNING clause if a single row is
returned.

See Also:

"Performing DML with RETURNING Clause Using Dynamic SQL:


Example" for examples of DBMS_SQL package code and native dynamic SQL
code that uses a RETURNING clause.
DBMS_SQL Supports SQL Statements Larger than 32KB

The DBMS_SQL package supports SQL statements larger than 32KB; native dynamic SQL does
not.

DBMS_SQL Lets You Reuse SQL Statements

The PARSE procedure in the DBMS_SQL package parses a SQL statement once. After the initial
parsing, you can use the statement multiple times with different sets of bind arguments.

Native dynamic SQL prepares a SQL statement each time the statement is used, which typically
involves parsing, optimization, and plan generation. Although the extra prepare operations incur
a small performance penalty, the slowdown is typically outweighed by the performance benefits
of native dynamic SQL.

Examples of DBMS_SQL Package Code and Native Dynamic SQL Code

The following examples illustrate the differences in the code necessary to complete operations
with the DBMS_SQL package and native dynamic SQL. Specifically, the following types of
examples are presented:

 A query
 A DML operation
 A DML returning operation

In general, the native dynamic SQL code is more readable and compact, which can improve
developer productivity.

Querying Using Dynamic SQL: Example

The following example includes a dynamic query statement with one bind variable (:jobname)
and two select columns (ename and sal):

www.saturninfotech.com
PL SQL Document 17

stmt_str := 'SELECT ename, sal FROM emp WHERE job = :jobname';

This example queries for employees with the job description SALESMAN in the job column of the
emp table. Table 8-2 shows sample code that accomplishes this query using the DBMS_SQL
package and native dynamic SQL.

Table 8-2 Querying Using the DBMS_SQL Package and Native Dynamic SQL
DBMS_SQL Query Operation Native Dynamic SQL Query Operation

DECLARE DECLARE
stmt_str varchar2(200); TYPE EmpCurTyp IS REF CURSOR;
cur_hdl int; cur EmpCurTyp;
rows_processed int; stmt_str VARCHAR2(200);
name varchar2(10); name VARCHAR2(20);
salary int; salary NUMBER;
BEGIN BEGIN
cur_hdl := dbms_sql.open_cursor; -- open stmt_str := 'SELECT ename, sal
cursor FROM emp
stmt_str := 'SELECT ename, sal FROM emp WHERE job = :1';
WHERE OPEN cur FOR stmt_str USING
job = :jobname'; 'SALESMAN';
dbms_sql.parse(cur_hdl, stmt_str,
dbms_sql.native); LOOP
FETCH cur INTO name, salary;
-- supply binds (bind by name) EXIT WHEN cur%NOTFOUND;
dbms_sql.bind_variable( -- <process data>
cur_hdl, 'jobname', 'SALESMAN'); END LOOP;
CLOSE cur;
-- describe defines END;
dbms_sql.define_column(cur_hdl, 1, name, /
200);
dbms_sql.define_column(cur_hdl, 2,
salary);

rows_processed :=
dbms_sql.execute(cur_hdl); --
execute

LOOP
-- fetch a row
IF dbms_sql.fetch_rows(cur_hdl) > 0 then

-- fetch columns from the row


dbms_sql.column_value(cur_hdl, 1,
name);
dbms_sql.column_value(cur_hdl, 2,
salary);

-- <process data>

ELSE
EXIT;

www.saturninfotech.com
PL SQL Document 18

DBMS_SQL Query Operation Native Dynamic SQL Query Operation

END IF;
END LOOP;
dbms_sql.close_cursor(cur_hdl); -- close
cursor
END;
/

Performing DML Using Dynamic SQL: Example

The following example includes a dynamic INSERT statement for a table with three columns:

stmt_str := 'INSERT INTO dept_new VALUES (:deptno, :dname, :loc)';

This example inserts a new row for which the column values are in the PL/SQL variables
deptnumber, deptname, and location. Table 8-3 shows sample code that accomplishes this
DML operation using the DBMS_SQL package and native dynamic SQL.

Table 8-3 DML Operation Using the DBMS_SQL Package and Native Dynamic SQL
DBMS_SQL DML Operation Native Dynamic SQL DML Operation

DECLARE DECLARE
stmt_str VARCHAR2(200); stmt_str VARCHAR2(200);
cur_hdl NUMBER; deptnumber NUMBER := 99;
deptnumber NUMBER := 99; deptname VARCHAR2(20);
deptname VARCHAR2(20); location VARCHAR2(10);
location VARCHAR2(10); BEGIN
rows_processed NUMBER; stmt_str := 'INSERT INTO
BEGIN dept_new VALUES
stmt_str := 'INSERT INTO dept_new VALUES (:deptno, :dname, :loc)';
(:deptno, :dname, :loc)'; EXECUTE IMMEDIATE stmt_str
cur_hdl := DBMS_SQL.OPEN_CURSOR; USING deptnumber, deptname,
DBMS_SQL.PARSE( location;
cur_hdl, stmt_str, DBMS_SQL.NATIVE); END;
-- supply binds /
DBMS_SQL.BIND_VARIABLE
(cur_hdl, ':deptno', deptnumber);
DBMS_SQL.BIND_VARIABLE
(cur_hdl, ':dname', deptname);
DBMS_SQL.BIND_VARIABLE
(cur_hdl, ':loc', location);
rows_processed :=
dbms_sql.execute(cur_hdl);
-- execute
DBMS_SQL.CLOSE_CURSOR(cur_hdl); -- close
END;
/

www.saturninfotech.com
PL SQL Document 19

Performing DML with RETURNING Clause Using Dynamic SQL: Example

The following example uses a dynamic UPDATE statement to update the location of a department,
then returns the name of the department:

stmt_str := 'UPDATE dept_new


SET loc = :newloc
WHERE deptno = :deptno
RETURNING dname INTO :dname';

Table 8-4 shows sample code that accomplishes this operation using both the DBMS_SQL package
and native dynamic SQL.

Table 8-4 DML Returning Operation Using the DBMS_SQL Package and Native Dynamic SQL
Native Dynamic SQL DML Returning
DBMS_SQL DML Returning Operation Operation

DECLARE DECLARE
deptname_array deptname_array
dbms_sql.Varchar2_Table; dbms_sql.Varchar2_Table;
cur_hdl INT; stmt_str VARCHAR2(200);
stmt_str VARCHAR2(200); location VARCHAR2(20);
location VARCHAR2(20); deptnumber NUMBER := 10;
deptnumber NUMBER := 10; deptname VARCHAR2(20);
rows_processed NUMBER; BEGIN
BEGIN stmt_str := 'UPDATE dept_new
stmt_str := 'UPDATE dept_new SET loc = :newloc
SET loc = :newloc WHERE deptno = :deptno
WHERE deptno = :deptno RETURNING dname INTO :dname';
RETURNING dname INTO :dname'; EXECUTE IMMEDIATE stmt_str
USING location, deptnumber, OUT
cur_hdl := dbms_sql.open_cursor; deptname;
dbms_sql.parse END;
(cur_hdl, stmt_str, /
dbms_sql.native);
-- supply binds
dbms_sql.bind_variable
(cur_hdl, ':newloc', location);
dbms_sql.bind_variable
(cur_hdl, ':deptno', deptnumber);
dbms_sql.bind_array
(cur_hdl, ':dname',
deptname_array);
-- execute cursor
rows_processed :=
dbms_sql.execute(cur_hdl);
-- get RETURNING column into OUT bind
array
dbms_sql.variable_value
(cur_hdl, ':dname',
deptname_array);

www.saturninfotech.com
PL SQL Document 20

Native Dynamic SQL DML Returning


DBMS_SQL DML Returning Operation Operation

dbms_sql.close_cursor(cur_hdl);
END;
/

Using Dynamic SQL in Languages Other Than PL/SQL


Although this chapter discusses PL/SQL support for dynamic SQL, you can call dynamic SQL
from other languages:

 If you use C/C++, you can call dynamic SQL with the Oracle Call Interface (OCI), or you can use
the Pro*C/C++ precompiler to add dynamic SQL extensions to your C code.
 If you use COBOL, you can use the Pro*COBOL precompiler to add dynamic SQL extensions to
your COBOL code.
 If you use Java, you can develop applications that use dynamic SQL with JDBC.

If you have an application that uses OCI, Pro*C/C++, or Pro*COBOL to execute dynamic SQL,
you should consider switching to native dynamic SQL inside PL/SQL stored procedures and
functions. The network round-trips required to perform dynamic SQL operations from client-side
applications might hurt performance. Stored procedures can reside on the server, eliminating the
network overhead. You can call the PL/SQL stored procedures and stored functions from the
OCI, Pro*C/C++, or Pro*COBOL application.

See Also:

For information about calling Oracle stored procedures and stored functions
from various languages, refer to:

 Oracle Call Interface Programmer's Guide


 Pro*C/C++ Precompiler Programmer's Guide

www.saturninfotech.com
PL SQL Document 21

 Pro*COBOL Precompiler Programmer's Guide


 Oracle9i Java Stored Procedures Developer's Guide

Using PL/SQL Records in SQL INSERT and UPDATE


Statements
Although you can enumerate each field of a PL/SQL record when inserting or updating rows in a
table, the resulting code is not especially readable or maintainable. Instead, you can use PL/SQL
records directly in these statements. The most convenient technique is to declare the record using
a %ROWTYPE attribute, so that it has exactly the same fields as the SQL table.

DECLARE
emp_rec emp%ROWTYPE;
BEGIN
emp_rec.eno := 1500;
emp_rec.ename := 'Steven Hill';
emp_rec.sal := '40000';
-- A %ROWTYPE value can fill in all the row fields.
INSERT INTO emp VALUES emp_rec;

-- The fields of a %ROWTYPE can completely replace the table columns.


UPDATE emp SET ROW = emp_rec WHERE eno = 100;
END;
/

Although this technique helps to integrate PL/SQL variables and types more closely with SQL
DML statements, you cannot use PL/SQL records as bind variables in dynamic SQL statements.

www.saturninfotech.com
PL SQL Document 22

Send mail from PL/SQL


Sample scripts for sending E-mail messages from PL/SQL:

Starting from Oracle 8i release 8.1.6, one can send E-mail messages directly from PL/SQL using
either the UTL_TCP or UTL_SMTP packages. However, Oracle introduced an improved
package for sending E-mail in Oracle 10g - called UTL_MAIL - that should be used instead of
UTL_SMTP.

All examples below require a working SMTP mail server. Customize these scripts to point the IP
Address of your SMTP server. The SMTP port is usually 25. Configuring an SMTP server is not
an Oracle function and thus out of scope for this site.

Send mail with UTL_MAIL


For security reasons, UTL_MAIL is not enabled by default. You must install it by connecting to
SYS, then executing the utlmail.sql and prvtmail.plb scripts in the
$ORACLE_HOME/rdbms/admin directory. In addition, you must configure an initialization
parameter, SMTP_OUT_SERVER, to point to an outgoing SMTP server (unlike UTL_SMTP,
this is not specified in the function arguments and must be pre-defined).

IMPORTANT: The scripts do not seem to grant the needed EXECUTE permission to PUBLIC.
Thus, after running the scripts, while still logged in as SYS you should run the following line:

grant execute on UTL_MAIL to public;

Example:

BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''127.0.0.1''';
UTL_MAIL.send(sender => 'me@address.com',
recipients => 'you@address.com',
subject => 'Test Mail',
message => 'Hello World',
mime_type => 'text; charset=us-ascii');
END;
/

Send mail with UTL_SMTP


Send mail without attachments using the UTL_SMTP package:

DECLARE
v_From VARCHAR2(80) := 'oracle@mycompany.com';
v_Recipient VARCHAR2(80) := 'test@mycompany.com';
v_Subject VARCHAR2(80) := 'test subject';
v_Mail_Host VARCHAR2(30) := 'mail.mycompany.com';

www.saturninfotech.com
PL SQL Document 23

v_Mail_Conn utl_smtp.Connection;
crlf VARCHAR2(2)  := chr(13)||chr(10);
BEGIN
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, 25);
utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);
utl_smtp.Mail(v_Mail_Conn, v_From);
utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);
utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || crlf ||
'From: ' || v_From || crlf ||
'Subject: '|| v_Subject || crlf ||
'To: ' || v_Recipient || crlf ||
crlf ||
'some message text'|| crlf || -- Message body
'more message text'|| crlf
);
utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail: '||sqlerrm);
END;
/

Send mail with UTL_SMTP - with attachments


Send mail with attachments using the UTL_SMTP package:

DECLARE
v_From VARCHAR2(80) := 'oracle@mycompany.com';
v_Recipient VARCHAR2(80) := 'test@mycompany.com';
v_Subject VARCHAR2(80) := 'test subject';
v_Mail_Host VARCHAR2(30) := 'mail.mycompany.com';
v_Mail_Conn utl_smtp.Connection;
crlf VARCHAR2(2)  := chr(13)||chr(10);
BEGIN
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, 25);

utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);

utl_smtp.Mail(v_Mail_Conn, v_From);

utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);

utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || crlf ||
'From: ' || v_From || crlf ||
'Subject: '|| v_Subject || crlf ||
'To: ' || v_Recipient || crlf ||

'MIME-Version: 1.0'|| crlf || -- Use MIME mail standard


'Content-Type: multipart/mixed;'|| crlf ||
' boundary="-----SECBOUND"'|| crlf ||
crlf ||

www.saturninfotech.com
PL SQL Document 24

'-------SECBOUND'|| crlf ||
'Content-Type: text/plain;'|| crlf ||
'Content-Transfer_Encoding: 7bit'|| crlf ||
crlf ||
'some message text'|| crlf || -- Message body
'more message text'|| crlf ||
crlf ||

'-------SECBOUND'|| crlf ||
'Content-Type: text/plain;'|| crlf ||
' name="excel.csv"'|| crlf ||
'Content-Transfer_Encoding: 8bit'|| crlf ||
'Content-Disposition: attachment;'|| crlf ||
' filename="excel.csv"'|| crlf ||
crlf ||
'CSV,file,attachement'|| crlf || -- Content of attachment
crlf ||

'-------SECBOUND--' -- End MIME mail


);

utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail: '||sqlerrm);
END;
/

www.saturninfotech.com
PL SQL Document 25

Send mail with UTL_TCP


Send mail using the UTL_TCP package:

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'oracle',
msg_to varchar2,
msg_subject varchar2 := 'E-Mail message from your database',
msg_text varchar2 := )
IS
c utl_tcp.connection;
rc integer;
BEGIN
c := utl_tcp.open_connection('127.0.0.1', 25); -- open the SMTP port
25 on local machine
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'HELO localhost');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); -- Start message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, msg_text);
rc := utl_tcp.write_line(c, '.'); -- End of message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); -- Close the connection
EXCEPTION
when others then
raise_application_error(
-20000, 'Unable to send e-mail message from pl/sql because of: '||
sqlerrm);
END;
/
show errors
-- Test it:
set serveroutput on

exec send_mail(msg_to =>'you@yourdomain.com');

exec send_mail(msg_to =>'you@yourdomain.com', -


msg_text=>'Look Ma, I can send mail from plsql' -
);

www.saturninfotech.com
PL SQL Document 26

Send mail with UTL_TCP - with attachments


Send e-mail messages with attachments from PL/SQL using UTL_TCP:

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX SENDING
THE EMAIL
msg_to varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX
RECIEVING THE EMAIL
msg_subject varchar2 := 'Output file TEST1', ----- EMAIL SUBJECT
msg_text varchar2 := 'THIS IS THE TEXT OF THE EMAIL MESSAGE.',
v_output1 varchar2 := 'THIS IS THE TEXT OF THE ATTACHMENT FILE. THIS TEXT
WILL BE IN A TEXT FILE ATTACHED TO THE EMAIL.')
IS
c utl_tcp.connection;
rc integer;
crlf VARCHAR2(2):= CHR(13)||CHR(10);
mesg VARCHAR2(32767);
BEGIN
c := utl_tcp.open_connection('196.35.140.18', 25); ----- OPEN SMTP
PORT CONNECTION
rc := utl_tcp.write_line(c, 'HELO 196.35.140.18'); ----- PERFORMS
HANDSHAKING WITH SMTP SERVER
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'EHLO 196.35.140.18'); ----- PERFORMS
HANDSHAKING, INCLUDING EXTRA INFORMATION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from); ----- MAIL BOX
SENDING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to); ----- MAIL BOX
RECIEVING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); ----- EMAIL MESSAGE
BODY START
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Date: '||TO_CHAR( SYSDATE, 'dd Mon yy
hh24:mi:ss' ));
rc := utl_tcp.write_line(c, 'From: '||msg_from||' <'||msg_from||'>');
rc := utl_tcp.write_line(c, 'MIME-Version: 1.0');
rc := utl_tcp.write_line(c, 'To: '||msg_to||' <'||msg_to||'>');
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, 'Content-Type: multipart/mixed;'); -----
INDICATES THAT THE BODY CONSISTS OF MORE THAN ONE PART
rc := utl_tcp.write_line(c, ' boundary="-----SECBOUND"'); -----
SEPERATOR USED TO SEPERATE THE BODY PARTS
rc := utl_tcp.write_line(c, ); ----- DO NOT
REMOVE THIS BLANK LINE - PART OF MIME STANDARD
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain'); ----- 1ST
BODY PART. EMAIL TEXT MESSAGE
rc := utl_tcp.write_line(c, 'Content-Transfer-Encoding: 7bit');

www.saturninfotech.com
PL SQL Document 27

rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, msg_text); ----- TEXT
OF EMAIL MESSAGE
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain;'); ----- 2ND
BODY PART.
rc := utl_tcp.write_line(c, ' name="Test.txt"');
rc := utl_tcp.write_line(c, 'Content-Transfer_Encoding: 8bit');
rc := utl_tcp.write_line(c, 'Content-Disposition: attachment;'); -----
INDICATES THAT THIS IS AN ATTACHMENT
rc := utl_tcp.write_line(c, ' filename="Test.txt"'); -----
SUGGESTED FILE NAME FOR ATTACHMENT
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, v_output1);
rc := utl_tcp.write_line(c, '-------SECBOUND--');
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, '.'); ----- EMAIL MESSAGE
BODY END
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT'); ----- ENDS EMAIL
TRANSACTION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); ----- CLOSE SMTP PORT
CONNECTION
EXCEPTION
when others then
raise_application_error(-20000, SQLERRM);
END;
/

Send multiple E-mails with or without attachment Package.


Send E-mail messages with or without attachment from PL/SQL using UTL_TCP:

Use CLOB for no limit on attachment text size. Multiple E-mail addresses must be separated by
commas. Do not add comma for single address or at the end of multiple address string; even if
you do, it still works. These procedures add a comma at the end of the E-mail string.

CSV Files require CHR(10) at the end of each line.


TEXT Files require CHR(13)||CHR(10) at the end of each line.
create or replace Package SEND_EMAIL_FROM_PL_SQL IS
procedure send_email_with_attachment
( sender_email_address varchar2, -- Must be single E-mail
address
recipient_email_addresses varchar2,
email_subject varchar2,
email_body_text varchar2,
email_attachment_text clob, -- No restriction on size of
text
email_attachment_file_name varchar2
);
procedure send_email_without_attachment

www.saturninfotech.com
PL SQL Document 28

( sender_email_address varchar2, -- Must be single E-mail


address
recipient_email_addresses varchar2,
email_subject varchar2,
email_body_text varchar2
);
END; ---- of Specification for Package SEND_EMAIL_FROM_PL_SQL
/
show errors
create or replace Package Body SEND_EMAIL_FROM_PL_SQL IS
procedure send_email_with_attachment
( sender_email_address varchar2, -- Must be single E-mail
address
recipient_email_addresses varchar2,
email_subject varchar2,
email_body_text varchar2,
email_attachment_text clob, -- No restriction on size of
text
email_attachment_file_name varchar2
)
IS
c utl_tcp.connection;
rc binary_integer;
v_next_column binary_integer;
v_recipient_email_length binary_integer;
v_recipient_email_addresses varchar2 (500);
v_single_recipient_email_addr varchar2 (100);
-- Variables to divide the CLOB into 32000 character segments to write to
the file
j binary_integer;
current_position binary_integer :=1;
email_attachment_text_segment clob :=;
BEGIN
v_recipient_email_addresses := recipient_email_addresses;
v_recipient_email_length  := length(v_recipient_email_addresses);
v_recipient_email_addresses := v_recipient_email_addresses||','; -- Add
comma for the last asddress
v_next_column  := 1;
if instr(v_recipient_email_addresses, ',') = 0 then
-- Single E-mail address
v_single_recipient_email_addr := v_recipient_email_addresses;
v_recipient_email_length := 1;
end if;
-- Open the SMTP port 25 on UWA mailhost local machine
c := utl_tcp.open_connection('mailhost.yourcompany.com', 25);
-- Performs handshaking with the SMTP Server. HELO is the correct
spelling.
-- DO NOT CHANGE. Causes problems.
rc := utl_tcp.write_line(c, 'HELO localhost');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
while v_next_column <= v_recipient_email_length
loop
-- Process Multiple E-mail addresses in the loop OR single E-mail address
once.
v_single_recipient_email_addr  :=
substr( v_recipient_email_addresses, v_next_column,

www.saturninfotech.com
PL SQL Document 29

instr(v_recipient_email_addresses, ',',v_next_column)
-v_next_column
);
v_next_column := instr(v_recipient_email_addresses,',',v_next_column)+1;
rc := utl_tcp.write_line(c, 'MAIL FROM: '||sender_email_address); -- MAIL
BOX SENDING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||v_single_recipient_email_addr);
-- MAIL BOX RECIEVING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); --
START EMAIL MESSAGE BODY
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Date: '||TO_CHAR( SYSDATE, 'dd Mon yy
hh24:mi:ss' ));
rc := utl_tcp.write_line(c, 'From: '||sender_email_address);
rc := utl_tcp.write_line(c, 'MIME-Version: 1.0');
rc := utl_tcp.write_line(c, 'To: '||v_single_recipient_email_addr);
rc := utl_tcp.write_line(c, 'Subject: '||email_subject);
rc := utl_tcp.write_line(c, 'Content-Type: multipart/mixed;'); --
INDICATES THAT THE BODY CONSISTS OF MORE THAN ONE PART
rc := utl_tcp.write_line(c, ' boundary="-----SECBOUND"'); --
SEPERATOR USED TO SEPERATE THE BODY PARTS
rc := utl_tcp.write_line(c, ); -- DO NOT
REMOVE THIS BLANK LINE - MIME STANDARD
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain'); -- 1ST
BODY PART. EMAIL TEXT MESSAGE
rc := utl_tcp.write_line(c, 'Content-Transfer-Encoding: 7bit');
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, email_body_text); -- TEXT
OF EMAIL MESSAGE
rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain;'); -- 2ND
BODY PART.
rc := utl_tcp.write_line(c, ' name="' ||email_attachment_file_name ||
'"');
rc := utl_tcp.write_line(c, 'Content-Transfer_Encoding: 8bit');
rc := utl_tcp.write_line(c, 'Content-Disposition: attachment;');--
INDICATES THAT THIS IS AN ATTACHMENT
rc := utl_tcp.write_line(c, );
-- Divide the CLOB into 32000 character segments to write to the file.
-- Even though SUBSTR looks for 32000 characters, UTL_TCP.WRITE_LINE does
not blank pad it.
current_position := 1;
j  := (round(length(email_attachment_text))/32000) + 1;
dbms_output.put_line('j= '||j||' length= '||
length(email_attachment_text));
for i in 1..j
loop
email_attachment_text_segment := substr(email_attachment_text,
current_position, 32000);
rc := utl_tcp.write_line(c, email_attachment_text_segment);
current_position := current_position + 32000;
end loop;

www.saturninfotech.com
PL SQL Document 30

rc := utl_tcp.write_line(c, '-------SECBOUND--');


rc := utl_tcp.write_line(c, );
rc := utl_tcp.write_line(c, '.'); -- END
EMAIL MESSAGE BODY
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
dbms_output.put_line('***** Single E-mail= '||
v_single_recipient_email_addr||' '||v_next_column);
end loop;
rc := utl_tcp.write_line(c, 'QUIT'); -- ENDS
EMAIL TRANSACTION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); -- CLOSE
SMTP PORT CONNECTION
END; -- of Procedure SEND_EMAIL_WITH_ATTACHMENT
procedure send_email_without_attachment
( sender_email_address varchar2, -- Must be single E-mail
address
recipient_email_addresses varchar2,
email_subject varchar2,
email_body_text varchar2
)
IS
c utl_tcp.connection;
rc binary_integer;
v_next_column binary_integer;
v_recipient_email_length binary_integer;
v_recipient_email_addresses varchar2 (500);
v_single_recipient_email_addr varchar2 (100);
BEGIN
v_recipient_email_addresses := recipient_email_addresses;
v_recipient_email_length  := length(v_recipient_email_addresses);
v_recipient_email_addresses := v_recipient_email_addresses||','; --Add
comma for the last asddress
v_next_column  := 1;
if instr(v_recipient_email_addresses, ',') = 0 then
-- Single E-mail address
v_single_recipient_email_addr := v_recipient_email_addresses;
v_recipient_email_length := 1;
end if;
-- open the SMTP port 25 on UWA mailhost local machine
c := utl_tcp.open_connection('mailhost.yourcompany.com', 25);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
-- Performs handshaking with the SMTP Server. HELO is the correct
spelling.
-- DO NOT CHANGE. Causes problems.
rc := utl_tcp.write_line(c, 'HELO localhost');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
while v_next_column <= v_recipient_email_length
loop
-- Process Multiple E-mail addresses in the loop or single E-mail address
once.
v_single_recipient_email_addr  :=
substr( v_recipient_email_addresses, v_next_column,
instr(v_recipient_email_addresses, ',',v_next_column)
-v_next_column );
v_next_column := instr(v_recipient_email_addresses,',',v_next_column)+1;

www.saturninfotech.com
PL SQL Document 31

rc := utl_tcp.write_line(c, 'MAIL FROM: '||sender_email_address);


dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||v_single_recipient_email_addr);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); -- Start E-mail
message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Date: '||TO_CHAR( SYSDATE, 'dd Mon yy
hh24:mi:ss' ));
rc := utl_tcp.write_line(c, 'From: '||sender_email_address);
rc := utl_tcp.write_line(c, 'MIME-Version: 1.0');
rc := utl_tcp.write_line(c, 'To: '||v_single_recipient_email_addr);
rc := utl_tcp.write_line(c, 'Subject: '||email_subject);
rc := utl_tcp.write_line(c, email_body_text);
rc := utl_tcp.write_line(c, '.'); -- End of E-mail
message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
dbms_output.put_line('***** Single E-mail= '||
v_single_recipient_email_addr||' '||v_next_column);
end loop;
rc := utl_tcp.write_line(c, 'QUIT'); -- End E-mail
transaction
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); -- Close the
connection
END; -- of Procedure SEND_EMAIL_WITHOUT_ATTACHMENT

END; -- of Specification for Package SEND_EMAIL_FROM_PL_SQL


/
show errors
create public synonym SEND_EMAIL_FROM_PL_SQL for SEND_EMAIL_FROM_PL_SQL;

www.saturninfotech.com
PL SQL Document 32

UTL_SMTP
The UTL_SMTP package is designed for sending electronic mails (emails) over Simple Mail
Transfer Protocol (SMTP) as specified by RFC821.

See Also:

How to use the SMTP package to send email in Oracle Database Application Developer's Guide -
Fundamentals

This chapter contains the following topics:

 Using UTL_SMTP

o Overview

o Types

o Reply Codes

o Exceptions

o Rules and Limits

o Examples

 Summary of UTL_SMTP Subprograms

Using UTL_SMTP
 Overview

 Types

 Reply Codes

 Exceptions

 Rules and Limits

 Examples

www.saturninfotech.com
PL SQL Document 33

Overview

The protocol consists of a set of commands for an email client to dispatch emails to a SMTP
server. The UTL_SMTP package provides interfaces to the SMTP commands. For many of the
commands, the package provides both a procedural and a functional interface. The functional
form returns the reply from the server for processing by the client. The procedural form checks
the reply and will raise an exception if the reply indicates a transient (400-range reply code) or
permanent error (500-range reply code). Otherwise, it discards the reply.

Note that the original SMTP protocol communicates using 7-bit ASCII. Using UTL_SMTP, all text
data (in other words, those in VARCHAR2) will be converted to US7ASCII before it is sent over
the wire to the server. Some implementations of SMTP servers that support SMTP extension
8BITMIME [RFC1652] support full 8-bit communication between client and server. The body of
the DATA command may be transferred in full 8 bits, but the rest of the SMTP command and
response should be in 7 bits. When the target SMTP server supports 8BITMIME extension, users
of multibyte databases may convert their non-US7ASCII, multibyte VARCHAR2 data to RAW
and use the WRITE_RAW_DATA subprogram to send multibyte data using 8-bit MIME
encoding.

UTL_SMTP provides for SMTP communication as specified in RFC821, but does not provide an
API to format the content of the message according to RFC 822 (for example, setting the subject
of an electronic mail).You must format the message appropriately. In addition, UTL_SMTP does
not have the functionality to implement an SMTP server for an email clients to send emails using
SMTP.

Note :

RFC documents are "Request for Comments" documents that describe proposed standards for public
review on the Internet. For the actual RFC documents, please refer to:

http://www.ietf.org/rfc/

Types

 CONNECTION Record Type

 REPLY, REPLIES Record Types

CONNECTION Record Type

This is a PL/SQL record type used to represent an SMTP connection.

www.saturninfotech.com
PL SQL Document 34

Syntax

TYPE connection IS RECORD (


host VARCHAR2(255), -- remote host name
port PLS_INTEGER, -- remote port number
tx_timeout PLS_INTEGER, -- Transfer time out (in seconds)
private_tcp_con utl_tcp.connection, -- private, for implementation use
private_state PLS_INTEGER -- private, for implementation use
);

Fields

Table 178-1 CONNECTION Record Type Fields

Field Description

host The name of the remote host when connection is established. NULL when no
connection is established.
port The port number of the remote SMTP server connected. NULL when no
connection is established.
tx_timeout The time in seconds that the UTL_SMTP package waits before giving up in a
read or write operation in this connection. In read operations, this package
gives up if no data is available for reading immediately. In write operations,
this package gives up if the output buffer is full and no data is to be sent into
the network without being blocked. 0 indicates not to wait at all. NULL
indicates to wait forever.
private_tcp_con Private, for implementation use only. You should not modify this field.
private_state Private, for implementation use only. You should not modify this field.

Usage Notes

The read-only fields in a connection record are used to return information about the SMTP
connection after the connection is successfully made with open_connection(). Changing the
values of these fields has no effect on the connection. The fields private_xxx are for
implementation use only. You should not modify these fields.

REPLY, REPLIES Record Types

These are PL/SQL record types used to represent an SMTP reply line. Each SMTP reply line
consists of a reply code followed by a text message. While a single reply line is expected for
most SMTP commands, some SMTP commands expect multiple reply lines. For those situations,
a PL/SQL table of reply records is used to represent multiple reply lines.

www.saturninfotech.com
PL SQL Document 35

Syntax

TYPE reply IS RECORD (


code PLS_INTEGER, -- 3-digit reply code
text VARCHAR2(508) -- text message
);
TYPE replies IS TABLE OF reply INDEX BY BINARY_INTEGER; -- multiple reply
lines

Fields

Table 178-2 REPLY, REPLIES Record Type Fields

Field Description

code The 3-digit reply code.


text The text message of the reply.

Reply Codes

The following is a list of the SMTP reply codes.

Table 178-3 SMTP Reply Codes

Reply
Code Meaning

211 System status, or system help reply


214 Help message [Information on how to use the receiver or the meaning of a particular
non-standard command; this reply is useful only to the human user]
220 <domain> Service ready
221 <domain> Service closing transmission channel
250 Requested mail action okay, completed
251 User not local; will forward to <forward-path>
252 OK, pending messages for node <node> started. Cannot VRFY user (for example, info
is not local), but will take message for this user and attempt delivery.
253 OK, <messages> pending messages for node <node> started
354 Start mail input; end with <CRLF>.<CRLF>

www.saturninfotech.com
PL SQL Document 36

Reply
Code Meaning

355 Octet-offset is the transaction offset


421 <domain> Service not available, closing transmission channel (This may be a reply to
any command if the service knows it must shut down.)
450 Requested mail action not taken: mailbox unavailable [for example, mailbox busy]
451 Requested action terminated: local error in processing
452 Requested action not taken: insufficient system storage
453 You have no mail.
454 TLS not available due to temporary reason. Encryption required for requested
authentication mechanism.
458 Unable to queue messages for node <node>
459 Node <node> not allowed: reason
500 Syntax error, command unrecognized (This may include errors such as command line
too long.)
501 Syntax error in parameters or arguments
502 Command not implemented
503 Bad sequence of commands
504 Command parameter not implemented
521 <Machine> does not accept mail.
530 Must issue a STARTTLS command first. Encryption required for requested
authentication mechanism.
534 Authentication mechanism is too weak.
538 Encryption required for requested authentication mechanism.
550 Requested action not taken: mailbox unavailable [for , mailbox not found, no access]
551 User not local; please try <forward-path>
552 Requested mail action terminated: exceeded storage allocation
553 Requested action not taken: mailbox name not allowed [for example, mailbox syntax
incorrect]
554 Transaction failed

www.saturninfotech.com
PL SQL Document 37

Exceptions

The table lists the exceptions that can be raised by the interface of the UTL_SMTP package. The
network error is transferred to a reply code of 421- service not available.

Table 178-4 UTL_SMTP Exceptions

INVALID_OPERATION Raised when an invalid operation is made. In other words, calling API
other than write_data(), write_raw_data() or close_data() after
open_data() is called, or calling write_data(), write_raw_data() or
close_data() without first calling open_data().
TRANSIENT_ERROR Raised when receiving a reply code in 400 range.
PERMANENT_ERROR Raised when receiving a reply code in 500 range.

Rules and Limits

No limitation or range-checking is imposed by the API. However, you should be aware of the
following size limitations on various elements of SMTP. Sending data that exceed these limits
may result in errors returned by the server.

Table 178-5 SMTP Size Limitation

Element Size Limitation

user The maximum total length of a user name is 64 characters.


domain The maximum total length of a domain name or number is 64 characters.
path The maximum total length of a reverse-path or forward-path is 256 characters
(including the punctuation and element separators).
command line The maximum total length of a command line including the command word
and the <CRLF> is 512 characters.
reply line The maximum total length of a reply line including the reply code and the
<CRLF> is 512 characters.
text line The maximum total length of a text line including the <CRLF> is 1000
characters (but not counting the leading dot duplicated for transparency).
recipients The maximum total number of recipients that must be buffered is 100
buffer
recipients.

www.saturninfotech.com
PL SQL Document 38

Examples

The following example illustrates how UTL_SMTP is used by an application to send e-mail. The
application connects to an SMTP server at port 25 and sends a simple text message.

DECLARE
c UTL_SMTP.CONNECTION;

PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS


BEGIN
UTL_SMTP.WRITE_DATA(c, name || ': ' || header || UTL_TCP.CRLF);
END;

BEGIN
c := UTL_SMTP.OPEN_CONNECTION('smtp-server.acme.com');
UTL_SMTP.HELO(c, 'foo.com');
UTL_SMTP.MAIL(c, 'sender@foo.com');
UTL_SMTP.RCPT(c, 'recipient@foo.com');
UTL_SMTP.OPEN_DATA(c);
send_header('From', '"Sender" <sender@foo.com>');
send_header('To', '"Recipient" <recipient@foo.com>');
send_header('Subject', 'Hello');
UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF || 'Hello, world!');
UTL_SMTP.CLOSE_DATA(c);
UTL_SMTP.QUIT(c);
EXCEPTION
WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
BEGIN
UTL_SMTP.QUIT(c);
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
NULL; -- When the SMTP server is down or unavailable, we don't have
-- a connection to the server. The QUIT call will raise an
-- exception that we can ignore.
END;
raise_application_error(-20000,
'Failed to send mail due to the following error: ' || sqlerrm);
END;

Summary of UTL_SMTP Subprograms


UTL_SMTP Package Subprograms

Subprogram Description

CLOSE_DATA Function and Closes the data session


Procedure

www.saturninfotech.com
PL SQL Document 39

Subprogram Description

COMMAND Function and Performs a generic SMTP command


Procedure
COMMAND_REPLIES Performs initial handshaking with SMTP server after
Function connecting
DATA Function and Procedure Performs initial handshaking with SMTP server after
connecting, with extended information returned
EHLO Function and Procedure Performs initial handshaking with SMTP server after
connecting, with extended information returned
HELO Function and Procedure Performs initial handshaking with SMTP server after
connecting
HELP Function Sends HELP command
MAIL Function and Procedure Initiates a mail transaction with the server, the destination is a
mailbox
NOOP Function and Procedure The null command
OPEN_CONNECTION Opens a connection to an SMTP server
Functions
OPEN_DATA Function and Sends the DATA command
Procedure
QUIT Function and Procedure Terminates an SMTP session and disconnects from the server
RCPT Function Specifies the recipient of an e-mail message
RSET Function and Procedure Terminates the current mail transaction
VRFY Function Verifies the validity of a destination e-mail address
WRITE_DATA Procedure Writes a portion of the e-mail message
WRITE_RAW_DATA Writes a portion of the e-mail message with RAW data
Procedure

CLOSE_DATA Function and Procedure

The CLOSE_DATA call ends the e-mail message by sending the sequence <CR><LF>.<CR><LF> (a
single period at the beginning of a line).

Syntax

www.saturninfotech.com
PL SQL Document 40

UTL_SMTP.CLOSE_DATA (
c IN OUT NOCOPY connection)
RETURN reply;

UTL_SMTP.CLOSE_DATA (
c IN OUT NOCOPY connection);

Parameters

Table 178-7 CLOSE_DATA Function and Procedure Parameters

Parameter Description

c The SMTP connection.

Return Values

Table 178-8 CLOSE_DATA Function and Procedure Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

The calls to OPEN_DATA, WRITE_DATA, WRITE_RAW_DATA and CLOSE_DATA must be made in the
right order. A program calls OPEN_DATA to send the DATA command to the SMTP server. After
that, it can call WRITE_DATA or WRITE_RAW_DATA repeatedly to send the actual data. The data is
terminated by calling CLOSE_DATA. After OPEN_DATA is called, the only subprograms that can be
called are WRITE_DATA, WRITE_RAW_DATA, or CLOSE_DATA. A call to other APIs will result in an
INVALID_OPERATION exception being raised.

CLOSE_DATA should be called only after OPEN_CONNECTION, HELO or EHLO, MAIL, and RCPT have
been called. The connection to the SMTP server must be open and a mail transaction must be
active when this routine is called.

Note that there is no function form of WRITE_DATA because the SMTP server does not respond
until the data-terminator is sent during the call to CLOSE_DATA.

www.saturninfotech.com
PL SQL Document 41

COMMAND Function and Procedure

This function/procedure performs a generic SMTP command.

Syntax

UTL_SMTP.COMMAND (
c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL)
RETURN reply;

UTL_SMTP.COMMAND (
c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL);

Parameters

Table 178-9 COMMAND Function and Procedure Parameters

Parameter Description

c The SMTP connection.


cmd The SMTP command to send to the server.
arg The optional argument to the SMTP argument. A space will be inserted between cmd
and arg.

Return Values

Table 178-10 COMMAND Function and Procedure Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

This function is used to invoke generic SMTP commands. Use COMMAND if only a single reply
line is expected. Use COMMAND_REPLIES if multiple reply lines are expected.

www.saturninfotech.com
PL SQL Document 42

For COMMAND, if multiple reply lines are returned from the SMTP server, it returns the last reply
line only.

COMMAND_REPLIES Function

This functions performs a generic SMTP command.

Syntax

UTL_SMTP.COMMAND_REPLIES (
c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL)
RETURN replies;

Parameters

Table 178-11 COMMAND_REPLIES Function Parameters

Parameter Description

c The SMTP connection.


cmd The SMTP command to send to the server.
arg The optional argument to the SMTP argument. A space will be inserted between cmd
and arg.

Return Values

Table 178-12 COMMAND_REPLIES Function Return Values

Return Value Description

replies Reply of the command (see REPLY, REPLIES Record Types).

Usage Notes

This function is used to invoke generic SMTP commands. Use COMMAND if only a single reply
line is expected. Use COMMAND_REPLIES if multiple reply lines are expected.

www.saturninfotech.com
PL SQL Document 43

For COMMAND, if multiple reply lines are returned from the SMTP server, it returns the last reply
line only.

DATA Function and Procedure

This function/procedure specifies the body of an e-mail message.

Syntax

UTL_SMTP.DATA (
c IN OUT NOCOPY connection
body IN VARCHAR2 CHARACTER SET ANY_CS)
RETURN reply;

UTL_SMTP.DATA (
c IN OUT NOCOPY connection
body IN VARCHAR2 CHARACTER SET ANY_CS);

Parameters

Table 178-13 DATA Function and Procedure Parameters

Parameter Description

c The SMTP Connection.


body The text of the message to be sent, including headers, in [RFC822] format.

Return Values

Table 178-14 DATA Function and Procedure Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

www.saturninfotech.com
PL SQL Document 44

The application must ensure that the contents of the body parameter conform to the
MIME(RFC822) specification. The DATA routine will terminate the message with a
<CR><LF>.<CR><LF> sequence (a single period at the beginning of a line), as required by
RFC821. It will also translate any sequence of <CR><LF>.<CR><LF> (single period) in body to
<CR><LF>..<CR><LF> (double period). This conversion provides the transparency as described
in Section 4.5.2 of RFC821.

The DATA call should be called only after OPEN_CONNECTION, HELO or EHLO, MAIL and RCPT have
been called. The connection to the SMTP server must be open, and a mail transaction must be
active when this routine is called.

The expected response from the server is a message beginning with status code 250. The 354
response received from the initial DATA command will not be returned to the caller.

EHLO Function and Procedure

This function/procedure performs initial handshaking with SMTP server after connecting, with
extended information returned.

Syntax

UTL_SMTP.EHLO (
c IN OUT NOCOPY connection,
domain IN)
RETURN replies;

UTL_SMTP.EHLO (
c IN OUT NOCOPY connection,
domain IN);

Parameters

Table 178-15 EHLO Function and Procedure Parameters

Parameter Description

c The SMTP connection.


domain The domain name of the local (sending) host. Used for identification purposes.

Return Values

Table 178-16 EHLO Function and Procedure Return Values

www.saturninfotech.com
PL SQL Document 45

Return Value Description

replies Reply of the command (see REPLY, REPLIES Record Types).

Usage Notes

The EHLO interface is identical to HELO except that it allows the server to return more descriptive
information about its configuration. [RFC1869] specifies the format of the information returned,
which the PL/SQL application can retrieve using the functional form of this call. For
compatibility with HELO, each line of text returned by the server begins with status code 250.

Related Functions

HELO

HELO Function and Procedure

This function/procedure performs initial handshaking with SMTP server after connecting.

Syntax

UTL_SMTP.HELO (
c IN OUT NOCOPY connection,
domain IN VARCHAR2)
RETURN reply;

UTL_SMTP.HELO (
c IN OUT NOCOPY connection,
domain IN VARCHAR2);

Parameters

Table 178-17 HELO Function and Procedure Parameters

Parameter Description

c The SMTP connection.


domain The domain name of the local (sending) host. Used for identification purposes.

Return Values

www.saturninfotech.com
PL SQL Document 46

Table 178-18 HELO Function and Procedure Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

RFC 821 specifies that the client must identify itself to the server after connecting. This routine
performs that identification. The connection must have been opened through a call to
OPEN_CONNECTION Functions before calling this routine.

The expected response from the server is a message beginning with status code 250.

Related Functions

EHLO

HELP Function

This function sends the HELP command.

Syntax

UTL_SMTP.HELP (
c IN OUT NOCOPY connection,
command IN VARCHAR2 DEFAULT NULL)
RETURN replies;

Parameters

Table 178-19 HELP Function Parameters

Parameter Description

c The SMTP connection.


command The command to get the help message.

www.saturninfotech.com
PL SQL Document 47

Return Values

Table 178-20 HELP Function Return Values

Return Value Description

replies Reply of the command (see REPLY, REPLIES Record Types).

MAIL Function and Procedure

This function/procedure initiates a mail transaction with the server. The destination is a mailbox.

Syntax

UTL_SMTP.MAIL (
c IN OUT NOCOPY connection,
sender IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL)
RETURN reply;

UTL_SMTP.MAIL (
c IN OUT NOCOPY connection,
sender IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL);

Parameters

Table 178-21 MAIL Function and Procedure Parameters

Parameter Description

c The SMTP connection.


sender The e-mail address of the user sending the message.
parameters The additional parameters to MAIL command as defined in Section 6 of
[RFC1869]. It should follow the format of "XXX=XXX (XXX=XXX ....)".

Return Values

Table 178-22 MAIL Function and Procedure Return Values

www.saturninfotech.com
PL SQL Document 48

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

This command does not send the message; it simply begins its preparation. It must be followed
by calls to RCPT and DATA to complete the transaction. The connection to the SMTP server must
be open and a HELO or EHLO command must have already been sent.

The expected response from the server is a message beginning with status code 250.

NOOP Function and Procedure

The null command.

Syntax

UTL_SMTP.NOOP (
c IN OUT NOCOPY connection)
RETURN reply;

UTL_SMTP.NOOP (
c IN OUT NOCOPY connection);

Parameter

Table 178-23 NOOP Function and Procedure Parameters

Parameter Description

c The SMTP connection.

Return Values

Table 178-24 NOOP Function and Procedure Return Values

www.saturninfotech.com
PL SQL Document 49

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

This command has no effect except to elicit a successful reply from the server. It can be issued at
any time after the connection to the server has been established with OPEN_CONNECTION. The
NOOP command can be used to verify that the server is still connected and is listening properly.

This command will always reply with a single line beginning with status code 250.

OPEN_CONNECTION Functions

These functions open a connection to an SMTP server.

Syntax

UTL_SMTP.OPEN_CONNECTION (
host IN VARCHAR2,
port IN PLS_INTEGER DEFAULT 25,
c OUT connection,
tx_timeout IN PLS_INTEGER DEFAULT NULL)
RETURN reply;

UTL_SMTP.OPEN_CONNECTION (
host IN VARCHAR2,
port IN PLS_INTEGER DEFAULT 25,
tx_timeout IN PLS_INTEGER DEFAULT NULL)
RETURN connection;

Parameters

Table 178-25 OPEN_CONNECTION Functions Parameters

Parameter Description

host The name of the SMTP server host


port The port number on which SMTP server is listening (usually 25).
c The SMTP connection.

www.saturninfotech.com
PL SQL Document 50

Parameter Description

tx_timeout The time in seconds that the UTL_SMTP package waits before giving up in a read or
write operation in this connection. In read operations, this package gives up if no
data is available for reading immediately. In write operations, this package gives
up if the output buffer is full and no data is to be sent into the network without
being blocked. 0 indicates not to wait at all. NULL indicates to wait forever.

Return Values

Table 178-26 OPEN_CONNECTION Functions Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

 The expected response from the server is a message beginning with status code 220.

 The version of OPEN_CONNECTION that returns UTL_SMTP.CONNECTION record checks the


reply code returned by an SMTP server when the connection is first established. It raises
an exception when the reply indicates an error. Otherwise, it discards the reply. If a user
is interested in examining the reply, he or she can invoke the version of
OPEN_CONNECTION that returns REPLY.

 A timeout on the WRITE operations feature is not supported in the current release of this
package.

OPEN_DATA Function and Procedure

OPEN_DATA sends the DATA command after which you can use WRITE_DATA and WRITE_RAW_DATA
to write a portion of the e-mail message.

Syntax

UTL_SMTP.OPEN_DATA (
c IN OUT NOCOPY connection)

www.saturninfotech.com
PL SQL Document 51

RETURN reply;

UTL_SMTP.OPEN_DATA (
c IN OUT NOCOPY connection);

Parameters

Table 178-27 OPEN_DATA Function and Procedure Parameters

Parameter Description

c The SMTP connection.


data The portion of the text of the message to be sent, including headers, in [RFC822]
format.

Return Values

Table 178-28 OPEN_DATA Function and Procedure Function Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

The calls to OPEN_DATA, WRITE_DATA, WRITE_RAW_DATA and CLOSE_DATA must be made in the
right order. A program calls OPEN_DATA to send the DATA command to the SMTP server. After
that, it can call WRITE_DATA or WRITE_RAW_DATA repeatedly to send the actual data. The data is
terminated by calling CLOSE_DATA. After OPEN_DATA is called, the only subprograms that can be
called are WRITE_DATA, WRITE_RAW_DATA, or CLOSE_DATA. A call to other APIs will result in an
INVALID_OPERATION exception being raised.

OPEN_DATA should be called only after OPEN_CONNECTION, HELO or EHLO, MAIL, and RCPT have
been called. The connection to the SMTP server must be open and a mail transaction must be
active when this routine is called.

QUIT Function and Procedure

www.saturninfotech.com
PL SQL Document 52

This function terminates an SMTP session and disconnects from the server.

Syntax

UTL_SMTP.QUIT (
c IN OUT NOCOPY connection)
RETURN reply;

UTL_SMTP.QUIT (
c IN OUT NOCOPY connection);

Parameter

Table 178-29 QUIT Function and Procedure Parameters

Parameter Description

c The SMTP connection.

Return Values

Table 178-30 QUIT Function and Procedure Function Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

The QUIT command informs the SMTP server of the client's intent to terminate the session. It
then closes the connection established by OPEN_CONNECTION which must have been called before
executing this command. If a mail transaction is in progress when QUIT is issued, it is abandoned
in the same manner as RSET.

The function form of this command returns a single line beginning with the status code 221 on
successful termination. In all cases, the connection to the SMTP server is closed. The fields
REMOTE_HOST and REMOTE_PORT of c are reset.

Related Functions

RSET

www.saturninfotech.com
PL SQL Document 53

RCPT Function

This function/procedure specifies the recipient of an e-mail message.

Syntax

UTL_SMTP.RCPT (
c IN OUT NOCOPY connection,
recipient IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL)
RETURN reply;

UTL_SMTP.RCPT (
c IN OUT NOCOPY connection,
recipient IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL);

Table 178-31 RCPT Function and Procedure Parameters

Parameter Description

c The SMTP connection.


recipient The e-mail address of the user to which the message is being sent.
parameters The additional parameters to RCPT command as defined in Section 6 of
[RFC1869]. It should follow the format of "XXX=XXX (XXX=XXX ....)".

Return Values

Table 178-32 RCPT Function and Procedure Function Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

To send a message to multiple recipients, call this routine multiple times. Each invocation
schedules delivery to a single e-mail address. The message transaction must have been begun by

www.saturninfotech.com
PL SQL Document 54

a prior call to MAIL, and the connection to the mail server must have been opened and initialized
by prior calls to OPEN_CONNECTION and HELO or EHLO respectively.

The expected response from the server is a message beginning with status code 250 or 251.

RSET Function and Procedure

This function terminates the current mail transaction.

Syntax

UTL_SMTP.RSET (
c IN OUT NOCOPY connection)
RETURN reply;

UTL_SMTP.RSET (
c IN OUT NOCOPY connection);

Parameters

Table 178-33 RSET Function and Procedure Parameters

Parameter Description

c The SMTP connection.

Return Values

Table 178-34 RSET Function and Procedure Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

This command allows the client to abandon a mail message it was in the process of composing.
No mail will be sent. The client can call RSET at any time after the connection to the SMTP

www.saturninfotech.com
PL SQL Document 55

server has been opened by means of OPEN_CONNECTION until DATA or OPEN_DATA is called. Once
the email data has been sent, it will be too late to prevent the email from being sent.

The server will always respond to RSET with a message beginning with status code 250.

Related Functions

QUIT

VRFY Function

This function verifies the validity of a destination e-mail address.

Syntax

UTL_SMTP.VRFY (
c IN OUT NOCOPY connection
recipient IN VARCHAR2)
RETURN reply;

Parameters

Table 178-35 VRFY Function Parameters

Parameter Description

c The SMTP connection.


recipient The e-mail address to be verified.

Return Values

Table 178-36 VRFY Function Return Values

Return
Value Description

reply Reply of the command (see REPLY, REPLIES Record Types). In cases where there
are multiple replies, the last reply will be returned.

Usage Notes

www.saturninfotech.com
PL SQL Document 56

The server attempts to resolve the destination address recipient. If successful, it returns the
recipient's full name and fully qualified mailbox path. The connection to the server must have
already been established by means of OPEN_CONNECTION and HELO or EHLO before making this
request.

Successful verification returns one or more lines beginning with status code 250 or 251.

WRITE_DATA Procedure

Use WRITE_DATA to write a portion of the e-mail message. A repeat call to WRITE_DATA appends
data to the e-mail message.

Syntax

UTL_SMTP.WRITE_DATA (
c IN OUT NOCOPY connection,
data IN VARCHAR2 CHARACTER SET ANY_CS);

Parameters

Table 178-37 WRITE_DATA Procedure Parameters

Parameter Description

c The SMTP connection.


data The portion of the text of the message to be sent, including headers, in [RFC822]
format.

Usage Notes

The calls to OPEN_DATA, WRITE_DATA, WRITE_RAW_DATA and CLOSE_DATA must be made in the
right order. A program calls OPEN_DATA to send the DATA command to the SMTP server. After
that, it can call WRITE_DATA or WRITE_RAW_DATA repeatedly to send the actual data. The data is
terminated by calling CLOSE_DATA. After OPEN_DATA is called, the only subprograms that can be
called are WRITE_DATA, WRITE_RAW_DATA, or CLOSE_DATA. A call to other APIs will result in an
INVALID_OPERATION exception being raised.

The application must ensure that the contents of the body parameter conform to the
MIME(RFC822) specification. The DATA routine will terminate the message with a
<CR><LF>.<CR><LF> sequence (a single period at the beginning of a line), as required by
RFC821. It will also translate any sequence of <CR><LF>.<CR><LF> (single period) in the body

www.saturninfotech.com
PL SQL Document 57

to <CR><LF>..<CR><LF> (double period). This conversion provides the transparency as


described in Section 4.5.2 of RFC821.

Notice that this conversion is not bullet-proof. Consider this code fragment:

UTL_SMTP.WRITE_DATA('some message.' || chr(13) || chr(10));


UTL_SMTP.WRITE_DATA('.' || chr(13) || chr(10));

Since the sequence <CR><LF>.<CR><LF> is split between two calls to WRITE_DATA, the
implementation of WRITE_DATA will not detect the presence of the data-terminator sequence, and
therefore, will not perform the translation. It will be the responsibility of the user to handle such
a situation, or it may result in premature termination of the message data.

WRITE_DATA should be called only after OPEN_CONNECTION, HELO or EHLO, MAIL, and RCPT have
been called. The connection to the SMTP server must be open and a mail transaction must be
active when this routine is called.

Note that there is no function form of WRITE_DATA because the SMTP server does not respond
until the data-terminator is sent during the call to CLOSE_DATA.

Text (VARCHAR2) data sent using WRITE_DATA is converted to US7ASCII before it is sent. If the
text contains multibyte characters, each multibyte character in the text that cannot be converted
to US7ASCII is replaced by a '?' character. If 8BITMIME extension is negotiated with the SMTP
server using the EHLO subprogram, multibyte VARCHAR2 data can be sent by first converting the
text to RAW using the UTL_RAW package, and then sending the RAW data using WRITE_RAW_DATA.

WRITE_RAW_DATA Procedure

Use WRITE_RAW_DATA to write a portion of the e-mail message. A repeat call to WRITE_RAW_DATA
appends data to the e-mail message.

Syntax

UTL_SMTP.WRITE_RAW_DATA (
c IN OUT NOCOPY connection
data IN RAW);

Parameters

Table 178-38 WRITE_RAW_DATA Procedure Parameters

www.saturninfotech.com
PL SQL Document 58

Parameter Description

c The SMTP connection.


data The portion of the text of the message to be sent, including headers, in [RFC822]
format.

Usage Notes

The calls to OPEN_DATA, WRITE_DATA, WRITE_RAW_DATA and CLOSE_DATA must be made in the
right order. A program calls OPEN_DATA to send the DATA command to the SMTP server. After
that, it can call WRITE_DATA or WRITE_RAW_DATA repeatedly to send the actual data. The data is
terminated by calling CLOSE_DATA. After OPEN_DATA is called, the only subprograms that can be
called are WRITE_DATA, WRITE_RAW_DATA, or CLOSE_DATA. A call to other APIs will result in an
INVALID_OPERATION exception being raised.

The application must ensure that the contents of the body parameter conform to the
MIME(RFC822) specification. The DATA routine will terminate the message with a
<CR><LF>.<CR><LF> sequence (a single period at the beginning of a line), as required by
RFC821. It will also translate any sequence of <CR><LF>.<CR><LF> (single period) in the body
to <CR><LF>..<CR><LF> (double period). This conversion provides the transparency as
described in Section 4.5.2 of RFC821.

Notice that this conversion is not bullet-proof. Consider this code fragment:

UTL_SMTP.WRITE_DATA('some message.' || chr(13) || chr(10));


UTL_SMTP.WRITE_DATA('.' || chr(13) || chr(10));

Since the sequence <CR><LF>.<CR><LF> is split between two calls to WRITE_DATA, the
implementation of WRITE_DATA will not detect the presence of the data-terminator sequence, and
therefore, will not perform the translation. It will be the responsibility of the user to handle such
a situation, or it may result in premature termination of the message data.

XXX_DATA should be called only after OPEN_CONNECTION, HELO or EHLO, MAIL, and RCPT have
been called. The connection to the SMTP server must be open and a mail transaction must be
active when this routine is called.

Note that there is no function form of WRITE_DATA because the SMTP server does not respond
until the data-terminator is sent during the call to CLOSE_DATA.

Text (VARCHAR2) data sent using WRITE_DATA is converted to US7ASCII before it is sent. If the
text contains multibyte characters, each multibyte character in the text that cannot be converted
to US7ASCII is replaced by a '?' character. If 8BITMIME extension is negotiated with the SMTP

www.saturninfotech.com
PL SQL Document 59

server using the EHLO subprogram, multibyte VARCHAR2 data can be sent by first converting the
text to RAW using the UTL_RAW package, and then sending the RAW data using WRITE_RAW_DATA.

UTL_TCP
With the UTL_TCP package and its procedures and functions, PL/SQL applications can
communicate with external TCP/IP-based servers using TCP/IP. Because many Internet
application protocols are based on TCP/IP, this package is useful to PL/SQL applications that
use Internet protocols and e-mail.

This chapter contains the following topics:

 Using UTL_TCP

o Overview

o Types

o Exceptions

o Rules and Limits

o Examples

 Summary of UTL_TCP Subprograms

Using UTL_TCP
 Overview

 Types

www.saturninfotech.com
PL SQL Document 60

 Exceptions

 Rules and Limits

 Examples

Overview

The UTL_TCP package provides TCP/IP client-side access functionality in PL/SQL.

Types

 CONNECTION Type

 CRLF

CONNECTION Type

This is a PL/SQL record type used to represent a TCP/IP connection.

Syntax

TYPE connection IS RECORD (


remote_host VARCHAR2(255),
remote_port PLS_INTEGER,
local_host VARCHAR2(255),
local_port PLS_INTEGER,
charset VARCHAR2(30),
newline VARCHAR2(2),
tx_timeout PLS_INTEGER,
private_sd PLS_INTEGER);

Fields

Table 179-1 Connection Record Type Fields

Field Description

remote_host The name of the remote host when connection is established. NULL when no
connection is established.
remote_port The port number of the remote host connected. NULL when no connection is

www.saturninfotech.com
PL SQL Document 61

Field Description

established.
local_host The name of the local host used to establish the connection. NULL when no
connection is established.
local_port The port number of the local host used to establish the connection. NULL when no
connection is established.
charset The on-the-wire character set. Since text messages in the database may be
encoded in a character set that is different from the one expected on the wire (that
is, the character set specified by the communication protocol, or the one
stipulated by the other end of the communication), text messages in the database
will be converted to and from the on-the-wire character set as they are sent and
received on the network.
newline The newline character sequence. This newline character sequence is appended to
the text line sent by WRITE_LINE API.
tx_timeout A time in seconds that the UTL_TCP package waits before giving up in a read or
write operation in this connection. In read operations, this package gives up if no
data is available for reading immediately. In write operations, this package gives
up if the output buffer is full and no data is to be sent in the network without
being blocked. Zero (0) indicates not to wait at all. NULL indicates to wait
forever.

Usage Notes

The fields in a connection record are used to return information about the connection, which is
often made using OPEN_CONNECTION. Changing the values of those fields has no effect on the
connection. The fields private_XXXX are for implementation use only. You should not modify
the values.

In the current release of the UTL_TCP package, the parameters local_host and local_port are
ignored when open_connection makes a TCP/IP connection. It does not attempt to use the
specified local host and port number when the connection is made. The local_host and
local_port fields will not be set in the connection record returned by the function.

Time out on write operations is not supported in the current release of the UTL_TCP package.

CRLF

The character sequence carriage-return line-feed. It is the newline sequence commonly used
many communication standards.

www.saturninfotech.com
PL SQL Document 62

Syntax

CRLF varchar2(10);

Usage Notes

This package variable defines the newline character sequence commonly used in many Internet
protocols. This is the default value of the newline character sequence for WRITE_LINE, specified
when a connection is opened. While such protocols use <CR><LF> to denote a new line, some
implementations may choose to use just line-feed to denote a new line. In such cases, users can
specify a different newline character sequence when a connection is opened.

This CRLF package variable is intended to be a constant that denotes the carriage- return line-
feed character sequence. Do not modify its value. Modification may result in errors in other
PL/SQL applications.

Exceptions

The exceptions raised by the TCP/IP package are listed in Table 179-2.

Table 179-2 TCP/IP Exceptions

Exception Description

BUFFER_TOO_SMALL Buffer is too small for input that requires look-ahead.


END_OF_INPUT Raised when no more data is available to read from the connection.
NETWORK_ERROR Generic network error.
BAD_ARGUMENT Bad argument passed in an API call (for example, a negative buffer
size).
TRANSFER_TIMEOUT No data is read and a read time out occurred.
PARTIAL_MULTIBYTE_CHA No complete character is read and a partial multibyte character is
R
found at the end of the input.

Rules and Limits

www.saturninfotech.com
PL SQL Document 63

The interface provided in the package only allows connections to be initiated by the PL/SQL
program. It does not allow the PL/SQL program to accept connections initiated outside the
program.

Examples

The following code example illustrates how the TCP/IP package can be used to retrieve a Web
page over HTTP. It connects to a Web server listening at port 80 (standard port for HTTP) and
requests the root document.

DECLARE
c utl_tcp.connection; -- TCP/IP connection to the Web server
ret_val pls_integer;
BEGIN
c := utl_tcp.open_connection(remote_host => 'www.acme.com',
remote_port => 80,
charset => 'US7ASCII'); -- open connection
ret_val := utl_tcp.write_line(c, 'GET / HTTP/1.0'); -- send HTTP request
ret_val := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE)); -- read result
END LOOP;
EXCEPTION
WHEN utl_tcp.end_of_input THEN
NULL; -- end of input
END;
utl_tcp.close_connection(c);
END;

The following code example illustrates how the TCP/IP package can be used by an application to
send e-mail (also known as email from PL/SQL). The application connects to an SMTP server at
port 25 and sends a simple text message.

PROCEDURE send_mail (sender IN VARCHAR2,


recipient IN VARCHAR2,
message IN VARCHAR2) IS
mailhost VARCHAR2(30) := 'mailhost.mydomain.com';
smtp_error EXCEPTION;
mail_conn utl_tcp.connection;
PROCEDURE smtp_command(command IN VARCHAR2,
ok IN VARCHAR2 DEFAULT '250')
IS
response varchar2(3);
len pls_integer;
BEGIN
len := utl_tcp.write_line(mail_conn, command);
response := substr(utl_tcp.get_line(mail_conn), 1, 3);
IF (response <> ok) THEN
RAISE smtp_error;

www.saturninfotech.com
PL SQL Document 64

END IF;
END;

BEGIN
mail_conn := utl_tcp.open_connection(remote_host => mailhost,
remote_port => 25,
charset => 'US7ASCII');
smtp_command('HELO ' || mailhost);
smtp_command('MAIL FROM: ' || sender);
smtp_command('RCPT TO: ' || recipient);
smtp_command('DATA', '354');
smtp_command(message);
smtp_command('QUIT', '221');
utl_tcp.close_connection(mail_conn);
EXCEPTION
WHEN OTHERS THEN
-- Handle the error
END;

Summary of UTL_TCP Subprograms


Table 179-3 UTL_TCP Package Subprograms

Subprogram Description

AVAILABLE Function Determines the number of bytes available for reading


from a TCP/IP connection
CLOSE_ALL_CONNECTIONS Closes all open TCP/IP connections
Procedure
CLOSE_CONNECTION Procedure Closes an open TCP/IP connection
FLUSH Procedure Transmits all data in the output buffer, if a buffer is used,
to the server immediately
GET_LINE Function Convenient forms of the read functions, which return the
data read instead of the amount of data read
GET_RAW Function Convenient forms of the read functions, which return the
data read instead of the amount of data read
GET_TEXT Function Convenient forms of the read functions, which return the
data read instead of the amount of data read
OPEN_CONNECTION Function Opens a TCP/IP connection to a specified service
READ_LINE Function Receives a text line from a service on an open
connection
READ_RAW Function Receives binary data from a service on an open
connection

www.saturninfotech.com
PL SQL Document 65

Subprogram Description

READ_TEXT Function Receives text data from a service on an open connection


WRITE_LINE Function Transmits a text line to a service on an open connection
WRITE_RAW Function Transmits a binary message to a service on an open
connection
WRITE_TEXT Function Transmits a text message to a service on an open
connection

AVAILABLE Function

This function determines the number of bytes available for reading from a TCP/IP connection. It
is the number of bytes that can be read immediately without blocking. Determines if data is
ready to be read from the connection.

Syntax

UTL_TCP.AVAILABLE (
c IN OUT NOCOPY connection,
timeout IN PLS_INTEGER DEFAULT 0)
RETURN num_bytes PLS_INTEGER;

Parameters

Table 179-4 AVAILABLE Function Parameters

Parameter Description

c The TCP connection to determine the amount of data that is available to be read
from.
timeout A time in seconds to wait before giving up and reporting that no data is available.
Zero (0) indicates not to wait at all. NULL indicates to wait forever.

Return Values

Table 179-5 AVAILABLE Function Return Values

www.saturninfotech.com
PL SQL Document 66

Parameter Description

num_bytes The number of bytes available for reading without blocking.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION. Users may
use this API to determine if data is available to be read before calling the read API so that the
program will not be blocked because data is not ready to be read from the input.

The number of bytes available for reading returned by this function may less than than what is
actually available. On some platforms, this function may only return 1, to indicate that some data
is available. If you are concerned about the portability of your application, assume that this
function returns a positive value when data is available for reading, and 0 when no data is
available. This function returns a positive value when all the data at a particular connection has
been read and the next read will result in the END_OF_INPUT exception.

The following example illustrates using this function in a portable manner:

DECLARE
c utl_tcp.connection
data VARCHAR2(256);
len PLS_INTEGER;
BEGIN
c := utl_tcp.open_connection(...);
LOOP
IF (utl_tcp.available(c) > 0) THEN
len := utl_tcp.read_text(c, data, 256);
ELSE
---do some other things
. . . .
END IF
END LOOP;
END;

Related Functions

READ_RAW, READ_TEXT, READ_LINE

CLOSE_ALL_CONNECTIONS Procedure

This procedure closes all open TCP/IP connections.

Syntax

www.saturninfotech.com
PL SQL Document 67

UTL_TCP.CLOSE_ALL_CONNECTIONS;

Usage Notes

This call is provided to close all connections before a PL/SQL program avoid dangling
connections.

Related Functions

OPEN_CONNECTION, CLOSE_CONNECTION.

CLOSE_CONNECTION Procedure

This procedure closes an open TCP/IP connection.

Syntax

UTL_TCP.CLOSE_CONNECTION (
c IN OUT NOCOPY connection);

Parameters

Table 179-6 CLOSE_CONNECTION Procedure Parameters

Parameter Description

c The TCP connection to close.

Usage Notes

Connection must have been opened by a previous call to OPEN_CONNECTION. The fields
remote_host, remote_port, local_host, local_port and charset of c will be reset after
the connection is closed.

An open connection must be closed explicitly. An open connection will remain open when the
PL/SQL record variable that stores the connection goes out-of-scope in the PL/SQL program.
Failing to close unwanted connections may result in unnecessary tying up of local and remote
system resources.

FLUSH Procedure

www.saturninfotech.com
PL SQL Document 68

This procedure transmits all data in the output buffer, if a buffer is used, to the server
immediately.

Syntax

UTL_TCP.FLUSH (
c IN OUT NOCOPY connection);

Parameters

Table 179-7 FLUSH Procedure Parameters

Parameter Description

c The TCP connection to send data to.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION.

Related Functions

WRITE_RAW, WRITE_TEXT, WRITE_LINE

GET_LINE Function

This function returns the data read instead of the amount of data read.

Syntax

UTL_TCP.GET_LINE (
c IN OUT NOCOPY connection,
remove_crlf IN BOOLEAN DEFAULT FALSE,
peek IN BOOLEAN DEFAULT FALSE)
RETURN VARCHAR2;

Parameters

Table 179-8 GET_LINE Function Parameters

www.saturninfotech.com
PL SQL Document 69

Parameter Description

c The TCP connection to receive data from.


remove_crlf If TRUE, the trailing CR/LF character(s) are removed from the received message.
peek Normally, you want to read the data and remove it from the input queue, that is,
consume it. In some situations, you may just want to look ahead at the data, that
is, peek at it, without removing it from the input queue, so that it is still available
for reading (or even peeking) in the next call. To keep the data in the input
queue, set this flag to TRUE and set up an input buffer before the connection is
opened. The amount of data you can peeked at (that is, read but keep in the input
queue) must be less than the size of input buffer.

Usage Notes

 The connection must have already been opened through a call to OPEN_CONNECTION.

 See READ_LINE for the read time out, character set conversion, buffer size, and multibyte
character issues.

Related Functions

GET_RAW, GET_TEXT, READ_LINE

GET_RAW Function

This function returns the data read instead of the amount of data read.

Syntax

UTL_TCP.GET_RAW (
c IN OUT NOCOPY connection,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE)
RETURN RAW;

Parameters

Table 179-9 GET_RAW Function Parameters

www.saturninfotech.com
PL SQL Document 70

Parameter Description

c The TCP connection to receive data from.


len The number of bytes (or characters for VARCHAR2) of data to receive. Default is 1.
peek Normally, you want to read the data and remove it from the input queue, that is,
consume it. In some situations, you may just want to look ahead at the data, that
is, peek at it, without removing it from the input queue, so that it is still available
for reading (or even peeking) in the next call. To keep the data in the input
queue, set this flag to TRUE and set up an input buffer before the connection is
opened. The amount of data you can peeked at (that is, read but keep in the input
queue) must be less than the size of input buffer.
remove_crlf If TRUE, the trailing CR/LF character(s) are removed from the received message.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION.

For all the get_* APIs described in this section, see the corresponding READ_* API for the read
time out issue. For GET_TEXT and GET_LINE, see the corresponding READ_* API for character set
conversion, buffer size, and multibyte character issues.

Related Functions

GET_RAW, GET_TEXT, READ_RAW, READ_TEXT, READ_LINE

GET_TEXT Function

This function returns the data read instead of the amount of data read.

Syntax

UTL_TCP.GET_TEXT (
c IN OUT NOCOPY connection,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE)
RETURN VARCHAR2;

Parameters

Table 179-10 GET_TEXT Function Parameters

www.saturninfotech.com
PL SQL Document 71

Parameter Description

c The TCP connection to receive data from.


len The number of bytes (or characters for VARCHAR2) of data to receive. Default is 1.
peek Normally, you want to read the data and remove it from the input queue, that is,
consume it. In some situations, you may just want to look ahead at the data, that
is, peek at it, without removing it from the input queue, so that it is still available
for reading (or even peeking) in the next call. To keep the data in the input
queue, set this flag to TRUE and set up an input buffer before the connection is
opened. The amount of data you can peeked at (that is, read but keep in the input
queue) must be less than the size of input buffer.
remove_crlf If TRUE, the trailing CR/LF character(s) are removed from the received message.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION.

For all the get_* APIs described in this section, see the corresponding read_* API for the read
time out issue. For GET_TEXT and GET_LINE, see the corresponding READ_* API for character set
conversion, buffer size, and multibyte character issues.

Related Functions

READ_RAW, READ_TEXT, READ_LINE

OPEN_CONNECTION Function

This function opens a TCP/IP connection to a specified service.

Syntax

UTL_TCP.OPEN_CONNECTION (
remote_host IN VARCHAR2,
remote_port IN PLS_INTEGER,
local_host IN VARCHAR2 DEFAULT NULL,
local_port IN PLS_INTEGER DEFAULT NULL,
in_buffer_size IN PLS_INTEGER DEFAULT NULL,
out_buffer_size IN PLS_INTEGER DEFAULT NULL,
charset IN VARCHAR2 DEFAULT NULL,
newline IN VARCHAR2 DEFAULT CRLF,
tx_timeout IN PLS_INTEGER DEFAULT NULL)
RETURN connection;

www.saturninfotech.com
PL SQL Document 72

Parameters

Table 179-11 OPEN_CONNECTION Function Parameters

Parameter Description

remote_host The name of the host providing the service. When remote_host is NULL, it
connects to the local host.
remote_port The port number on which the service is listening for connections.
local_host The name of the host providing the service. NULL means don't care.
local_port The port number on which the service is listening for connections. NULL
means don't care.
in_buffer_size The size of input buffer. The use of an input buffer can speed up execution
performance in receiving data from the server. The appropriate size of the
buffer depends on the flow of data between the client and the server, and the
network condition. A 0 value means no buffer should be used. A NULL
value means the caller does not care if a buffer is used or not. The maximum
size of the input buffer is 32767 bytes.
out_buffer_size The size of output buffer. The use of an output buffer can speed up
execution performance in sending data to the server. The appropriate size of
buffer depends on the flow of data between the client and the server, and the
network condition. A 0 value means no buffer should be used. A NULL
value means the caller does not care if a buffer is used or not. The maximum
size of the output buffer is 32767 bytes.
charset The on-the-wire character set. Since text messages in the database may be
encoded in a character set that is different from the one expected on the wire
(that is, the character set specified by the communication protocol, or the
one stipulated by the other end of the communication), text messages in the
database will be converted to and from the on-the-wire character set as they
are sent and received on the network using READ_TEXT, READ_LINE,
WRITE_TEXT and WRITE_LINE. Set this parameter to NULL when no
conversion is needed.
newline The newline character sequence. This newline character sequence is
appended to the text line sent by WRITE_LINE API.
tx_timeout A time in seconds that the UTL_TCP package should wait before giving up in
a read or write operations in this connection. In read operations, this
package gives up if no data is available for reading immediately. In write
operations, this package gives up if the output buffer is full and no data is to
be sent in the network without being blocked. Zero (0) indicates not to wait
at all. NULL indicates to wait forever.

www.saturninfotech.com
PL SQL Document 73

Return Values

Table 179-12 OPEN_CONNECTION Function Return Values

Parameter Description

connection A connection to the targeted TCP/IP service.

Usage Notes

Note that connections opened by this UTL_TCP package can remain open and be passed from one
database call to another in a shared server configuration. However, the connection must be
closed explicitly. The connection will remain open when the PL/SQL record variable that stores
the connection goes out-of-scope in the PL/SQL program. Failing to close unwanted connections
may result in unnecessary tying up of local and remote system resources.

In the current release of the UTL_TCP package, the parameters local_host and local_port are
ignored when open_connection makes a TCP/IP connection. It does not attempt to use the
specified local host and port number when the connection is made. The local_host and
local_port fields will not be set in the connection record returned by the function.

Time out on write operations is not supported in the current release of the UTL_TCP package.

Related Functions

CLOSE_CONNECTION, CLOSE_ALL_CONNECTIONS

READ_LINE Function

This function receives a text line from a service on an open connection. A line is terminated by a
line-feed, a carriage-return or a carriage-return followed by a line-feed.

Syntax

UTL_TCP.READ_LINE (
c IN OUT NOCOPY connection,
data IN OUT NOCOPY VARCHAR2 CHARACTER SET ANY_CS,
peek IN BOOLEAN DEFAULT FALSE)
RETURN num_chars PLS_INTEGER;

Parameters

www.saturninfotech.com
PL SQL Document 74

Table 179-13 READ_LINE Function Parameters

Parameter Description

c The TCP connection to receive data from.


data The data received.
remove_crlf If TRUE, the trailing CR/LF character(s) are removed from the received message.
peek Normally, you want to read the data and remove it from the input queue, that is,
consume it. In some situations, you may just want to look ahead at the data, that
is, peek at it, without removing it from the input queue, so that it is still available
for reading (or even peeking) in the next call. To keep the data in the input
queue, set this flag to TRUE and set up an input buffer before the connection is
opened. The amount of data you can peeked at (that is, read but keep in the input
queue) must be less than the size of input buffer.

Return Values

Table 179-14 READ_LINE Function Return Values

Parameter Description

num_chars The actual number of characters of data received.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION. This
function does not return until the end-of-line have been reached, or the end of input has been
reached. Text messages will be converted from the on-the-wire character set, specified when the
connection was opened, to the database character set before they are returned to the caller.

If transfer time out is set when the connection is opened, this function waits for each data packet
to be ready to read until time out occurs. If it occurs, this function stops reading and returns all
the data read successfully. If no data is read successfully, the transfer_timeout exception is
raised. The exception can be handled and the read operation can be retried later.

If a partial multibyte character is found at the end of input, this function stops reading and returns
all the complete multibyte characters read successfully. If no complete character is read
successfully, the partial_multibyte_char exception is raised. The exception can be handled
and the bytes of that partial multibyte character can be read as binary by the READ_RAW function.
If a partial multibyte character is seen in the middle of the input because the remaining bytes of

www.saturninfotech.com
PL SQL Document 75

the character have not arrived and read time out occurs, the transfer_timeout exception is
raised instead. The exception can be handled and the read operation can be retried later.

Related Functions

READ_RAW, READ_TEXT, AVAILABLE

READ_RAW Function

This function receives binary data from a service on an open connection.

Syntax

UTL_TCP.READ_RAW (
c IN OUT NOCOPY connection,
data IN OUT NOCOPY RAW,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE)
RETURN num_bytes PLS_INTEGER;

Parameters

Table 179-15 READ_RAW Function Parameters

Parameter Description

c The TCP connection to receive data from.


data (IN The data received.
OUT COPY)
len The number of bytes of data to receive.
peek Normally, you want to read the data and remove it from the input queue, that is,
consume it. In some situations, you may just want to look ahead at the data, that
is, peek at it, without removing it from the input queue, so that it is still available
for reading (or even peeking) in the next call. To keep the data in the input queue,
set this flag to TRUE and set up an input buffer before the connection is opened.
The amount of data you can peeked at (that is, read but keep in the input queue)
must be less than the size of input buffer.

Return Values

Table 179-16 READ_RAW Function Return Values

www.saturninfotech.com
PL SQL Document 76

Parameter Description

num_bytes The actual number of bytes of data received.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION. This
function does not return until the specified number of bytes have been read, or the end of input
has been reached.

If transfer time out is set when the connection is opened, this function waits for each data packet
to be ready to read until time out occurs. If it occurs, this function stops reading and returns all
the data read successfully. If no data is read successfully, the transfer_timeout exception is
raised. The exception can be handled and the read operation can be retried later.

Related Functions

READ_TEXT, READ_LINE, AVAILABLE

READ_TEXT Function

This function receives text data from a service on an open connection.

Syntax

UTL_TCP.READ_TEXT (
c IN OUT NOCOPY connection,
data IN OUT NOCOPY VARCHAR2 CHARACTER SET ANY_CS,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE)
RETURN num_chars PLS_INTEGER;

Parameters

Table 179-17 READ_TEXT Function Parameters

Parameter Description

c The TCP connection to receive data from.


data The data received.
len The number of characters of data to receive.

www.saturninfotech.com
PL SQL Document 77

Parameter Description

peek Normally, users want to read the data and remove it from the input queue, that is,
consume it. In some situations, users may just want to look ahead at the data without
removing it from the input queue so that it is still available for reading (or even
peeking) in the next call. To keep the data in the input queue, set this flag to TRUE
and an input buffer must be set up when the connection is opened. The amount of
data that you can peek at (that is, read but keep in the input queue) must be less than
the size of input buffer.

Return Values

Table 179-18 READ_TEXT Function Return Values

Parameter Description

num_chars The actual number of characters of data received.

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION. This
function does not return until the specified number of characters has been read, or the end of
input has been reached. Text messages will be converted from the on-the-wire character set,
specified when the connection was opened, to the database character set before they are returned
to the caller.

Unless explicitly overridden, the size of a VARCHAR2 buffer is specified in terms of bytes, while
the parameter len refers to the maximum number of characters to be read. When the database
character set is multibyte, where a single character may consist of more than 1 byte, you should
ensure that the buffer can hold the maximum of characters. In general, the size of the VARCHAR2
buffer should equal the number of characters to be read, multiplied by the maximum number of
bytes of a character of the database character set.

If transfer time out is set when the connection is opened, this function waits for each data packet
to be ready to read until time out occurs. If it occurs, this function stops reading and returns all
the data read successfully. If no data is read successfully, the transfer_timeout exception is
raised. The exception can be handled and the read operation can be retried later.

If a partial multibyte character is found at the end of input, this function stops reading and returns
all the complete multibyte characters read successfully. If no complete character is read
successfully, the partial_multibyte_char exception is raised. The exception can be handled
and the bytes of that partial multibyte character can be read as binary by the READ_RAW function.

www.saturninfotech.com
PL SQL Document 78

If a partial multibyte character is seen in the middle of the input because the remaining bytes of
the character have not arrived and read time out occurs, the transfer_timeout exception is
raised instead. The exception can be handled and the read operation can be retried later.

Related Functions

READ_RAW, READ_LINE, AVAILABLE

WRITE_LINE Function

This function transmits a text line to a service on an open connection. The newline character
sequence will be appended to the message before it is transmitted.

Syntax

UTL_TCP.WRITE_LINE (
c IN OUT NOCOPY connection,
data IN VARCHAR2 DEFAULT NULL CHARACTER SET ANY_CS)
RETURN PLS_INTEGER;

Parameters

Table 179-19 WRITE_LINE Function Parameters

Parameter Description

c The TCP connection to send data to.


data The buffer containing the data to be sent.

Return Values

Table 179-20 WRITE_LINE Function Return Values

Parameter Description

num_chars The actual number of characters of data transmitted.

Usage Notes

www.saturninfotech.com
PL SQL Document 79

The connection must have already been opened through a call to OPEN_CONNECTION. Text
messages will be converted to the on-the-wire character set, specified when the connection was
opened, before they are transmitted on the wire.

Related Functions

WRITE_RAW, WRITE_TEXT, FLUSH

WRITE_RAW Function

This function transmits a binary message to a service on an open connection. The function does
not return until the specified number of bytes have been written.

Syntax

UTL_TCP.WRITE_RAW (
c IN OUT NOCOPY connection,
data IN RAW,
len IN PLS_INTEGER DEFAULT NULL)
RETURN num_bytes PLS_INTEGER;

Parameters

Table 179-21 WRITE_RAW Function Parameters

Parameter Description

c The TCP connection to send data to.


data The buffer containing the data to be sent.
len The number of bytes of data to transmit. When len is NULL, the whole length of data
is written.

Return Values

Table 179-22 WRITE_RAW Function Return Values

Parameter Description

num_bytes The actual number of bytes of data transmitted.

www.saturninfotech.com
PL SQL Document 80

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION.

Related Functions

WRITE_TEXT, WRITE_LINE, FLUSH

WRITE_TEXT Function

This function transmits a text message to a service on an open connection.

Syntax

UTL_TCP.WRITE_TEXT (
c IN OUT NOCOPY connection,
data IN VARCHAR2 CHARACTER SET ANY_CS,
len IN PLS_INTEGER DEFAULT NULL)
RETURN num_chars PLS_INTEGER;

Parameters

Table 179-23 WRITE_TEXT Function Parameters

Parameter Description

c The TCP connection to send data to.


data The buffer containing the data to be sent.
len The number of characters of data to transmit. When len is NULL, the whole length of
data is written. The actual amount of data written may be less because of network
condition.

Return Values

Table 179-24 WRITE_TEXT Function Return Values

Parameter Description

num_chars The actual number of characters of data transmitted.

www.saturninfotech.com
PL SQL Document 81

Usage Notes

The connection must have already been opened through a call to OPEN_CONNECTION. Text
messages will be converted to the on-the-wire character set, specified when the connection was
opened, before they are transmitted on the wire.

Related Functions

WRITE_RAW, WRITE_LINE, FLUSH

UTL_URL
The UTL_URL package has two functions: ESCAPE and UNESCAPE.

See Also:

Chapter 169, "UTL_HTTP"

This chapter contains the following topics:

 Using UTL_URL

o Overview

o Exceptions

o Examples

 Summary of UTL_URL Subprograms

www.saturninfotech.com
PL SQL Document 82

Using UTL_URL
 Overview

 Exceptions

 Examples

Overview

A Uniform Resource Locator (URL) is a string that identifies a Web resource, such as a page or
a picture. Use a URL to access such resources by way of the HyperText Transfer Protocol
(HTTP). For example, the URL for Oracle's Web site is:

http://www.oracle.com

Normally, a URL contains English alphabetic characters, digits, and punctuation symbols. These
characters are known as the unreserved characters. Any other characters in URLs, including
multibyte characters or binary octet codes, must be escaped to be accurately processed by Web
browsers or Web servers. Some punctuation characters, such as dollar sign ($), question mark
(?), colon (:), and equals sign (=), are reserved as delimiters in a URL. They are known as the
reserved characters. To literally process these characters, instead of treating them as delimiters,
they must be escaped.

The unreserved characters are:

 A through Z, a through z, and 0 through 9

 Hyphen (-), underscore (_), period (.), exclamation point (!), tilde (~), asterisk (*),
accent ('), left parenthesis ( ( ), right parenthesis ( ) )

The reserved characters are:

 Semi-colon (;) slash (/), question mark (?), colon (:), at sign (@), ampersand (&), equals
sign (=), plus sign (+), dollar sign ($), and comma (,)

The UTL_URL package has two functions that provide escape and unescape mechanisms for URL
characters. Use the escape function to escape a URL before the URL is used fetch a Web page by
way of the UTL_HTTP package. Use the unescape function to unescape an escaped URL before
information is extracted from the URL.

www.saturninfotech.com
PL SQL Document 83

For more information, refer to the Request For Comments (RFC) document RFC2396. Note that
this URL escape and unescape mechanism is different from the x-www-form-urlencoded
encoding mechanism described in the HTML specification:

http://www.w3.org/TR/html

Exceptions

Table 180-1 lists the exceptions that can be raised when the UTL_URL package API is invoked.

Table 180-1 UTL_URL Exceptions

Error
Exception Code Reason

BAD_URL 29262 The URL contains badly formed escape code sequences
BAD_FIXED_WIDTH_CHARSE 29274 Fixed-width multibyte character set is not allowed as a
T
URL character set.

Examples

You can implement the x-www-form-urlencoded encoding using the UTL_URL.ESCAPE function
as follows:

CREATE OR REPLACE FUNCTION form_url_encode (


data IN VARCHAR2,
charset IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN utl_url.escape(data, TRUE, charset); -- note use of TRUE
END;

For decoding data encoded with the form-URL-encode scheme, the following function
implements the decording scheme:

CREATE OR REPLACE FUNCTION form_url_decode(


data IN VARCHAR2,
charset IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN utl_url.unescape(
replace(data, '+', ' '),
charset);
END;

www.saturninfotech.com
PL SQL Document 84

Summary of UTL_URL Subprograms


Table 180-2 UTL_URL Package Subprograms

Subprogram Description

ESCAPE Returns a URL with illegal characters (and optionally reserved characters)
Function escaped using the %2-digit-hex-code format
UNESCAPE Unescapes the escape character sequences to their original forms in a URL.
Function Convert the %XX escape character sequences to the original characters

ESCAPE Function

This function returns a URL with illegal characters (and optionally reserved characters) escaped
using the %2-digit-hex-code format.

Syntax

UTL_URL.ESCAPE (
url IN VARCHAR2 CHARACTER SET ANY_CS,
escape_reserved_chars IN BOOLEAN DEFAULT FALSE,
url_charset IN VARCHAR2 DEFAULT utl_http.body_charset)
RETURN VARCHAR2;

Parameters

Table 180-3 ESCAPE Function Parameters

Parameter Description

url The original URL


escape_reserved_char Indicates whether the URL reserved characters should be escaped. If
s
set to TRUE, both the reserved and illegal URL characters are
escaped. Otherwise, only the illegal URL characters are escaped. The
default value is FALSE.
url_charset When escaping a character (single-byte or multibyte), determine the
target character set that character should be converted to before the
character is escaped in %hex-code format. If url_charset is NULL,

www.saturninfotech.com
PL SQL Document 85

Parameter Description

the database charset is assumed and no character set conversion will


occur. The default value is the current default body character set of
the UTL_HTTP package, whose default value is ISO-8859-1. The
character set can be named in Internet Assigned Numbers Authority
(IANA) or in the Oracle naming convention.

Usage Notes

Use this function to escape URLs that contain illegal characters as defined in the URL
specification RFC 2396. The legal characters in URLs are:

 A through Z, a through z, and 0 through 9

 Hyphen (-), underscore (_), period (.), exclamation point (!), tilde (~), asterisk (*),
accent ('), left parenthesis ( ( ), right parenthesis ( ) )

The reserved characters consist of:

 Semi-colon (;) slash (/), question mark (?), colon (:), at sign (@), ampersand (&), equals
sign (=), plus sign (+), dollar sign ($), and comma (,)

Many of the reserved characters are used as delimiters in the URL. You should escape characters
beyond those listed here by using escape_url. Also, to use the reserved characters in the name-
value pairs of the query string of a URL, those characters must be escaped separately. An
escape_url cannot recognize the need to escape those characters because once inside a URL,
those characters become indistinguishable from the actual delimiters. For example, to pass a
name-value pair $logon=scott/tiger into the query string of a URL, escape the $ and /
separately as %24logon=scott%2Ftiger and use it in the URL.

Normally, you will escape the entire URL, which contains the reserved characters (delimiters)
that should not be escaped. For example:

utl_url.escape('http://www.acme.com/a url with space.html')

Returns:

In other situations, you may want to send a query string with a value that contains reserved
characters. In that case, escape only the value fully (with escape_reserved_chars set to TRUE)
and then concatenate it with the rest of the URL. For example:

url := 'http://www.acme.com/search?check=' || utl_url.escape


('Is the use of the "$" sign okay?', TRUE);

www.saturninfotech.com
PL SQL Document 86

This expression escapes the question mark (?), dollar sign ($), and space characters in 'Is the
use of the "$" sign okay?' but not the ? after search in the URL that denotes the use of a
query string.

The Web server that you intend to fetch Web pages from may use a character set that is different
from that of your database. In that case, specify the url_charset as the Web server character set so
that the characters that need to be escaped are escaped in the target character set. For example, a
user of an EBCDIC database who wants to access an ASCII Web server should escape the URL
using US7ASCII so that a space is escaped as %20 (hex code of a space in ASCII) instead of %40
(hex code of a space in EBCDIC).

This function does not validate a URL for the proper URL format.

UNESCAPE Function

This function unescapes the escape character sequences to its original form in a URL, to convert
the %XX escape character sequences to the original characters.

Syntax

UTL_URL.UNESCAPE (
url IN VARCHAR2 CHARACTER SET ANY_CS,
url_charset IN VARCHAR2 DEFAULT utl_http.body_charset)
RETURN VARCHAR2;

Parameters

Table 180-4 UNESCAPE Function Parameters

Parameter Description

url The URL to unescape


url_charset After a character is unescaped, the character is assumed to be in the
source_charset character set and it will be converted from the
source_charset to the database character set before the URL is returned. If
source_charset is NULL, the database charset is assumed and no character set
conversion occurred. The default value is the current default body character set
of the UTL_HTTP package, whose default value is "ISO-8859-1". The character set
can be named in Internet Assigned Numbers Authority (IANA) or Oracle naming
convention.

Usage Notes
www.saturninfotech.com
PL SQL Document 87

The Web server that you receive the URL from may use a character set that is different from that
of your database. In that case, specify the url_charset as the Web server character set so that the
characters that need to be unescaped are unescaped in the source character set. For example, a
user of an EBCDIC database who receives a URL from an ASCII Web server should unescape
the URL using US7ASCII so that %20 is unescaped as a space (0x20 is the hex code of a space in
ASCII) instead of a ? (because 0x20 is not a valid character in EBCDIC).

This function does not validate a URL for the proper URL format.

PL/SQL forall operator

Loading an Oracle table from a PL/SQL array involves expensive context switches, and the
PL/SQL FORALL operator speed is amazing.

The best overall reference of hypercharging PL/SQL table insert performance with forall array
collections is Dr. Tim Hall (Oracle ACE of the year 2006) and his landmark book "Oracle
PL/SQL Tuning: Expert Secrets for High Performance Programming".

Kent Crotty, author of Easy Oracle Application Express (HTML-DB) conducted a study to prove
the speed of the PL/SQL forall over vanilla SQL inserts, and found that FORALL was 30x faster
in his small test:

www.saturninfotech.com
PL SQL Document 88

DECLARE
TYPE prod_tab IS TABLE OF products%ROWTYPE;
products_tab   prod_tab := prod_tab();
start_time  number;  end_time   number;
BEGIN
-- Populate a collection - 100000 rows
SELECT * BULK COLLECT INTO products_tab FROM products;
 
EXECUTE IMMEDIATE 'TRUNCATE TABLE products';
Start_time := DBMS_UTILITY.get_time;
FOR i in products_tab.first .. products_tab.last LOOP
 INSERT INTO products (product_id, product_name, effective_date)
   VALUES (products_tab(i).product_id, products_tab(i).product_name,
           products_tab(i).effective_date);
END LOOP;
end_time := DBMS_UTILITY.get_time;
DBMS_OUTPUT.PUT_LINE(‘Conventional Insert: ’||to_char(end_time-start_time));
 
EXECUTE IMMEDIATE 'TRUNCATE TABLE products';
Start_time := DBMS_UTILITY.get_time;
FORALL i in products_tab.first .. products_tab.last
 INSERT INTO products VALUES products_tab(i);
end_time := DBMS_UTILITY.get_time;
DBMS_OUTPUT.PUT_LINE(‘Bulk Insert: ’||to_char(end_time-start_time));
COMMIT;
END;

Crotty notes a great speed improvement with very few code changes, from 622 seconds to only
22 seconds:
SQL> /

Conventional Insert: 686

Bulk Insert: 22

Using Bulk Binds And Oracle FORALL

Yesterday, I looked at collections, an evolution of PL/SQL tables that allows us to manipulate


many variables at once, as a unit. Collections, coupled with two new features introduced with
Oracle 8i, Oracle BULK_COLLECT and Oracle FORALL, can dramatically increase the
performance of data manipulation code within PL/SQL.

As a reminder, we were looking at a piece of code that implemented collections, Oracle BULK
COLLECT and Oracle FORALL, taken from a question and answer posed online by Daniel
Morgan.

www.saturninfotech.com
PL SQL Document 89

CREATE OR REPLACE PROCEDURE fast_proc (p_array_size IN PLS_INTEGER DEFAULT


100)
IS

TYPE ARRAY IS TABLE OF all_objects%ROWTYPE;


l_data ARRAY;

CURSOR c IS
SELECT *
FROM all_objects;

BEGIN
    OPEN c;
    LOOP
    FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;

    FORALL i IN 1..l_data.COUNT


    INSERT INTO t2 VALUES l_data(i);

    EXIT WHEN c%NOTFOUND;


    END LOOP;
    CLOSE c;
END fast_proc;
/

which was subsequently refined in a later answer to;

SQL> create or replace procedure fast_proc is


2         type TObjectTable is table of ALL_OBJECTS%ROWTYPE;
3         ObjectTable$ TObjectTable;
4         begin
5         select
6                     * BULK COLLECT INTO ObjectTable$
7         from ALL_OBJECTS;
8
9         forall x in ObjectTable$.First..ObjectTable$.Last
10       insert into t1 values ObjectTable$(x) ;
11       end;
12 /

The key things here are the collection that is set up to hold the table data, the BULK COLLECT
clause and the Oracle FORALL statement. I went into detail about the collection yesterday, so
now it's time to look at the other two.

Steven Feuernstein explains the basics behind BULK_COLLECT and Oracle FORALL in a
recent OTN article, and together these two features are known as 'Bulk Binding'. Bulk Binds are
a PL/SQL technique where, instead of multiple individual SELECT, INSERT, UPDATE or
DELETE statements are executed to retrieve from, or store data in, at table, all of the operations
are carried out at once, in bulk. This avoids the context-switching you get when the PL/SQL
engine has to pass over to the SQL engine, then back to the PL/SQL engine, and so on, when you
individually access rows one at a time. To do bulk binds with INSERT, UPDATE, and DELETE

www.saturninfotech.com
PL SQL Document 90

statements, you enclose the SQL statement within a PL/SQL FORALL statement. To do bulk binds
with SELECT statements, you include the BULK COLLECT clause in the SELECT statement instead of
using INTO.

SELECT FOR UPDATE in Cursors


When you issue a SELECT statement against the database to query some records, no locks are
placed on the selected rows. In general, this is a wonderful feature because the number of records
locked at any given time is (by default) kept to the absolute minimum: only those records which
have been changed but not yet committed are locked. Even then, others will be able to read those
records as they appeared before the change (the "before image" of the data).

There are times, however, when you will want to lock a set of records even before you change
them in your program. Oracle offers the FOR UPDATE clause of the SELECT statement to
perform this locking.

www.saturninfotech.com
PL SQL Document 91

When you issue a SELECT...FOR UPDATE statement, the RDBMS automatically obtains
exclusive row-level locks on all the rows identified by the SELECT statement, holding the
records "for your changes only" as you move through the rows retrieved by the cursor. No one
else will be able to change any of these records until you perform a ROLLBACK or a COMMIT.

Here are two examples of the FOR UPDATE clause used in a cursor:

CURSOR toys_cur IS
SELECT name, manufacturer, preference_level, sell_at_yardsale_flag
FROM my_sons_collection
WHERE hours_used = 0
FOR UPDATE;

CURSOR fall_jobs_cur IS
SELECT task, expected_hours, tools_required, do_it_yourself_flag
FROM winterize
WHERE year = TO_CHAR (SYSDATE, 'YYYY')
FOR UPDATE OF task;

The first cursor uses the unqualified FOR UPDATE clause, while the second cursor qualifies the
FOR UPDATE with a column name from the query.

You can use the FOR UPDATE clause in a SELECT against multiple tables. In this case, rows in
a table are locked only if the FOR UPDATE clause references a column in that table. In the
following example the FOR UPDATE clause does not result in any locked rows in the winterize
table:

CURSOR fall_jobs_cur IS
SELECT w.task, w.expected_hours,
w.tools_required, w.do_it_yourself_flag
FROM winterize w, husband_config hc
WHERE year = TO_CHAR (SYSDATE, 'YYYY')
FOR UPDATE OF husband_config.max_procrastination_allowed;

The FOR UPDATE OF clause only mentions the max_procrastination_allowed column; no


columns in the winterize table are listed.

The OF list of the FOR UPDATE clause does not restrict you to changing only those columns
listed. Locks are still placed on all rows; the OF list just gives you a way to document more
clearly what you intend to change. If you simply state FOR UPDATE in the query and do not
include one or more columns after the OF keyword, then the database will then lock all identified
rows across all tables listed in the FROM clause.

Furthermore, you do not have to actually UPDATE or DELETE any records just because you
issued a SELECT...FOR UPDATE -- that act simply states your intention to be able to do so.

Finally, you can append the optional keyword NOWAIT to the FOR UPDATE clause to tell
Oracle not to wait if the table has been locked by another user. In this case, control will be
returned immediately to your program so that you can perform other work or simply wait for a

www.saturninfotech.com
PL SQL Document 92

period of time before trying again. Without the NOWAIT clause, your process will block until
the table is available. There is no limit to the wait time unless the table is remote. For remote
objects, the Oracle initialization parameter, DISTRIBUTED_LOCK_TIMEOUT, is used to set
the limit.

6.11.1 Releasing Locks with COMMIT

As soon as a cursor with a FOR UPDATE clause is OPENed, all rows identified in the result set
of the cursor are locked and remain locked until your session issues either a COMMIT statement
to save any changes or a ROLLBACK statement to cancel those changes. When either of these
actions occurs, the locks on the rows are released. As a result, you cannot execute another
FETCH against a FOR UPDATE cursor after you COMMIT or ROLLBACK. You will have lost
your position in the cursor.

Consider the following program, which assigns winterization chores:[1]

[1] Caveat: I don't want to set false expectations with anyone, especially my wife. The code in
this block is purely an example. In reality, I set the max_procrastination_allowed to five years
and let my house decay until I can afford to pay someone to do something, or my wife does it, or
she gives me an ultimatum. Now you know why I decided to write a book...

DECLARE
/* All the jobs in the Fall to prepare for the Winter */
CURSOR fall_jobs_cur
IS
SELECT task, expected_hours, tools_required, do_it_yourself_flag
FROM winterize
WHERE year = TO_CHAR (SYSDATE, 'YYYY')
AND completed_flag = 'NOTYET'
FOR UPDATE OF task;
BEGIN
/* For each job fetched by the cursor... */
FOR job_rec IN fall_jobs_cur
LOOP
IF job_rec.do_it_yourself_flag = 'YOUCANDOIT'
THEN
/*
|| I have found my next job. Assign it to myself (like someone
|| is going to do it!) and then commit the changes.
*/
UPDATE winterize SET responsible = 'STEVEN'
WHERE task = job_rec.task
AND year = TO_CHAR (SYSDATE, 'YYYY');
COMMIT;
END IF;
END LOOP;
END;

Suppose this loop finds its first YOUCANDOIT job. It then commits an assignment of a job to
STEVEN. When it tries to FETCH the next record, the program raises the following exception:

www.saturninfotech.com
PL SQL Document 93

ORA-01002: fetch out of sequence

If you ever need to execute a COMMIT or ROLLBACK as you FETCH records from a SELECT
FOR UPDATE cursor, you should include code (such as a loop EXIT or other conditional logic)
to halt any further fetches from the cursor.

The WHERE CURRENT OF Clause

PL/SQL provides the WHERE CURRENT OF clause for both UPDATE and DELETE
statements inside a cursor in order to allow you to easily make changes to the most recently
fetched row of data.

The general format for the WHERE CURRENT OF clause is as follows:

UPDATE table_name
SET set_clause
WHERE CURRENT OF cursor_name;

DELETE
FROM table_name
WHERE CURRENT OF cursor_name;

Notice that the WHERE CURRENT OF clause references the cursor and not the record into
which the next fetched row is deposited.

The most important advantage to using WHERE CURRENT OF where you need to change the
row fetched last is that you do not have to code in two (or more) places the criteria used to
uniquely identify a row in a table. Without WHERE CURRENT OF, you would need to repeat
the WHERE clause of your cursor in the WHERE clause of the associated UPDATEs and
DELETEs. As a result, if the table structure changes in a way that affects the construction of the
primary key, you have to make sure that each SQL statement is upgraded to support this change.
If you use WHERE CURRENT OF, on the other hand, you only have to modify the WHERE
clause of the SELECT statement.

This might seem like a relatively minor issue, but it is one of many areas in your code where you
can leverage subtle features in PL/SQL to minimize code redundancies. Utilization of WHERE
CURRENT OF, %TYPE, and %ROWTYPE declaration attributes, cursor FOR loops, local
modularization, and other PL/SQL language constructs can have a big impact on reducing the
pain you may experience when you maintain your Oracle-based applications.

Let's see how this clause would improve the previous example. In the jobs cursor FOR loop
above, I want to UPDATE the record that was currently FETCHed by the cursor. I do this in the
UPDATE statement by repeating the same WHERE used in the cursor because (task, year)
makes up the primary key of this table:

WHERE task = job_rec.task


AND year = TO_CHAR (SYSDATE, 'YYYY');

www.saturninfotech.com
PL SQL Document 94

This is a less than ideal situation, as explained above: I have coded the same logic in two places,
and this code must be kept synchronized. It would be so much more convenient and natural to be
able to code the equivalent of the following statements:

Delete the record I just fetched.

or:

Update these columns in that row I just fetched.

A perfect fit for WHERE CURRENT OF! The next version of my winterization program below
uses this clause. I have also switched to a simple loop from FOR loop because I want to exit
conditionally from the loop:

DECLARE
CURSOR fall_jobs_cur IS SELECT ... same as before ... ;
job_rec fall_jobs_cur%ROWTYPE;
BEGIN
OPEN fall_jobs_cur;
LOOP
FETCH fall_jobs_cur INTO job_rec;

IF fall_jobs_cur%NOTFOUND
THEN
EXIT;

ELSIF job_rec.do_it_yourself_flag = 'YOUCANDOIT'


THEN
UPDATE winterize SET responsible = 'STEVEN'
WHERE CURRENT OF fall_jobs_cur;
COMMIT;
EXIT;
END IF;
END LOOP;
CLOSE fall_jobs_cur;
END;

www.saturninfotech.com
PL SQL Document 95

www.saturninfotech.com

You might also like