PL SQL Document by MV
PL SQL Document by MV
PL SQL DOCUMENTATION
ON
DYNAMIC SQL,
SEND MAIL,
FOR ALL,
FOR UPDATE
BY
Mahesh M. Vishwekar
www.saturninfotech.com | [email protected]
Oracle Gold Partner
www.saturninfotech.com
PL SQL Document 2
INDEX -
SR NO TITLE PAGE_NO.
UTL_SMTP - 32
UTL_TCP - 59
UTL_URL - 81
www.saturninfotech.com
PL SQL Document 3
Native dynamic SQL, where you place dynamic SQL statements directly into PL/SQL blocks.
Calling procedures in the DBMS_SQL package.
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.
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
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:
www.saturninfotech.com
PL SQL Document 5
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;
/
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".
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:
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;
/
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:
In this example, the user can pass any of the following values for a_hint:
See Also:
www.saturninfotech.com
PL SQL Document 7
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.
Using native dynamic SQL, you can write a smaller, more flexible event dispatcher similar to the
following:
www.saturninfotech.com
PL SQL Document 8
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.
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:
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.
www.saturninfotech.com
PL SQL Document 9
The following sections describe various native dynamic SQL operations that can be performed
on the data in the hr database.
The following native dynamic SQL procedure gives a raise to all employees with a particular job
title:
The EXECUTE IMMEDIATE statement can perform DDL operations. For example, the following
procedure adds an office location:
www.saturninfotech.com
PL SQL Document 10
job VARCHAR2(9),
sal NUMBER(7,2),
deptno NUMBER(2)
)';
END;
/
SHOW ERRORS;
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:
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:
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:
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.
Native dynamic SQL provides the following advantages over the DBMS_SQL package:
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
www.saturninfotech.com
PL SQL Document 13
-- 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 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
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:
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:
Here, the same cursor is reused for different values of the bind my_deptno, improving
performance and scalabilty.
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 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;
/
The DBMS_SQL package provides the following advantages over native dynamic SQL:
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.
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:
The DBMS_SQL package supports SQL statements larger than 32KB; native dynamic SQL does
not.
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.
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.
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
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
-- <process data>
ELSE
EXIT;
www.saturninfotech.com
PL SQL Document 18
END IF;
END LOOP;
dbms_sql.close_cursor(cur_hdl); -- close
cursor
END;
/
The following example includes a dynamic INSERT statement for a table with three columns:
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
The following example uses a dynamic UPDATE statement to update the location of a department,
then returns the name of the department:
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
dbms_sql.close_cursor(cur_hdl);
END;
/
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:
www.saturninfotech.com
PL SQL Document 21
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;
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
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.
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:
Example:
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''127.0.0.1''';
UTL_MAIL.send(sender => '[email protected]',
recipients => '[email protected]',
subject => 'Test Mail',
message => 'Hello World',
mime_type => 'text; charset=us-ascii');
END;
/
DECLARE
v_From VARCHAR2(80) := '[email protected]';
v_Recipient VARCHAR2(80) := '[email protected]';
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;
/
DECLARE
v_From VARCHAR2(80) := '[email protected]';
v_Recipient VARCHAR2(80) := '[email protected]';
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 ||
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 ||
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
www.saturninfotech.com
PL SQL Document 26
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;
/
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.
www.saturninfotech.com
PL SQL Document 28
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
www.saturninfotech.com
PL SQL Document 31
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
Using UTL_SMTP
o Overview
o Types
o Reply Codes
o Exceptions
o Examples
Using UTL_SMTP
Overview
Types
Reply Codes
Exceptions
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
www.saturninfotech.com
PL SQL Document 34
Syntax
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.
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
Fields
Field Description
Reply Codes
Reply
Code Meaning
www.saturninfotech.com
PL SQL Document 36
Reply
Code Meaning
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.
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.
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.
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;
BEGIN
c := UTL_SMTP.OPEN_CONNECTION('smtp-server.acme.com');
UTL_SMTP.HELO(c, 'foo.com');
UTL_SMTP.MAIL(c, '[email protected]');
UTL_SMTP.RCPT(c, '[email protected]');
UTL_SMTP.OPEN_DATA(c);
send_header('From', '"Sender" <[email protected]>');
send_header('To', '"Recipient" <[email protected]>');
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;
Subprogram Description
www.saturninfotech.com
PL SQL Document 39
Subprogram Description
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
Parameter Description
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
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
Parameter Description
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
Syntax
UTL_SMTP.COMMAND_REPLIES (
c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL)
RETURN replies;
Parameters
Parameter Description
Return Values
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.
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
Parameter Description
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.
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
Parameter Description
Return Values
www.saturninfotech.com
PL SQL Document 45
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
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
Parameter Description
Return Values
www.saturninfotech.com
PL SQL Document 46
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
Syntax
UTL_SMTP.HELP (
c IN OUT NOCOPY connection,
command IN VARCHAR2 DEFAULT NULL)
RETURN replies;
Parameters
Parameter Description
www.saturninfotech.com
PL SQL Document 47
Return Values
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
Parameter Description
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.
Syntax
UTL_SMTP.NOOP (
c IN OUT NOCOPY connection)
RETURN reply;
UTL_SMTP.NOOP (
c IN OUT NOCOPY connection);
Parameter
Parameter Description
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
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
Parameter Description
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
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.
A timeout on the WRITE operations feature is not supported in the current release of this
package.
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
Parameter Description
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.
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
Parameter Description
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
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);
Parameter Description
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.
Syntax
UTL_SMTP.RSET (
c IN OUT NOCOPY connection)
RETURN reply;
UTL_SMTP.RSET (
c IN OUT NOCOPY connection);
Parameters
Parameter Description
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
Syntax
UTL_SMTP.VRFY (
c IN OUT NOCOPY connection
recipient IN VARCHAR2)
RETURN reply;
Parameters
Parameter Description
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
Parameter Description
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
Notice that this conversion is not bullet-proof. Consider this code fragment:
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
www.saturninfotech.com
PL SQL Document 58
Parameter Description
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:
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.
Using UTL_TCP
o Overview
o Types
o Exceptions
o Examples
Using UTL_TCP
Overview
Types
www.saturninfotech.com
PL SQL Document 60
Exceptions
Examples
Overview
Types
CONNECTION Type
CRLF
CONNECTION Type
Syntax
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.
Exception Description
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.
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;
Subprogram Description
www.saturninfotech.com
PL SQL Document 65
Subprogram Description
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
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
www.saturninfotech.com
PL SQL Document 66
Parameter Description
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.
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
CLOSE_ALL_CONNECTIONS Procedure
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
Syntax
UTL_TCP.CLOSE_CONNECTION (
c IN OUT NOCOPY connection);
Parameters
Parameter Description
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
Parameter Description
Usage Notes
The connection must have already been opened through a call to OPEN_CONNECTION.
Related Functions
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
www.saturninfotech.com
PL SQL Document 69
Parameter Description
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 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
www.saturninfotech.com
PL SQL Document 70
Parameter Description
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_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
www.saturninfotech.com
PL SQL Document 71
Parameter Description
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
OPEN_CONNECTION Function
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
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
Parameter Description
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
Parameter Description
Return Values
Parameter Description
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 Function
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
Parameter Description
Return Values
www.saturninfotech.com
PL SQL Document 76
Parameter Description
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 Function
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
Parameter Description
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
Parameter Description
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
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
Parameter Description
Return Values
Parameter Description
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 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
Parameter Description
Return Values
Parameter Description
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 Function
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
Parameter Description
Return Values
Parameter Description
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
UTL_URL
The UTL_URL package has two functions: ESCAPE and UNESCAPE.
See Also:
Using UTL_URL
o Overview
o Exceptions
o Examples
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.
Hyphen (-), underscore (_), period (.), exclamation point (!), tilde (~), asterisk (*),
accent ('), left parenthesis ( ( ), right parenthesis ( ) )
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.
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:
For decoding data encoded with the form-URL-encode scheme, the following function
implements the decording scheme:
www.saturninfotech.com
PL SQL Document 84
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
Parameter Description
www.saturninfotech.com
PL SQL Document 85
Parameter Description
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:
Hyphen (-), underscore (_), period (.), exclamation point (!), tilde (~), asterisk (*),
accent ('), left parenthesis ( ( ), right parenthesis ( ) )
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:
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:
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
Parameter Description
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.
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> /
Bulk Insert: 22
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
CURSOR c IS
SELECT *
FROM all_objects;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;
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.
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 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.
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.
[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
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.
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.
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:
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:
or:
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;
www.saturninfotech.com
PL SQL Document 95
www.saturninfotech.com