OracleToDb2MigrateData PDF
OracleToDb2MigrateData PDF
Front cover
Yvonne Chan
Nick Ivanov
Olaf Mueller
ibm.com/redbooks
SG24-7736-02
Note: Before using this information and the product it supports, read the information in
Notices on page ix.
Contents
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Authors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
Now you can become a published author, too! . . . . . . . . . . . . . . . . . . . . . . . . xiv
Comments welcome. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv
Stay connected to IBM Redbooks publications . . . . . . . . . . . . . . . . . . . . . . . . xv
Summary of changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
September 2013, Third Edition. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
Chapter 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 DB2 family of products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 DB2 editions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2 IBM DB2 10.5 Advanced Enterprise Edition features . . . . . . . . . . . . . 6
1.1.3 DB2 10 autonomic computing features . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.4 Introduction to PureData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2 DB2 Oracle database compatibility features overview . . . . . . . . . . . . . . . . 9
1.2.1 Concurrency control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.2 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.3 Implicit casting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.4 SQL Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.5 PL/SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.6 Built-in packages. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.7 Oracle specific JDBC extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.8 SQL*Plus scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.9 Oracle Call Interface and Pro*C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3 DB2 educational resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1 IBM professional certification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3.2 Other resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3.3 DB2 10 videos and topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Chapter 2. Language compatibility features. . . . . . . . . . . . . . . . . . . . . . . . 21
2.1 DB2 compatibility features references. . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.1.1 SQL compatibility setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.1.2 PL/SQL record and collection types . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.3 Subtypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
iii
iv
Contents
vi
Contents
vii
viii
Notices
This information was developed for products and services offered in the U.S.A.
IBM may not offer the products, services, or features discussed in this document in other countries. Consult
your local IBM representative for information on the products and services currently available in your area.
Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM
product, program, or service may be used. Any functionally equivalent product, program, or service that
does not infringe any IBM intellectual property right may be used instead. However, it is the user's
responsibility to evaluate and verify the operation of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter described in this document.
The furnishing of this document does not give you any license to these patents. You can send license
inquiries, in writing, to:
IBM Director of Licensing, IBM Corporation, North Castle Drive, Armonk, NY 10504-1785 U.S.A.
The following paragraph does not apply to the United Kingdom or any other country where such
provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION
PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer
of express or implied warranties in certain transactions, therefore, this statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically made
to the information herein; these changes will be incorporated in new editions of the publication. IBM may
make improvements and/or changes in the product(s) and/or the program(s) described in this publication at
any time without notice.
Any references in this information to non-IBM websites are provided for convenience only and do not in any
manner serve as an endorsement of those websites. The materials at those websites are not part of the
materials for this IBM product and use of those websites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without
incurring any obligation to you.
Information concerning non-IBM products was obtained from the suppliers of those products, their published
announcements or other publicly available sources. IBM has not tested those products and cannot confirm
the accuracy of performance, compatibility or any other claims related to non-IBM products. Questions on
the capabilities of non-IBM products should be addressed to the suppliers of those products.
This information contains examples of data and reports used in daily business operations. To illustrate them
as completely as possible, the examples include the names of individuals, companies, brands, and products.
All of these names are fictitious and any similarity to the names and addresses used by an actual business
enterprise is entirely coincidental.
COPYRIGHT LICENSE:
This information contains sample application programs in source language, which illustrate programming
techniques on various operating platforms. You may copy, modify, and distribute these sample programs in
any form without payment to IBM, for the purposes of developing, using, marketing or distributing application
programs conforming to the application programming interface for the operating platform for which the
sample programs are written. These examples have not been thoroughly tested under all conditions. IBM,
therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.
ix
Trademarks
IBM, the IBM logo, and ibm.com are trademarks or registered trademarks of International Business
Machines Corporation in the United States, other countries, or both. These and other IBM trademarked
terms are marked on their first occurrence in this information with the appropriate symbol ( or ),
indicating US registered or common law trademarks owned by IBM at the time this information was
published. Such trademarks may also be registered or common law trademarks in other countries. A current
list of IBM trademarks is available on the Web at http://www.ibm.com/legal/copytrade.shtml
The following terms are trademarks of the International Business Machines Corporation in the United States,
other countries, or both:
AIX
AS/400
DB2
DB2 Connect
developerWorks
DRDA
Express
IBM
IBM PureData
Informix
InfoSphere
Optim
OS/390
Passport Advantage
PureData
pureScale
pureXML
Redbooks
Redbooks (logo)
System z
Tivoli
WebSphere
z/OS
Preface
This IBM Redbooks publication describes IBM DB2 SQL compatibility
features. The latest version of DB2 includes extensive native support for the
PL/SQL procedural language, new data types, scalar functions, improved
concurrency, built-in packages, OCI, SQL*Plus, and more. These features can
help with developing applications that run on both DB2 and Oracle and can help
simplify the process of moving from Oracle to DB2.
In addition, IBM now provides tools to simplify the enablement process, such as
the highly scalable IBM Data Movement Tool for moving schema and data into
DB2, and an Editor and Profiler for PL/SQL provided by the IBM Data Studio
tool suite.
This Oracle to DB2 migration guide describes new technology, preferred
practices for moving to DB2, and common scenarios that can help you as you
move from Oracle to DB2. This book is intended for IT architects and developers
who are converting from Oracle to DB2.
DB2 compatibility with Oracle is provided through native support. The new
capabilities in DB2 that provide compatibility are implemented at the lowest and
most intimate levels of the database kernel, as though they were originally
engineered for DB2. Native support means that the DB2 implementation is done
without the aid of an emulation layer. This intimacy leads to the scalable
implementation that DB2 offers, providing identical performance between DB2
compatibility features and DB2 other language elements. For example, DB2 runs
SQL PL at the same performance as PL/SQL implementations of the
same function.
xi
Authors
This book was produced by a team of specialists from around the world working
at the International Technical Support Organization, San Jose Center.
Yvonne Chan is the DB2 IBM pureScale Principal on the
IBM PureData Ecosystem team helping customers
implement DB2 pureScale and IBM PureData System for
Transactions solutions. Before her current role, she spent
several years on the DB2 pureScale kernel team, and led
the DB2 Linux Team that is responsible for porting DB2 to
several Linux platforms, including x86_64, IBM System z,
and Itanium Yvonne has been with IBM for over 14 years
and earned a Bachelor of Applied Science degree from the
University of Toronto in Computer Engineering
Nick Ivanov is a DB2 Solutions Migration Consultant at
the IBM Toronto Software Laboratory. Before joining IBM
Canada Labs in 2010, Nick Ivanov worked for many years
as an independent DBA consultant for companies in
financial and retail industries and the public sector. Nick
has experience working with DB2, Oracle, MS SQL Server,
and other database systems.
Olaf Mueller is the WW Principal of DB2 Conversion in the
IBM Information Management Technical Enablement
organization. He is based at the Toronto Lab in Canada.
With his more than 20 years of experience, he helps major
IBM customers convert their existing Oracle -based
applications to DB2. Olaf is also the chief architect of the
IBM Database Conversion Workbench. This workbench
helps automate the whole database conversion process.
Olaf is an experienced IBM Redbooks publications author.
He holds a degree in Chemistry from
Johannes-Gutenberg-Universitaet at Mainz, Germany.
xii
Acknowledgements
The authors would like to express their great appreciation to the following
contributing authors:
Sabyasachi Routray is a senior software developer at IBM India
Software Labs. After starting his career with IBM in 2005,
Sabyasachi has been involved in software testing and
development across different projects. Sabyasachi has
experience in Core Java, Realtime Java, Perl, XML, and DB2.
The authors also thank the following people for their contributions to this project:
Joshua Kim
Project Manager, IBM Information Management
Esteban Laver
Software Engineer, IBM Information Management
Robert Matchett
Senior Software Engineer, IBM Information Management
Jana Palmer
Application Architect, IBM Information Management
Fraser McArthur
Technical Enablement Specialist, IBM Information Management
Preface
xiii
Whei-Jen Chen
Project Leader, IBM International Technical Support Organization
Thanks to the authors of the previous editions of this book.
The authors of the second edition, Oracle to DB2 Conversion Guide:
Compatibility Made Easy, published in September 2012, were:
Nick Ivanov
Romeo Lupascu
Olaf Mueller
Comments welcome
Your comments are important to us!
We want our books to be as helpful as possible. Send us your comments about
this book or other IBM Redbooks publications in one of the following ways:
Use the online Contact us review Redbooks form found at:
ibm.com/redbooks
Send your comments in an email to:
redbooks@us.ibm.com
xiv
Preface
xv
xvi
Summary of changes
This section describes the technical changes that were made in this edition of the
book and in previous editions. This edition might also include minor corrections
and editorial changes that are not identified.
Summary of Changes
for SG24-7736-02
for Oracle to DB2 Conversion Guide: Compatibility Made Easy
as created or updated on July 2, 2014.
New information
New product packaging for DB2 10.5 (Chapter 1)
New compatibility features (Chapters 1 and 2)
IBM Database Conversion Workbench (Chapter 3)
Changed information
Improvements to compatibility features (Chapter 2)
Description of the latest version of enablement tools (Chapter 3)
Enablement scenario modified to use updated enablement tools (Chapter 4)
xvii
xviii
Chapter 1.
Introduction
IBM DB2 10.5 for Linux, UNIX, and Windows operating systems offers extensive
native support for Oracle compatibility, including native support for Oracle SQL
and PL/SQL dialects. This support allows many applications that are written
against Oracle to run against DB2 with minimal or no changes, allowing you to
easily migrate your applications to DB2.
In this book, we explore the preferences and practices for migrating your
application from Oracle to DB2.
Terminology note: In this book, references to DB2 UDB or DB2 are used
generically to mean IBM DB2 10.5 for Linux, UNIX, and Windows.
This chapter includes the following topics:
DB2 family of products (including an introduction to PureData)
DB2 Oracle database compatibility features overview
DB2 educational resources
Express-C
IBM DB2 Express-C is available as a no cost, entry-level edition of the DB2
data server for developers and the IBM Business Partner community. It can be up
and running in minutes and includes self-management features. It includes the
following capabilities of DB2 for Linux, UNIX, and Windows
operating systems:
Solutions that are developed using DB2 Express-C can be seamlessly deployed
using scalable DB2 editions without modifications to the application code.
DB2 Express-C can be used for development and deployment at no charge and
can also be distributed with third-party solutions without any royalties to IBM. It
can be installed on physical or virtual systems with any number of processors
and memory and is optimized to use up to a maximum of two processor cores
and 16 GB of memory.
DB2 Express-C is refreshed at major release milestones and comes with online
community-based assistance. Users requiring more formal support, access to fix
packs, or additional capabilities, such as high availability clustering and
replication features, can purchase an optional yearly subscription for DB2
Express (FTL) or upgrade to other DB2 editions.
Express Edition
DB2 Express is a full-function DB2 data server, which provides attractive
entry-level pricing for the small and medium business (SMB) market. It is offered
in per Authorized User, Processor Value Unit, or Limited Use Virtual Server
based pricing models to provide choices to match SMB customer needs. It
comes with simplified packaging and is easy to transparently install within
an application.
DB2 Express can also be licensed on a yearly fixed term Limited Use Virtual
Server license. Although it is easy to upgrade to the other editions of DB2 10.5,
DB2 Express includes the same autonomic manageability features of the more
scalable editions. You never have to change your application code to upgrade;
simply install the license certificate to upgrade. DB2 Express adds the following
additional features to the Express-C edition:
pureXML
Web services federation
DB2 Homogeneous Federation
High Availability and Disaster Recovery (HADR)
Homogeneous SQL replication between DB2 for Linux, UNIX, and Windows,
and Informix
IBM Tivoli System Automation
DB2 Express can be deployed on pervasive SMB operating systems, such as
Linux, Windows, or Solaris systems. If licensed as a yearly subscription (DB2
Express FTL), it also includes a high availability feature if both primary and
secondary servers in the high availability cluster are licensed. If licensed under
the Limited Use Virtual Server metric, DB2 Express uses up to eight cores on the
server. The DB2 data server cannot use more than 64 GB of memory per server.
You must acquire a separate user license for each authorized user of this product
with a minimum purchase of five users per server.
Chapter 1. Introduction
Business intelligence
Content management
E-commerce
Enterprise Resource Planning
Customer Relationship Management
Supply Chain Management
Chapter 1. Introduction
Developer Edition
This edition offers a package for a single application developer to design, build,
and prototype applications for deployment on any of the IBM Information
Management client or server platforms. This comprehensive developer offering
includes all the DB2 server editions and DB2 Connect Enterprise Edition so you
can build solutions that use the latest data server technologies.
The software in this edition cannot be used for production systems. You must
acquire a separate user license for each authorized user of this product.
Chapter 1. Introduction
Buffer pools
Locking memory
Package cache
Sort memory
Configuration Advisor
You can use the Configuration Advisor to obtain recommendations for the
initial values of the buffer pool size, database configuration parameters, and
database manager configuration parameters.
Design Advisor
The DB2 Design Advisor is a tool that can help you improve your workload
performance. The DB2 Design Advisor provides recommendations about
selecting indexes and other physical database structures, such as
materialized query tables (MQTs), multidimensional clustering tables (MDC),
and database partitioning features (used with DPF). The Design Advisor
identifies all of the objects that you must improve the performance of
your workload.
Utility throttling
Utility throttling regulates the performance impact of maintenance utilities so
that they can run concurrently during production periods. Although the impact
policy (a setting that allows utilities to run in throttled mode) is defined by
default, you must set the impact priority (a setting that each cleaner has,
indicating its throttling priority) when you run a utility (if you want to throttle it).
Chapter 1. Introduction
10
Scanner
User 1:
update T1 set name = 'Russo'
where country= 'Italy'
Memory Lookup
Table T1
Name
X
User 2:
select * from T1
Country
Russo
Italy
Bernard
France
Garcia
Spain
Pappas
Greece
Levi
Israel
Peters
Belgium
Log Buffer
RID 1 = Rossi->Russo
Log Files
Lods
This new behavior and its implementation introduce no new objects, such as a
rollback segment, and has no performance impact on the writer, because the log
must be written.
This new behavior cannot cause a situation similar to Snapshot too old because
in the unlikely event that the necessary log file is archived, DB2 falls back and
waits for the lock to go away. It is a rare occurrence for this situation to occur, as it
requires archival of a log file while a transaction is still uncommitted.
In addition to these changes, additional lock avoidance techniques were
introduced in to DB2 to eliminate a reader holding a lock under CS isolation.
Chapter 1. Introduction
11
In adherence with the SQL Standard and following a philosophy that a type
mismatch is likely an indication of a coding mistake, DB2 has traditionally
followed strong typing rules, where strings and numerics cannot be compared
unless one is explicitly cast to the other.
Often, Oracle applications use weak typing in their SQL. These applications
previously failed to compile against DB2. In DB2, implicit casting (or weak typing)
is added, that is, strings and numbers can be compared, assigned, and operated
on in a flexible fashion.
In addition, untyped NULLs can be used in many more places, and untyped
parameter markers can be used nearly anywhere, thanks to deferred prepare,
where DB2 does not resolve the type of a parameter marker until it sees the first
actual value.
DB2 also supports defaulting procedure parameters and the association of
arguments to parameters by name.
Implicit casting is also supported during function resolution. When the data types
of the arguments of a function being started cannot be promoted to the data
types of the parameters of the selected function, the data types of the arguments
are implicitly cast to the data types of the parameters.
12
1.2.5 PL/SQL
DB2 introduced native PL/SQL support. Figure 1-2 on page 13 shows that the
DB2 engine now includes a PL/SQL compiler with the SQL PL compiler. Both
compilers produce virtual machine code for DB2 SQL Unified Runtime Engine. It
is important to note that monitoring and development tools such as Optim
Database Tools are hooked into DB2 at the runtime engine level. DBAs and
application programmers develop and debug their PL/SQL source.
Editor
PL/SQL
compiler
Optim
Development
Studio
PL/SQL
compiler
Debugger
Profiler
DB2 server
Database
The integration of PL/SQL into DB2 as a first class procedural language has
several implications:
There is no translation. The source code remains as it is in the schema
catalog.
Developers can continue working in the language with which they are familiar.
There is no need to translate logic to DB2 dialect even if new logic is written in
SQL PL. Routines using different dialects can call each other.
Packaged application vendors can use one source code against both Oracle
and DB2.
Both PL/SQL and SQL PL produce the same virtual machine code for the
DB2 SQL Unified Runtime Engine. Therefore, by design, both PL/SQL and
SQL PL perform at the same speed.
Because the debugger infrastructure hooks directly into the SQL Unified
Runtime Engine, PL/SQL is naturally supported by IBM Data Studio.
Chapter 1. Introduction
13
Scalar functions
Before each row triggers
After each row triggers
Procedures
Anonymous blocks
PL/SQL packages
14
Package initialization.
Public synonyms on packages.
DBMS_OUTPUT
DBMS_SQL
DBMS_ALERT
DBMS_PIPE
DBMS_JOB
DBMS_LOB
DBMS_UTILITY
DBMS_DDL
UTL_FILE
UTL_MAIL
UTL_SMTP
UTL_DIR
Chapter 1. Introduction
15
16
Enjoy further savings when you purchase training at a discount with an IBM
Education Pack online account, which is a flexible and convenient way to pay,
track, and manage your education expenses online. Check your local Information
Management Training and Education website or with your training representative
for the most recent training schedule.
Table 1-1 lists the DB2 educational offerings that are available.
Table 1-1 DB2 educational offerings
Course title
Classroom
Code
CE031
SQL Workshop
CE121
1E131
CL284
DB2 10.1 for Linux, UNIX, and Windows Quickstart for Exp.
Relational DBAs
CL484
DB2 10.1 for Linux, UNIX, and Windows New Feat. and DB Migr.
Considerations
CL313
CL2X3
DB2 9 pureScale Implementation and Ctrl for DB2 for Linux, UNIX,
and Windows Admins
CL800
CL121
CL141
CL131
CL413
CF492
CL720
Chapter 1. Introduction
17
18
Videos
The following videos are available from YouTube:
Native PL/SQL support:
http://www.youtube.com/watch?v=EnpDMvobUmE
Moving to DB2 is easy:
http://www.youtube.com/watch?v=HJx3KZ5byN0
CLPPlus:
http://www.youtube.com/watch?v=3PndCKWlpJk
Online Schema change:
http://www.youtube.com/watch?v=wvM0xl9OXyE
DB2 10.1 - Time Travel Query:
http://www.youtube.com/watch?v=7JrQdzdYwOA
Topics
Run Oracle applications on DB2 10.1 for Linux, UNIX, and Windows is available
from IBM developerWorks at:
http://www.ibm.com/developerworks/data/library/techarticle/dm-0907oracl
eappsondb2/index.html
Chapter 1. Introduction
19
20
Chapter 2.
Language compatibility
features
IBM DB2 10.5 for Linux, UNIX, and Windows operating systems includes
Structured Query Language (SQL) and Procedural Language SQL (PL/SQL)
capabilities that facilitate database enablement from Oracle. These features
provide native support for the data types, scalar functions, packages and
language elements, built-in packages, and the PL/SQL procedural language.
Native support means that these interfaces are supported in the engine of the
DB2 database server at the same level of integrity and efficiency as any other
DB2 native language element. As a result, when you use these features, they
perform with the same speed and efficiency that the DB2 product offers.
This chapter introduces the language features that DB2 supports and provides
examples of their use. It includes the following sections:
DB2 compatibility features references
Schema compatibility features
DB2 command-line utilities
21
22
Symbol
Description
ORA
SYB
MYS
You can also selectively enable specific compatibility features by setting specific
pieces of the DB2_COMPATIBILITY_VECTOR registry variable. For best results, when
established, keep the selected compatibility level for the life of the database.
Table 2-2 presents the possible variable settings for the
DB2_COMPATIBILITY_VECTOR registry variable.
Table 2-2 DB2_COMPATIBILITY_VECTOR values
Bit position
Compatibility feature
Description
1 (0x01)
ROWNUM
2 (0x02)
DUAL
3 (0x04)
4 (0x08)
Hierarchical queries
5 (0x10)
6 (0x20)
7 (0x40)
8 (0x80)
TRUNCATE TABLE
9 (0x100)
Character literals
10 (0x200)
Collection methods
23
Bit position
Compatibility feature
Description
11 (0x400)
Data dictionary-compatible
viewsa
12 (0x800)
PL/SQL compilationb
13 (0x1000)
Insensitive cursors
14 (0x2000)
INOUT parameters
15 (0x8000)
17 (0x10000)
SQL data-access-level
enforcement
18 (0x20000)
a. Applicable only during database creation. Enabling or disabling this feature affects only later
created databases.
b. For more information, see:
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/index.jsp?topic=/com.ibm.db2.luw.apdv.p
lsql.doc/doc/c0053608.html
You must set the DB2_COMPATIBILITY_VECTOR registry variable to the wanted level
before you create a database, as shown in Example 2-1. In addition, you must
also restart the DB2 instance after the value is changed for it to take effect.
Example 2-1 Setting DB2_COMPATIBILITY_VECTOR
db2set DB2_COMPATIBILITY_VECTOR=ORA
The DB2_DEFERRED_PREPARE_SEMANTICS registry variable also enhances
compatibility between Oracle and DB2 user applications, such as those written in
Java. By setting this registry variable to YES, dynamic SQL statements are not
evaluated at the PREPARE step, but rather are evaluated on OPEN or EXECUTE
calls. You can use this setting to take advantage of the DB2 implicit data type
casting feature. It also avoids errors that might otherwise occur during the
PREPARE step when untyped parameter markers are present.
24
db2set DB2_DEFERRED_PREPARE_SEMANTICS=YES
If you plan on using DBMS_JOB module/package, you might also want to activate
the Administrative Task Scheduler (ATS) facility. This facility is turned off by
default, although you can still define and modify jobs (tasks).
To enable the ATS, set the variable that is shown in Example 2-3.
Example 2-3 Setting DB2_ATS_ENABLE
db2set DB2_ATS_ENABLE=YES
Example 2-4 demonstrates the commands and the correct sequence of
these steps.
Example 2-4 Setting DB2_COMPATIBILITY_VECTOR
25
connect to testdb;
db2 update db cfg using auto_reval DEFERRED_FORCE;
db2 update db cfg using decflt_rounding ROUND_HALF_UP;
db2 update db cfg using nchar_mapping CHAR_CU32;
db2 update db cfg using extended_row_sz ENABLE;
db2 connect reset;
db2 deactivate db testdb;
db2 connect to testdb;
NCHAR_MAPPING
This parameter determines the data type mapping for national character
string data types in Unicode databases. It optimizes NCHAR types to
predominantly multi-byte or single-byte Unicode character environments. In
Europe, Africa, and the Americas, UTF-8 (1 - 4 bytes encoding) is more
efficient, and in Asia, UCS-2 (2 or 4 bytes encoding) is often more efficient.
26
27
Record types
Record types are supported in PL/SQL contexts when declared as part of
PL/SQL packages (header or body). Alternatively, you can create the same type
of constructs outside of a PL/SQL package and still reference them inside of
routines and packages.
28
29
30
Collection types
The DB2 data server supports collection types, such as the VARRAY collection
type and associative arrays in PL/SQL. A PL/SQL collection is a set of ordered
data elements with the same data type. Individual data items in the set can be
referenced using a subscript notation within parentheses. When the database is
in Oracle compatible mode, you can use collection methods to obtain information
about collections or to modify them.
31
You cannot use chained access operators to access deep nested objects.
Instead, you must create a local variable and use one level access operator at
the time. For example, if you have an array of arrays of integers that are called
int_int_array that accesses the integer with the index 1 inside of the array index
2, you cannot access the array through the expression int_int_array(2)(1).
You must define a local variable (called int_array for example) and access it in
two steps, first using int_array:=int_int_array(2) and then using
int_array(1) to access the nested value.
The PL/SQL VARRAY syntax is as follows:
TYPE <varraytype> IS VARRAY( <max_index_value> ) OF <datatype>;
Table 2-3 summarizes the VARRAY collection methods that are supported by the
DB2 data server in a PL/SQL context.
Table 2-3 VARRAY collection methods that are supported in PL/SQL
Collection method
Description
COUNT
DELETE
EXISTS (n)
EXTEND
EXTEND (n)
FIRST
LAST
LIMIT
NEXT (n)
PRIOR (n)
TRIM
TRIM (n)
In DB2, do not define the VARRAY keyword either inside the PL/SQL packages or
in stand-alone CREATE TYPE statements.
32
Example 2-10 shows how to declare a VARRAY as part of a PL/SQL package and
how to start this VARRAY and its collection methods in an anonymous block. This
syntax is similar to the syntax that is used in Oracle. The example also
demonstrates how to insert multiple rows in a table.
Example 2-10 VARRAY usage in DB2
-- Example setup:
CREATE TABLE emp(ENAME VARCHAR2(10))/
INSERT INTO emp(ENAME) VALUES
('Mike' ), ('Peter'), ('Larry'), ('Joe'), ('Curly')/
CREATE PACKAGE Types_package
AS
TYPE emp_arr_typ IS VARRAY(5) OF VARCHAR2(10);
TYPE emp_group_typ is VARRAY(10) of emp_arr_typ; -- nesting arrays
END;
/
SET SERVEROUTPUT ON
/
DECLARE
emp_arr
emp_arr_1
emp_arr_2
emp_grp
Types_package.emp_arr_typ;
Types_package.emp_arr_typ;
Types_package.emp_arr_typ;
Types_package.emp_group_typ;
33
END LOOP;
-- Excercise nested collections:
h := emp_arr.LAST/2;
FOR j IN emp_arr.FIRST..emp_arr.LAST LOOP
if j < h then
emp_arr_1[j] := emp_arr[j];
else
emp_arr_1[j-h+1] := emp_arr[j];
end if;
END LOOP;
-- create the nested array
emp_grp[1]:=emp_arr_1;
emp_grp[2]:= emp_arr_2;
-- to access the second group elements we use one of the existing emp_arr_x to
hold the temporary value (any variable of same tile would do)
emp_arr_1 := emp_grp[2];
FOR j IN emp_arr_1.FIRST..emp_arr_1.LAST LOOP
DBMS_OUTPUT.PUT_LINE(emp_arr(j));
END LOOP;
-- Use NEXT(n) to obtain the subscript of the next element:
k := emp_arr.FIRST;
WHILE k IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(emp_arr(k));
k := emp_arr.NEXT(k);
END LOOP;
-- Use PRIOR(n) to obtain the subscript of the previous element:
l := emp_arr.LAST;
WHILE l IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(emp_arr(l));
l := emp_arr.PRIOR(l);
END LOOP;
DBMS_OUTPUT.PUT_LINE('COUNT: ' || emp_arr.COUNT);
emp_arr.TRIM;
DBMS_OUTPUT.PUT_LINE('COUNT: ' || emp_arr.COUNT);
emp_arr.TRIM(2);
DBMS_OUTPUT.PUT_LINE('COUNT: ' || emp_arr.COUNT);
34
35
36
Collection method
Description
COUNT
DELETE
DELETE (n)
Collection method
Description
EXISTS (n)
EXTEND
EXTEND (n)
FIRST
LAST
LIMIT
NEXT (n)
PRIOR (n)
You can define associative arrays in DB2 inside the PL/SQL packages or using
SQL PL syntax as stand-alone types.
Example 2-12 shows how to create, initialize, and display the values of an
associative array. The ROWTYPE attribute is used to define emp_arr_typ.
Example 2-12 Associative array
37
emp_arr(i).empno := r_emp.empno;
emp_arr(i).ename := r_emp.ename;
END LOOP;
FOR j IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE(emp_arr(j).empno || '
emp_arr(j).ename);
END LOOP;
' ||
END;
/
38
Example 2-13 exemplifies this conversion strategy. The application must retrieve
the data tree that is represented in Example 2-13 (for better readability) as an
XML document.
Example 2-13 XML encoding of the sample data model
<?xml version="1.0" encoding="UTF-8"?>
<customer> <!-- Object, deep nested L0 -->
<info> <!-- Object, deep nested L1 -->
<name> <!-- Object L2 -->
<first_name>John</first_name> <!-- Leaf value -->
<last_name>Smith</last_name>
</name>
<birth_date>1963-03-29</birth_date>
</info>
<adresses> <!-- Object Array, deep nested L1 -->
<address> <!-- Object, deep nested L2 -->
<phones> <!-- Object Array, deep nested L3 -->
<phone> <!-- Object L4 -->
<phone_provider>Bell</phone_provider>
<phone_number>000-111-2222</phone_number>
</phone>
<phone>
<phone_provider>Rogers</phone_provider>
<phone_number>000-111-2223</phone_number>
</phone>
</phones>
<country>Canada</country>
<country_div>Ontario</country_div>
<city>Toronto</city>
<street>Warden Ave.</street>
<number>8200</number>
<code>M2H-2P7</code>
<building_address> <!-- Object, deep nested L3 -->
<entry>A1</entry>
<floor>2</floor>
<apartment_number>120</apartment_number>
</building_address>
</address>
</adresses>
</customer>
39
Using the DB2 nested objects and object instance factory UDFs, you can build
the same document at the server side for retrieval in one single query
(Example 2-14).
Example 2-14 DB2 object notation encoding of the model from the previous example
CUSTOMER(
INFO(
NAME('John','Smith'),
'1967-02-23'
),
ADDRESSES(
ADDRESS(
PHONES(
PHONE(
'Rogers',
'000-111-2223'
)
), -- phone array
'Canada', -- country
'Ontario', -- country_div
'Toronto', -- city
'Warden Ave.', -- street
'M2H-2P7', -- code
BUILDING_ADDRESS(
'A1', -- entry
3, -- floor
120 -- apartment
) -- building_ddress
) -- address
)-- address array
)
This structure can be retrieved at the application side using an application code
equivalent to the code in the JUnit test case (Example 2-15).
Example 2-15 JUnit test client sample that retrieves the model from the previous example
40
"CALL
dnobj.generate_customer_sample_object_array(?,?,?,?,?)"
);
cstmt.registerOutParameter(1, java.sql.Types.ARRAY);
cstmt.setInt(2, 1);// using same seed to keep the test happy
:)
cstmt.setInt(3, 1);
cstmt.setInt(4, 1);
cstmt.setInt(5, 1);
cstmt.execute();
// simply dump of the received data tree by walking
// the tree down for any "A" array or "S" structure
// entity detected, leaf field discrimination is positional
String result = sqlObjectToText("customers->",
cstmt.getArray(1));
...
// verify by comparing to the expected value
String expected=
"customers->A[0]S[0]S[0]S[0]=John\n"
+ "customers->A[0]S[0]S[0]S[1]=Smith\n"
+ "customers->A[0]S[0]S[1]=2012-02-23 22:04:31.0\n"
+ "customers->A[0]S[1]A[0]S[0]A[0]S[0]=Rogers\n"
+ "customers->A[0]S[1]A[0]S[0]A[0]S[1]=000-111-2223\n"
+ "customers->A[0]S[1]A[0]S[1]=Canada\n"
+ "customers->A[0]S[1]A[0]S[2]=Ontario\n"
+ "customers->A[0]S[1]A[0]S[3]=Toronto\n"
+ "customers->A[0]S[1]A[0]S[4]=Warden Ave.\n"
+ "customers->A[0]S[1]A[0]S[5]=M2H-2P7\n"
+ "customers->A[0]S[1]A[0]S[6]S[0]=A1\n"
+ "customers->A[0]S[1]A[0]S[6]S[1]=3\n"
+ "customers->A[0]S[1]A[0]S[6]S[2]=120\n"
;
assertEquals(expected,result);
} finally {
if(cstmt != null) cstmt.close();
if(con
!= null) con.close();
}
}
...
// a method useful for walking a deep nested object and
// convert it in a simple textual representation
// the text representation is used for inspection and validation
41
// of the result
// In this representation Arrays are named with A and structures
S
public String sqlObjectToText(String name, Object obj) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bos);
if (obj instanceof java.sql.Array) {
try {
Object[] a = (Object[]) ((Array) obj).getArray();
for (int i = 0; i < a.length; ++i) {
out.print(sqlObjectToText(name + "A[" + i + "]", a[i]));
}
} catch (SQLException e) {
out.println(name + "Array:-exception->" + e.getMessage());
}
} else if (obj instanceof java.sql.Struct) {
Struct a = (Struct) obj;
try {
Object[] attrs = a.getAttributes();
for (int i = 0; i < attrs.length; ++i) {
out.print(sqlObjectToText(name + "S[" + i + "]",
attrs[i]));
}
} catch (SQLException e) {
out.println(name + "struct:-exception->" + e.getMessage());
}
} else {
out.println(name + "=" + obj);
}
out.flush();
return bos.toString();
}
Object (structured) types are not supported in DB2. However, equivalent
structures exist to support the same functions. When you convert Oracle object
types (Example 2-16), you can use the DB2 ROW type to port the data structure
and use UDFs to port object methods.
Example 2-16 Oracle object sample
create or replace
TYPE PHONE_TYPE AS OBJECT
(
42
PHONE_PROVIDER VARCHAR2(32),
PHONE_NUMBER VARCHAR2(32),
CONSTRUCTOR FUNCTION PHONE_TYPE (
p_phone_provider VARCHAR2,
p_phone_nr
VARCHAR2
)
RETURN SELF AS RESULT
)
/
CREATE OR REPLACE
TYPE BODY PHONE_TYPE AS
CONSTRUCTOR FUNCTION PHONE_TYPE (
p_phone_provider VARCHAR2,
p_phone_nr
VARCHAR2
)
RETURN SELF AS RESULT
AS
BEGIN
PHONE_PROVIDER := p_phone_provider;
PHONE_NUMBER
:= p_phone_nr;
RETURN;
END;
END;
/
Example 2-17 shows the code that is converted to DB2 compatible syntax.
Example 2-17 Conversion to DB2 row type to allow deep nesting of the structures
create or replace
TYPE PHONE_TYPE AS ROW
(
PHONE_PROVIDER VARCHAR2(32),
PHONE_NUMBER VARCHAR2(32)
)
/
create or replace
FUNCTION PHONE (
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
) RETURNS PHONE_TYPE
LANGUAGE SQL
BEGIN
declare OBJ PHONE_TYPE;
set OBJ.PHONE_PROVIDER= PHONE_PROVIDER;
43
CREATE OR REPLACE
TYPE PHONE_TYPE_ARRAY AS
TABLE OF PHONE_TYPE
/
Example 2-19 Array of rows for the equivalent type definition of Oracle type
CREATE OR REPLACE
TYPE PHONE_TYPE AS
ARRAY[]
/
44
2.1.3 Subtypes
A subtype is a definition of a type that is based on a built-in type. Subtypes
provide a layer of abstraction between variables and parameters and the data
types that they use. This layer allows you to concentrate any changes to the data
types in one location. You can add constraints to subtypes so that they cannot be
nullable or limited to a specific range of values.
45
DECLARE
SUBTYPE tinyint IS INTEGER RANGE -256..255 NOT NULL;
val tinyint := 255;
BEGIN
val := val + 1;
END;
/
At run time, you receive the following error message:
SQL20552N The cast or assignment failed because the value does not
conform to the data type constraint of the user-defined type.
User-defined type: "TINYINT". Value: "256".
This error message appears when the values of a parameter or a variable that is
based on the subtype violate the boundaries of the defined range of that subtype.
46
Assignment statement
The assignment statement sets a previously declared variable or formal
parameter (OUT or IN OUT) to the value of an expression. Example 2-23 shows the
assignment syntax in several combinations, such as variables, parameters, and
constants.
Example 2-23 Assignment statement
47
DECLARE
found BOOLEAN;
var1 NAMEARRAY;
cur0 SYS_REFCURSOR;
n number :=0;
BEGIN
FOR cur0 IN (SELECT name FROM sysibm.systables ORDER BY 1) LOOP
n := n + 1;
var1(n):= cur0.name;
IF ( var1(n) = 'TEST' ) THEN
found := TRUE;
dbms_output.put_line('TEST table found at index entry = '||
N);
EXIT;
END IF;
END LOOP;
END;
/
48
DECLARE
var1 VARCHAR2(200);
BEGIN
var1:= 'declare currentime timestamp;
BEGIN
currentime := sysdate;
dbms_output.put_line(''The time now is ''||currentime);
END';
EXECUTE IMMEDIATE var1;
END;
/
For more details about anonymous blocks, see Anonymous blocks on page 84.
SQL statements
You can use SQL statements that are supported within PL/SQL contexts to
modify data or to specify the manner in which statements are executed. These
SQL statements have the same usage and meaning in both PL/SQL and
SQL PL.
Table 2-5 lists the SQL statements that can be run by the DB2 server within
PL/SQL contexts.
Table 2-5 SQL statements
Command
Description
DELETE
INSERT
MERGE
SELECT INTO
UPDATE
49
BEGIN
NULL;
END;
/
Sometimes, a NULL statement is used in the EXCEPTION section to indicate that
the raised exception condition should be ignored and that processing should
continue to next sequential block or statement.
50
d_empid VARCHAR2(6);
BEGIN
-- Return names information about deleted rows
DELETE FROM emp WHERE empid=v_empid
RETURNING emplastname, empfirstname INTO d_lastname, d_firstname;
-- Return empid after modification, as all the employee IDs will have
the prefix '99'
INSERT INTO emp(empid, emplastname, empfirstname)
VALUES(990000+v_empid, d_lastname, d_firstname)
RETURNING empid INTO d_empid;
-- Return properly formated names with INITCAP as part of data cleaning
UPDATE emp SET emplastname = INITCAP(emplastname),
empfirstname = INITCAP(empfirstname)
WHERE empid=d_empid
RETURNING emplastname, empfirstname INTO d_lastname, d_firstname;
END;
/
Assume that you have a denormalized EMP table with three columns (empid,
emplastname, and empfirstname). The example hypothetically cleans up
(maintains) data to change the empid, lastname, and firstname of the EMP table.
Statement attributes
You can use the following PL/SQL attributes to determine the effect of an
SQL statement:
SQL%FOUND
This attribute has a Boolean value that returns TRUE if at least one row is
affected by an INSERT, UPDATE, or DELETE statement, or if a SELECT INTO
statement retrieved one row.
51
BEGIN
INSERT INTO employee (empno, lastname, job, salary)
VALUES (9001, 'JONES', 'CLERK', 850.00);
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('Row has been inserted');
END IF;
END;
/
SQL%NOTFOUND
This attribute has a Boolean value that returns TRUE if no rows are affected by
an INSERT, UPDATE, or DELETE statement. The value is also TRUE if no row is
retrieved by a SELECT INTO statement, although it is more typical to check for a
NO_DATA_FOUND exception rather than SQL%NOTFOUND in this case.
Example 2-29 shows how to use SQL%NOTFOUND.
Example 2-29 Using SQL%NOTFOUND
BEGIN
UPDATE employee SET hiredate = '03-JUN-07' WHERE empno = 90000;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('No rows were updated');
END IF;
END;
/
SQL%ROWCOUNT
This attribute has an integer value that represents the number of rows that are
affected by an INSERT, UPDATE, or DELETE statement. Example 2-30 illustrates
how to use SQL%ROWCOUNT.
Example 2-30 Using SQL%ROWCOUNT
BEGIN
UPDATE employee SET hiredate = '03-JUN-07' WHERE empno = 000010;
DBMS_OUTPUT.PUT_LINE('# rows updated: ' || SQL%ROWCOUNT);
END;
/
52
DECLARE
a INTEGER := 1;
b VARCHAR2(30) := 'default string';
BEGIN
IF (a = 1) THEN
B:='this is string';
ELSIF (a = 2) THEN
B:='this is another string';
ELSE
NULL;
END IF;
END;
/
Similar to an IF statement, CASE statements and expressions execute one or a
set of statements when a specified search condition is true. The CASE statement
is a stand-alone statement that is distinct from the CASE expression, which must
appear as part of an expression.
CASE statements and expressions can take one of the following forms:
Simple CASE statement
The simple CASE statement and expression attempt to match an expression
(known as the selector) to another expression that is specified in one or more
WHEN clauses. A match results in the execution of one or more
corresponding statements.
53
BEGIN
FOR my_cursor IN (SELECT lastname, empno, workdept FROM employee
ORDER BY empno) LOOP
DBMS_OUTPUT.PUT(my_cursor.lastname || ', ' || my_cursor.empno ||
' belongs to: ');
CASE my_cursor.workdept
WHEN 'A00' THEN
DBMS_OUTPUT.PUT_LINE('SPIFFY COMPUTER SERVICE DIV.');
WHEN 'B01' THEN
DBMS_OUTPUT.PUT_LINE('PLANNING');
WHEN 'C01' THEN
DBMS_OUTPUT.PUT_LINE('INFORMATION CENTER');
WHEN 'D01' THEN
DBMS_OUTPUT.PUT_LINE('DEVELOPMENT CENTER');
ELSE
DBMS_OUTPUT.PUT_LINE('Not IT related');
END CASE;
END LOOP;
END;
/
54
DECLARE
workdept
VARCHAR2(3);
current_dept
VARCHAR2(40);
BEGIN
current_dept := CASE workdept
WHEN 'A00' THEN 'SPIFFY COMPUTER SERVICE DIV.'
WHEN 'B01' THEN 'PLANNING'
WHEN 'C01' THEN 'INFORMATION CENTER'
WHEN 'D01' THEN 'DEVELOPMENT CENTER'
WHEN 'E01' THEN 'SUPPORT SERVICES'
WHEN 'E11' THEN 'OPERATIONS'
ELSE 'Not IT related'
END;
DBMS_OUTPUT.PUT_LINE(current_dept);
END;
/
Searched CASE statement
A searched CASE statement uses one or more Boolean expressions to
determine which statements to execute.
Example 2-35 presents an example of a searched CASE statement. The usage
of the searched CASE expression in PL/SQL is similar to the simple CASE
shown in Example 2-32 on page 54; only a comparison expression is used to
find a match.
Example 2-35 Searched CASE
55
DECLARE
a INTEGER := 0;
BEGIN
a:= 1;
LOOP
DBMS_OUTPUT.PUT_LINE('this is string #'|| a);
EXIT WHEN a <= 10;
a:=a+1;
END LOOP;
END;
/
The WHILE statement repeats a set of SQL statements if a specified expression is
true and it can be embedded within a PL/SQL procedure, function, or anonymous
block statement. The condition is evaluated immediately before each entry into
the loop body. The following lines are the syntax of the WHILE LOOP statement:
WHILE <condition> LOOP
<statements>
[< EXIT WHEN condition>]
END LOOP;
56
DECLARE
a INTEGER := 0;
BEGIN
a:= 1;
WHILE (a <= 10) LOOP
DBMS_OUTPUT.PUT_LINE('this is string #'|| a);
a:=a+1;
END LOOP;
END;
/
A FOR LOOP over a predetermined number of values (FOR with integer variant) can
iterate over a range of values to execute a set of SQL statements more than
once. With the REVERSE keyword, it decrements the variable over the range of
value. The syntax is as follows:
FOR <loop_variable> IN [REVERSE] <range_of_values> LOOP
<statements>
[<EXIT WHEN condition>]
END LOOP;
Example 2-38 shows a simple FOR (integer variant) statement (or LOOP) that loops
10 times.
Example 2-38 FOR LOOP over predetermined number of values
DECLARE
a INTEGER := 0;
BEGIN
a:= 1;
FOR a in 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('this is string #'|| a);
END LOOP;
END;
/
Another variation of the FOR LOOP statement in PL/SQL is the FOR (cursor variant)
statement. The cursor FOR loop statement opens a previously declared cursor,
fetches all rows in the cursor result set, and then closes the cursor. The columns
of the SELECT statement are directly accessible inside the body of the FOR LOOP
with the <for_loop_variable.column_name> syntax.
57
Example 2-39 shows a FOR LOOP statement over a cursor statement that uses the
cursor values mapped from the cursor column for display.
Example 2-39 A FOR LOOP statement over a cursor
DECLARE
CURSOR ACCOUNT_cur IS
SELECT ACCOUNT_id, NUM_PROJECTS
FROM ACCOUNTS WHERE CREATE_DATE < SYSDATE + 30;
BEGIN
FOR ACCOUNT_rec IN ACCOUNT_cur
LOOP
update_ACCOUNT (ACCOUNT_rec.ACCOUNT_id,
ACCOUNT_rec.NUM_PROJECTS);
END LOOP;
END;
BEGIN
FOR i IN (SELECT
table_name FROM user_tables )
LOOP
DBMS_OUTPUT.PUT_LINE(i.table_name);
IF (i.table_name = 'DEPARTMENT') THEN GOTO exit_prem; END IF;
DBMS_OUTPUT.PUT_LINE(i.table_name);
END LOOP;
<<exit_prem>>
DBMS_OUTPUT.PUT_LINE('done');
END;
/
58
BEGIN
< executable statements>
EXCEPTION
WHEN condition1 [ OR
<exception handler
[ WHEN condition3 [ OR
<exception handler
END;
Predefined exceptions
DB2 provides support for the following Oracle predefined exceptions:
CASE_NOT_FOUND
CURSOR_ALREADY_OPEN
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALID_NUMBER
NO_DATA_FOUND
OTHERS
NOT_LOGGED_ON
SUBSCRIPT_BEYOND_COUNT
LOGIN_DENIED
SUBSCRIPT_OUTSIDE_LIMIT
TOO_MANY_ROWS
VALUE_ERROR
ZERO_DIVIDE
59
Custom exceptions
DB2 supports defining and calling custom defined exceptions. In Example 2-42,
custom exceptions are defined with an exception declaration and the PRAGMA
EXCEPTION_INIT syntax.
Example 2-42 Custom exception
60
THEN
RAISE statement
You can use the RAISE statement to raise a previously defined condition. For
details, see Example 2-43 and Example 2-44.
61
RAISE_APPLICATION_ERROR
The RAISE_APPLICATION_ERROR procedure provides the capability to intentionally
stop a process within an SQL procedural code from which it is called to cause an
exception. The exception is handled in the same manner as described
previously. In addition, the RAISE_APPLICATION_ERROR procedure makes a
user-defined code and error message available to the program, which can then
be used to identify the exception. This procedure uses the following
general syntax:
RAISE_APPLICATION_ERROR(error_number, message);
Example 2-45 shows a procedure that is based on the EMPLOYEES table with
multiple declarations of RAISE_APPLICATION_ERROR and its output when executed
for an employee who has no corresponding manager ID in the table.
Example 2-45 RAISE_APPLICATION _ERROR
62
END IF;
DBMS_OUTPUT.PUT_LINE ('Employee ' || p_empno || ' validated without errors');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('SQLCODE: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE ('SQLERRM: ' || SQLERRM);
END;
/
Output:
SQL> set serveroutput on
SQL> execute verify_employee (1);
SQLCODE: -438
SQLERRM: SQL0438N Application raised error or warning with diagnostic text:
"No manager entered for 1".
SQLSTATE=UD999
DB250000I: The command completed successfully.
For help with mapping PL/SQL error codes and exception names to DB2 error
codes and SQLSTATE values, see the DB2 Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.plsql.doc/doc/r0055262.html
63
3. Fetch one record at a time in a loop, moving the columns values of every
record into application host variables or a PL/SQL variable (sometimes
multiple fetches can be done at once to batch the fetches).
4. Close the cursor to free database resources.
An application (PL/SQL program or client program) consumes the records that
are produced by the cursors by fetching the records from the cursor one by one.
Cursors are record-at-a-time lengthy operations. Thus, they are less efficient
than a set-at-a-time operation. Cursors hold resources while opened. When a
cursor is the main construct that is used for passing information from RDBMS to
client applications, you must manage them properly. Close cursors as soon as
they are no longer in use or minimize cursors use for better performance.
Depending on the cursor type, the operations that you do on cursors might be
slightly different.
REF CURSOR
REF CURSOR is a type in PL/SQL that allows you to define cursor variables. These
variables hold the pointers to the cursors. Cursor variables are frequently used to
pass the result sets from the queries between various PL/SQL objects. In DB2,
REF CURSOR must be created inside a package.
Example 2-46 shows REF CURSOR with the Cur0 cursor variable.
Example 2-46 Using REF CURSOR
64
RETURN;
END;
/
65
LOOP
FETCH v_ref_cur INTO r_dept;
EXIT WHEN v_ref_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r_dept.deptname);
END LOOP;
CLOSE v_ref_cur;
END;
/
66
ORDER BY lastname;
FETCH curs INTO emp_rows;
DBMS_OUTPUT.PUT_LINE(emp_rows.lastname ||', '|| emp_rows.firstnme);
CLOSE curs;
END;
/
Implicit cursors
Implicit cursors are cursors that are automatically declared, opened, and closed
by certain PL/SQL constructs, such as a cursor declared in the FOR LOOP
structure, or for a single row fetch of an SQL SELECT INTO statement.
Example 2-49 demonstrates a usage of the implicit cursor. It does not include an
explicit cursor declaration.
Example 2-49 Using an implicit cursor
DECLARE
BEGIN
FOR rec IN (SELECT lastname, firstnme
FROM employee ORDER BY lastname)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.lastname ||', '||rec.firstnme);
END LOOP;
END;
/
Parameterized cursors
Parameterized cursors are strongly typed cursors where the parameters are
associated with the cursor in the definition, as shown in Example 2-50. DB2
requires that you specify the length of the parameter in the parameterized cursor,
name0 VARCHAR2(30).
Example 2-50 Using a parameterized cursor
67
Static cursors
Static cursors are cursors that are associated with one query that is known at
compile time. Example 2-51 modifies the parameterized cursor example
(Example 2-50 on page 67) to illustrate the static cursor.
Example 2-51 Using a static cursor
68
Avoid returning cursor variables to the application layer or, if that is not possible,
use insensitive or forward only cursor types. The application code must read the
full amount of data from the cursors and close (release the server-side
resources) as soon as possible. This development pattern can provide much
better throughput and overall application performance. Also, if more than one
cursor is returned by the SQL statement, wrapping all the data using XML
publishing or deep-nested objects (a combination of rows and arrays) in one
single entity can improve the overall throughput with the database server. This
case is specific to applications that retrieve the full data model of a complex entity
(such as a customer profile) and then work with it in the application
server memory.
You want to avoid the worst case scenario where the application layer opens a
cursor for update and keeps the cursor open for an undetermined amount of
time. This pattern might work well in non-OLTP environments, but in an OLTP
environment, it can lead to low performance and locking issues.
69
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE empltest RENAME COLUMN empid TO
emp_id ';
EXECUTE IMMEDIATE 'ALTER TABLE empltest ALTER COLUMN emp_id SET DATA
TYPE INT';
END;
/
When you manipulate data within a database server, it is usually better to use
statically compiled code. In this case, the database compiles, verifies, and
optimizes the SQL statements immediately.
Example 2-53 shows a simple procedure with a few static SQL statements for
manipulating data.
Example 2-53 Data manipulation
CREATE OR
BEGIN
INSERT
INSERT
UPDATE
DELETE
END;
/
In this case, using the EXECUTE IMMEDIATE statement (Example 2-54) to achieve
the same purpose provides no advantage. In addition, if a syntax error is present
in one of these EXECUTE IMMEDIATE statements, it is discovered at their run time,
which is too late, instead of being caught at compilation time, which is early in the
development process.
Example 2-54 EXECUTE IMMEDIATE is not required
70
Alternatively, the EXECUTE IMMEDIATE statement is often used to run a DML that is
constructed, as shown in Example 2-55. It also demonstrates how variables can
be passed between procedures.
Example 2-55 A common usage of EXECUTE IMMEDIATE
CREATE OR
v_var1
v_var2
v_var3
v_var4
BEGIN
v_var1
v_var2
v_var3
v_var4
'111111';
'L1';
'F1';
'999999';
71
DECLARE
id VARCHAR2(6);
LName VARCHAR2(6);
FName VARCHAR2(6);
BEGIN
EXECUTE IMMEDIATE 'BEGIN add_emp3; END' ;
--- if the procedure has arguments, use the following syntax
--- EXECUTE IMMEDIATE 'BEGIN add_emp3(:1, :2 ,:3); END'
--- using id, LName, FName;
END;
/
72
In DB2, the EXECUTE IMMEDIATE statement does not currently support SELECT
INTO or VALUES for retrieving data. You can use a cursor operation to achieve the
direct equivalent.
ASCII
CHR, CHAR
CONCAT
CONCAT with ||
INITCAP
INSTR
INSTRB
73
LENGTH
LOWER
LPAD
LTRIM
REPLACE
RPAD
TO_SINGLE_BYTE
RTRIM
SOUNDEX
SUBSTR
SUBSTR4
TRANSLATE
TRIM
UPPER
Conversion functions
DB2 extends the TO_CHAR, TO_NUMBER, TO_DATE, and TO_TIMESTAMP conversion
functions. These functions are identical to their respective Oracle functions,
except for the support of the NLS parameter.
The following conversion functions are supported in DB2:
CAST
CONVERT
TO_CHAR
TO_DATE
TO_TIMESTAMP
TO_CLOB
TO_NUMBER
74
ADD_MONTHS
CURRENT_DATE
CURRENT_TIMESTAMP
LAST_DAY
TIMESTAMPDIFF
LOCALTIMESTAMP
MONTHS_BETWEEN
NEXT_DAY
ROUND (date)
SYSDATE
TO_CHAR
TO_DATE
TRUNC (date)
75
ELSE -negative
tz1 := '-0'||substr(tz,2, length(tz)-1);
END IF;
END;
ELSIF ( colind = 4 ) THEN
BEGIN
IF ( indminus != 0 ) OR ( indplus != 0 ) THEN
tz1 := tz;
END IF;
END;
ELSE
RAISE syntax_function;
END IF;
IF (length(tz1) = 5 ) AND ( locate(':', tz1) = 4 ) THEN
tz2 := tz1||'0';
ELSIF (length(tz1) = 4 ) AND ( locate(':', tz1) = 4 ) THEN
tz2 := tz1||'00';
ELSE
tz2 := tz1;
END IF;
am_pm := CASE WHEN (hour(ts) <= 12) THEN ' AM ' ELSE ' PM ' END;
RETURN CAST(ts AS TIMESTAMP(9)) || am_pm || tz2;
EXCEPTION
WHEN syntax_function THEN
RAISE_APPLICATION_ERROR(-20001, 'incorrect syntax format for '||tz);
END;
/
Example 2-60 shows a simple implementation for Oracle TZ_OFFSET function that
returns the zone offset for a time zone. The exception checking is not included for
simplicity. Also, not all the zones are shown, because there are more than 400
zones.
Example 2-60 TZ_OFFSET implementation
76
'+03:00'
'+00:00'
'+02:00'
'+01:00'
'+01:00'
'-09:00'
'-08:00'
'-04:00'
'-03:00'
'-04:00'
'+00:00'
'+01:00'
'+02:00'
'+03:00'
RETURN offset;
END;
/
From TZ_OFFSET_ZONE, you can develop a DB2 version of the NEW_TIME scalar
function, which takes a time in one time zone and converts it to a time in another
time zone, for example, NEW_TIME (time, zone1, zone2). This function returns
the correct output only if the database is in Oracle compatibility mode.
Example 2-61 shows a simple implementation (assuming that you use the
correct inputs).
Example 2-61 NEW_TIME implementation
CREATE OR REPLACE FUNCTION new_time_db2(date1 DATE, zone1 VARCHAR, zone2 varChar)
RETURN DATE
IS
offset1 NUMBER;
offset2 NUMBER;
BEGIN
offset1 := CASE
WHEN zone1 = 'AST' THEN
-4
77
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
ELSE
99
END;
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
zone1
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
'ADT'
'BST'
'BDT'
'CST'
'CDT'
'EST'
'EDT'
'GMT'
'HST'
'HDT'
'MST'
'MDT'
'NST'
'PST'
'PDT'
'YST'
'YDT'
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
-3
-11
-10
-6
-5
-5
-4
0
-10
-9
-7
-6
-3.5
-8
-7
-9
-8
offset2
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
WHEN
ELSE
99
END;
:= CASE
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
zone2 =
'AST'
'ADT'
'BST'
'BDT'
'CST'
'CDT'
'EST'
'EDT'
'GMT'
'HST'
'HDT'
'MST'
'MDT'
'NST'
'PST'
'PDT'
'YST'
'YDT'
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
THEN
-4
-3
-11
-10
-6
-5
-5
-4
0
-10
-9
-7
-6
-3.5
-8
-7
-9
-8
78
Oracle interval functions that use Interval Year To Date and Interval Day To
Second data types could be implemented in DB2 by using the DB2 timestamp
data type, which components are considered as intervals, not a point in time. A
group of DB2 scalar functions, such as YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
and MICROSECOND, could be employed to manipulate the data. These functions
have no equivalent in Oracle.
For more examples, see Oracle to DB2 Conversion Guide for Linux, UNIX, and
Windows, SG24-7048.
Mathematical functions
DB2 also provides the following set of mathematical functions:
ABS
ACOS
ASIN
ATAN
ATAN2
AVG
BITAND
BITANDNOT
BITOR
BITXOR
CEIL
COS
COSH
COUNT
DENSE_RANK
EXP
EXTRACT
FLOOR
GREATEST
LEAST
MAX
MIN
MOD
LN
POWER
RANK
ROUND (numbers)
SIGN
SIN
SINH
STDDEV
SUM
79
TAN
SQRT
TANH
TRUNC (numbers)
VAR_POP
VAR_SAMP
VARIANCE
Error functions
The SQLCODE and SQLERRM error functions raise exception or retrieve error codes.
These functions can be used only in the EXCEPTION section of PL/SQL blocks.
SQLCODE
The SQLCODE function returns the SQLCODE value that is associated with the raised
exception. Example 2-62 shows a usage of the SQLCODE function.
Example 2-62 SQLCODE function
SQLERRM
The SQLERRM function returns the error message that is associated with the raised
exception. Example 2-63 shows a usage of the SQLERRM function.
Example 2-63 SQLERRM function
Miscellaneous functions
Oracle offers some specific functions that are not available on other database
platforms. To ensure a seamless transition from Oracle to DB2, DB2 provides
support for the following Oracle functions:
80
CARDINALITY
COALESCE
CURRENT_USER
DECODE
LAG
LEAD
NULLIF
NVL
The following examples illustrate the DECODE and NVL functions for reference to
show that there is no difference in how these functions operate on DB2 and
Oracle. The examples also demonstrate how to implement the USERENV function
in DB2.
DECODE
DB2 supports the DECODE function in the same way Oracle does, which is also
similar to the CASE statement. In Example 2-64, the query selects the number of
current projects per employee and displays a conditional message that is based
on this number.
Example 2-64 DECODE function
29
3
7
2
5
9
18
14
1
19
PARKER
KWAN
PULASKI
THOMPSON
GEYER
HENDERSON
SCOUTTEN
NICHOLLS
HAAS
WALKER
0
1
1
2
2
3
3
4
5
5
Attention
Attention
Attention
Attention
Attention
Good job
Good job
Great job
Excellent
Excellent
No projects
Single project
Single project
Need to assign projects
Need to assign projects
Working on 3 projects
Working on 3 projects
Working on 4 projects
Do not assign more projects
Do not assign more projects
81
NVL
In a similar fashion to the DECODE function, the NVL function handles
conditional NULL values and is fully supported in DB2. The query in
Example 2-66 returns a specific message when department information is not
yet assigned to the employee.
Example 2-66 Query with NVL function
31
34
32
33
30
MAUDE
JASON
RAMLAL
WING
PHILIP
SETRIGHT
GOUNOT
MEHTA
LEE
SMITH
NEW
NEW
E21
E21
E11
Today, there is a wide range of scalar functions that are used in the database
industry, and each RDBMS provides specific functions that are designed to
satisfy the requirements of their users. New functions are released by the
database vendors with every new release.
For any function that is not provided in DB2, you can develop a corresponding
DB2 equivalent using either PL/SQL or SQL PL language. In Oracle, a raw type
stores character data and is byte-oriented. In DB2, the equivalent type for raw
type is VARCHAR FOR BIT DATA.
Example 2-68 shows an example of RawToHex implementation in DB2.
Example 2-68 RawToHex implementation
82
RETURNS VARCHAR2(100)
SOURCE HEX(VARCHAR(100) FOR BIT DATA)
USERENV
The USERENV function is specific to Oracle. You can use it to retrieve information
about the current Oracle session. The function accepts several different input
parameters, as listed in Table 2-6.
Table 2-6 Parameters that could be passed to the USERENV function
Parameter
Returned value
CLIENT_INFO
ENTRYID
SESSIONID
Current session ID
INSTANCE
Oracle instance ID
ISDBA
LANG
LANGUAGE
TERMINAL
To enforce the language statement, Example 2-69 shows how to implement the
USERENV function in DB2 in both DB2 SQL PL and PL/SQL styles. Both functions
return the same result set. Depending on the value of the input parameter, the
function returns specific information for the current session identifier. In the
example, only a few parameters are used.
Example 2-69 USERENV function
--SQL PL style:
-----------------------------------------------------------------------CREATE OR REPLACE FUNCTION userenv( p VARCHAR(250))
RETURNS VARCHAR(128)
DETERMINISTIC
RETURN ( CASE upper(p)
WHEN 'SCHEMAID'
then current schema
WHEN 'CURRENT_USER' then current user
WHEN 'SESSIONID'
then APPLICATION_ID()
ELSE 'other_user_info'
END) ;
83
Anonymous blocks
Anonymous blocks are PL/SQL constructs that contain unnamed blocks of code,
which are not stored persistently in the database, but are primarily intended for a
single time execution. Unlike named blocks, which are persistently stored in the
database, the compilation and execution of an anonymous block are combined in
one step, which offers the flexibility to make immediate changes and execute
them in the same time. For comparison, a stored procedure must be recompiled
in a separate step every time its definition changes.
84
Anonymous blocks are often used to test, troubleshoot, and develop stored
procedures, simulate application runs, and build complex, ad hoc queries. During
the execution of anonymous blocks, if an exception occurs and is caught, the
transaction control can be handled in the exception section. If the exception is
not caught, all statements before the exception are rolled back to the previous
commit point.
Many examples throughout the book illustrate the use of anonymous blocks.
Example 2-70 shows a simple anonymous block to illustrate the basic construct.
Example 2-70 Simple anonymous block
DECLARE
current_date DATE := SYSDATE;
BEGIN
DBMS_OUTPUT.PUT_LINE( current_date );
END;
/
For more information about anonymous blocks, see the developerWorks topic
DB2 9.7: Using PL/SQL anonymous blocks in DB2 9.7, which is available at:
http://www.ibm.com/developerworks/data/library/techarticle/dm-0908anony
mousblocks/index.html
You can also refer to the DB2 Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.plsql.doc/doc/c0053848.html
Procedures compatibility
DB2 PL/SQL supports a wide range of Oracle PL/SQL features, syntactically and
semantically. Within this compatible context, an Oracle PL/SQL procedure can be
compiled and started directly on DB2.
Example 2-71 shows a PL/SQL procedure. With DB2 PL/SQL support, you can
directly compile and run this procedure in DB2 without any modification.
Example 2-71 PL/SQL procedure
85
86
87
Functions compatibility
The building blocks for PL/SQL functions are similar to the PL/SQL procedures,
except, as noted earlier, regarding the returning value and the invocation
method. In general, the supported features for procedures are applied to
functions except for the autonomous transaction features. However, because
DB2 implementation details of procedures and functions are a bit different, in
some exceptional cases, a handful of constructs that work in procedures might
not be applicable to functions.
Recursion is a powerful technique in programming language. Just as Oracle
does, DB2 supports recursive SQL calls through mechanisms, such as the
CONNECT BY/PRIOR constructs or by using COMMON TABLE EXPRESSION, examples of
which are available in Hierarchical queries.
DB2 also supports recursion through function calls in PL/SQL, where a function
or a procedure calls itself with different arguments. A recursive function or
procedure should have the logical conditions to ensure that the routine does not
loop forever. DB2 recursion support in PL/SQL is semantically similar to slightly
syntax differences because of implementation differences.
88
89
BEGIN
SELECT
INTO
FROM
WHERE
emp_mgr_id
empmgrid
employees
emp_id = p_emp_id;
p := report_chain(empmgrid);
IF empmgrid is not NULL THEN
RETURN '|'||empmgrid || '#' || p || '*';
END IF;
EXCEPTION WHEN OTHERS THEN RETURN ' ';
END;
/
-- clear the registry variable or set a new option for the next
-- routine
call set_routine_opt()
/
-- expect and ignore this type of message...
DB21034E The command was processed as an SQL statement because it was
not a
valid Command Line Processor command. During SQL processing it
returned:
SQL20481N The creation or revalidation of object
"DB2INST1.REPORT_CHAIN"
would result in an invalid direct or indirect self-reference. LINE
NUMBER=32.
SQLSTATE=429C3
-- ... after all code is deployed, run the command below to revalidate
all objects
call SYSPROC.ADMIN_REVALIDATE_DB_OBJECTS(NULL,NULL,NULL)
/
-- check the invalid object count
select count(1) from syscat.invalidobjects
/
-- which should be 0 !
90
External routines
External procedures and functions that are written in Java and C/C++ are
frequently found in both Oracle and DB2 applications. We provide two examples
of building routines using C and Java. Although in most cases there are no
changes to the code, it is important to review the code and ensure that it is
compatible with parameter style handling in DB2.
91
For complete information about building and running external procedures and
functions, consult the following IBM DB2 documents:
Getting Started with Database Application Development, SC10-4252
Developing SQL and External Routines, SC10-4373
92
The build scripts have the .bat (batch) extension on Windows, and have no
extension on UNIX platforms. For example, bldrtn.bat is a script to build C/C++
stored procedure on a Windows platform; bldrtn is the equivalent on UNIX.
The following procedure creates and catalogs a stored procedure that is written
in C. This procedure queries the SYSPROCEDURES table from the DB2 System
Catalog to determine in which language (Java, C, SQL, and so on) the
BONUS_INCREASE procedure is written.
1. Create and save the C source file (Example 2-77), with embedded SQL, as
outlanguage.sqc.
Example 2-77 Stored procedure in C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlca.h>
#include <sqludf.h>
#include <sql.h>
#include <memory.h>
SQL_API_RC SQL_API_FN outlanguage(char language[9]){
struct sqlca sqlca;
EXEC SQL BEGIN DECLARE SECTION;
char out_lang[9];
EXEC SQL END DECLARE SECTION;
/* Initialize strings used for output parameters to NULL */
memset(language, '\0', 9);
EXEC SQL SELECT language INTO :out_lang
FROM sysibm.sysprocedures
WHERE procname = 'BONUS_INCREASE';
strcpy(language, out_lang);
return 0;
} /* outlanguage function */
2. Create and save the .exp file as outlanguage.exp. Here are the contents of
that file:
outlanguage
3. Create and save the outlanguage_crt.db2 file, which catalogs the procedure.
Here are its contents:
CREATE PROCEDURE outlanguage (OUT language CHAR(8))
DYNAMIC RESULT SETS 0
LANGUAGE C
PARAMETER STYLE SQL
NO DBINFO
FENCED NOT THREADSAFE
93
94
Here is an example of a procedure that creates a Java UDF that retrieves the
system name from the DB2 Registry variable DB2SYSTEM:
1. The Java source file that is shown in Example 2-78 is saved as
db2system_nameUDF.java.
Example 2-78 UDF Java source
import java.io.*;
public class db2system_nameUDF {
public static String db2system_name() {
Runtime rt = Runtime.getRuntime();
Process p=null;
String s = null;
String returnString = "";
try {
// WINDOWS: **** uncomment and compile the following for
Windows
// p = rt.exec("cmd /C db2set DB2SYSTEM");
// UNIX: **** uncomment and compile the following for UNIX
p = rt.exec("db2set DB2SYSTEM");
BufferedInputStream buffer =
new BufferedInputStream(p.getInputStream());
BufferedReader commandResult =
new BufferedReader(new InputStreamReader(buffer));
try {
while ((s = commandResult.readLine()) != null)
returnString = returnString.trim() + s.trim() ;
// MAX number of chars for the DB2SYSTEM variable is 209
characters
commandResult.close();
// Ignore read errors; they mean process is done
} catch (Exception e) {
}
} catch (IOException e) {
returnString = "failure!";
}
return(returnString);
}
}
2. Compile the Java source. The compile command is:
javac db2system_nameUDF.java
95
96
User-defined packages
You can create user-defined packages in DB2 using the same syntax that you
use in Oracle. As expected, these packages have the same structure and
requirements. Similar to Oracle, DB2 user-defined packages have schema
extensions that provide name space support for the objects that they reference.
They are repositories in which executable code can be defined. Using a package
involves referencing or executing objects that are defined in the package
specification and implemented within the package.
97
You can use the CREATE OR REPLACE PACKAGE syntax to create a package
specification, which defines the interface to a package. You can create a package
specification to encapsulate related database objects, such as type, procedure,
and function definitions, within a single context in the database. A package
specification establishes which package objects can be referenced from outside
of the package (known as public elements of that package).
Similar to Oracle, you can refer to any of the public objects that are defined in the
package specification (variable, constant, exception, function, and procedure)
with the following three-part name qualifier:
<schema_name>.<package_name>.<object_name>
A package body contains the implementation of all of the procedures and
functions that are declared within the package specification. The CREATE PACKAGE
BODY statement creates a package body that contains the implementation of all of
the procedures and functions that are declared within the package specification,
and any declaration of private types, variables, and cursors.
Synonyms can be created in packages the same way as in any other
database object.
An example of a basic package, called c, is in Appendix C, Built-in modules on
page 321. This package example demonstrates the traditional PL/SQL
functionality that you frequently see in Oracle user-defined packages, such as
creating a package specification and body, a procedures definition within
package, a definition of associative array, anchor data types %TYPE and %ROWTYPE,
exception handling, cursor definition, DBMS_OUTPUT built-in package, and
other functions.
98
Description
DBMS_ALERT
DBMS_JOB
DBMS_LOB
DBMS_OUTPUT
DBMS_PIPE
DBMS_SQL
DBMS_DDL
DBMS_UTILITY
UTL_DIR
UTL_FILE
UTL_MAIL
UTL_SMTP
Provides a set of routines for sending email using the Simple Mail
Transfer Protocol (SMTP).
Detailed references about these packages, their method, and some examples
are in Appendix C, Built-in modules on page 321. More detailed information
about the built-in packages (system-defined module) is available in the DB2
Information Center at:
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2.luw.ap
dv.sqlpl.doc/doc/c0053670.html
99
2.1.12 Triggers
A trigger is a database object that consists of set of SQL statements that are
automatically executed when a specified action occurs. A trigger is associated
with a specific table and defines a set of actions within the trigger construct
(consisting of SQL or PL/SQL statements) that are triggered in response to an
SQL INSERT, DELETE, or UPDATE operation on the specified table.
The trigger functionality is supported in both Oracle and DB2. In both databases,
triggers can be used for various actions, for example, updates to other tables,
automatically generating or transforming values for inserted or updated rows, or
started functions to perform tasks, such as issuing alerts.
Triggers can be fired once for the FOR EACH ROW or FOR EACH STATEMENT
triggers, and before, instead of, or after the triggered operation occurs. In
PL/SQL, DB2 supports FOR EACH ROW BEFORE and AFTER triggers.
You can use Oracle to define a single trigger that can contain triggered actions
for INSERT, DELETE, and UPDATE actions on the table, which is known as a
multi-action trigger.
Example 2-79 shows an Oracle multi-action trigger with INSERT, DELETE, and
UPDATE actions that synchronize the entries in the EMPLOYEES and
DEPARTMENTS table when the data changes in the EMPLOYEES table. DB2
supports the SELECT statement on the same table on which the trigger is defined,
which Oracle does not.
Example 2-79 Multi-action trigger
100
101
102
Often, inside of the trigger, there are common statements that are outside of the
DELETING, INSERTING, and UPDATING blocks. In this case, it is necessary to add
these common statements to each of the separated triggers. As a preferred
practice, extract complex logic from a trigger, place it in a procedure, and started
the procedure from the trigger.
Oracle supports the enablement and disablement of triggers globally using the
ALTER TRIGGER statement. In DB2, triggers can either be dropped and re-created
instead, or global or package variables can be used to control whether the
triggers are fired in a specific context.
DB2 has a feature that is equivalent (to a certain degree) to Oracle database
triggers. Basically, you can instruct DB2 to run a stored procedure each time a
new connection to the database is created. You can use this generic feature to
configure database session environments by setting session parameters.
For more information, see the description for the connect_proc database
configuration parameter at:
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/index.jsp?topic=%2Fcom.i
bm.db2.luw.admin.dbobj.doc%2Fdoc%2Fc0057372.html
103
Autonomous transaction
DB2 provides support for autonomous transaction, a mechanism that you can
use to run a block of statements (or a separate transaction) independently of the
outcome of the started transaction. This feature is useful when you move
applications with autonomous transactions supported by Oracle. DB2 supports
PRAGMA AUTONOMOUS_TRANSACTION for the outer block of a stored procedure. A
procedure that you define with this clause runs within its own session, meaning
that the procedure is independent of the calling transaction. If you need separate
blocks of a procedure, a trigger, or a function to run autonomously, wrap the
statements into an autonomous procedure.
104
Hierarchical queries
Hierarchical queries are a form of recursive query that provides support for
retrieving a hierarchy from relational data using a CONNECT BY clause,
pseudo-columns (LEVEL), unary operators (CONNECT_BY_ROOT and PRIOR), and the
SYS_CONNECT_BY_PATH scalar function.
CONNECT BY recursion uses the same subquery for the seed (start) and the
recursive step (connect). This combination provides a concise method of
representing recursions, such as reports-to-chains presented in
CONNECT BY recursion.
The query in Example 2-82 relies on the child-parent relationship between
emp_id and emp_mgr_id in the Employees table. It returns the ID and the last
name for each employee together with their manager's employee ID and the
number of people in the reporting chain. This syntax is supported on both Oracle
and DB2.
Example 2-82 CONNECT BY recursion
SELECT
105
SELECT
(INITCAP(last_name) || ', ' || substr(first_name,1,1)) as employee,
CONNECT_BY_ROOT (INITCAP(last_name) ||', '|| substr(first_name,1,1))
as top_manager,
SYS_CONNECT_BY_PATH ((INITCAP(last_name) ||', '||
substr(first_name,1,1)),' > ') as report_chain
FROM employees
START WITH band = '5'
CONNECT BY nocycle PRIOR emp_id = emp_mgr_id
ORDER BY employee;
106
The query returns the report chain of the employees in the EMPLOYEES table,
as shown in Figure 2-3. The names shown in Figure 2-3 are fictitious. These
names are used for instructional purposes only.
ROWNUM
Oracle uses the ROWNUM pseudo-column to control the number of rows that
are returned from an SQL statement. DB2 supports the same exact syntax. The
following statement runs in both Oracle and DB2:
SELECT * FROM employees WHERE ROWNUM < 10 ;
Additionally, DB2 has its own syntax to achieve the same task: The number of
rows to read is determined by the FETCH FIRST n ROWS ONLY statement:
SELECT * FROM employees FETCH FIRST 9 ROWS ONLY;
107
UPDATE employees
SET office_id = 5
WHERE create_date < SYSDATE - 365
AND dept_code = 'D11'
AND ROWNUM <= 5 ;
DELETE FROM employees
WHERE office_id = 5
AND ROWNUM <= 5;
108
DB2 provides support for this qualifier. Figure 2-4 shows the ROWID from the
EMPLOYEE table that is retrieved with the following query:
SELECT ROWID, emp_id FROM employees;
ROWID is often used in the procedural language to speed up the row access in
high volume insert, update, and delete operations. With the current support of
ROWID in DB2, the logic works without changes. If you want to store a ROWID in
a PL/SQL variable, create the following DB2 user distinct types:
CREATE DISTINCT TYPE ROWID AS VARCHAR(16) FOR BIT DATA WITH
COMPARISONS;
CREATE FUNCTION CHARTOROWID(VARCHAR(16) FOR BIT DATA)
RETURNS ROWID SOURCE ROWID(VARCHAR());
CREATE FUNCTION ROWIDTOCHAR(ROWID)
RETURNS VARCHAR(16) FOR BIT DATA SOURCE VARCHAR(ROWID);
Example 2-85 shows a way to store a ROWID in a PL/SQL variable.
Example 2-85 ROWID distinct type
109
ln VARCHAR2(30);
BEGIN
SELECT ROWID INTO x FROM emp WHERE rownum=1;
END;
/
SELECT
e.emp_id, e.first_name, substr(e.last_name, 1, 1) last_initial,
e.dept_code,
NVL(d.dept_name, 'Unassigned or unknown Department') as department
FROM
employees e,
departments d
WHERE
e.dept_code = d.dept_code (+)
ORDER BY department DESC, emp_id asc;
110
Be aware of an invalid cycle. A cycle is formed across multiple joins when the
chain of predicates reference back to an earlier table reference. In Example 2-87,
T1 is the outer table in the first predicate and later, in the third predicate, there is
a circular reference back to T1. Although, T2 is referenced twice in both first and
second predicates, this usage is not itself a cycle.
Example 2-87 Demonstration of valid and invalid cycles
SELECT * FROM
WHERE T1.a1
AND T2.b2
AND T3.c3
allowed!
T1,T2,T3
= T2.b2(+)
= T3.c3(+)
= T1.a1(+)
-- T2 - OK
-- T2 - OK
-- Be aware of invalid cycle on T1 - Not
111
112
DECLARE
empid_var employees.empid%TYPE;
new_lastname employees.lastname%TYPE;
name_var employees.lastname%TYPE;
BEGIN
empid_var := 1000;
new_lastname := 'NEW_NAME';
--- changing the lastname of employee because of the marital status
changed
--- empid is a unique key
SELECT lastname INTO name_var FROM employees
WHERE empid = empid_var FOR UPDATE OF lastname;
UPDATE employees SET lastname = new_lastname
WHERE empid = empid_var;
END;
113
Important: If you transfer Oracle DATE in DB2 TIMESTAMP(0), all the time
parts of the TIMESTAMP data are filled with 0. This automatic fill induces a
lower selectivity for that field and can, in turn, induce suboptimal plans. If so,
you must ensure that detailed statistics are collected for the column to provide
the optimizer with correct input. Failing to do so can impact request response
time and the overall database performance.
For more information about the new data types, see the following topics at the
DB2 Information Center:
VARCHAR2 data type:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.
db2.luw.apdv.porting.doc/doc/r0052880.html
NUMBER data type:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.
db2.luw.apdv.porting.doc/doc/r0052879.html
DATE data type based on TIMESTAMP(0):
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.
db2.luw.apdv.porting.doc/doc/r0053667.html
NCHAR data types:
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2.luw
.sql.ref.doc/doc/r0057004.html
114
Altering objects
The alter objects options, such as CREATE OR REPLACE and REVALIDATE, can
simplify the process of altering database objects.
CREATE OR REPLACE allows for dynamic replacement for definitions of views,
sequences, routines, and packages, which could be altered in any order and then
be revalidated automatically the first time they are used. This situation is
especially important when there are dependencies of the database objects that
were altered. For example, if a function selects data from a view that is built on a
table that you alter, both the view and the function definition are invalidated when
the change in the table definition occurs. DB2 revalidates automatically both the
function and the view the first time when they are called.
You can also revalidate the database objects manually by running a single
database procedure named ADMIN_REVALIDATE_DB_OBJECTS. In the following
example, all objects in the schema that is called MY_SCHEMA are revalidated:
CALL ADMIN_REVALIDATE_DB_OBJECTS ( NULL, 'MY_SCHEMA', NULL);
To see different options of this procedure, see the DB2 Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/index.jsp?topic=/
com.ibm.db2.luw.sql.rtn.doc/doc/r0053626.html
The revalidation option depends on a database configuration parameter that is
called AUTO_REVAL, which is described in 2.1.1, SQL compatibility setup on
page 22.
2.2.3 Sequences
The sequences in Oracle and DB2 have the same definition and syntax. The
enablement process requires no manual changes to the CREATE SEQUENCE
statements. In addition to the sequence functionality, DB2 provides an option for
the user to use the identity column functionality when you must automatically
generate values for the table.
115
Sequence characteristics
The sequence includes the following characteristics:
Sequences are not tied to any one table.
Sequences generate sequential values that can be used in any SQL or
XQuery statement.
Because sequences can be used by any application, two expressions are
used to control the retrieval of the next value in the specified sequence and
the retrieval of the value that is generated previous to the statement that
is executed.
The PREVIOUS VALUE expression returns the most recently generated
value for the specified sequence for a previous statement within the
current session.
The NEXT VALUE expression returns the next value for the specified
sequence. The use of these expressions allows the same value to be used
across several SQL and XQuery statements within several tables.
After you create a sequence that is called EMPLOYEE_SEQUENCE, see
Example 2-89 for typical PL/SQL code calls to this sequence. This code can be
run in both Oracle and DB2.
Example 2-89 Sequence
116
117
Bitmap indexes
Support for the Oracle bitmap index is not available in DB2. This type of index is
aimed at data warehousing and is suitable for an index where there are few key
values (low cardinality), for example, gender or state. In DB2, this type of index is
not required because the DB2 optimizer might create dynamic bitmap indexes
during the execution of certain types of queries (if needed).
Indexing expressions
In Oracle, this functionality is known as a function-based index. It computes the
value of the function or expression and stores it in the index. DB2 provides the
exact same functionality. Here is its syntax:
CREATE INDEX emp_name ON emp(UPPER(name));
This feature can also be applied to multi-column indexes and it supports unique
indexes and INCLUDE columns.
118
For example, all columns that are specified in a unique constraint in DB2 must be
defined as NOT NULL, while Oracle allows NULL values in a unique constraint. You
overcome this issue in DB2 by defining a unique index on these columns instead
of defining the unique constraint in the CREATE TABLE statement. Before DB2
10.5, you can have only one NULL key value in your unique index. With DB2
10.5, a new clause, EXCLUDE NULL KEYS, is introduced. This clause prevents
NULL key values from being considered for the uniqueness of an index. A NULL
key value in a multi-column index is defined as all key parts containing the
NULL value.
In Oracle, you are not required to add a NOT NULL attribute to a column in a table
to define a primary key that includes the column. In DB2, you must explicitly
specify the NOT NULL attribute if a primary key is to be defined on that column.
To enforce referential integrity, both DB2 and Oracle support foreign key
constraints, in which a parent tables primary key is referenced by the child table.
Additionally, you can use DB2 to enforce the referential integrity for any unique
constraints (not just for the primary key). This constraint is especially useful when
the unique constraint is a composite key and it can automatically apply more
complex dependencies between the tables.
When you must perform operations, such as loading data into a table, altering a
table by adding constraints, or adding a generated column, you usually want to
temporarily disable the constraints, complete the operation, and then revalidate
and enable the constraints. You can use the DB2 SET INTEGRITY mechanism to
perform this manual integrity processing, as shown in Example 2-92.
Example 2-92 How to use SET INTEGRITY in DB2
119
120
EMP_ID
------23.
26.
24.
20024.
27.
7.
25.
FIRST_NAME
-------------------JAMES
SYBIL
SALVATORE
ROBERT
MARIA
EVA
DANIEL
LAST_INITIAL
-----------J
J
M
M
P
P
S
2.2.7 Synonyms
A synonym in Oracle is an alternative name for a database object, such as a
table, view, sequence, procedure, stored function, package, snapshot, or another
synonym. In DB2, the support for this alternative name is also known as an alias
(and vice versa; the aliases in DB2 are also known as synonyms).
DB2 recognizes the same syntax as Oracle for creating synonyms on tables.
When you create a synonym in DB2 for a package, nickname, sequence, view, or
another synonym, specify the type of the database object on which the synonym
is defined. Note the following FOR SEQUENCE clause:
CREATE PUBLIC SYNONYM sequence_syn FOR SEQUENCE myschema.mysequence;
121
As with Oracle, the aliases (or synonyms) can be PUBLIC (with the schema
SYSPUBLIC) or private (with the schema of the CURRENT USER, if the PUBIC
keyword is not specified).
Example 2-96 creates a public synonym and an alias for the catalog view
SYSCAT.TABLES using two different types of syntax. In the first case, the CREATE
SYNONYM syntax is used in the same exact way as in Oracle. The second example
demonstrates the CREATE PUBLIC ALIAS syntax that is specific to DB2. Although
the syntax is different, the result is the same: two alternative names for
SYSCAT.TABLES are created and the SYSCAT.TABLES could be referenced
anywhere by either of them.
Example 2-96 Two different ways to create a synonym/alias in DB2
It also contains a CASE statement that conditionally calls two functions from a
package named project_package.
Example 2-97 Create or replace view example with multiple SQL syntax structures
122
123
-- DB2 style
CREATE TYPE address_type AS
(STREET
VARCHAR2(30),
STREETNUMBER
CHAR(15),
CITY
VARCHAR2(30),
STATE
VARCHAR2(10))
NOT FINAL
MODE DB2SQL
METHOD SAMEZIP (addr address_type)
RETURNS INTEGER
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
NO EXTERNAL ACTION,
METHOD DISTANCE (addr address_type)
RETURNS FLOAT
LANGUAGE C
DETERMINISTIC
PARAMETER STYLE SQL
NO SQL
NO EXTERNAL ACTION
-- Oracle syntax
CREATE Or Replace TYPE address_type AS OBJECT (
STREET
VARCHAR2(30),
STREETNUMBER
CHAR(15),
CITY
VARCHAR2(30),
STATE
VARCHAR2(10),
MEMBER FUNCTION SAMEZIP (addr address_type) RETURN INTEGER,
124
-- DB2 syntax
CREATE TYPE Address_type AS
(StreetNumber VARCHAR (10),
Street VARCHAR(50),
City VARCHAR(50),
Zip VARCHAR (15)
) MODE DB2SQL
125
/
-- Oracle syntax
create type Address_type AS OBJECT
(
StreetNumber VARCHAR (10),
street
VARCHAR (50),
city
VARCHAR (50),
zip
VARCHAR (15)
);
/
--Create table (same syntax for both Oracle and DB2)
CREATE TABLE customer_with_type
(cust_ID
integer,
first_name
VARCHAR(50),
last_name
VARCHAR(50),
Address
Address_type
)
/
-DESCRIBE TABLE customer_with_type;
Column name
--------------------CUST_ID
FIRST_NAME
LAST_NAME
ADDRESS
Data type
schema
--------SYSIBM
SYSIBM
SYSIBM
MyUser
Column
Data type name
Length
Scale Nulls
------------------- ---------- ----- ----INTEGER
4
0
Yes
VARCHAR
50
0
Yes
VARCHAR
50
0
Yes
ADDRESS_TYPE
0
0
Yes
4 record(s) selected.
126
Table partitioning
Database partitioning
Multidimensional clustering
Combining methods of data organization
Rolling in and rolling out of data
Oracle flashback-archive
Indexes on partitioned tables
Table partitioning
Table partitioning in DB2 is also referred to as range partitioning or data
partitioning. This data organization scheme is one in which table data is divided
across multiple storage objects that are called data partitions or ranges
according to values in one or more table columns. Each data partition is stored
separately and can be in different table spaces.
Example 2-101 shows the creation of table partitioning on DB2. The example
demonstrates the use of a shorthand notation that automatically generates 24
partitions of uniform size, that is, one partition for each month over a 2-year
period. The MINVALUE and MAXVALUE catch all values that fall below and above the
defined ranges.
Example 2-101 DB2 table partitioning
127
128
129
Database partitioning
On DB2, database partitioning is one of the scalability features used to host a
large-size database in multiple partitions within and across different physical
nodes. This feature is an optional DB2 feature that is known as the Database
Partitioning Feature (DPF).
DPF is mostly used for large, data warehousing applications, although it can be
used in some types of OLTP applications. It implements a shared nothing
architecture in which every partition has its set of resources. When database
partitioning is used, the multiple database partitions appear and work together as
a single unit, This architecture allows complex data access tasks to run on
different parts of data in parallel.
When this feature is enabled, data organization is based on a hashing algorithm
that distributes table data across multiple database partitions. Each database
partition can be on a separate partition in a physical or logical machine. Data is
hashed according to a distribution key that is either explicitly defined in the table
using the DISTRIBUTE BY HASH clause, or defaults to the first qualified column.
Ideally, a distribution key is chosen that can hash the table data evenly across all
database partitions.
130
Table 2-8 summarizes the differences between the Oracle and DB2
partitioning methods.
Table 2-8 Mapping Oracle data organization schemes to DB2
Oracle partitioning
DB2 syntax
No equivalent
Round-robin
None
Default: Occurs
automatically on a
single partition
database
Range partitioning
Table partitioning
PARTITION BY RANGE
PARTITION BY RANGE
Hash partitioning
Database partitioning
PARTITION BY HASH
DISTRIBUTE BY HASH
List partitioning
PARTITION BY LIST
PARTITION BY RANGE
Composite partitioning:
Hash-range
Hash-list
Combination of
database partitioning,
table partitioning, and
multi-dimensional
clustering
PARTITION BY RANGE,
SUBPPARTITION BY
HASH, and
SUBPARTITION BY LIST
DISTRIBUTE BY HASH,
PARTITION BY RANGE,
and ORGANIZE BY
DIMENSIONS
No equivalent
Multidimensional
clustering
None
ORGANIZE BY
DIMENSIONS
Example 2-106 shows a table that is defined when database partitioning is used.
Example 2-106 Define a table in a partitioned database
131
Multidimensional clustering
Multidimensional clustering, also known as an MDC table, is a method of data
organization that clusters data together on disk according to multiple dimension
key values. A dimension is a key, such as product, time period, or geography,
used to group factual data into a meaningful way for a particular application.
A dimension can consist of a composite of two or more columns. A desirable
characteristic of dimension values is that they have low cardinality and consist of
a minimal number of unique values.
Example 2-108 is an example of an MDC table definition.
Example 2-108 MDC table definition
132
When dimension keys are used as predicates in the WHERE clause of a SELECT
statement, query performance is usually greatly improved because many rows
are retrieved with fewer I/Os. In addition, performance benefits are gained from
the smaller block index that is used with MDC tables. Because all rows in a block
are referenced by the same dimensions, only one index entry per dimension is
required to locate all the rows in that block.
Oracle does not have a data organization scheme that is similar to the
MDC table.
In Example 2-109, the data is distributed over multiple database partitions using
a hashed value of ORDER_ID. Within each database partition, the table is
partitioned by the SHIP_DATE month, and within each table partition the data is
organized in blocks by the dimensions REGION and CATEGORY.
133
134
The new table that is attached must match the existing table in several ways, that
is, the source and target tables must match in column order and definitions,
default values, nullability, compression, and table space types used.
When a source is newly attached, it is offline and remains offline until the SET
INTEGRITY statement is executed. The following example shows the SET
INTEGRITY statement:
SET INTEGRITY FOR stock ALLOW WRITE ACCESS
IMMEDIATE CHECKED FOR EXCEPTION IN stock USE stock_ex;
COMMIT WORK;
SET INTEGRITY validates the data in the newly attached data partition. The COMMIT
WORK elements are needed to end the transaction and to make the table available
for use.
In a similar way, an existing table can have a partition that is detached into a
separate table by using the ALTER statement:
ALTER TABLE stock DETACH PART dec01 INTO stock_drop;
DROP TABLE stock_drop;
In addition, you can modify partitioned tables using the ADD PARTITION and DROP
PARTITION options of the ALTER TABLE statement. Use the ADD PARTITION clause
to add an empty partition with a new range to an existing partitioned table. After it
is added, load the partitioned table with data.
Oracle flashback-archive
DB2 10.1 introduces temporal tables or Time Travel Query. This feature matches
(and exceeds) the Oracle flashback-archive feature. Temporal tables allow the
database to manage data change history (also called data versioning).
The processes of setting up the Oracle flashback-archive and DB2 temporal
tables features are different. However, if your application uses Oracle
flashback-enabled queries, such as the SELECT ... AS OF... / VERSIONS
BETWEEN... syntax, you can migrate this feature in DB2 with minor changes.
You can transparently enable temporal behavior for regular queries to retrieve
data as of a given moment in time. This behavior can be useful when you are
working with prepackaged or compiled applications, such as reporting tools that
are unaware of temporal features.
Use the following syntax to set or clear the temporal parameter in the
current session:
SET CURRENT TEMPORAL SYSTEM_TIME = <timestamp> | NULL
SET CURRENT TEMPORAL BUSINESS_TIME = <timestamp> | NULL
135
When used with the CONNECT_PROC database parameter, you can set the temporal
parameters in the current session based on the distinct session parameters,
such as user name. These parameters allow transparent shifting of reporting
applications back in time and allow you to build reports as they were built in past.
Web applications can also benefit from this feature to implement site versioning
to timeline features without the need to change the existing code. When those
parameters are not null, the client cannot change the temporal data. Thus, the
application cannot change the historic data by mistake.
DB2 offers more flexibility in dealing with data versioning. When migrating Oracle
flashback-enabled queries to DB2, you can choose between the system-period
or application-period temporal tables. In each case Oracle flashback clause
enabled queries can be translated, as shown in Example 2-112 through
Example 2-119 on page 142.
Example 2-112 Oracle flashback AS OF query type sample
136
137
After the FEDERATED value in DBM CFG is changed, it is applied after you
restart the database server, as shown in Example 2-116.
Example 2-116 Restart the server
138
SERVER
USER MAPPING
Example 2-117 shows the fed_config.db2 script, which is used to set up the
federated system.
Example 2-117 The fed_config.db2 script
139
For your existing PL/SQL code, that means that you do not have to change your
calls to tables in remote databases anymore. This mechanism even works when
you convert your remote database to DB2: You define a different wrapper, server,
and user mapping for your remote DB2 database.
140
Category
View
General
*_CATALOG
*_DEPENDENCIES
*_OBJECTS
*_SEQUENCES
DBA/USER_TABLESPACES
DICTIONARY
DICT_COLUMNS
Category
View
Tables/View
*_COL_COMMENTS
*_CONSTRAINTS
*_CONS_COLUMNS
*_INDEXES
*_IND_COLUMNS
*_PART_TABLES
*_PART_KEY_COLUMNS
*_SYNONYMS
*_TABLES
*_TAB_COL_STATISTICS
*_TAB_COLUMNS
*_TAB_COMMENTS
*_TAB_PARTITIONS
*_VIEWS
*_VIEW_COLUMNS
Programming
objects
*_PROCEDURES
*_SOURCE
*_TRIGGERS
*_ERRORS
Security
DBA/USER_ROLE_PRIVS, ROLE_ROLE_PRIVS,
SESSION_ROLES
DBA/USER_SYS_PRIVS, ROLE_SYS_PRIVS,
SESSION_PRIVS
*_TAB_PRIVS, ROLE_TAB_PRIVS
ALL/USER_TAB_PRIVS_MADE
ALL/USER_TAB_PRIVS_RECD
DBA_USERS
DBA_ROLES
From the applications stand point, all these views can be referenced in the SQL
and PL/SQL code, as shown in Example 2-118. The code snippet shows a part
of a procedure that gathers information, such as owner, object name, and object
type, about all invalid objects in a schema. The string can be used later for
recompiling, sending an email notification, and other maintenance operations.
Example 2-118 Using Data Dictionary views in a PL/SQL procedure
141
SELECT owner,
object_name,
object_type
FROM all_objects
WHERE owner = DECODE (v_owner, 'ALL', owner, v_owner)
AND owner NOT IN ('SYSTEM', 'SYS')
AND status = 'INVALID'
ORDER BY owner, object_name, object_type)
LOOP
existing_invalid_objects := existing_invalid_objects
|| CHR(10)
|| RPAD (TRIM (invalid_objects_list.owner), 13)
|| RPAD (TRIM (invalid_objects_list.object_name), 31)
|| RPAD (TRIM (invalid_objects_list.object_type), 18);
END LOOP;
END;
/
Example 2-119 loops through the source code for database objects that are
stored in the ALL_SOURCE Data Dictionary view. The application code prepares
an SQL statement later for a dynamic execution that is based on object name
and owner.
Example 2-119 Using ALL_SOURCE view
FOR get_source_info IN
(SELECT owner, name, type, text
FROM all_source
WHERE owner = DECODE (v_owner, NULL, owner, v_owner)
AND name = DECODE (v_name , NULL, name, v_name )
ORDER BY owner, name, type
)
LOOP
END LOOP;
Oracle also provides dynamic performance views that are updated dynamically
by the Oracle instance with performance data. Dynamic performance views are
prefixed with V_$ and have public synonyms that are created with the V$ prefix.
These views are used by database administrators to monitor the database, and
track cumulative information since startup. They are also sometime used in the
application code to provide information about the database instance and
contribute to the programming logic. Examples of such views are V$INSTANCE,
V$DATABASE, V$TABLESPACE, V$DATAFILE, and V$LOCK.
142
The Oracle dynamic performance views are part of the dictionary. It is a concept
similar to the DB2 catalog views that are based on SYSIBM tables.
The DB2 SQL administrative views and routines provide an easy-to-use
programmatic interface to the DB2 admin functionality through a construct that
could be used in SQL PL. They encompass a collection of built-in views, table
functions, procedures, and scalar functions for performing various administrative
tasks, such as reorganizing a table, capturing and retrieving monitor data, and
retrieving the application ID of the current connection.
These routines and views can be started from an SQL-based application, a
command line, or a command script.
The DB2 administrative views can be considered equivalent to the V$ views in
Oracle. Although the information provided by the V$ views and the administrative
views cannot be the same, the information is common, and both return
dynamic data.
For example, to obtain information about applications that are connected to the
database form Oracle V$ views, use the following query:
SELECT * FROM V$SESSION
An equivalent query to query data from DB2 administrative views is:
SELECT * FROM TABLE (SNAP_GET_APPL(CAST(NULL AS VARCHAR(128)),-1)) AS T
There are certain views that are useful. One useful view is
SYSENV_SYS_RESOURCES, which provides system information, such as
memory, processor, operating system, and host information. ENV_INST_INFO
returns information about the current instance. The APPLICATIONS view returns
information about connected applications. The DBCFG and DBMCFG views
return information about database configuration and database
manager configurations.
Table 2-10 shows some of the V$ views and the equivalent administrative views
or table functions.
Table 2-10 Oracle V$ views and DB2 administrative views and table functions
Oracle
DB2
V$INSTANCE
V$DATABASE
143
Oracle
DB2
V$TABLESPACE
V$DATAFILE
V$SESSION
V$SQLTEXT
V$LOCK
V$SYSSTAT
(information for data buffer)
V$SESSION_LONGOPS
144
For more information about DB2 administrative views and table functions, see
Administrative SQL Routines and Views, SC10-4293.
145
Inside CLPPlus, you can run both operating system and database commands. To
run the operating system commands, place the HOST operator in front of them.
For example, if you were running on UNIX and you wanted to verify the existence
of the plsql.txt file in your current directory, simply run:
SQL> HOST ls | grep plsql.txt
As an example on a Windows system, from within CLPPlus, you can run the HOST
command followed by the ipconfig command to display the Windows IP
configuration as follows:
SQL> HOST ipconfig
CLPPlus can connect to any DB2 database without the need to catalog the
database before connecting. Use the data server host name, port number, and
database name along with your user credentials.
146
Working with the SQL buffer is an essential part of the CLPPlus functionality. The
SQL buffer is an in memory working area where CLPPlus keeps a copy of the
most recently entered SQL statement or PL/SQL block. CLPPlus provides many
commands to help manage the SQL buffer.
In Figure 2-8 on page 148, first load some PL/SQL code that is stored in a file
using the GET command. Then, run this code straight from the buffer with the RUN
command. For testing purposes, this example uses a code sample that has a
syntax error in it to show how CLPPlus can help manage debugging and
execution of database code. In this case, the RUN command fails with a syntax
error that is properly displayed in the CLPPlus editor. (There is no space between
the FROM clause and the table name.)
147
Figure 2-8 Working with the SQL buffer - GET, RUN, and EDIT commands
To fix the intentional error, edit the code using the EDIT command. With the EDIT
command, the buffer content is displayed in the preferred text editor (in this case,
Notepad). You can correct the error and save the changes, as shown in
Figure 2-9.
After the error is corrected (by adding space between the FROM clause and the
table name), the SQL buffer is updated with the new version of the SQL script.
The script is then ready to be used after you close the text editor. If you run the
script again, the query succeeds and displays the result set in the
CLPPlus window.
148
You can execute the PL/SQL procedures in CLPPlus by using the EXECUTE
command. Example 2-120 shows a simple PL/SQL procedure that is saved in
the example.sql file.
Example 2-120 Sample procedure
To execute the procedure, run the EXECUTE command followed by the procedure
name and enter a parameter of 100. Because CLPPlus provides high-level
compatibility, using the DB2 CALL statement to run the PL/SQL procedure
produces the same result, as demonstrated in the second call to this procedure
using a parameter of 200.
149
As shown in Figure 2-10 on page 149, you can use the DB2 syntax to call
PL/SQL procedures in CLPPlus, and you also can create and run SQL PL
procedures in CLPPlus. Support for SQL PL is another significant advantage of
CLPPlus. In Figure 2-11, you create a SQL PL procedure using the OUT
parameter in the SAMPLE database, call it, and display the output parameter in
DB2 style. The result is the same as the CLP provides.
Figure 2-11 Executing the SQL PL procedure with OUT parameters in CLPPlus
150
Using the rich pallet of formatting options that are offered by CLPPlus, you can
improve the appearance of this result set by changing the output settings and
using the column formatting options.
151
In Figure 2-13, first check for the current settings by running the SHOW command.
Then, provide new values for the output-related parameters with the SET
command. The figure also shows how long it took to execute this query by
running the SET TIMING ON command.
Increase the width of the output line and apply special formatting rules (dollar
notation) to the salary and bonus columns by using the COLUMN FORMAT
command. Additionally, using the HEADING command, we select new titles for the
columns, such as lastname or firstname, to make them more meaningful to the
user, and we add an alternative name to the salary column. The query now
produces a well-formatted, more meaningful output and displays the
execution time.
152
153
Figure 2-14 shows the DB2 CLP in a Windows operating system environment.
In a Linux or UNIX operating system environment, you can start the CLP by
running db2. You also can run a statement directly on the operating system
prompt by adding the db2 prefix to the statement.
Run the DB2 SQL scripts by using the following flags:
db2 -tvf <scriptName>
Where
The -t flag tells the CLP to use a semicolon (;) as the statement
termination character.
The -v flag stands for verbose so that the statements display before
being executed.
The -f flag tells DB2 that the next parameter is a script file name to execute.
154
Example 2-121 shows how to run a script to create a table using the CLP.
Example 2-121 Running a procedure with the CLP
SQLCOMPAT mode
When you execute scripts using the db2 command with the -t flag, the default
statement terminator is a semicolon (alternative terminators can be specified).
Oracle uses the forward slash (/) as the default statement terminator. To
enhance compatibility and allow scripts that were written for Oracle and
containing forward slashes to run seamlessly in DB2, you can use the -td option
to tell the CLP to use a different statement terminator. For example, to use the
forward-slash (/) as the statement terminator, run:
db2 -td/ -vf <scriptName>
Example 2-122 shows how to run a script with a forward slash using the -td
command-line option.
Example 2-122 Script using forward-slash as the statement terminator
155
p_acct_id
IN accounts.acct_id%TYPE,
p_Employees_Name_Cache OUT Customer_Name_Cache);
END Account_Package;
/
db2inst1> db2 -td/ -vf sample_create_proc.sql
CREATE TABLE "ACCOUNTS" ( "ACCT_ID" NUMBER(31) NOT NULL, "DEPT_CODE" CHAR(3) NOT
NULL, "ACCT_DESC" VARCHAR2(2000), "MAX_EMPLOYEES" NUMBER(3), "CURRENT_EMPLOYEES
" NUMBER(3), "NUM_PROJECTS" NUMBER(1), "CREATE_DATE" DATE DEFAULT SYSDATE, "CLOS
ED_DATE" DATE DEFAULT SYSDATE)
DB20000I The SQL command completed successfully.
CREATE OR REPLACE PACKAGE Account_Package AS
TYPE customer_name_cache IS TABLE OF Employees%ROWTYPE INDEX BY PLS_INTEGER;
PROCEDURE Account_List(p_dept_code IN accounts.dept_code%TYPE,
p_acct_id
IN accounts.acct_id%TYPE);
END Account_Package;
DB20000I The SQL command completed successfully.
You can achieve the same results by changing the SQLCOMPAT mode at the
beginning of a script or by setting the SQLCOMPAT mode on the command line
before you run the script. Setting the mode on the command line before you run
the script allows you to leave the script unchanged from its Oracle origins. This
function is shown in Example 2-123, which run the script in Example 2-122 on
page 155.
Example 2-123 Executing a script in SQLCOMPAT PLSQL mode
-- To execute this script, run: db2 -tvf <scriptname>
-- Uncomment the next line or make sure to set beforehand
-- SET SQLCOMPAT PLSQL;
db2inst1> db2 SET SQLCOMPAT PLSQL
DB20000I The SET SQLCOMPAT command completed successfully.
db2inst1> db2 -tvf sample_create_proc.sql
CREATE TABLE "ACCOUNTS" ( "ACCT_ID" NUMBER(31) NOT NULL, "DEPT_CODE" CHAR(3)
NOT
NULL, "ACCT_DESC" VARCHAR2(2000), "MAX_EMPLOYEES" NUMBER(3),
"CURRENT_EMPLOYEES
" NUMBER(3), "NUM_PROJECTS" NUMBER(1), "CREATE_DATE" DATE DEFAULT SYSDATE,
"CLOS
ED_DATE" DATE DEFAULT SYSDATE)
DB20000I The SQL command completed successfully.
CREATE OR REPLACE PACKAGE Account_Package AS
TYPE customer_name_cache IS TABLE OF Employees%ROWTYPE INDEX BY PLS_INTEGER;
PROCEDURE Account_List(p_dept_code IN accounts.dept_code%TYPE,
156
p_acct_id
IN accounts.acct_id%TYPE);
END Account_Package;
DB20000I The SQL command completed successfully.
157
158
Chapter 3.
159
160
161
162
163
The process involves entering basic server information and user credentials to
create the connection profiles. Complete the following steps:
1. Open the Data Source Explorer view. Right-click Database Connections and
then select New. This opens the New Connection wizard, which is shown in
Figure 3-2.
2. Choose the appropriate Database Manager from the list on the left side of the
wizard window.
3. Enter the server and user credentials information in the fields on the
right side.
4. Select the correct database driver from the JDBC driver drop-down list.
164
Note: If the driver you need is not shown in the list, you must download the
required driver and specify the path.
5. Click Test Connection to confirm that your server and user credentials
are correct.
6. Click Finish to complete the process. The connection profile is stored by Data
Studio and can be accessed in the Data Source Explorer view.
165
Installation
Installation of DCW requires a working instance of Data Studio. For instructions
about obtaining the Data Studio software, see 3.2.1, IBM Data Studio on
page 162.
You can download the DCW plug-in and view supported versions of Data Studio
at this address:
http://www.ibm.com/developerworks/data/ibmdcw
Note: Before preceding with a new installation of DCW, uninstall any previous
versions of DCW that are installed in your instance of Data Studio.
166
Figure 3-3 DCW installation - adding the download directory to Data Studio
Based on the directory that you selected, Data Studio automatically registers
the DCW plug-in as available for installation.
167
3. Click Next to initiate the installation. During the installation process, you are
prompted to verify the terms of the software license and acknowledge a
security warning (see Figure 3-5).
168
4. After the installation process is complete, select Finish to close the wizard.
5. Restart Data Studio to activate the newly installed DCW plug-in.
169
You are prompted to specify a project name and to select the source and target
databases from the drop-down lists, as shown in Figure 3-6. For an Oracle to
DB2 migration, select Oracle as the source and the applicable DB2 database as
the target.
Figure 3-6 Creating a DCW project in the New DCW Project wizard
170
The task launcher provides a set of steps for your Oracle to DB2 migration.
Selecting a step launches the appropriate wizard, along with help topics that
provide step-by-step guidelines and useful hints to help your migration.
171
DCW gives you two options for extracting the DDL from your source database. If
you are able to connect to your source database, DCW can extract DDL through
a connection. Alternatively, if the source database is not directly accessible
through the system where DCW is installed, you can extract DDL by running
extraction scripts manually on the source Oracle database. In both cases, DCW
either generates or runs the commands and queries that are required to create
the DDL. Both approaches use the functions of the source database and apply
industry-leading practices.
You can choose your extraction method in the DDL Extraction wizard, as shown
in Figure 3-8. The wizard can be accessed from the DCW Task Launcher or by
right-clicking your DCW project folder in the Project Explorer view and then
selecting Database Conversion DDL Extraction.
172
173
3. On the next page of the wizard, select the schema that you want to extract.
Only the schemas that are authorized for the user credentials that you used in
the connection profile are listed, as shown in Figure 3-10. Click Next.
Recommendation: To reduce the risk of failure, it is a good idea to
perform migrations on one schema at a time.
174
4. On the next window of the wizard, you can specify the model elements that
you want to include in your DDL script (see Figure 3-11). After you select the
elements, click Next.
175
5. You are now prompted to select a project directory, specify a file name for the
extracted DDL, and select a statement terminator, as shown in Figure 3-12.
The recommended statement terminator is the forward slash / character.
This ensures that statements with embedded semicolons are extracted.
6. On the final window of the wizard, verify your selections and click Finish.
The extraction process generates a single SQL file containing the DDL of the
selected schemas. The directory is placed in the project directory that
you specified.
176
2. Follow the instructions in the wizard to specify the prefix of the DDL files to be
generated and the name and location under which to save the custom DDL
extraction script. When the wizard completes, the script is placed in the
location that you specified.
Note: As a preferred practice, select the Exclude all system schemas
option to prevent the extraction of Oracle system schemas.
3. Run the extraction script through Oracle SQL *Plus on a machine that has
access to the source Oracle database. This generates the DDL file, which is
created in .out format and placed in the same location as the script.
177
The DDL file is now generated and available for use, but you must import the file
into DCW before the rest of the migration process can proceed. This process
might require you to move the DDL files manually from your Oracle system to the
system on which DCW is installed.
178
After you open the wizard, select Oracle as the Source SQL dialect and your
DB2 version as the Target SQL dialect. Click Finish to generate the report.
179
The output of the evaluation process is not just the report but also an .xmle file,
which is the encrypted version of the report in XML format. This file is placed in
your DCW project using the format <filename>-<timestamp>_report.xmle. The
user must send the .xmle report to askdcw@ca.ibm.com, where IBM decrypts the
report and returns it to the user in HTML format.
Note: The .xmle file does not contain any source code. It contains only the list
of identified incompatibilities and the number of those incompatibilities in the
original source code.
180
Note:
Any SQL syntax that is not recognized by DCW is not considered for
statistical calculation.
Statements that can be successfully converted to their corresponding DB2
equivalent (using the DCW auto-conversion feature) are flagged as
compatible and are not considered in the percentage calculations
of incompatibility.
The Compatibility Report is divided into four sections.
The Executive Summary is a high-level summary that cites the compatibility
percentage of all PL/SQL and DDL Statements that can run natively on a DB2
database that were enabled for Oracle compatibility.
The Technical Summary (see Figure 3-15) breaks up the statistics across
different types of PL/SQL Objects and DDL Statements. For each type, the
report provides the total number of statements that are analyzed and the
percentage of incompatibility that is detected.
181
Figure 3-15 on page 181 shows that the Technical Summary is divided into
two sections, one for PL/SQL and the other for DDL. The PL/SQL Summary
contains statistics for PL/SQL Objects and PL/SQL Statements.
The Detailed Technical Summary gives an intricately detailed breakdown of
the statements that were analyzed. Like the Technical Summary, this
summary also has separate sections for PL/SQL and DDL.
The PL/SQL Statistics table (Figure 3-16) and DDL Statistics table
(Figure 3-17) provide details about the total number of incompatible
statements of each type. The PL/SQL table contains counts of the lines of
code for each Object Type, whereas the DDL table shows the total count for
each type of DDL (the sum of the Create, Alter, and Drop statements).
182
183
The three sections of the Compatibility Report are aimed at different audiences
and used for different purposes. The Executive Summary is intended for
management and for gauging the complexity of a migration project at a high level.
The Technical Summary helps users to create an initial sketch of their conversion
plan. The Detailed Technical Summary is intended for the users who perform the
migration tasks.
184
185
In the wizard, select Oracle as the Source SQL dialect and your DB2 version as
the Target SQL dialect. Click Finish to initiate the conversion and a new
converted SQL file is generated in your DCW project folder, where it exists
alongside the original SQL file.
The rules and logic of code conversions in DCW are based on field knowledge
and the leading practices that are developed by IBM consultants. It is not
possible to list all of these rules and configurations in this book. However, several
of the most important and complex conversion rules are worth explaining:
Nested conversions:
Nested functions are not supported in DB2 but are a common practice in
Oracle. DCW unnests these functions, gives them a new name, and then
changes all of the calls to the functions to use the new name.
Data type conversions:
Many Oracle data types are not compatible with DB2 syntax, either because
the data type is represented differently or the offset values for the arguments
of the data type are different in DB2. DCW overcomes this issue with a set of
Mapper rules for converting from Oracle to DB2 data types. The Mapper rules
are listed in Appendix B, Data types on page 309.
Statement separator conversions:
DCW converts each Oracle statement separator (typically the ; or /
characters) to the @ character, which is the default separator in DB2.
Using these rules, the Code Conversion wizard performs the following actions:
Remove code fragment: DCW comments out the Oracle clauses that are not
supported and not required on DB2. Code that is removed is tagged with the
phrase Code Fragment was removed in the converted file. Figure 3-23 shows
an example in which both the Enable Disable Clause and the Table
Compression Clause are removed.
Code conversion: For many Oracle statements, DCW can fully convert the
syntax to be compatible with DB2. Statements that are converted are not
flagged by DB2.
186
187
2. Review code that was processed but for which DCW was unable to find a
viable alternative. Search for DCW Evaluation Issue to locate these
statements in your converted code.
For help identifying a solution to the problem code, see the Compatibility
Report. The report is often where the issue was first listed and usually
includes recommended solutions.
3. After the unrecognized syntax and evaluation issues are resolved, search
your converted code for Code Fragment Removed, which marks statements
that were commented out by DCW. Review these sections of code to ensure
that their removal does not have negative effects on your application.
4. Finally, check the code for segments that are tagged with the phrase Requires
Attention. Such code is likely to require verification to ensure that the
conversion that was applied is valid for your application. You might need to
make adjustments to ensure that it is fully compatible with DB2.
188
189
3. The process creates a directory in your DCW project and puts the split files in
specific directories that are named according to the Object types, as shown in
Figure 3-28.
Although splitting the DDL is an optional step, it is a useful utility that can be
applied to many processes. The DCW documentation recommends using the
Split DLL function as a way to achieve better management and understanding of
the SQL code you are converting.
190
3.6.2 Deploying the DDL objects that are required for data movement
After the source DDL is converted and all issues are resolved, you can create the
required objects on the target DB2 database by running SQL statements directly
from DCW. This action is a precondition for data movement because the target
DB2 database objects must exist before data can be mapped and migrated from
the source Oracle database.
In most cases, you must start this step by creating the required buffer pools, table
spaces, and tables. As a preferred practice, all other database objects should be
created after data movement is complete.
191
To create objects using SQL Editor, select the target DB2 database from the
Connection drop-down list and then click Play (the green button in the upper
right line of SQL Editor). Upon initiating the process, the DDL statements are run
against the target database that you specified.
You can monitor the status and results of the statement execution on the editors
SQL Results tab.
192
Figure 3-30 Deploying DDL files by using the DCW Run SQL Files feature
193
Each of these methods can be accessed through the DCW context menu
(accessible by right-clicking the listing for your DCW project or from the Task
Launcher). The next subsections describe each option. A quick reference guide
that summarizes all data movement methods is provided in 3.7.6, Selecting the
appropriate data movement method on page 210.
194
Figure 3-31 Selecting schemas for movement by using the Extract Data to Flat Files
wizard
195
4. Select the tables that you want to move (see Figure 3-32) and click Next.
Figure 3-32 Selecting tables for movement in the Extract Data to Flat Files wizard
5. Specify the data movement configuration parameters. Novice users can use
the default selections. Click Next.
6. View the summary to confirm your selections and click Finish to begin
extracting the data.
The extraction log is automatically displayed in Data Studio. The data is extracted
to the directory that was specified in step 3 on page 195 using the file extension
.tables.
Note: To transfer the extracted data to a different computer, copy the specified
directory to an external storage medium.
196
197
4. On the second window (Figure 3-33), you can select an existing target DB2
database connection, or create a connection by clicking New....
198
5. On the third window (Figure 3-34), select the schema that you want to extract.
6. On the fourth window (Figure 3-35), select the tables that you want to extract.
199
7. On the fifth window (Figure 3-36), specify the data movement configuration
parameters. Novice users can use the default selections.
8. View the summary on the final window of the wizard and click Finish. This
starts the extraction process, during which a progress indicator is displayed.
A log of the extraction is automatically displayed in Data Studio after the process
is finished. The log is also stored in the Data Movement folder of your
DCW project.
200
Prerequisites
You must ensure that these prerequisites are met before continuing with this
approach to data movement:
IBM InfoSphere Federation Server must be installed on the target DB2
system. Depending on your DB2 edition, this might require additional
licensing.
The Oracle client software must be installed on the system hosting the target
DB2 database.
201
(HOST = oracle_host)
(PORT = host_port)
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = oracle_sid)
)
)
4. Test the Oracle connection by running tnsping alias_name after you connect
to the Oracle database through SQL Plus by running the following command:
sqlplus user/password@alias_name
202
7. Select the schemas that you want to move. Click Next. DCW creates
wrappers for your objects, and provides a summary.
8. Click Finish to start the data movement using federation.
203
Prerequisites
CDC-based data replication requires these key components:
InfoSphere CDC license: A license is required to use the CDC software.
Data store replication engine: This is an InfoSphere CDC process that sends
or receives replicated data. The required data store replication engine
processes on both the source and target servers are created when you install
InfoSphere CDC.
DBMS: This is the source or target database. You can work only with
databases that are supported by InfoSphere CDC as a source for target of
replicated data.
Data store: This is an InfoSphere CDC process on a source or target server
that accepts requests from an instance of Access Server and communicates
with the data store replication engine to initiate and manage replication
activity. Data store processes on source and target servers are created when
you install InfoSphere CDC.
Access Server: Access Server is the server application that controls access
to your replication environment. You can have multiple instances of Access
Server in your working environment, but you can connect only to one server at
a time. For more information about installing Access Server, see Management
Console and Access Server - Installation Guide at this address:
http://pic.dhe.ibm.com/infocenter/soliddb/v6r5/index.jsp?topic=%2Fco
m.ibm.swg.im.soliddb.universalcacheuserguide.doc%2Fdoc%2Fs0009042_in
stall_configure_AS.html
In addition, the user must ensure that the following actions occurred before
initiating data replication by using CDC:
CDC must be installed on the source and target systems.
Access Server and the replication servers must be up and running.
The data store must be defined.
The source database must have logging enabled in Archive mode, and
supplemental logs must be enabled at the table and column levels.
Note: You need a separate license to install CDC. A license for CDC does not
come with DCW.
204
205
7. On the Subscription Details window (Figure 3-38), specify the Access Server
Details (host name, port, user, and password) and replication agent names.
Provide a unique subscription name and publisher ID. The subscription name
and publisher ID are used for replicating the data. Click Next.
Figure 3-38 Subscription Details window in the Configure Data Replication wizard
8. On the summary window, review your settings and click Finish to initiate
configuration of the data replication process and follow the progress.
206
207
Figure 3-39 Choosing a mirroring method in the Initiate Data Replication wizard
5. View the summary and then click Finish. This initiates the data replication
process and shows the progress that occurs until replication is complete.
208
4. Select your preferred method for stopping active replication for the
subscription. If you also want to delete the subscription and remove any
related configuration, select Delete subscription, as shown in Figure 3-40.
When you click Finish, the wizard requests that the CDC server stop data
replication and deletes the subscription if that option was selected.
209
2. Open the load script (db2load.sql) and change the path of the data,
message, and dump folders.
3. If the OS of the machine where the extraction occurred is different from the
OS of the machine where the data will be deployed, replace the directory
separator to either the \ character (for Windows) or the / character (for Linux).
4. Run the db2load.sql script on the DB2 command line processor, as shown
here:
db2 => @db2load.sql
210
211
3.9 Conclusion
The Database Conversion Workbench and Data Studio form a smart and
efficient toolset that offers a holistic approach toward the database conversion
process, with an automated solution for each stage plus hints and guidelines to
assist you in completing your project. Together with the improved Oracle
compatibility features in DB2 10.5 for Linux, UNIX, and Windows, IBM has
created a base for simplifying Oracle migrations and has added a new dimension
to database development in DB2.
If necessary, contact your IBM marketing representative to inquire about
additional assistance and services to help you enable your applications for a
DB2 database.
The latest information about the Database Conversion Workbench can be found
by visiting the Database Conversion Workbench community in IBM
developerWorks at this address:
http://www.ibm.com/developerworks/data/ibmdcw
212
Chapter 4.
Enablement scenario
Using the Oracle compatibility features of IBM DB2 10.5, you can move your
Oracle database application to DB2 with few or no changes.
The previous chapters highlighted the available compatibility features of DB2
10.5, including natively supported syntax and enablement tools. This chapter
presents a complete migration scenario to illustrate how the compatibility
features can make conversions faster and easier. The scenario includes installing
DB2 software and creating a DB2 database in the Oracle compatibility mode. It
also demonstrates moving an Oracle database with a PL/SQL application to DB2
using the IBM Database Conversion Workbench (DCW).
The chapter includes these sections:
213
214
After you download and unpack DB2 10.5 for Linux, UNIX, and Windows
operating systems, run the db2setup command as a user with administrative
privileges, such as root on Linux. This command starts the DB2 Setup
Launchpad that is shown in Figure 4-1.
215
Select the Install a Product option and then click Install New (Figure 4-2).
Follow the setup wizard prompts, accepting the default values as you proceed.
For the DB2 administrator, provide a new user ID (this scenario uses db2inst1),
which is created as part of the installation.
When the process concludes, the setup wizard opens a window where you
confirm that the DB2 installation was successful.
216
/home/db2inst1>db2set DB2_COMPATIBILITY_VECTOR=ORA
/home/db2inst1>db2set DB2_DEFERRED_PREPARE_SEMANTICS=YES
/home/db2inst1>db2set -all
[i] DB2_DEFERRED_PREPARE_SEMANTICS=YES
[i] DB2_COMPATIBILITY_VECTOR=ORA
[i] DB2COMM=TCPIP
[i] DB2AUTOSTART=YES
[g] DB2SYSTEM=oc2522778176.ibm.com
[g] DB2INSTDEF=db2inst1
[g] DB2ADMINSERVER=dasusr1
/home/db2inst1>db2stop
2012-03-06 18.16.46
0
0
SQL1064N DB2STOP processing was
successful.
SQL1064N DB2STOP processing was successful.
/home/db2inst1>db2start
03/06/2012 18:16:54
0
0
SQL1063N DB2START processing was
successful.
SQL1063N DB2START processing was successful.
217
CONNECT TO sales;
CREATE USER TEMPORARY TABLESPACE user_temp;
UPDATE DB CFG USING auto_reval deferred_force;
UPDATE DB CFG USING decflt_rounding round_half_up;
TERMINATE;
To run the script, save it in a text file and run the following command:
db2 -tvf <scriptname>
Important: To ensure that the DB2 Oracle compatibility features function
correctly, create the database in Unicode, which is the default code page for all
new DB2 databases.
The command parameters for this enablement example scenario activate certain
DB2 features, which are explained below, that can help simplify database design,
future management, and initial tuning. The scenario also uses automation
features that are enabled by default. You do not need to specify parameters to
benefit from these automation features.
Automatic storage
When you use this feature, which is enabled by default, you do not have to
configure the table space sizes or placement or be concerned about the
maintenance of the table spaces as they grow in size. On Linux and AIX
platforms, DB2 creates databases and their table spaces under the instance
owners home directory by default (for example, /home/db2inst1) or on a path
that is specified by the user.
Page size 32 KB
The default page size that is used by the CREATE DATABASE command is 4 KB,
but you can override this value by selecting 32 KB, which is the largest page
size available. This page size ensures a seamless migration of tables with
both small and large row sizes.
Example 4-2 on page 217 also defines the following parameters:
User temporary table space
A user temporary table space is used to allow the future creation of a global
temporary table.
Automatic object revalidation
You change the database AUTO_REVAL configuration parameter to
DEFERRED_FORCE, as explained in 2.1.1, SQL compatibility setup on page 22.
218
219
220
221
3. Install the IBM Database Conversion Workbench plug-in. Following the wizard
prompts, review and accept the terms of the license and select Finish. You
might see a warning indicating that you are installing an unsigned plug-in;
click OK (see Figure 4-4) to proceed with the installation.
222
To work with the Database Conversion Workbench in Data Studio, you use the
Database Conversion perspective. Switch to this perspective if it is not active
when the Data Studio opens. The first step is to create a DCW project by clicking
File New DCW Project, and then you can specify the project name and
select the source and target databases (Figure 4-5).
223
One method, often called online extraction, is useful when you can establish a
direct connection from the DCW workstation to the Oracle database server. It
uses an existing Data Studio connection to read the object definitions directly
from the source database. When using this method, you can choose schemas
and object types to be extracted, define the statement terminator, and preview
the generated script before saving it, as shown in Figure 4-6.
Figure 4-6 Extracting the source DDL using a direct database connection
224
Before you proceed with the extraction of database objects and data, you must
create a connection to the Oracle database in Data Studio. For more information,
see the Data Studio documentation at the following address:
http://pic.dhe.ibm.com/infocenter/dstudio/v4r1/topic/com.ibm.datatools.
qrytune.configothers.doc/topics/configuretuning_ds.html
For our sample project, we import an existing Oracle database script. You can
find the source code in Appendix E, Code samples on page 351 or download it
from the IBM Redbooks website.
Right-click your project, select Database Conversion Import a DDL file...,
and then select the file containing the DDL statements. The imported script
displays in the DCW project in Data Studio.
Important: Whether extracting the source DDL using a direct database
connection or importing an existing script, make sure that the statement
terminator is set to the forward slash symbol (/) to avoid conflicts with the
default terminator, the semicolon (;) found inside the PL/SQL code.
225
Begin by right-clicking the .sql file containing the source DDL and select
Database Conversion Evaluate Compatibility. The Compatibility Evaluation
wizard opens with the option to select the source and target database
(Figure 4-7).
When you click Finish, the DCW generates an encrypted XML file with the
extension .xmle, containing the summary of the source script. You see the
message shown in Figure 4-8.
226
227
The evaluation report for our sample application (Figure 4-9) indicates that most
of its SQL and PL/SQL statements run in DB2 without changes. Among the
detected incompatibilities is the use of Oracle XMLType and the DBMS_LOB
package, which we address later. Some issues, such as the NUMBER data type
precision exceeding the DB2 limit, are listed in the Informational section. They
are resolved automatically during the DCW conversion.
4.5.4 Conversion
The Database Conversion Workbench offers a powerful tool that can
automatically convert certain known Oracle syntax incompatibilities to the DB2
compatible syntax with a simple click.
228
Begin by right-clicking the .sql file containing the Oracle DDL statements and
select Database Conversion Convert Code.... The Code Conversion wizard
opens with the option to select the source and target databases (Figure 4-10).
Figure 4-10 Selecting the source and target databases in the Code Conversion wizard
229
230
After clicking Next, you see the list of objects that are identified by the wizard and
a confirmation window, showing what folders and files will be created. After the
wizard completes, you find the new files in the DCW project, as you can see in
Figure 4-12.
Figure 4-12 Split DDL wizard creates a directory tree with new scripts
231
First, use the DDL files generated by the Split DDL wizard to create tables
(DDL_table.sql) in the DB2 target database. This can be done by using the SQL
Editor in Data Studio. Double-click any SQL file to open it in the editor and select
Run Run SQL. When prompted, choose an existing connection to the target
DB2 database or create a connection. The results and status are displayed in the
SQL Results tab.
For this enablement scenario, we use the functionality that provided by the DCW
that allows us to run multiple files at once. You can select multiple files or a folder
containing SQL scripts, then right-click the selection and select Database
Conversion Run SQL Files.... In the Run SQL Files window, set the
statement delimiter to @, as shown in Figure 4-13, and choose the connection
profile of your target database.
When the deployment is complete, you see a message similar to Figure 4-14.
Each statement result displays in the SQL Results tab.
232
Important: When deploying sequences, you might want to reset their current
values to avoid generating duplicate values or gaps. Use an ALTER SEQUENCE
statement similar to the following one:
ALTER SEQUENCE myseq RESTART WITH <new_value>
In our sample application, one of the tables, CUSTOMERS, has a function-based
index on an XML expression. However, DB2 10.5 does not yet directly support
function-based indexes and the Oracle XMLTYPE data type. Although the
XMLType column is automatically converted to the DB2 XML type by the DCW,
the creation of the index SALES.CUSTOMER_CITY_IND fails, as you can
confirm by viewing the SQL Results tab for the script DDL_index.sql.
The usual workaround for an Oracle function-based index in DB2 is to create a
generated column that is derived from the index expression and then build an
index on that column. However, in this case, the index expression involves
XMLTYPE, as shown in Example 4-3.
Example 4-3 Index that is based on an Oracle XMLTYPE function
233
234
The Data Movement wizard provides an easy to use method to transfer the data
for entire schemas. Right-click the conversion project and select Database
Conversion Extract Data.... The Extract Data to Flat Files wizard opens. It
guides you through the required steps:
1. Select the source Oracle database and choose the schemas to extract, as
shown in Figure 4-15. Click Next.
235
2. After you select the schemas, pick the tables you want to move, as shown in
Figure 4-16. Click Next.
236
After configuring the Data Movement parameters, you can view the summary of
settings for data extraction and click Finish. The extraction starts and the
progress is displayed in a separate window. The data movement log is
automatically shown in Data Studio after the process is finished. These logs are
also created in your DCW project under the Data Movement folder.
237
You can use the same functionality that is used to deploy tables and other DDL
scripts. Here you can select all the SQL scripts that you did not run yet, and the
entire folders for PLSQL objects. Right-click the project and navigate to
Database Conversion Run SQL Files..., set the statement delimiter to @,
and select the connection profile of your target database. When the process
completes, the results display in the SQL Results tab, as shown in Figure 4-18.
Important: Some of the views and routines that you migrate might contain
unqualified references to tables in the SALES schema. To avoid potential
problems during deployment and at run time, connect to the target database
as the sales user for deployment. This connection ensures that unqualified
references are resolved to the correct schema.
238
Deploy PL/SQL routines one by one, modifying statements when necessary. You
must make the following changes:
In the ADD_NEW_EMPLOYEE procedure, replace the reference to the
EMP_INFO_TYPE constructor with type field assignments, as shown in
Example 4-6. You can also define a PL/SQL function with the same signature
as the constructor, which performs the field assignment.
Example 4-6 Correcting an unsupported object data type constructor
-- constructors not supported >> o_Employee:=EMP_INFO_TYPE(
-x.emp_id, x.first_name, x.last_name, x.band);
o_Employee.emp_id
:= x.emp_id;
o_Employee.first_name := x.first_name;
o_Employee.last_name := x.last_name;
o_Employee.band
:= x.band;
SELECT extract(
cust_details_xml,
'//customer-details/name/text()').getStringVal()
as cust_name
FROM CUSTOMERS
WHERE existsNode(cust_details_xml,'//customer-details') = 1
AND extract(
239
cust_details_xml,
'//customer-details/addr/city/text()').getStringVal()=v_city
AND cust_id<>v_cust_id
Example 4-9 Querying DB2 XML column using pureXML
SELECT x.cust_name
FROM sales.CUSTOMERS c, XMLTABLE(
'$CUST_DETAILS_XML//customer-details[addr/city=$city]'
PASSING v_city as "city"
COLUMNS
cust_name VARCHAR(128) PATH 'name'
) x
Where cust_id<>v_cust_id
Replace the Oracle CREATE DIRECTORY statements in the DDL_other.sql file
with calls to the DB2 built-in procedure
UTL_DIR.CREATE_OR_REPLACE_DIRECTORY().
Important: The CREATE DIRECTORY statement references a directory name.
Verify that the directory paths exist on the target server and that the DB2
instance owner (db2inst1) can write to these paths.
In addition to the manual changes described here, the converted PL/SQL code
might include more modifications that the DCW performs automatically during
conversion. For example, the nested function AVERAGE_BAND() in the procedure
ACCOUNT_PACKAGE.DISPLAY_ACCOUNT_LIST is automatically converted
into a package-level function, as DB2 10.5 does not yet support nested functions.
240
Use the DB2 CLPPlus command-line interface (CLI), as shown in Example 4-10.
You can use CLPPlus to run Oracle SQL*Plus scripts with minimal or no
modifications.
Example 4-10 Running an Oracle SQL*Plus script using DB2 CLPPlus
4.7 Summary
This chapter demonstrated how to move a sample Oracle database application to
DB2, complete with different data types and PL/SQL procedures and packages,
with minimal effort. Almost all of the statements in the source database were
supported by DB2 without modifications, and the few incompatible statements
were quickly identified, modified, and deployed using IBM Database Conversion
Workbench. The result of this short enablement process is a fully functional DB2
database.
241
242
Chapter 5.
Application conversion
This chapter provides examples of converting client-side applications from an
Oracle environment to a DB2 environment. The applications use various
languages, and each language can have its unique way of using a DB2
application interface (API). This chapter explains the necessary steps for
converting client-side applications from the Oracle database to DB2.
This chapter covers the following topics:
243
You can download Perl source code or a precompiled binary distribution at:
http://www.perl.org/get.html
244
In addition to Perl, you need to download and install the following modules to use
the Perl driver for DB2:
DBI
DBD::DB2
You can download these modules from the Comprehensive Perl Archive Network
(CPAN) at:
http://www.CPAN.org
For the installation instructions, see the module documentation.
More information about Perl and DB2: For the latest information about Perl
and DB2 and related Perl modules, including installation advice, see:
http://www.ibm.com/software/data/db2/perl/
PHP extensions
IBM supports access to DB2 databases from PHP applications through the
following extensions, which offer distinct sets of features:
ibm_db2
This extension is an extension that is written, maintained, and supported
feature by IBM for accessing DB2 databases and Cloudscape. It is an
optimized driver that is built on top of the DB2 Call Level Interface (CLI) driver.
It offers a procedural API that, in addition to the normal create, read, update,
and delete database operations, offers extensive access to the database
metadata. This extension can be compiled with either PHP 4 or PHP 5.
PDO_IBM and PDO_ODBC
These are drivers for the PHP Data Objects (PDO) extension that offers
access to DB2 databases through the standard object-oriented database
interface. PDO_IBM is an IBM database driver. Both PDO_IBM and
PDO_ODBC extensions can be compiled directly against the DB2 libraries to
avoid the communications impact and potential interference of an ODBC
driver manager. This extension can be compiled with PHP 5.1.
An easy method of installing and configuring PHP on Linux, UNIX, or Windows
operating systems is to use Zend Server Community Edition, which provides a
ready to use experience. You can download and install Zend Server for use in
production systems from the following website:
http://www.zend.com/downloads
245
Additionally, precompiled binary versions of PHP are available for download at:
http://www.php.net/
Most Linux distributions include a precompiled version of PHP. On UNIX
operating systems that do not include a precompiled version of PHP, your own
version of PHP can be compiled.
To learn how to set up the PHP environment on Linux, UNIX, or Windows
operating systems and develop applications, see Developing Perl, PHP, Python,
and Ruby on Rails Applications, SC27-3876.
246
Application
With Embedded SQL
Step 1: Precompile(db2 PREP)
Modified Source File
Step 2: Host Language Compiler
Object files
Bind file
Step 4: Binder (db2 BIND)
DB2 supports the C/C++, Fortran, COBOL, and Java (SQLJ) programming
languages for embedded SQL.
247
EXEC
char
char
EXEC
248
Planning includes the creation of a project plan. Plan enough time and resources
for each task. IBM and IBM Business Partners can help you define a
well-prepared project.
When migrating applications developed in-house, the entire enablement effort
falls in to the hands of the development team. With packaged applications, you
can contact the vendor for a recommended enablement process.
249
250
251
252
5.3.1 SQL/XML
SQL/XML is an extension of SQL that is part of the ISO SQL specification. The
SQL/XML functions start XPath or XQuery expressions and are used in SQL
statements to access XML data or to generate (publish) XML documents from
relational data.
SQL/XML functions can be categorized into two groups:
Functions that query and access XML content
Functions that generate (publish) XML content from SQL data
For those SQL/XML functions that query and access XML content, Oracle
Database provides a set of functions that use XPath to access XML content.
Included in these functions are what are known as XMLType methods. The
XMLType methods belong to the XMLType data type. Some examples are
getStringVal(), getClobVal(), getNumberVal(), getNamespace(), and
getBlobVal(). In addition to the XMLType methods, Oracle Database also
provides other SQL/XML functions, such as EXTRACT(), EXISTSNODE(),
and EXTRACTVALUE().
Oracle Database also supports the ISO/IEC standard SQL/XML functions,
XMLQuery, XMLTable, and XMLExists. These functions, known as the XQuery
functions, are also supported on DB2, with XMLExists.
In addition to the SQL/XML querying functions, both DB2 and Oracle databases
support several other types of functions, such as XMLCast, XMLParse, and
XMLSerialize. Oracle Database also supports the casting function XMLType that
converts an XML string value to an XMLType value. XMLType is most similar to the
DB2 XMLParse function. To cast XML values to scalar string values, you can use
the DB2 XMLCast function.
DB2 supports standard functions that generate XML documents and fragments
from relational data. These functions are referred to as the publishing functions,
and include XMLDocument, XMLElement, XMLAgg, XMLAttribute, XMLConcat,
and XMLForest.
In short, you can use the DB2 pureXML feature to map the Oracle database
XMLType data type and the corresponding methods and functions to the DB2
XML data type and DB2 pureXML functions.
253
Table 5-1 lists some of the frequently used SQL/XML functions that are
supported by Oracle Database and maps them to DB2 equivalents.
Table 5-1 SQL/XML function mapping
Oracle Database
SQL/XML
SQL/XML
category
Oracle
specific
DB2 equivalent
existsNode
Access
Yes
XMLEXISTS
extract
Access
Yes
XMLQUERY
extractValue
Access
Yes
XMLQUERY
XMLCAST
getstringVal
Access
Yes
XMLCAST
getNumberVal
Access
Yes
XMLCAST
XMLCAST
Access
No
XMLCAST
XMLEXISTS
Access
No
XMLEXISTS
XMLQUERY
Access
No
XMLQUERY
XMLTABLE
Access
No
XMLTABLE
XMLPARSE
Casting
No
XMLPARSE
XMLTYPE
Casting
Yes
XMLPARSE
XMLSERIALIZE
Casting
No
XMLSERIALIZE
XMLCONCAT
Generate
No
XMLCONCAT
XMLELEMENT
Generate
No
XMLELEMENT
XMLAGG
Generate
No
XMLAGG
XMLATTRIBUTES
Generate
No
XMLATTRIBUTES
XMLFOREST
Generate
No
XMLFOREST
254
To convert Oracle SQL/XML functions to DB2, find the best equivalent DB2
pureXML functions and see pureXML Guide, SC27-3892-00 and DB2 SQL
Reference, Volume 1, SC27-3872-00 for correct syntax and function usage
guidelines. There might be some syntax differences even if a particular function
is a part of the ISO/IEC or W3C standard and is supported both by Oracle
Database and DB2.
Example 5-3 and Example 5-4 demonstrate some of these differences. In
Example 5-3, both extract() and existsNode() functions are Oracle SQL/XML
functions; the XML namespace is specified.
Example 5-3 Querying XML data with SQL/XML functions in an Oracle database
SELECT
extract(info,'/customerinfo//addr','xmlns="http://posample.org"')
FROM customer
WHERE
existsnode(info,'/customerinfo//addr[city="Aurora"]',
'xmlns="http://posample.org"')=1;
Example 5-4, the DB2 example, uses the wildcard notation (*:) for namespace,
which is prefixed to the XML elements. The wildcard matches any name space.
Although the wildcard notation is part of the W3C standard, it is not supported
by Oracle.
Example 5-4 Querying XML data with pureXML functions in DB2
255
Example 5-6 and Example 5-7 demonstrate the casting of XML values in an
Oracle database and DB2.
Oracle database
Example 5-6 Extracting XML sequences and scalar values using Oracle functions
cityxml := incust.extract('/customerinfo//city');
city := cityxml.extract('//text()').getstringval();
DB2
Example 5-7 Extracting XML sequences and scalar values using the DB2 pureXML
feature
5.3.2 XQuery
Although standardization was a goal of the World Wide Web Consortium (W3C)
when it designed the XQuery language, there are differences in how Oracle and
IBM implemented XQuery within their database products.
With DB2, XQuery is a case-sensitive, primary language that can be embedded
directly within applications that access a DB2 database, or issued interactively
from the DB2 command line processor (CLP). An XQuery statement is prefixed
with the keyword XQUERY and is not limited to being started only from an
SQL/XML function. The keyword indicates that the primary language is XQuery.
256
Two DB2 defined functions are used in In XQuery to access relational data in a
DB2 database:
db2-fn:xmlcolumn: For retrieving an XML sequence from an XML column.
db2-fn:sqlquery: For retrieving a sequence of XML values based on the result
of an SQL query.
In an Oracle database, the XQuery statement cannot be embedded directly
within SQL applications. The XQuery language statements in applications are
run with the functions XMLQuery() and XMLtable(). The XQuery command can be
executed natively only from the SQL*Plus command processor environment.
However, before you run XQuery from SQL*Plus, the environment must be
properly initialized, which is accomplished by running an Oracle-provided script.
After you run this script and setting some additional parameters, the XQUERY
command can be used.
Oracle provides several XQuery and XPath extension functions that have a prefix
of ora. Some of these functions are ora:view, ora:contains, and ora:replace.
These extension functions do not have equivalents in DB2. To convert the Oracle
XQuery extension functions to DB2, you must rewrite the XQuery expressions
that contain them. For example, ora:view is used to create XML views on
relational data so that the data can be manipulated as an XML document. In
DB2, this task is accomplished by using the SQL/XML publishing functions that
were described earlier.
Oracle Database supports the standard XQuery functions fn:doc and
fn:collection. These functions are used to retrieve a single document or a
collection of documents that are stored in files in the Oracle XML DB repository.
In DB2, XML documents are always stored in tables as column values, and the
db2-fn:xmlcolumn function can be used instead.
Example 5-8 compares the differences between the XMLQuery function that is
used in the Oracle and DB2 databases.
Example 5-8 XQuery differences
257
Example 5-11 and Example 5-12 on page 259 compare the use of the XMLTABLE
function in the Oracle and DB2 databases. The XMLTABLE function is a standard
SQL/XML function, and the same name space declaration can be used in
both cases.
Example 5-11 Using the XMLTABLE function in Oracle database
select X.*
from customer_us,
xmltable (XMLNAMESPACES (DEFAULT 'http://posample.org'),
'for $m in $col/customerinfo
258
return $m'
passing customer_us.info as "col"
columns
"CUSTNAME" char(30) path 'name',
"phonenum" xmltype path 'phone')
as X;
Example 5-12 Using the XMLTBLE function in DB2
select X.*
from xmltable (XMLNAMESPACES (DEFAULT 'http://posample.org'),
'db2-fn:xmlcolumn("CUSTOMER.INFO")/customerinfo'
columns
"CUSTNAME" char(30) path 'name',
"phone" xml path 'phone')
as X;
259
<addr country="Canada">
<street>10 University Lane</street>
<city>Toronto</city>
<prov>Ontario</prov>
</addr>
<phone>X2222</phone>
</student>
</studentinfo>
Assume that you want to update the address of the student and remove his
phone number. In an Oracle database application, these changes can be carried
out as shown in Example 5-14.
Example 5-14 Delete/update XML elements in a document in an Oracle database
UPDATE students
SET classdatainfo = deleteXML(
updateXML(classdatainfo,
'/studentinfo/student[@studentno="2"]/addr/street/text()',
'999 College Street','http://posample.org'),
'/studentinfo/student[@studentno="2"]/phone','http://posample.org'
)
WHERE ...
In DB2, the same changes are performed by using the standard XQuery syntax,
as shown in Example 5-15.
Example 5-15 Delete/update XML elements in a document in a DB2 database
260
261
262
In a Pro*C program, you can have host variables that are declared as theUser_t:
EXEC SQL BEGIN DECLARE SECTION;
theUser_t *myUser;
EXEC SQL END DECLARE SECTION;
To use this host variable for DB2, you must take it out of EXEC SQL DECLARE
SECTION and define the host variable myUser as a structure.
You can use DB2 to declare a host variable as a pointer. However, if a host
variable is declared as a pointer, no other host variable can be declared with that
same name within the same source file.
The host variable declaration char *ptr is accepted but should not point to a
null-terminated character string of an undetermined length. It is assumed that the
pointer is to a fixed-length, single-character host variable. This value might not be
what was intended for the Oracle host variable declaration.
Use sqlint32 and sqlint64 for INTEGER and BIGINT host variables. By default,
the usage of long host variables results in the precompiler error SQL0402 on
platforms where long is a 64-bit quantity, such as 64-bit UNIX. Use the PREP
option LONGERROR NO to force DB2 to accept long variables as acceptable host
variable types and treat them as BIGINT variables.
One useful DB2 type is the CLOB type, which you use if you need to deal with a
large character string. For example, you can declare:
EXEC SQL BEGIN DECLARE;
SQL TYPE IS CLOB(200K) *statement
EXEC SQL END DECLARE SECTION;
You can later populate statement->data with, for example, a long SQL
statement, and process it.
Starting with Version 9, DB2 supports the XML type for host variables. In the
declarative section of the application, declare the XML host variables as LOB
data types, as follows:
EXEC SQL
SQL
SQL
EXEC SQL
BEGIN DECLARE;
TYPE IS XML as CLOB(N) my_xml_var1;
TYPE IS XML as BLOB(N) my_xml_var2;
END DECLARE SECTION;
For more information about handling XML types within C applications, see DB2
Express-C: The Developer Handbook for XML, PHP, C/C++, Java, and .NET,
SG24-7301 and Developing Embedded SQL Applications, SC27-3874.
263
Example 5-16 shows a fragment of PRO*C code that demonstrates array host
variables. The last statement places all 10 rows from the cursor into arrays.
Example 5-16 Array host variables in Oracle Pro*C
dept_numb[10];
dept_name[10][14];
v_location[12];
h_dept_numb = 0;
h_dept_name[14] = {'\0'};
v_location[12] = {'\0'};
264
/*we need Fetch one row at the time and move to corresponding
member of array */
for (i=0;i<10;i++){
EXEC SQL FETCH CUR1 INTO :h_dept_num, :h_dept_name;
if (SQLCODE == 100) {
break;
}
dept_numb[i] = h_dept_numb;
strcpy(dept_name[i], h_dept_name);
}
265
INTO :address
FROM
test_table
WHERE phone = :pnone_num;
..
EXEC SQL WHENEVER NOT FOUND CONTINUE;
EXEC SQL SELECT commis_rate
INTO :rate :rateind
WHERE prod_id = :prodId;
if (rateind == -1) rate = 0.15;
266
If you need more information about a particular error, DB2 provides an API
function that returns an extended message that is associated with the
specific SQLSTATE:
rc=sqlogstt(msg_sqlstate_buffer, 1024, 80, sqlca.sqlcode);
As a result of starting this function, char msg_sqlstate_buffer[1024] contains,
for example, the following message:
SQLSTATE 22001: Character data, right truncation occurred; for example,
an update or insert value is a string that is too long for the column,
or datetime value cannot be assigned to a host variable, because it is
too small.
267
A DB2 client application start a stored procedure by using the CALL statement,
which can pass parameters to the stored procedure and receive parameters that
are returned from the stored procedure. Using the previous example, it uses the
following syntax:
EXEC SQL CALL package_name.SP_name (:arg_in1, :arg_in2, :status_out);
As with all SQL statements, you can also prepare the CALL statement with
parameter markers and then supply values for the markers using SQLDA:
EXEC SQL CALL package_name.SP_name USING DESCRIPTOR :*psqlda;
You must set up the SQL Data Area (SQLDA) before use. SQLDA can be helpful if
you have an unknown number of host variables or many variables.
To start a stored procedure from a C client, the following items need to be
in place:
A stored procedure needs to be created and registered with the database.
A host variable for each IN and INOUT parameter of the stored procedure
should be declared and initialized.
Consider Example 5-18, in which the program is written to give a raise to each
employee whose current salary is less than certain value. The program passes
that value to a stored procedure, performs an update, and returns the status. The
client code in C looks as shown in Example 5-18.
Example 5-18 Passing data to a stored procedure
#include <sqlenv.h>
main()
{
EXEC SQL BEGIN DECLARE SECTION;
Sqlint32 salary_val=0;
Sqlint16 salind=1;
Sqlint16 status=0;
Sqlint16 statind=0;
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA;
EXEC SQL CONNECT TO sample;
EXEC SQL WHENEVER SQLERROR GOTO err_routine;
salary_val = getSalaryForRaise();
statind = -1;
/* set indicator variable to -1 */
/* for status as output-only variable */
268
\n ", SQLCODE);
}
All host variables that are used as parameters in the statement are declared and
initialized in EXEC SQL DECLARE SECTION.
269
DB2 provides a different method for optimizer directives, but tolerates and
ignores Oracle optimizer hints that appear in SQL statements. Although it is not
necessary to remove these hints, you should consider doing so to reduce the
complexity of your source code.
For complete information about the Java environment, drivers, programming, and
other relevant information, see Developing Java Applications, SC27-3875-00.
270
Type 4 connectivity
For IBM DB2 Driver for JDBC and SQLJ Type 4 connectivity, the getConnection
method must specify a user ID and a password through parameters or through
property values. See Example 5-19.
Example 5-19 getConnection syntax for Type 4 connectivity
271
The following is the syntax for a URL for IBM DB2 Driver for JDBC and SQLJ
Type 4 connectivity.
>>-+-jdbc:db2:------+-//server--+-------+--/database------------>
'-jdbc:db2j:net:-'
'-:port-'
>--+-----------------------------+-----------------------------><
|
.-----------------------. |
|
V
| |
'-:---property--=--value--;-+-'
Example 5-20 shows how to set the user ID and password in the user and
password parameters.
Example 5-20 Setting the user ID and password in the user and password parameters
import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;
class rsetClient
{
public static void main (String args []) throws SQLException
272
{
// Load the driver
DriverManager.registerDriver(new
oracle.jdbc.driver.OracleDriver());
// Connect to the database
Connection conn =
DriverManager.getConnection
("jdbc:oracle:oci8:@oracle","uid","pwd");
// ...
}
}
DB2 does not support the JDBC OCI driver. You do not need to run a Java
application using OCI, even though DB2 provides OCI support since DB2 9.7 Fix
Pack 1. Previous versions of DB2 JDBC drivers used DB2 Call Level Interface to
communicate with the database manager, but the newer DB2 JCC Driver is
rewritten to eliminate the DB2 Call Level Interface layer. It is a pure Java
implementation of the IBM DRDA communication protocol.
If an Oracle application is using the Oracle JDBC OCI driver, you can change it
directly to use the JCC driver. It is not necessary to import a JDBC library when
you connect to DB2. The registration and connection to DB2 is demonstrated in
Example 5-22. The parameters for the getConnection method are determined by
the connection type.
Example 5-22 DB2 JDBC connection
import java.sql.*;
class rsetClient
{
public static void main (String args []) throws SQLException
273
274
For JDBC and SQLJ applications that use Version 4.9, or later, of the IBM Data
Server Driver for JDBC and SQLJ, binary XML is the default format when the
application connects to a DB2 10.1 or later release server. You can use the
xmlFormat property in the DriverManager and DataSource interfaces to control
whether the transmission of XML data is in textual or binary format.
You can use binary XML format with any valid SQL/XML or XQuery statements,
as shown in Example 5-23.
Example 5-23 Enable XML binary usage with JDBC driver
...
properties.put("xmlFormat", DB2BaseDataSource.XML_FORMAT_BINARY);
DriverManager.getConnection(url, properties);
...
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT XMLCOL FROM XMLTABLE");
ContentHandler handler = new MyContentHandler();
while (rs.next()) {
SQLXML sqlxml = rs.getSQLXML(1);
SAXSource source = sqlxml.getSource(SAXSource.class);
XMLReader reader = source.getXMLReader();
reader.setContentHandler(handler);
reader.parse(source.getInputSource());
}
...
For more information, go to:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/index.jsp?topic=%
2Fcom.ibm.db2.luw.xml.doc%2Fdoc%2Fc0056290.html
275
The procedure has one input parameter and one output parameter. There is no
difference in the call between the two database platforms. In both cases, the
parameter values need to be set before the stored procedure can be run.
Example 5-24 demonstrates this point.
Example 5-24 Calling a stored procedure with input and output parameters from Java
276
try {
CallableStatement stmt = conn.prepareCall(SP_CALL);
stmt.registerOutParameter (1, OracleTypes.CURSOR);
stmt.execute();
ResultSet rs = (ResultSet) stmt.getObject(1);
while(rs.next())
{
System.out.println(rs.getString(1));
// ...
}
}
With DB2 9.7, when you use the CURSOR type, you register the output
parameter as a similar DB2Type. See Example 5-26.
Example 5-26 Java call of DB2procedure with result set
277
If you are not using the output parameter of CURSOR type, then you do not need
to register the result set with the registerOutParameter() method in the Java
application. To get the result set, call the getResultSet() method instead of
getObject(), as demonstrated in Example 5-27.
Example 5-27 Java call of DB2 procedure with result set
278
try {
CallableStatement stmt = conn.prepareCall(SP_CALL);
stmt.registerOutParameter (1, OracleTypes.CURSOR);
stmt.setInt(2, 6);
stmt.execute();
ResultSet rs = (ResultSet) stmt.getObject(1);
while(rs.next())
{
// ...
}
}
To call a function that returns a cursor directly with the DB2 JDBC driver, convert
the Oracle function to a stored procedure in DB2. To avoid changes to the
function source code, you can also wrap the existing function into a procedure
that returns the cursor as an output parameter. Example 5-29 illustrates a
function wrapper.
Example 5-29 Function wrapper
279
The DB2CI driver provides extensive support for the commonly used OCI
functions. The OCI functions include connection, initializations, handle and
descriptor function, binding, define, statement, execution, result set, transaction
control, data type (NUMBER, STRING, and DATE) functions, date and time,
large object processing, arrays, stored procedure executions, and file I/O. These
supported functions have a syntax that is compatible with the Oracle
OCI functions.
The DB2CI driver is evolving and provides the OCI functions listed in Table 5-2 in
the current release of the DB2 OCI driver (DB2 10.5). The list can be expanded
but is accurate at the time of the writing of this book.
Table 5-2 DB2 OCI-compatible functions
280
OCIAttrGet
OCILobGetLength
OCINumberTan
OCIAttrSet
OCILobIsEqual
OCINumberToInt
OCIBindArrayOfStruct
OCILobIsTemporary
OCINumberToReal
OCIBindByName
OCILobIsOpen
OCINumberToRealArray
OCIBindByPos
OCILobLocatorAssign
OCINumberToText
OCIBindDynamic
OCILobLocatorIsInit
OCINumberTrunc
OCIBreak
OCILobRead
OCIParamGet
OCIClientVersion
OCILobTrim
OCIParamSet
OCIDateAddDays
OCILobWrite
OCIPasswordChange
OCIDateAddMonths
OCILogoff
OCIPing
OCIDateAssign
OCILogon
OCIRawAllocSize
OCIDateCheck
OCILogon2
OCIRawAssignBytes
OCIDateCompare
OCINumberAbs
OCIRawAssignRaw
OCIDateDaysBetween
OCINumberAdd
OCIRawPtr
OCIDateFromText
OCINumberArcCos
OCIRawResize
OCIDateLastDay
OCINumberArcSin
OCIRawSize
OCIDateNextDay
OCINumberArcTan
OCIReset
OCIDateSysDate
OCINumberArcTan2
OCIResultSetToStmt
OCIDateToText
OCINumberAssign
OCIServerAttach
OCIDefineArrayOfStruct
OCINumberCeil
OCIServerDetach
OCIDefineByPos
OCINumberCmp
OCIServerVersion
OCIDefineDynamic
OCINumberCos
OCISessionBegin
OCIDescribeAny
OCINumberDec
OCISessionEnd
OCIDescriptorAlloc
OCINumberDiv
OCISessionGet
OCIDescriptorFree
OCINumberExp
OCISessionRelease
OCIEnvCreate
OCINumberFloor
OCIStmtExecute
OCIEnvInit
OCINumberFromInt
OCIStmtFetch
OCIErrorGet
OCINumberFromReal
OCIStmtFetch2
OCIFileClose
OCINumberFromText
OCIStmtGetBindInfo
OCIFileExists
OCINumberHypCos
OCIStmtGetPieceInfo
OCIFileFlush
OCINumberHypSin
OCIStmtPrepare
OCIFileGetLength
OCINumberHypTan
OCIStmtPrepare2
OCIFileInit
OCINumberInc
OCIStmtRelease
OCIFileOpen
OCINumberIntPower
OCIStmtSetPieceInfo
OCIFileRead
OCINumberIsInt
OCIStringAllocSize
OCIFileSeek
OCINumberIsZero
OCIStringAssign
OCIFileTerm
OCINumberLn
OCIStringAssignText
OCIFileWrite
OCINumberLog
OCIStringPtr
OCIHandleAlloc
OCINumberMod
OCIStringResize
OCIHandleFree
OCINumberMul
OCIStringSize
OCIInitialize
OCINumberNeg
OCITerminate
OCILobAppend
OCINumberPower
OCITransCommit
OCILobAssign
OCINumberPrec
OCITransDetach
OCILobClose
OCINumberRound
OCITransForget
OCILobCopy
OCINumberSetPi
OCITransMultiPrepare
OCILobCreateTemporary
OCINumberSetZero
OCITransPrepare
OCILobDisableBuffering
OCINumberShift
OCITransRollback
OCILobEnableBuffering
OCINumberSign
OCITransStart
281
OCILobErase
OCINumberSin
xaoEnv
OCILobFreeTemporary
OCINumberSqrt
xaosterr
OCILobFlushBuffer
OCINumberSub
xaoSvcCtx
If your application uses OCI calls that are not currently available in DB2, you can
replace them using combinations of supported calls.
Because the DB2CI provides a high level of compatibility with Oracle OCI calls,
you need only to compile and link the application using the DB2 specific include
file db2ci.h and the DB2CI library, for example, libdb2ci.a on AIX. A sample
DB2 OCI program is provided in Appendix D, DB2CI sample program on
page 345.
282
Procedure
For a DB2 CLI application to access a DB2 database successfully, complete the
following steps:
1. Catalog the DB2 database and node if the database is being accessed from a
remote client. On the Windows platform, you can use the DB2 CLI/ODBC
settings GUI to catalog the DB2 database.
2. Optional: Explicitly bind the DB2 CLI/ODBC bind files to the database by
running the following command:
db2 bind ~/sqllib/bnd/@db2cli.lst blocking all messages cli.msg\
grant public
On the Windows platform, you can use the DB2 CLI/ODBC settings GUI to
bind the DB2 CLI/ODBC bind files to the database.
3. Optional: Change the DB2 CLI/ODBC configuration keywords by editing the
db2cli.ini file.
On the Windows platform, you can use the DB2 CLI/ODBC settings GUI to
set the DB2 CLI/ODBC configuration keywords.
283
#!/usr/bin/perl
use DBI;
$database='dbi:Oracle:xp10g';
$user='sample';
$password='sample';
$dbh = DBI->connect($database,$user,$password);
print " Connected to database.\n";
$name = 'Ariel';
$message;
$sth = $dbh->prepare(q{
BEGIN
Greeting(:name, :message);
END;
284
});
$sth->bind_param(":name", $name);
$sth->bind_param_inout(":message", \$message, 100);
$sth->execute;
print "$message", "\n";
# check for problems ...
warn $DBI::errstr if $DBI::err;
$dbh->disconnect;
The results of the execution of oraCallGreeting.pl are shown in Figure 5-3.
285
Minor changes might be necessary to convert the Oracle Perl application to use
DB2. In addition to entering the correct values for the user ID and password, you
must complete the following steps:
Observe the syntax difference in the parameters for the connect method and
make the necessary changes.
Observe the syntax differences for calling stored procedures and make the
necessary changes.
$sth = $dbh->prepare(q{
BEGIN
286
Greeting(:name, :message);
END;
});
In contrast, a DB2 stored procedure is run by issuing a CALL statement from
within a PREPARE statement. Also, the stored procedure input and output
parameters are indicated by parameter markers (?, ?), as shown in
Example 5-35.
Example 5-35 Calling a stored procedure in a DB2 Perl program
$sth = $dbh->prepare(q{
CALL Greeting(?,?);
});
The complete Perl program, converted to DB2, is shown in Example 5-36.
Example 5-36 DB2 Perl program db2CallGreeting.pl
#!/usr/bin/perl
use DBI;
$database='dbi:DB2:sample';
$user='db2inst1';
$password='db2inst1';
$dbh = DBI->connect($database, $user, $password) or die "Cant connect
to $database: $DBI::errstr";
print " Connected to database.\n";
$name = 'Ariel';
$message;
$sth = $dbh->prepare(q{
CALL Greeting(?,?);
});
$sth->bind_param(1,$name);
$sth->bind_param_inout(2, \$message, 100);
$sth->execute;
print "$message", "\n";
287
288
<?php
try {
$dbh = new PDO('OCI:', 'userid', 'password');
echo "Connected\n";
} catch (Exception $e) {
echo "Failed: " . $e->getMessage();
}
?>
<?php
try {
$dbh = new PDO('odbc:SAMPLE', 'userid', 'password');
echo "Connected\n";
} catch (Exception $e) {
echo "Failed: " . $e->getMessage();
}
?>
289
Example 5-39 shows the call to the Greeting procedure after the connection
is established.
Example 5-39 Call a PHP procedure
<?php
$conn = oci_connect("userid","password") or die;
$sql = "BEGIN Greeting(:name, :message); END;";
$stmt = oci_parse($conn,$sql);
// Bind the input parameter
oci_bind_by_name($stmt,":name",$name,32);
// Bind the output parameter
oci_bind_by_name($stmt,":message",$message,100);
// Assign a value to the input
$name = "Ariel";
oci_execute($stmt);
290
<?php
$database = 'sample';
$user = 'db2inst1';
$password = 'db2inst1';
// Next parameters used when making an uncataloged connection
// $hostname = 'localhost';
// $port = 50000;
$conn = db2_connect($database, $user, $password) or die;
// use this connection string for uncataloged connections:
//$conn_string = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$database;
//HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$passwo//r
d;";
// $conn = db2_connect($conn_string, , );
if ($conn) {
291
292
When you connect to DB2 in a PHP application, the connection can be made to
either a cataloged or an uncataloged database.
Additional information: For detailed information about cataloging a DB2
database, see Database Administration Concepts and Configuration
Reference, SC27-3871.
For an uncataloged connection to a database, the database parameter
represents a complete connection string in the format that is shown in
Example 5-42.
Example 5-42 Connection string for an uncataloged DB2 database
$database = 'sample';
$user = 'db2inst1';
$password = 'db2inst1';
$hostname = 'localhost';
$port = 50000;
$conn_string = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$database;
HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$password;";
$conn = db2_connect($conn_string, , );
293
parameter-number
variable-name
parameter-type
data-type
precision
scale
With this information, you bind the stored procedure input and the output
parameters are converted, as shown in Example 5-44.
Example 5-44 Converting oci_bind_by_name to db2_bind_param
ORACLE:
// Bind the input parameter
oci_bind_by_name($stmt,':name',$name,32);
// Bind the output parameter
oci_bind_by_name($stmt,':message',$message,100);
DB2 conversion:
// Bind the input parameter
db2_bind_param($stmt, 1, "name", DB2_PARAM_IN);
// Bind the output parameter
db2_bind_param($stmt, 2, "message", DB2_PARAM_OUT);
294
statement
options
Oracle:
$sql = 'BEGIN Greeting(:name, :message); END;';
$stmt = oci_parse($conn,$sql);
DB2 conversion:
$sql = 'CALL Greeting(?, ?)';
$stmt = db2_prepare($conn, $sql);
295
After these changes are implemented, the application is fully converted to DB2.
Figure 5-6 shows the result of running this program.
Note: For complete information about PHP, see Developing Perl, PHP, Python,
and Ruby on Rails Applications, SC27-3876.
296
297
In addition to the DB2 .NET Data Provider, IBM also provides a collection of
add-ins to the Microsoft Visual Studio .NET IDE, providing tight integration
between IBM Data Studio and DB2 for Linux, UNIX, and Windows and the host
databases using IBM DB2 Connect. The add-ins simplify the creation of DB2
applications that use the ADO.NET interface. The add-ins can also be used to
develop server-side objects, such as SQL stored procedures and user-defined
functions. The DB2 Visual Studio add-ins can be obtained at the
following website:
http://www-306.ibm.com/software/data/db2/windows/dotnet.html
The IBM.Data.DB2 name space contains the DB2 .NET Data Provider. To use
the DB2 .NET Data Provider, you must add the Imports or using statement for
the IBM.Data.DB2 name space to your application .DLL, as shown in
Example 5-46.
Example 5-46 Examples of the required Imports or using statement
[Visual Basic]
Imports IBM.Data.DB2
[C#]
using IBM.Data.DB2;
Also, you need to add references to IBM.Data.db2.dll and
IBM.Data.DB2.Server.dll to the project.
298
Figure 5-7 GUI for the Visual Basic .NET application conversion example
The essential components of this application are contained within the Click event
for the RUN QUERY control button. Example 5-47 shows the code in the
Button1_Click event as it might appear in an Oracle application. Some
explanations of the changes are documented in the notes that appear after the
code example.
Example 5-47 The Button1_Click event
299
[3]
[5]
While dr.Read()
ListBox1.Items.Add("The name of this employee is: " +
dr.Item("first_name") + dr.Item("last_name"))
[6]
End While
conn.Dispose()
End Sub
Notes about the Button1_Click event:
IMPORT Oracle.DataAccess.Client is added to the application .DLL.
A string (OraDb) is declared as the connection string for the
Oracle database.
A connection (conn) is defined as an OracleConnection.
A command (cmd) is defined as an OracleCommand and populated with the
text of the query.
A DataReader (dr) is defined as an OracleDataReader and the query
is run.
The List Box is populated with the results of the query.
300
When the application runs, clicking RUN QUERY yields the results that are
shown in Figure 5-8.
Figure 5-8 The results of the Oracle Example are displayed in the list box
Imports IBM.Data.DB2
[1]
______________________________________________________________
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
301
[3]
conn.Open()
Dim cmd As New DB2Command
cmd.Connection = conn
[4]
[5]
[6]
conn.Dispose()
End Sub
302
After you implement the changes, click RUN QUERY to produce the results that
are shown in Figure 5-9.
For further information: You can find more information about the DB2 .NET
provider at:
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.swg.im.
dbclient.adonet.doc/doc/c0010960.html
For in-depth information about .NET programming, see Developing ADO.NET
and OLE DB Applications, SC27-3873.
303
304
Appendix A.
Terminology mapping
All relational database systems, including Oracle Database and DB2, are based
upon similar concepts. At the same time, each database platform operates with a
set of terms that might be different. These differences in terminology can present
difficulties when you are converting from one platform to another.
Table A-1 introduces the most common Oracle database terms and provides the
DB2 equivalent of the term.
DB2 term
Comments
Alert log
Diagnostic log
Data blocks
Pages
Data cache
Buffer pools
Memory areas that cache table and index pages when they are
read from disk. A DB2 database must have at least one buffer
pool, and each table space can be assigned a separate buffer
pool.
Data dictionary
System catalog
305
Oracle term
DB2 term
Comments
Data files
Containers
Database
Database
Database link
(DBlink)
Federated server
Extents
Extents
Instance
Instance
Large pool
Utility heap
Oracle EE
DB2 Enterprise
Server Edition
Oracle
Transparent
Gateway
DB2 Connect
InfoSphere
Federation
Server
Package
Module
N/A
Package
DBM and
database
configuration
306
Oracle term
DB2 term
Comments
PL/SQL
SQL Procedural
Language (SQL
PL)
Process Global
Area (PGA)
Agent /
application
shared
memory
Redo logs
Database logs
Statement cache
Package cache
SQL*PLUS
CLPPlus
System Global
Area (SGA)
Instance and
database shared
memory
Table spaces
Table spaces
Undo table
spaces
N/A
Store the before image of data when they are being modified.
In DB2, the before image of data are stored in the database log
along with the after image.
307
308
Appendix B.
Data types
This appendix explains data types in the following environments:
309
C/C++ type
sqllen
Description
Integer
SMALLINT
(500 or 501)
short
short int
sqlint 16
INTEGER
INT
(496 or 497)
long
long int
sqlint32
BIGINT
(492 or 493)
310
long long
long
__int64
sqlint64
Item
C/C++ type
Floating
point
REAL
FLOAT
(480 or 481)
float
DOUBLE
(480 or 481)
DOUBLE
PRECISION
double
sqllen
Description
Decimal
DECIMAL(p,s)
DEC(p,s)
(484 or 485)
double /
decimal
p/2+1
NUMERIC(p,s)
NUM(p,s)
Date and
time
DATE
(384 or 385)
struct {
short len;
char data[10];
} dt;
10
char dt[11];
TIME
(388 or 389)
char
8
TIMESTAMP
(392 or 393)
char
26
Packed decimal.
If precision /scale is not specified, the
default is (5.0).
The max precision is 31 digits, and the
max range is between -10E31+1 to
10E31 -1.
Consider using char / decimal functions
to manipulate packed decimal fields as
char data.
Null-terminated character form (11
characters) or varchar struct form (10
characters).
struct can be divided as wanted to obtain
the individual fields.
Example: 11/02/2000.
Stored internally as a packed string of 4
bytes.
Null-terminated character form (9
characters) or varchar struct form (8
characters).
struct can be divided as wanted to obtain
the individual fields.
Example: 19:21:39.
Stored internally as a packed string of 3
bytes.
Null-terminated character form or
varchar struct form.
Allows 19 - 32 characters.
struct can be divided as wanted to obtain
the individual fields.
Example: 2003-08-04-01.02.03.000000
Stored internally as a packed string of 10
bytes.
311
Item
C/C++ type
sqllen
Description
character
CHAR(1)
(452 or 453)
char
Single character.
CHAR(n)
(452 or 453)
char
VARCHAR
(460 or 461)
char
VARCHAR
(448 or 449)
or
VARCHAR2
(448 or 449)
len
LONG
VARCHAR
(456 or 457)
CLOB(n)
(408 or 409)
clob
len
Binary
CLOB
(964 or 965)
clob_locator
CLOB
(920 or 921)
clob_file
BLOB(n)
(404 or 405)
blob
312
BLOB
(960 or 961)
blob_locator
BLOB
(916 or 917)
blob_file
Item
C/C++ type
sqllen
Description
Doublebyte
GRAPHIC(1)
GRAPHIC(n)
(468 or 469)
sqldbchar
24
VARGRAPHIC(n
)
(464 or 465)
LONG
VARGRAPHIC(n
)
(472 or 473)
struct {
short int;
sqldbchar[n]
} tag;
n*2+4
alternately:
sqldbchar[n+1]
struct {
short int;
sqldbchar[n]
} tag;
DBCLOB(n)
(412 or 413)
dbclob
DBCLOB
dbclob_locator
DBCLOB
dbclob_file
External
data
Datalink(n)
XML
(988 or 989)
n+54
struct { sqluint32
length;
char data[n]; }
XML value.
1<=n<=2 147 483 647.
313
Java type
sqllen
Description
Integer
SMALLINT
(500 or 501)
short
INTEGER
(496 or 497)
int
BIGINT 1
(492 or 493)
long
REAL
(480 or 481)
float
DOUBLE
(480 or 481)
double
DOUBLE
(480 or 481)
double
DECIMAL(p,s)
(484 or 485)
java.math. BigDecimal
n/2
Packed decimal
Floating
point
Decimal
Date and
time
314
DECFLOAT(n)
java.math. BigDecimal
DATE
(384 or 385)
java.sql.Date
10
n=16 or n=34
10-byte character string
TIME
(388 or 389)
java.sql.Time
TIMESTAMP
(392 or 393)
java.sql.Timestamp
26
TIMESTAMP(n)
java.sql.Timestamp
26
0<=p<=12, default 6
Item
Java type
sqllen
Description
Character
CHAR(n)
(452 or 453)
java.lang.String
byte[]
VARCHAR (n)
(448 or 449)
java.lang.String
VARCHAR (n)
FOR BIT DATA
byte[]
LONG VARCHAR
(456 or 457)
java.lang.String
CLOB(n)
(408 or 409)
java.lang.Clob,
n
java.lang.String,
java.io.ByteArrayInputStream,
java.io.StringReader
BLOB(n)
(404 or 405)
java.lang.Blob,
byte[]
BINARY(n)
byte[]
Binary
Doublebyte
Variable-length character
string, n <= 32672
Variable-length character
string n <= 32672
n<=254
VARBINARY(n)
byte[]
GRAPHIC(n)
(468 or 469)
java.lang.String
Fixed-length double-byte
character string, n<=127
n<=32672
VARGRAPHIC(n)
(464 or 465)
java.lang.String
n*2+4
Non-null-terminated varying
double-byte character string
with 2-byte string length
indicator, n<=16336
LONG
VARGRAPHIC(n)
(472 or 473)
java.lang.String
Non-null-terminated varying
double-byte character string
with 2-byte string length
indicator
DBCLOB(n)
(412 or 413)
java.lang.Clob
CLOB(n)
(408 or 409)
java.sql.Clob,
java.lang.String
Non-null-terminated varying
character string with 4-byte
string length indicator
315
Item
Java type
sqllen
Description
binary
BLOB(n)
(404 or 405)
java.sql.Blob,
byte[],
Non-null-terminated varying
binary string with 4-byte string
length indicator
ROWID
java.sql.RowId,
byte[],
com.ibm.db2.jcc.DB2RowIDa
RowId identifier
XML
java.lang.String,
com.ibm.db2.jcc.DB2Xmla ,
java.sql.SQLXML,
java.io.InputStream,
java.sql.Clob,
java.sql.Blob,
byte[]
XML value
a. Deprecated
Description
BINARY_INTEGER
INTEGER
BLOB
BLOB(4096)
Binary data
BLOB (n)
BLOB (n)
n = 1 - 2 147 483 647
BOOLEAN
BOOLEAN
CHAR
CHAR (1)
CHAR (n)
CHAR (n)
n = 1 - 254
VARCHAR (n)
CHARACTER
CHARACTER (1)
CHARACTER (n)
CHARACTER (n)
n = 1 - 254
CHARACTER VARYING
(n)
VARCHAR (n)
n = 1 - 32 672
316
Description
CLOB
CLOB (1 MB)
CLOB (n)
CLOB (n)
n = 1 - 2 147 483 647
DATE
DATE a
DEC
DEC (9, 2)
DEC (p)
DEC (p)
p = 1 - 31
DEC (p, s)
DEC (p, s)
p = 1 - 31; s = 1 - 31
DECIMAL
DECIMAL (9, 2)
DECIMAL (p)
DECIMAL (p)
p = 1 - 31
DECIMAL (p, s)
DECIMAL (p, s)
p = 1 - 31; s = 1 - 31
DOUBLE
DOUBLE
DOUBLE PRECISION
DOUBLE
PRECISION
FLOAT
FLOAT
FLOAT (n)
n = 1 - 24
REAL
FLOAT (n)
n = 25 - 53
DOUBLE
INT
INT
INTEGER
INTEGER
LONG
CLOB (32760)
LONG RAW
BLOB (32760)
LONG VARCHAR
CLOB (32760)
NATURAL
INTEGER
NCHAR
GRAPHIC (127)
NCHAR (n)
n = 1 - 2000
GRAPHIC (n)
n = 1 - 127
NCLOBb 2
DBCLOB (1 MB)
NCLOB (n)
DBCLOB (2000)
NVARCHAR2
VARGRAPHIC (2048)
or
NVARCHAR2(2048)
317
Description
NVARCHAR2 (n)
VARGRAPHIC (n)
NVARCHAR2(n)
NUMBER
NUMBERc
NUMBER (p)
NUMBER (p)c
NUMBER (p, s)
NUMERIC
NUMERIC (9.2)
NUMERIC (p)
NUMERIC (p)
p = 1 - 31
NUMERIC (p, s)
NUMERIC (p, s)
p = 1 - 31; s = 0 - 31
PLS_INTEGER
INTEGER
RAW
BLOB (32767)
RAW (n)
BLOB (n)
n = 1 - 32 767
SMALLINT
SMALLINT
TIMESTAMP (0)
TIMESTAMP (0)
TIMESTAMP (p)
TIMESTAMP (p)
VARCHAR
VARCHAR (4096)
VARCHAR (n)
VARCHAR (n)
VARCHAR2 (n)
VARCHAR2 (n)d
a. When the DB2_COMPATIBILITY_VECTOR registry variable is set for the DATE data type, DATE is equivalent to
TIMESTAMP (0).
b. For restrictions for the NCLOB data type in certain database environments, see Restrictions on PL/SQL
support in the DB2 Information Center.
c. This data type is supported when the number_compat database configuration parameter is set to ON.
d. This data type is supported when the varchar2_compat database configuration parameter is set to ON.
318
CHAR(n)
CHAR(n)
VARCHAR2(n)
VARCHAR2(n)
n <= 32762.
NCHAR(n)
CHAR(n) a
NVARCHAR2(n)
NVARCHAR2(n)
Notes
n <= 32762.
LONG
LONG VARCHAR(n)
LONG
CLOB(2 GB)
if n <= 2 GB.
NUMBER(p)
NUMBER(p)
NUMBER(p,s)
NUMBER(p,s)
if s > 0.
NUMBER
NUMBER
RAW(n)
LONG RAW
BLOB
BLOB(n)
If n <= 2 GB.
CLOB
CLOB(n)
If n <= 2 GB.
NCLOB
DBCLOB(n)
DATE
DATE
DATE (MM/DD/YYYY)
TIME (HH24:MI:SS)
TIMESTAMP (0)
TIMESTAMP (0)
TIMESTAMP (p)
TIMESTAMP (p)
XMLType
XML
XML storage.
a. You can map Oracle NCHAR and NVARCHAR2 columns to DB2 CHAR and VARCHAR2 columns that are
created in a Unicode database or a column that is created with a CCSID clause to a Unicode compatible code
set.
319
320
Appendix C.
Built-in modules
This appendix provides details for the following built-in modules, equivalent to
Oracle Database supplied PL/SQL packages, that are supported in DB2 10:
DBMS_ALERT
DBMS_DDL
DBMS_JOB
DBMS_LOB
DBMS_OUTPUT
DBMS_PIPE
DBMS_SQL
DBMS_UTILITY
UTL_DIR
UTL_MAIL
UTL_SMTP
321
C.1 DBMS_ALERT
The DBMS_ALERT module provides a set of procedures for registering, sending,
and receiving alerts for a specific event. Alerts are stored in
SYSTOOLS.DBMS_ALERT_INFO, which is created in the SYSTOOLSPACE when you first
reference this module for a particular database. The DBMS_ALERT module
requires that the database configuration parameter CUR_COMMIT to be set to ON.
Table C-1 lists the system-defined routines included in the DBMS_ALERT
module.
Table C-1 System-defined routines available in the DBMS_ALERT module
Routine name
Description
REGISTER procedure
REMOVE procedure
REMOVEALL procedure
SIGNAL procedure
SET_DEFAULTS procedure
WAITANY procedure
WAITONE procedure
322
You can catch the alert through the WAITONE routine after you register the alert
name with a 60-second timeout, as shown in Example C-2.
Example: C-2 Intercepting the alert
DECLARE
v_stat1 INTEGER;
v_msg1 VARCHAR2(50);
BEGIN
DBMS_ALERT.REGISTER('alertfromtrigger');
DBMS_ALERT.WAITONE('alertfromtrigger', v_msg1, v_stat1, 60);
END;
/
For more information and examples about DBMS_ALERT, visit the DB2
Information Center:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.sqlpl.doc/doc/r0053671.html
C.2 DBMS_DDL
The DBMS_DDL module implements the routines that are listed in Table C-2 for
obfuscating (wrapping in Oracle terms) DDL statements and the database
objects that are created by such statements. You can use this function to deploy
database objects without displaying the procedural logic in them.
Table C-2 System-defined routines available in the DBMS_DDL module
Routine name
Description
WRAP function
CREATE_WRAPPED procedure
Example C-3 shows the use of DBMS_DDL.WRAP for generating the obfuscated
version of a user-defined function.
Example: C-3 Generating an obfuscated user-defined function.
323
C.3 DBMS_JOB
This module provides a set of procedures for creating, scheduling, and managing
jobs. DBMS_JOB is an alternative interface for the DB2 Administrative Task
Scheduler (ATS). To use the DBMS_JOB module, you must activate the ATS.
This facility is turned off by default, although you are still able to define and
modify jobs (tasks). You can enable the ATS by setting the registry variable as
follows:
db2set DB2_ATS_ENABLE=YES
For more information, see the Setting up the administrative task scheduler topic
in the Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.admin.gui.doc/doc/t0054396.html
Table C-3 shows the routines in the DBMS_JOB module.
Table C-3 Procedures that are provided by the DBMS_JOB module
324
Routine name
Description
BROKEN procedure
CHANGE procedure
INTERVAL procedure
Routine name
Description
NEXT_DATE procedure
REMOVE procedure
RUN procedure
SUBMIT procedure
WHAT procedure
Example C-4 shows the creation of a job that is called job_proc that first runs
immediately (at SYSDATE) and is scheduled to repeat every 24 hours.
Example: C-4 DBMS_JOB examples
INTEGER;
For more information about the DBMS_JOB module, see the Information
Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.sqlpl.doc/doc/r0055103.html
325
C.4 DBMS_LOB
This module offers a set of routines for operating on large objects (LOBs). These
routines are listed in Table C-4.
Table C-4 Routines in the DBMS_LOB module
326
Routine name
Description
APPEND procedure
CLOSE procedure
COMPARE function
CONVERTTOBLOB procedure
CONVERTTOCLOB procedure
COPY procedure
ERASE procedure
GET_STORAGE_LIMIT function
GETLENGTH function
INSTR function
ISOPEN function
OPEN procedure
READ procedure
SUBSTR function
TRIM procedure
WRITE procedure
WRITEAPPEND procedure
In Example C-5, the APPEND and ERASE routines are started from the DB2
command line processor to demonstrate some actions that can be performed on
a LOB.
Example: C-5 DBMS_LOB examples
call DBMS_LOB.APPEND_CLOB('ABCD','1234')
Value of output parameters
-------------------------Parameter Name : DEST_LOB
Parameter Value : ABCD1234
Return Status = 0
call DBMS_LOB.ERASE_CLOB('DBMS', 1,3)
Value of output parameters
-------------------------Parameter Name : LOB_LOC
Parameter Value : DB S
Parameter Name : AMOUNT
Parameter Value : 1
ReturnStatus=0
Example C-6 shows the use of DBMS_LOB routines in a PL/SQL block.
Example: C-6 DBMS_LOB in an anonymous block
DECLARE
v_dest_lob CLOB := 'ABCD';
BEGIN
DBMS_OUTPUT.PUT_LINE('Original lob: ' || v_dest_lob);
DBMS_LOB.APPEND_CLOB(v_dest_lob,'1234');
DBMS_OUTPUT.PUT_LINE('New lob : ' || v_dest_lob);
END;
/
DECLARE
v_dest_lob CLOB := 'DBMS';
v_amount INTEGER := 1;
BEGIN
DBMS_OUTPUT.PUT_LINE('Original lob: ' || v_dest_lob);
DBMS_LOB.ERASE_CLOB(v_dest_lob, v_amount, 3);
DBMS_OUTPUT.PUT_LINE('New lob : ' || v_dest_lob);
327
C.5 DBMS_OUTPUT
The DBMS_OUTPUT module provides a set of procedures that you can us to
work with the message buffer by putting lines of text (messages) in the message
buffer and getting messages from the buffer. These procedures can be useful
during application debugging when you must write messages to standard output.
You could use the command line processor command SET SERVEROUTPUT ON to
redirect messages to standard output.
Table C-5 lists the system-defined routines included in the DBMS_OUTPUT
module.
Table C-5 System-defined routines available in the DBMS_OUTPUT module
Routine name
Description
DISABLE procedure
ENABLE procedure
GET_LINE procedure
GET_LINES procedure
Gets one or more lines of text from the message buffer and
places the text into a collection.
NEW_LINE procedure
PUT procedure
PUT_LINE procedure
Example C-7 shows the use of DBMS_OUTPUT, and the messages that are
produced by the PUT and PUT_LINE procedures.
Example: C-7 Anonymous block with DBMS_OUTPUT procedures
SET SERVEROUTPUT ON
/
DECLARE
328
v_message VARCHAR2(50);
BEGIN
DBMS_OUTPUT.PUT(CHR(10));
DBMS_OUTPUT.PUT_LINE('This is the beginning');
DBMS_OUTPUT.PUT(CHR(10));
v_message := 'You''re seeing now the second line.';
DBMS_OUTPUT.PUT_LINE(v_message);
END;
/
DB20000I The SQL command completed successfully.
This is the beginning
You're seeing now the second line.
C.6 DBMS_PIPE
The DBMS_PIPE module provides a set of routines for sending messages
through a pipe within a session or between sessions that are connected to the
same database.
Pipes are created either implicitly or explicitly during procedure calls. An implicit
pipe is created when a procedure call contains a reference to a pipe name that
does not exist. An explicit pipe is created by calling the CREATE_PIPE function and
specifying the name of the pipe.
Pipes can be private or public. A private pipe can be accessed only by the user
who created the pipe. Even an administrator cannot access a private pipe that
was created by another user. A public pipe can be accessed by any user who has
access to the DBMS_PIPE module. Access level for a pipe is specified in a call to
the CREATE_PIPE function. If no value is specified, the default is to create a private
pipe. All implicit pipes are private.
Table C-6 lists the system-defined routines included in the DBMS_PIPE module.
Table C-6 System-defined routines available in the DBMS_PIPE module
Routine name
Description
CREATE_PIPE function
NEXT_ITEM_TYPE function
329
Routine name
Description
PACK_MESSAGE function
PACK_MESSAGE_RAW procedure
PURGE procedure
RECEIVE_MESSAGE function
REMOVE_PIPE function
RESET_BUFFER procedure
SEND_MESSAGE procedure
UNIQUE_SESSION_NAME function
UNPACK_MESSAGE procedures
DECLARE
status INT;
BEGIN
330
DECLARE
status INTEGER;
itemType INTEGER;
string1 VARCHAR(50);
BEGIN
status := DBMS_PIPE.RECEIVE_MESSAGE( 'pipe1' );
IF ( status = 0 ) THEN
itemType := DBMS_PIPE.NEXT_ITEM_TYPE();
IF ( itemType = 9 ) THEN
DBMS_PIPE.UNPACK_MESSAGE_CHAR( string1 );
DBMS_OUTPUT.PUT_LINE( 'string1 is: ' || string1 );
ELSE
DBMS_OUTPUT.PUT_LINE( 'unexpected data!');
END IF;
END IF;
END;
/
You can find more information and examples about DBMS_PIPE in the DB2
Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.sqlpl.doc/doc/r0053678.html
C.7 DBMS_SQL
The DBMS_SQL module provides a collection of procedures for running dynamic
SQL. The routines in the DBMS_SQL module are useful when you want to
construct and run SQL statements dynamically at run time or call a function that
uses dynamic SQL from within an SQL statement. DDL statements, such as
ALTER TABLE or DROP TABLE, can also be prepared and run when needed.
331
Table C-7 lists the system-defined routines included in the DBMS_SQL module.
Table C-7 System-defined routines available in the DBMS_SQL module
332
Procedure name
Description
BIND_VARIABLE_BLOB procedure
BIND_VARIABLE_CHAR procedure
BIND_VARIABLE_CLOB procedure
BIND_VARIABLE_DATE procedure
BIND_VARIABLE_DOUBLE procedure
BIND_VARIABLE_INT procedure
BIND_VARIABLE_NUMBER procedure
BIND_VARIABLE_RAW procedure
BIND_VARIABLE_TIMESTAMP
procedure
BIND_VARIABLE_VARCHAR procedure
Procedure name
Description
CLOSE_CURSOR procedure
Closes a cursor.
COLUMN_VALUE_BLOB procedure
COLUMN_VALUE_CHAR procedure
COLUMN_VALUE_CLOB procedure
COLUMN_VALUE_DATE procedure
COLUMN_VALUE_DOUBLE procedure
COLUMN_VALUE_INT procedure
COLUMN_VALUE_LONG procedure
COLUMN_VALUE_NUMBER procedure
COLUMN_VALUE_RAW procedure
COLUMN_VALUE_TIMESTAMP
procedure
COLUMN_VALUE_VARCHAR procedure
DEFINE_COLUMN_BLOB procedure
DEFINE_COLUMN_CHAR procedure
DEFINE_COLUMN_CLOB procedure
DEFINE_COLUMN_DATE procedure
DEFINE_COLUMN_DOUBLE procedure
DEFINE_COLUMN_INT procedure
DEFINE_COLUMN_LONG procedure
DEFINE_COLUMN_NUMBER procedure
DEFINE_COLUMN_RAW procedure
333
334
Procedure name
Description
DEFINE_COLUMN_TIMESTAMP
procedure
DEFINE_COLUMN_VARCHAR procedure
DESCRIBE_COLUMNS procedure
DESCRIBE_COLUMNS2 procedure
EXECUTE procedure
Executes a cursor.
EXECUTE_AND_FETCH procedure
FETCH_ROWS procedure
IS_OPEN procedure
LAST_ROW_COUNT procedure
OPEN_CURSOR procedure
Opens a cursor.
PARSE procedure
VARIABLE_VALUE_BLOB procedure
VARIABLE_VALUE_CHAR procedure
VARIABLE_VALUE_CLOB procedure
VARIABLE_VALUE_DATE procedure
VARIABLE_VALUE_DOUBLE procedure
VARIABLE_VALUE_INT procedure
VARIABLE_VALUE_NUMBER procedure
VARIABLE_VALUE_RAW procedure
Procedure name
Description
VARIABLE_VALUE_TIMESTAMP
procedure
VARIABLE_VALUE_VARCHAR
procedure
Some of the names of the procedures in this module are different from the
corresponding names in the Oracle DBMS_SQL package. For example, the
names of the DBMS_SQL.COLUMN_VALUE_* procedures reflect the data types they
handle: COLUMN_VALUE_NUMBER, COLUMN_VALUE_CHAR, or
COLUMN_VALUE_DATE.
Table C-8 lists the system-defined types and constants available in the
DBMS_SQL module.
Table C-8 DBMS_SQL system-defined types and constants
Name
Type or
constant
Description
DESC_REC
Type
DESC_REC2
Type
DESC_TAB
Type
DESC_TAB2
Type
NATIVE
Constant
C.8 DBMS_UTILITY
The DBMS_UTILITY module provides various utility routines for gathering
statistics for the entire database or a schema, compiling or validating objects,
running DDL statements, and getting information about database objects.
335
336
Routine name
Description
ANALYZE_DATABASE procedure
ANALYZE_PART_OBJECT
procedure
ANALYZE_SCHEMA procedure
CANONICALIZE procedure
COMMA_TO_TABLE procedure
COMPILE_SCHEMA procedure
DB_VERSION procedure
EXEC_DDL_STATEMENT
procedure
GET_CPU_TIME function
GET_DEPENDENCY procedure
GET_HASH_VALUE function
GET_TIME function
NAME_RESOLVE procedure
NAME_TOKENIZE procedure
Routine name
Description
TABLE_TO_COMMA procedure
VALIDATE procedure
Table C-10 lists the system-defined variables and types available in the
DBMS_UTILITY module.
Table C-10 DBMS_UTILITY public variables
Public variables
Data type
Description
lname_array
TABLE
uncl_array
TABLE
337
You can find details and examples about the usage of these procedures and
variables in the DB2 Information Center at:
http://publib.boulder.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2
.luw.apdv.sqlpl.doc/doc/r0055155.html
C.9 UTL_DIR
The UTL_DIR module implements routines for maintaining directory aliases that
are used with the UTL_FILE module. You can create, drop, and gather
information about the directory alias using this module.
Table C-11 shows the procedures that are defined in this module.
Table C-11 System-defined routines available in the UTL_DIR module
Routine name
Description
CREATE_DIRECTORY procedure
CREATE_OR_REPLACE_DIRECTORY
procedure
DROP_DIRECTORY procedure
GET_DIRECTORY_PATH procedure
BEGIN
UTL_DIR.CREATE_DIRECTORY('mydir', '/home/user/temp/mydir');
END;
/
If the directory alias exists, you can use the CREATE_OR_REPLACE_DIRECTORY
procedure.
Directory information is stored in the SYSTOOLS.DIRECTORIES table, which is
created in the SYSTOOLSPACE when you first reference this module for
each database.
338
SET SERVEROUTPUT ON
/
DECLARE
v_dir VARCHAR2(200);
BEGIN
UTL_DIR.GET_DIRECTORY_PATH('mydir', v_dir );
DBMS_OUTPUT.PUT_LINE('Directory path: ' || v_dir);
END;
/
--This example results in the following output:
Value of output parameters
-------------------------Parameter Name : PATH
Parameter Value : home/myuser/temp/mydir
Return Status = 0
Example C-13 uses the DROP_DIRECTORY procedure to drop the specified
directory alias.
Example: C-13 Drop a directory alias
BEGIN
UTL_DIR.DROP_DIRECTORY('mydir');
END;
C.10 UTL_MAIL
The UTL_MAIL module provides the capability to send email messages with or
without attachments. To successfully send an email message using this module,
the database configuration parameter SMTP_SERVER must contain one or more
valid Simple Mail Transfer Protocol (SMTP) server addresses.
339
Table C-12 lists the system-defined routines included in the UTL_MAIL module.
Table C-12 System-defined routines available in the UTL_MAIL module
Routine name
Description
SEND procedure
SEND_ATTACH_RAW procedure
SEND_ATTACH_VARCHAR2
procedure
Example C-14 shows how to set up your SMTP server entries. To set up a list of
SMTP servers, separate them by commas. You can specify non-default port
numbers if necessary. When it sends email messages, DB2 tries to connect to
the first listed server. If it is unreachable, DB2 tries other servers in the
listed order.
Example: C-14 SMTP server entry setup
340
C.11 UTL_SMTP
The UTL_SMTP module provides a set of routines for sending email over SMTP.
Compared to the UTL_MAIL module, UTL_SMTP gives you more control over
sending email messages.
Table C-13 lists the system-defined routines included in the UTL_SMTP module.
Table C-13 System-defined routines available in the UTL_SMTP module
Routine name
Description
CLOSE_DATA procedure
COMMAND procedure
COMMAND_REPLIES
procedure
DATA procedure
EHLO procedure
HELO procedure
HELP procedure
MAIL procedure
NOOP procedure
OPEN_CONNECTION function
Opens a connection.
OPEN_CONNECTION
procedure
Opens a connection.
OPEN_DATA procedure
QUIT procedure
RCPT procedure
RSET procedure
VRFY procedure
WRITE_DATA procedure
WRITE_RAW_DATA
procedure
341
Data type
Description
connection
RECORD
reply
RECORD
The send_mail procedure in Example C-16 constructs and sends a text email
message using the UTL_SMTP module and the UTL_SMTP.DATA method. This
example also demonstrates how to call this procedure.
Example: C-16 Sending a message with the UTL_SMTP module
342
343
344
Appendix D.
<stdio.h>
<string.h>
<stdlib.h>
<db2ci.h>
"utilci.h" /* Header file for DB2CI sample code */
#define ROWSET_SIZE 5
int TbSelectWithParam( OCIEnv * envhp, OCISvcCtx * svchp, OCIError * errhp );
int main(int argc, char *argv[])
{
sb4 ciRC = OCI_SUCCESS;
int rc = 0;
OCIEnv * envhp; /* environment handle */
OCISvcCtx * svchp; /* connection handle */
OCIError * errhp; /* error handle */
345
346
sb2
sb2
ub2
ub2
ind;
val;
length;
rcode;
}
deptnumb; /* variable to be bound to the DEPTNUMB column */
struct
{
sb2 ind;
char val[15];
ub2 length;
ub2 rcode;
}
location; /* variable to be bound to the LOCATION column */
printf("\n-----------------------------------------------------------");
printf("\nUSE THE DB2CI FUNCTIONS\n");
printf(" OCIHandleAlloc\n");
printf(" OCIStmtPrepare\n");
printf(" OCIStmtExecute\n");
printf(" OCIBindByPos\n");
printf(" OCIDefineByPos\n");
printf(" OCIStmtFetch\n");
printf(" OCIHandleFree\n");
printf("TO PERFORM A SELECT WITH PARAMETERS:\n");
/* allocate a statement handle */
ciRC = OCIHandleAlloc( (dvoid *)envhp, (dvoid **)&hstmt, OCI_HTYPE_STMT, 0, NU
LL );
ERR_HANDLE_CHECK(errhp, ciRC);
printf("\n Prepare the statement\n");
printf("
%s\n", stmt);
/* prepare the statement */
ciRC = OCIStmtPrepare(
hstmt,
errhp,
(OraText *)stmt,
strlen( stmt ),
OCI_NTV_SYNTAX,
OCI_DEFAULT );
ERR_HANDLE_CHECK(errhp, ciRC);
printf("\n
printf("
347
sizeof( divisionParam ),
SQLT_STR,
NULL,
NULL,
NULL,
0,
NULL,
OCI_DEFAULT );
ERR_HANDLE_CHECK(errhp, ciRC);
/* execute the statement for divisionParam = Eastern */
printf("\n Execute the prepared statement for\n");
printf("
divisionParam = 'Eastern'\n");
strcpy(divisionParam, "Eastern");
/* execute the statement */
ciRC = OCIStmtExecute(
svchp,
hstmt,
errhp,
0,
0,
NULL,
NULL,
OCI_DEFAULT );
ERR_HANDLE_CHECK(errhp, ciRC);
/* bind column 1 to variable */
ciRC = OCIDefineByPos(
hstmt,
&defnhp1,
errhp,
1,
&deptnumb.val,
sizeof( sb2 ),
SQLT_INT,
&deptnumb.ind,
&deptnumb.length,
&deptnumb.rcode,
OCI_DEFAULT );
ERR_HANDLE_CHECK(errhp, ciRC);
/* bind column 2 to variable */
ciRC = OCIDefineByPos(
hstmt,
&defnhp2,
errhp,
2,
location.val,
sizeof( location.val ),
SQLT_STR,
&location.ind,
&location.length,
&location.rcode,
OCI_DEFAULT );
348
ERR_HANDLE_CHECK(errhp, ciRC);
printf("\n
printf("
printf("
The easiest way to compile this sample program is to use the bldapp script that is
provided along with the DB2CI samples in sqllib/samples/db2ci, as shown in
Example D-2. Copy the utility source code to the directory where you created the
sample program (myoci.c) and run bldapp.
Example: D-2 Compiling and linking a DB2CI program myoci.c
cp /home/db2inst1/sqllib/samples/db2ci/utilci.* .
/home/db2inst1/sqllib/samples/db2ci/bldapp myoci
349
350
Appendix E.
Code samples
This appendix provides the following Oracle enablement PL/SQL code samples:
Oracle DDL statements that are used to create all database objects in the
source Oracle database
DB2 DDL statements that define the corresponding database objects that are
enabled in the DB2 database
An example of the DB2 10.5 deep nested objects feature
You can download these DDL scripts from the IBM Redbooks website as
described in Appendix F, Additional material on page 425.
351
352
(
NUMBER(5) NOT NULL,
VARCHAR2(20),
VARCHAR2(20),
NUMBER(3),
NUMBER(5),
CHAR(3) NOT NULL,
NUMBER(3) NOT NULL,
NUMBER(5),
CHAR(1),
TIMESTAMP(3) DEFAULT SYSDATE)
353
354
ALTER TABLE EMPLOYEES ADD CONSTRAINT FK_EMP_MGR_ID FOREIGN KEY ( "EMP_MGR_ID" ) REFERENCES
EMPLOYEES ( "EMP_ID" )
/
ALTER TABLE EMPLOYEES ADD CONSTRAINT FK_EMP_OFFICE_ID FOREIGN KEY ( "OFFICE_ID" ) REFERENCES
OFFICES ( "OFFICE_ID" )
/
ALTER TABLE EMPLOYEES ADD CONSTRAINT M_DEPT_CODE_ACCT_ID FOREIGN KEY ( "DEPT_CODE", "ACCT_ID" )
REFERENCES ACCOUNTS ( "DEPT_CODE", "ACCT_ID" )
/
ALTER TABLE EMP_DETAILS ADD CONSTRAINT FK_EMP_DETAILS_ID FOREIGN KEY ( "EMP_ID" ) REFERENCES
EMPLOYEES ( "EMP_ID" ) ON DELETE CASCADE
/
ALTER TABLE EMPLOYEES ADD CONSTRAINT BAND_VALIDATION CHECK (BAND IN ('1', '2', '3', '4', '5'))
/
CREATE INDEX ACCT_DEPT_IND ON ACCOUNTS (DEPT_CODE)
/
CREATE INDEX EMP_SEARCH_IND ON EMPLOYEES(LAST_NAME, FIRST_NAME, DEPT_CODE)
/
CREATE INDEX CUSTOMER_CITY_IND ON CUSTOMERS a
(XMLType.getStringVal(
XMLType.extract(a.cust_details_xml,'//customer-details/addr/city/text()')))
/
CREATE GLOBAL TEMPORARY TABLE TEMP_TABLE (
"NUM_COL" NUMBER,
"CHAR_COL" VARCHAR2(60))
/
CREATE OR REPLACE VIEW ORGANIZATION_STRUCTURE ("ORG_LEVEL", "FULL_NAME", "DEPARTMENT") AS
/*
||----------------------------------------------------------------------------|| DESCRIPTION: Dispaly hierarcy of people in the organization structure
||
||
|| DEMO PURPOSE: Support of recursive SQL "START WITH ... CONNECT BY" along
|| with LEVEL keyword.
||
New built-in functions INITCAP and NVL.
||
New syntax for outer join - (+)
||-------------------------------------------------------------------------*/
SELECT
355
LEVEL as ORG_LEVEL,
SUBSTR((LPAD(' ', 4 * LEVEL - 1) || INITCAP(e.last_name) || ', ' || INITCAP(e.first_name)),
1, 40),
NVL(d.dept_name, 'Uknown')
FROM
EMPLOYEES e,
DEPARTMENTS d
WHERE
e.dept_code=d.dept_code(+)
START WITH emp_id = 1
CONNECT BY NOCYCLE PRIOR emp_id = emp_mgr_id
/
-- public synonyms on sequence
CREATE PUBLIC SYNONYM EMPLOYEE_SEQUENCE
-- public synonyms on
CREATE PUBLIC SYNONYM
CREATE PUBLIC SYNONYM
CREATE PUBLIC SYNONYM
CREATE PUBLIC SYNONYM
FOR SALES.EMPLOYEE_SEQUENCE//
tables
DEPARTMENTS FOR SALES.DEPARTMENTS //
EMPLOYEES FOR SALES.EMPLOYEES //
EMP_DETAILS FOR SALES.EMP_DETAILS/
OFFICES FOR SALES.OFFICES /
356
IN ACCOUNTS.acct_id%TYPE,
IN ACCOUNTS.dept_code%TYPE,
357
p_AccountDesc IN ACCOUNTS.acct_desc%TYPE,
p_MaxEmployees IN ACCOUNTS.max_employees%TYPE);
PROCEDURE Remove_Account(p_AccountId IN ACCOUNTS.acct_id%TYPE,
p_DeptCode IN ACCOUNTS.dept_code%TYPE);
PROCEDURE Account_List(p_dept_code
IN ACCOUNTS.dept_code%TYPE,
p_acct_id
IN ACCOUNTS.acct_id%TYPE,
p_Employees_Name_Cache OUT Customer_Name_Cache);
PROCEDURE Display_Account_List(p_dept_code
p_acct_id
IN
IN
ACCOUNTS.dept_code%TYPE,
ACCOUNTS.acct_id%TYPE);
END ACCOUNT_PACKAGE;
/
CREATE OR REPLACE PACKAGE BODY ACCOUNT_PACKAGE AS
PROCEDURE ADD_ACCOUNT(p_AccountId
IN ACCOUNTS.acct_id%TYPE,
p_DeptCode
IN ACCOUNTS.dept_code%TYPE,
p_AccountDesc IN ACCOUNTS.acct_desc%TYPE,
p_MaxEmployees IN ACCOUNTS.max_employees%TYPE) IS
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: Add a new Employee into the specified Account
||
||
|| DEMO PURPOSE: predefined exceptions like DUP_VAL_ON_INDEX and OTHERS
||
||
|| EXAMPLE: EXEC ACCOUNT_PACKAGE.ADD_ACCOUNT(1, 1, 'Description', 10)
||
||-----------------------------------------------------------------------------------*/
BEGIN
INSERT INTO ACCOUNTS (acct_id, dept_code, acct_desc, max_employees, current_employees,
num_projects)
VALUES (p_AccountId, p_DeptCode, p_AccountDesc, p_MaxEmployees, 0, 0);
DBMS_OUTPUT.PUT_LINE('Account ' || p_AccountId || ' successfully created .');
EXCEPTION
WHEN dup_val_on_index THEN
DBMS_OUTPUT.PUT_LINE('Duplicate Account was rejected');
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);
358
RAISE;
END ADD_ACCOUNT;
PROCEDURE ACCOUNT_LIST(p_dept_code
IN ACCOUNTS.dept_code%TYPE,
p_acct_id
IN ACCOUNTS.acct_id%TYPE,
p_Employees_Name_Cache OUT CUSTOMER_NAME_CACHE) IS
/*
359
||-----------------------------------------------------------------------------------|| DESCRIPTION: Stores all employees from particular department in the associative
|| array
||
|| DEMO PURPOSE: Cursor definition, Cursor iteration through LOOP, population of
|| associative array
||-----------------------------------------------------------------------------------*/
-- variable declaration
v_NumEmployees NUMBER := 1;
-- cursor declaration
CURSOR c_RegisteredEmployees IS
-- Local cursor to fetch the registered Employees.
SELECT *
FROM EMPLOYEES
WHERE dept_code = p_dept_code
AND acct_id = p_acct_id;
BEGIN
/*
p_NumEmployees will be the table index. It will start at 0,
and be incremented each time through the fetch loop.
At the end of the loop, it will have the number of rows
fetched, and therefore the number of rows returned in p_IDs.
*/
OPEN c_RegisteredEmployees;
LOOP
FETCH c_RegisteredEmployees INTO p_Employees_Name_Cache(v_NumEmployees);
EXIT WHEN c_RegisteredEmployees%NOTFOUND;
v_NumEmployees := v_NumEmployees + 1;
END LOOP;
CLOSE c_RegisteredEmployees;
END ACCOUNT_LIST;
360
||
||-----------------------------------------------------------------------------------*/
-- variable declaration
v_customer_name_cache CUSTOMER_NAME_CACHE;
k
NUMBER := 1;
-- definition of the nested function
FUNCTION AVERAGE_BAND ( p_Department IN EMPLOYEES.dept_code%TYPE,
p_ACCT_ID
IN EMPLOYEES.acct_id%TYPE)
RETURN CHAR AS
/*
||---------------------------------------------------------------------------------|| DESCRIPTION: Nested procedure to derive the average band of employees in the
|| department
||
|| DEMO PURPOSE: Cursor definition, cursor attributes %NOTFOUND and %ROWCOUNT,
||
DECODE built-in function, user defined exception
||
||---------------------------------------------------------------------------------*/
-- variable declaration
v_AverageBAND
CHAR(1);
v_NumericBand
NUMBER;
v_TotalBand
NUMBER:=0;
v_NumberEmployees NUMBER;
-- CURSOR declaration
CURSOR c_Employees IS
SELECT band
FROM EMPLOYEES
WHERE dept_code = p_Department
AND acct_id = p_ACCT_ID;
BEGIN
OPEN c_Employees;
LOOP
FETCH c_Employees INTO v_NumericBand;
EXIT WHEN c_Employees%NOTFOUND;
v_TotalBand := v_TotalBand + v_NumericBand;
END LOOP;
v_NumberEmployees:=c_Employees%ROWCOUNT;
IF(v_NumberEmployees = 0) THEN
RAISE_APPLICATION_ERROR(-20001, 'No employees exist for ' || p_Department || ' ' ||
p_ACCT_ID);
END IF;
361
RETURN NUMBER AS
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: Function that counts the project based on the employeed id and also
||
returns information on total projects of the account to which employee
||
ID belongs
|| DEMO PURPOSE: Function with OUT parameter, FOR LOOP over cursor
||
||
|| EXAMPLE: SELECT COUNT_PROJECTS(1, acct_id) FROM DUAL;
||
||-----------------------------------------------------------------------------------*/
-- variable declaration
v_TotalProjects
NUMBER:=0;
v_AccountProjects NUMBER;
362
-- CURSOR
CURSOR
SELECT
FROM
WHERE
declaration
c_DeptAccts IS
dept_code, acct_id
EMPLOYEES
emp_id = p_empID;
BEGIN
FOR v_AccountRec IN c_DeptAccts LOOP
o_acct_id:=v_AccountRec.acct_id;
-- Determine the projects for this account.
SELECT num_projects
INTO v_AccountProjects
FROM ACCOUNTS
WHERE dept_code = v_AccountRec.dept_code
AND acct_id
= v_AccountRec.acct_id;
-- Add it to the total so far.
v_Totalprojects := v_Totalprojects + v_AccountProjects;
END LOOP;
-- different line for DB2 and Oracle
RETURN v_Totalprojects;
END COUNT_PROJECTS;
/
CREATE OR REPLACE PROCEDURE ADD_NEW_EMPLOYEE (
p_FirstName
EMPLOYEES.first_name%TYPE,
p_LastName
EMPLOYEES.last_name%TYPE,
p_EmpMgrId
EMPLOYEES.emp_mgr_id%TYPE,
p_DeptCode
EMPLOYEES.dept_code%TYPE,
p_Account
EMPLOYEES.acct_id%TYPE,
o_Employee OUT EMP_INFO_TYPE,
p_CreateDate
EMPLOYEES.create_date%TYPE DEFAULT SYSDATE,
p_OfficeId
EMPLOYEES.office_id%TYPE DEFAULT 2
) AS
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: Procedure to add a new employee
||
||
|| DEMO PURPOSE: Default values in the procedure definition, Regular loops,
||
sequence keywords like NEXTVAL and CURVAL
||
EXECUTE IMMEDIATE
||
||
|| EXAMPLE: EXEC ADD_NEW_EMPLOYEE('Max', "Trenton', 2, 1, 1, emp_info)
||
||-----------------------------------------------------------------------------------*/
363
-- variable declaration
v_EmployeeId
EMPLOYEES.emp_id%TYPE :=1;
v_EmployeeIdTemp EMPLOYEES.emp_id%TYPE;
-- cursor declaration
CURSOR c_CheckEmployeeId IS
SELECT 1
FROM EMPLOYEES
WHERE emp_id=v_EmployeeId;
CURSOR c_get_employee IS
SELECT emp_id, first_name, last_name, band
FROM EMPLOYEES
WHERE emp_id=v_EmployeeId;
BEGIN
-- Find Next available employee id from the employee sequence
LOOP
SELECT employee_sequence.NEXTVAL INTO v_EmployeeId FROM DUAL;
OPEN c_CheckEmployeeId;
FETCH c_CheckEmployeeId INTO v_EmployeeIdTemp;
EXIT WHEN c_CheckEmployeeId%NOTFOUND;
CLOSE c_CheckEmployeeId;
END LOOP;
select employee_sequence.CURRVAL INTO v_EmployeeId FROM DUAL;
EXECUTE IMMEDIATE 'INSERT INTO EMPLOYEES(emp_id, first_name, last_name, current_projects,
emp_mgr_id, dept_code, acct_id, office_id, band, create_date)
VALUES ( ' || v_EmployeeId || ', UPPER(''' || p_FirstName || '''),
UPPER(''' || p_LastName || '''),
0, '|| p_EmpMgrId || ',''' || p_DeptCode || ''', ' || p_Account || ',' ||
p_OfficeId || ', 1,''' || p_CreateDate || ''')';
FOR x IN c_get_employee
LOOP
o_Employee:=EMP_INFO_TYPE(x.emp_id, x.first_name, x.last_name, x.band);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Employee record id ' || v_EmployeeId || ' was created successfully.');
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Employee record was not created.');
RAISE;
END ADD_NEW_EMPLOYEE;
/
CREATE OR REPLACE PROCEDURE GET_EMPLOYEE_RESUME (p_empID
364
IN employees.emp_ID%TYPE,
PROCEDURE ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT (
EMPLOYEES.emp_id%TYPE,
ACCOUNTS.dept_code%TYPE,
ACCOUNTS.acct_id%TYPE) AS
/*
||------------------------------------------------------------------------------------
365
current_employees, max_employees
v_CurrentEmployees, v_MaxEmployees
ACCOUNTS
acct_id = p_AcctId
dept_code = p_DeptCode;
UPDATE EMPLOYEES
SET acct_id = p_AcctId, dept_code = p_DeptCode
WHERE emp_id=p_EmployeeId;
UPDATE ACCOUNTS
SET current_employees = current_employees+1
WHERE acct_id=p_AcctId;
EXCEPTION
WHEN NO_DATA_FOUND THEN
--Account information passed to this procedure doesn't exist. Raise an error
RAISE_APPLICATION_ERROR(-20001, p_DeptCode || ' ' || p_AcctId || ' doesn''t exist!');
END ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT;
366
/
CREATE OR REPLACE PROCEDURE EMPLOYEE_DYNAMIC_QUERY (
o_RefCur
OUT HELPER.RCT1,
p_department1 IN EMPLOYEES.dept_code%TYPE DEFAULT NULL,
p_department2 IN EMPLOYEES.dept_code%TYPE DEFAULT NULL) AS
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: Search routine that returns the list of employees in the form of
||
reference cursor based on the input of department code.
||
|| DEMO PURPOSE: Reference cursors, DBMS_SQL build-in package
||
||
|| EXAMPLE: EXEC EMPLOYEE_DYNAMIC_QUERY(ref_cursor, 1, 2)
||-----------------------------------------------------------------------------------*/
-- variable declaration
v_CursorID INTEGER;
v_SelectStmt VARCHAR2(500);
v_FirstName EMPLOYEES.first_name%TYPE;
v_LastName EMPLOYEES.last_name%TYPE;
v_DeptCode EMPLOYEES.dept_code%TYPE;
v_Dummy
INTEGER;
BEGIN
-- Open the cursor for processing.
v_CursorID := DBMS_SQL.OPEN_CURSOR;
-- Create the query string.
v_SelectStmt := 'SELECT first_name, last_name, dept_code
FROM EMPLOYEES
WHERE dept_code IN (:d1, :d2)
ORDER BY last_name';
-- Parse the query.
DBMS_SQL.PARSE(v_CursorID, v_SelectStmt, DBMS_SQL.NATIVE);
-- Bind the input variables.
DBMS_SQL.BIND_VARIABLE(v_CursorID, ':d1', p_department1);
DBMS_SQL.BIND_VARIABLE(v_CursorID, ':d2', p_department2);
-- Define the select list items.
DBMS_SQL.DEFINE_COLUMN(v_CursorID, 1, v_FirstName, 20);
DBMS_SQL.DEFINE_COLUMN(v_CursorID, 2, v_LastName, 20);
DBMS_SQL.DEFINE_COLUMN(v_CursorID, 3, v_DeptCode, 30);
-- Execute the statement. We don't care about the return
367
368
||-----------------------------------------------------------------------------------*/
-- variable declaration
v_filehandle
UTL_FILE.FILE_TYPE;
v_filename
VARCHAR2(100) DEFAULT 'catalog.out';
v_temp_line
VARCHAR2(100);
BEGIN
v_filehandle := UTL_FILE.FOPEN('MYDIR',v_filename,'w');
IF (UTL_FILE.IS_OPEN( v_filehandle ) = FALSE ) THEN
DBMS_OUTPUT.PUT_LINE('Cannot open file');
END IF;
FOR i IN (SELECT org_level, full_name, department
FROM ORGANIZATION_STRUCTURE)
LOOP
UTL_FILE.PUT_LINE(v_filehandle, 'Level: ' || i.org_level || ' ' || i.full_name || '
Department: ' || i.department);
END LOOP;
UTL_FILE.FCLOSE(v_filehandle);
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Some problem with saving organization structure to file');
END SAVE_ORG_STRUCT_TO_FILE;
/
CREATE OR REPLACE PROCEDURE INSERT_CUSTOMER_IN_XML (cust_in IN VARCHAR2)
IS
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: This procedure selects the customer information stored in XML data
|| type and process it in a cursor loop.
||
|| DEMO PURPOSE: Procedure that utilizes the power of XML and Xquery to process XML
|| data. It demonstrates a comparison between DB2 and Oracle syntax.
||
|| EXAMPLE: EXEC Insert_Customer_in_XML
|| ('<customerinfo xmlns="http://posample.org" Cid="1000">
||
<name>Kathy Smith</name><addr country="Canada">
||
<street>5 Rosewood</street>
||
<city>Toronto</city><prov-state>Ontario</prov-state>
||
<pcode-zip>M6W 1E6</pcode-zip></addr>
||
<phone type="work">416-555-1358</phone></customerinfo>')
||-----------------------------------------------------------------------------------*/
v_cust_id PLS_INTEGER;
369
v_city VARCHAR2(50);
BEGIN
SELECT customer_sequence.nextval INTO v_cust_id FROM DUAL;
INSERT INTO CUSTOMERS VALUES (v_cust_id, XMLType(cust_in), SYSDATE);
SELECT extract(cust_details_xml,'//customer-details/addr/city/text()').getStringVal()
INTO v_city
FROM CUSTOMERS
WHERE cust_id=v_cust_id;
DBMS_OUTPUT.PUT_LINE('Customers located in the same city: ');
FOR i IN (SELECT extract(cust_details_xml,'//customer-details/name/text()').getStringVal() as
cust_name
FROM CUSTOMERS
WHERE existsNode(cust_details_xml,'//customer-details') = 1
AND
extract(cust_details_xml,'//customer-details/addr/city/text()').getStringVal()=v_city
AND cust_id<>v_cust_id)
LOOP
DBMS_OUTPUT.PUT_LINE(i.cust_name);
END LOOP;
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Problem with inserting XML data in customers table');
END Insert_Customer_in_XML;
/
370
UPDATE
SET
WHERE
AND
ACCOUNTS
current_employees = current_employees + 1
dept_code = :new.dept_code
acct_id = :new.acct_id;
END UPDATE_ACC_ON_NEW_EMPL;
/
CREATE OR REPLACE TRIGGER UPDATE_DEPARTMENTS
/*
||----------------------------------------------------------------------------------|| DESCRIPTION: Trigger to keep the entries in the managers, employees, and accounts
|| tables in sync. When a record is inserted
||
|| DEMO PURPOSE: Showcase PL/SQL support in triggers
||
||----------------------------------------------------------------------------------*/
AFTER INSERT OR DELETE OR UPDATE ON employees FOR EACH ROW
BEGIN
IF DELETING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects-:old.current_projects,
total_employees=total_employees-1
WHERE dept_code=:old.dept_code;
ELSIF INSERTING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects+:new.current_projects,
total_employees=total_employees+1
WHERE dept_code=:new.dept_code;
ELSIF UPDATING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects+:new.current_projects-:old.current_projects
WHERE dept_code IN (:old.dept_code, :new.dept_code);
END IF;
END UPDATE_DEPARTMENTS;
/
/*
||-----------------------------------------------------------------------------------|| DESCRIPTION: Anonymous blocks that simulates the applications run
||
||
|| DEMO PURPOSE: Showcase Anonymous blocks and output to console
||
||-----------------------------------------------------------------------------------*/
371
DECLARE
v_emp_info
v_resume
temp_string
o_RefCur
BEGIN
EMP_INFO_TYPE;
CLOB;
VARCHAR2(4000);
HELPER.RCT1;
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('----Account manipulation test--');
ACCOUNT_PACKAGE.REMOVE_ACCOUNT(11, 'A00');
ACCOUNT_PACKAGE.ADD_ACCOUNT(11, 'A00', 'QUALITY PROGRAMS', 10);
ADD_NEW_EMPLOYEE('Max', 'Trenton', 2, 'A00', 1, v_emp_info);
ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT(v_emp_info.emp_id, 'A00', 11);
ACCOUNT_PACKAGE.DISPLAY_ACCOUNT_LIST('A00', 11);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('-----DBMS_LOB test-------------');
GET_EMPLOYEE_RESUME(1, v_resume);
DBMS_OUTPUT.PUT_LINE(v_resume);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('-----UTL_FILE test-------------');
SAVE_ORG_STRUCT_TO_FILE();
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('--DBMS_SQL and Ref cursor test-');
EMPLOYEE_DYNAMIC_QUERY(o_RefCur,'A00', 'B01');
FETCH o_RefCur INTO temp_string;
DBMS_OUTPUT.PUT_LINE(temp_string);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('----------XML test-------------');
Insert_Customer_in_XML ('<?xml version="1.0"?><customer-details>
<name>Kathy Smith</name>
<addr country="Canada">
<street>5 Rosewood</street>
<city>Toronto</city>
<prov-state>Ontario</prov-state>
<pcode-zip>M6W 1E6</pcode-zip>
</addr>
<phone type="work">416-555-1358</phone>
</customer-details>');
Insert_Customer_in_XML ('<?xml version="1.0"?><customer-details>
<name>John Edward</name>
<addr country="Canada">
<street>15 Mintwood</street>
<city>Toronto</city>
<prov-state>Ontario</prov-state>
<pcode-zip>L6A 4F2</pcode-zip>
372
</addr>
<phone type="work">416-112-2324</phone>
</customer-details>');
END;
/
ACCOUNTS
373
"ACCT_ID"
"DEPT_CODE"
"ACCT_DESC"
"MAX_EMPLOYEES"
"CURRENT_EMPLOYEES"
"NUM_PROJECTS"
"CREATE_DATE"
"CLOSED_DATE"
@
CREATE table CUSTOMERS
(
"CUST_ID"
NUMBER(5),
"CUST_DETAILS_XML" XML ,
"LAST_UPDATE_DATE" DATE)
@
CREATE table EMPLOYEES
"EMP_ID"
"FIRST_NAME"
"LAST_NAME"
"CURRENT_PROJECTS"
"EMP_MGR_ID"
"DEPT_CODE"
"ACCT_ID"
"OFFICE_ID"
"BAND"
"CREATE_DATE"
@
(
NUMBER(5) NOT NULL,
VARCHAR2(20),
VARCHAR2(20),
NUMBER(3),
NUMBER(5),
CHAR(3) NOT NULL,
NUMBER(3) NOT NULL,
NUMBER(5),
CHAR(1),
TIMESTAMP(3) DEFAULT SYSDATE)
374
375
376
SELECT
LEVEL as ORG_LEVEL,
SUBSTR((LPAD(' ', 4 * LEVEL - 1) || INITCAP(e.last_name) || ', '
|| INITCAP(e.first_name)), 1, 40),
NVL(d.dept_name, 'Uknown')
FROM
EMPLOYEES e,
DEPARTMENTS d
WHERE
e.dept_code=d.dept_code(+)
START WITH emp_id = 1
CONNECT BY NOCYCLE PRIOR emp_id = emp_mgr_id
@
CREATE PUBLIC SYNONYM EMPLOYEE_SEQUENCE
@
CREATE
@
CREATE
@
CREATE
@
CREATE
@
FOR SALES.EMPLOYEE_SEQUENCE
SAVE_ORG_TO_FILE
FOR SALES.SAVE_ORG_TO_FILE
377
378
||
||------------------------------------------------------------------------*/
-- type declaration
TYPE rct1 IS REF CURSOR;
TYPE emp_array_type IS VARRAY(10) OF EMP_INFO_TYPE;
END HELPER;
@
CREATE OR REPLACE PACKAGE ACCOUNT_PACKAGE AS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Account package contains the procedures to manage the accounts.
||
using the procedures in this package, users can add an account,
||
remove an account, provide account information in form of
||
associative array and display the account information using
||
server output.
||
|| DEMO PURPOSE: Package header declaration, anchor datatypes like %TYPE and
||
%ROWTYPE, definition of associative array
||
TYPE ... IS TABLE OF ... INDEX BY ...
||
||------------------------------------------------------------------------*/
-- type declaration
TYPE customer_name_cache IS TABLE OF EMPLOYEES%ROWTYPE INDEX BY PLS_INTEGER;
-- PROCEDURE declaration
PROCEDURE Add_Account(p_AccountId
p_DeptCode
p_AccountDesc
p_MaxEmployees
IN
IN
IN
IN
ACCOUNTS.acct_id%TYPE,
ACCOUNTS.dept_code%TYPE,
ACCOUNTS.acct_desc%TYPE,
ACCOUNTS.max_employees%TYPE);
IN
IN
ACCOUNTS.dept_code%TYPE,
ACCOUNTS.acct_id%TYPE);
379
END ACCOUNT_PACKAGE;
@
CREATE OR REPLACE PACKAGE BODY ACCOUNT_PACKAGE AS
PROCEDURE ADD_ACCOUNT(p_AccountId
IN ACCOUNTS.acct_id%TYPE,
p_DeptCode
IN ACCOUNTS.dept_code%TYPE,
p_AccountDesc IN ACCOUNTS.acct_desc%TYPE,
p_MaxEmployees IN ACCOUNTS.max_employees%TYPE) IS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Add a new Employee into the specified Account
||
||
|| DEMO PURPOSE: predefined exceptions like DUP_VAL_ON_INDEX and OTHERS
||
||
|| EXAMPLE: EXEC ACCOUNT_PACKAGE.ADD_ACCOUNT(1, 1, 'Description', 10)
||
||------------------------------------------------------------------------*/
BEGIN
INSERT INTO ACCOUNTS (acct_id, dept_code, acct_desc, max_employees,
current_employees, num_projects)
VALUES (p_AccountId, p_DeptCode, p_AccountDesc, p_MaxEmployees, 0, 0);
DBMS_OUTPUT.PUT_LINE('Account ' || p_AccountId || ' successfully created .');
EXCEPTION
WHEN dup_val_on_index THEN
DBMS_OUTPUT.PUT_LINE('Duplicate Account was rejected');
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);
RAISE;
END ADD_ACCOUNT;
PROCEDURE REMOVE_ACCOUNT(p_AccountId IN ACCOUNTS.acct_id%TYPE,
p_DeptCode IN ACCOUNTS.dept_code%TYPE) IS
/*
||--------------------------------------------------------------------------
380
381
||------------------------------------------------------------------------*/
-- variable declaration
v_NumEmployees NUMBER := 1;
-- cursor declaration
CURSOR c_RegisteredEmployees IS
-- Local cursor to fetch the registered Employees.
SELECT *
FROM EMPLOYEES
WHERE dept_code = p_dept_code
AND acct_id = p_acct_id;
BEGIN
p_NumEmployees will be the table index. It will start at 0,
and be incremented each time through the fetch loop.
At the end of the loop, it will have the number of rows
fetched, and therefore the number of rows returned in p_IDs.
/*
*/
OPEN c_RegisteredEmployees;
LOOP
FETCH c_RegisteredEmployees INTO p_Employees_Name_Cache(v_NumEmployees);
EXIT WHEN c_RegisteredEmployees%NOTFOUND;
v_NumEmployees := v_NumEmployees + 1;
END LOOP;
CLOSE c_RegisteredEmployees;
END ACCOUNT_LIST;
382
||
||----------------------------------------------------------------------*/
-- variable declaration
v_AverageBAND
CHAR(1);
v_NumericBand
NUMBER;
v_TotalBand
NUMBER:=0;
v_NumberEmployees NUMBER;
-- CURSOR declaration
CURSOR c_Employees IS
SELECT band
FROM EMPLOYEES
WHERE dept_code = p_Department
AND acct_id = p_ACCT_ID;
BEGIN
OPEN c_Employees;
LOOP
FETCH c_Employees INTO v_NumericBand;
EXIT WHEN c_Employees%NOTFOUND;
v_TotalBand := v_TotalBand + v_NumericBand;
END LOOP;
v_NumberEmployees:=c_Employees%ROWCOUNT;
IF(v_NumberEmployees = 0) THEN
RAISE_APPLICATION_ERROR(-20001, 'No employees exist for ' ||
p_Department || ' ' || p_ACCT_ID);
END IF;
SELECT
DECODE(
ROUND(v_TotalBand/v_NumberEmployees),
5, 'A', 4, 'B', 3, 'C', 2, 'D', 1, 'E')
INTO v_AverageBand
FROM DUAL;
RETURN v_AverageBand;
END AVERAGE_BAND_28036;
PROCEDURE DISPLAY_ACCOUNT_LIST(p_dept_code IN ACCOUNTS.dept_code%TYPE,
p_acct_id
IN ACCOUNTS.acct_id%TYPE) IS
/*
383
||------------------------------------------------------------------------|| DESCRIPTION: Displays the information about Employees and number of their
||
project assigned to specific account.
||
|| DEMO PURPOSE: Usage of associative arrays, DBMS_OUTPUT built-in package,
||
call to the procedure in the same package
||
|| EXAMPLE: EXEC ACCOUNT_PACKAGE.DISPLAY_ACCOUNT_LIST('A00', 1);
||
||------------------------------------------------------------------------*/
-- variable declaration
v_customer_name_cache CUSTOMER_NAME_CACHE;
k
NUMBER := 1;
-- definition of the nested function
BEGIN
DBMS_OUTPUT.PUT_LINE('List of employees');
DBMS_OUTPUT.PUT_LINE('----------------------------');
account_list(p_dept_code, p_acct_id, v_customer_name_cache);
FOR k IN 1..v_customer_name_cache.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Record id
: ' || k );
DBMS_OUTPUT.PUT_LINE('Employee
: ' ||
v_customer_name_cache(k).last_name);
DBMS_OUTPUT.PUT_LINE('Number of projects
: ' ||
v_customer_name_cache(k).Current_Projects);
DBMS_OUTPUT.PUT_LINE('Average Band in department : ' ||
average_band_28036(
v_customer_name_cache(k).dept_code,
v_customer_name_cache(k).acct_id));
END LOOP;
END DISPLAY_ACCOUNT_LIST;
END ACCOUNT_PACKAGE;
@
CREATE OR REPLACE
RETURN NUMBER AS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Function that counts the project based on the employeed id
||
and also returns information on total projects of the
384
||
account to which employee id belongs
||
|| DEMO PURPOSE: Function with OUT parameter, FOR LOOP over cursor
||
||
|| EXAMPLE: SELECT COUNT_PROJECTS(1, acct_id) FROM DUAL;
||
||------------------------------------------------------------------------*/
-- variable declaration
v_TotalProjects
NUMBER:=0;
v_AccountProjects NUMBER;
-- CURSOR
CURSOR
SELECT
FROM
WHERE
declaration
c_DeptAccts IS
dept_code, acct_id
EMPLOYEES
emp_id = p_empID;
BEGIN
FOR v_AccountRec IN c_DeptAccts LOOP
o_acct_id:=v_AccountRec.acct_id;
-- Determine the projects for this account.
SELECT num_projects
INTO v_AccountProjects
FROM ACCOUNTS
WHERE dept_code = v_AccountRec.dept_code
AND acct_id
= v_AccountRec.acct_id;
-- Add it to the total so far.
v_Totalprojects := v_Totalprojects + v_AccountProjects;
END LOOP;
-- different line for DB2 and Oracle
RETURN v_Totalprojects;
END COUNT_PROJECTS;
@
CREATE OR REPLACE PROCEDURE ADD_NEW_EMPLOYEE (
p_FirstName
EMPLOYEES.first_name%TYPE,
p_LastName
EMPLOYEES.last_name%TYPE,
p_EmpMgrId
EMPLOYEES.emp_mgr_id%TYPE,
p_DeptCode
EMPLOYEES.dept_code%TYPE,
p_Account
EMPLOYEES.acct_id%TYPE,
385
386
387
PROCEDURE ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT (
EMPLOYEES.emp_id%TYPE,
ACCOUNTS.dept_code%TYPE,
ACCOUNTS.acct_id%TYPE) AS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Re-assigns employee to a new account
||
||
|| DEMO PURPOSE: RAISE_APPLICATION_ERROR,
||
||
|| EXAMPLE: EXEC ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT(47, 'A01', 1)
||
||------------------------------------------------------------------------*/
-- variable declaration
v_CurrentEmployees NUMBER; -- Current no. of employees assigned to account
v_MaxEmployees
NUMBER; -- Maximum no. of employees assigned to account
BEGIN
388
SELECT
INTO
FROM
WHERE
AND
current_employees, max_employees
v_CurrentEmployees, v_MaxEmployees
ACCOUNTS
acct_id = p_AcctId
dept_code = p_DeptCode;
UPDATE EMPLOYEES
SET acct_id = p_AcctId, dept_code = p_DeptCode
WHERE emp_id=p_EmployeeId;
UPDATE ACCOUNTS
SET current_employees = current_employees+1
WHERE acct_id=p_AcctId;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Account information doesn't exist. Raise an error
RAISE_APPLICATION_ERROR(-20001, p_DeptCode || ' ' || p_AcctId ||
' doesn''t exist!');
END ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT;
@
CREATE OR REPLACE PROCEDURE EMPLOYEE_DYNAMIC_QUERY (
o_RefCur
OUT HELPER.RCT1,
p_department1 IN EMPLOYEES.dept_code%TYPE DEFAULT NULL,
p_department2 IN EMPLOYEES.dept_code%TYPE DEFAULT NULL) AS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Search routine that returns the list of employees in the
||
form of reference cursor based on the input of department
||
code.
389
||
|| DEMO PURPOSE: Reference cursors, DBMS_SQL build-in package
||
||
|| EXAMPLE: EXEC EMPLOYEE_DYNAMIC_QUERY(ref_cursor, 1, 2)
||------------------------------------------------------------------------*/
-- variable declaration
v_CursorID
INTEGER;
v_SelectStmt VARCHAR2(500);
v_FirstName EMPLOYEES.first_name%TYPE;
v_LastName
EMPLOYEES.last_name%TYPE;
v_DeptCode
EMPLOYEES.dept_code%TYPE;
v_Dummy
INTEGER;
BEGIN
-- Open the cursor for processing.
v_CursorID := DBMS_SQL.OPEN_CURSOR;
-- Create the query string.
v_SelectStmt := 'SELECT first_name, last_name, dept_code
FROM EMPLOYEES
WHERE dept_code IN (:d1, :d2)
ORDER BY last_name';
-- Parse the query.
DBMS_SQL.PARSE(v_CursorID, v_SelectStmt, DBMS_SQL.NATIVE);
-- Bind the input variables.
DBMS_SQL.BIND_VARIABLE_CHAR(v_CursorID, ':d1', p_department1);
DBMS_SQL.BIND_VARIABLE_CHAR(v_CursorID, ':d2', p_department2);
-- Define the select list items.
DBMS_SQL.DEFINE_COLUMN_VARCHAR(v_CursorID, 1, v_FirstName, 20);
DBMS_SQL.DEFINE_COLUMN_VARCHAR(v_CursorID, 2, v_LastName, 20);
DBMS_SQL.DEFINE_COLUMN_CHAR(v_CursorID, 3, v_DeptCode, 30);
-- Execute the statement. We don't care about the return
-- value, but we do need to declare a variable for it.
v_Dummy := DBMS_SQL.EXECUTE(v_CursorID);
-- This is the fetch loop.
LOOP
-- Fetch the rows into the buffer, and also check for the exit
390
PL/SQL variables.
1, v_FirstName);
2, v_LastName);
v_DeptCode);
v_DeptCode ||
END LOOP;
-- Close the cursor.
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
OPEN o_RefCur FOR SELECT char_col FROM TEMP_TABLE;
EXCEPTION
WHEN OTHERS THEN
-- Close the cursor, then raise the error again.
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
RAISE;
END EMPLOYEE_DYNAMIC_QUERY ;
@
CALL UTL_DIR.CREATE_OR_REPLACE_DIRECTORY('MYDIR','C:\temp' )
@
CREATE OR REPLACE PROCEDURE SAVE_ORG_STRUCT_TO_FILE IS
/*
||------------------------------------------------------------------------|| DESCRIPTION: Stores the hierarchy of organization in the OS file
||
||
|| DEMO PURPOSE: UTL_FILE built-in package
||
||
|| EXAMPLE: EXEC SAVE_ORG_STRUCT_TO_FILE
||
||-------------------------------------------------------------------------
391
*/
-- variable declaration
v_filehandle
UTL_FILE.FILE_TYPE;
v_filename
VARCHAR2(100) DEFAULT 'catalog.out';
v_temp_line
VARCHAR2(100);
BEGIN
v_filehandle := UTL_FILE.FOPEN('MYDIR',v_filename,'w');
IF (UTL_FILE.IS_OPEN( v_filehandle ) = FALSE ) THEN
DBMS_OUTPUT.PUT_LINE('Cannot open file');
END IF;
FOR i IN (SELECT org_level, full_name, department
FROM ORGANIZATION_STRUCTURE)
LOOP
UTL_FILE.PUT_LINE(v_filehandle, 'Level: ' || i.org_level || ' ' ||
i.full_name || '
Department: ' || i.department);
END LOOP;
UTL_FILE.FCLOSE(v_filehandle);
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Error saving organization structure to file');
END SAVE_ORG_STRUCT_TO_FILE;
@
392
||
||
||
||
||
||
||-----------------------------------------------------------------------------------*/
v_cust_id PLS_INTEGER;
v_city VARCHAR2(50);
BEGIN
SELECT customer_sequence.nextval INTO v_cust_id FROM DUAL;
INSERT INTO CUSTOMERS VALUES (v_cust_id, cust_in, SYSDATE);
SELECT XMLCAST(XMLQUERY('$customer/customer-details/addr/city'
PASSING cust_details_xml AS "customer") AS VARCHAR(50))
INTO v_city
FROM CUSTOMERS
WHERE cust_id=v_cust_id;
DBMS_OUTPUT.PUT_LINE('Customers located in the same city: ');
FOR i IN (SELECT x.cust_name
FROM sales.CUSTOMERS c, XMLTABLE(
'$CUST_DETAILS_XML//customer-details[addr/city=$city]'
PASSING v_city as "city"
COLUMNS
cust_name VARCHAR(128) PATH 'name'
) x
Where cust_id<>v_cust_id )
LOOP
DBMS_OUTPUT.PUT_LINE(i.cust_name);
END LOOP;
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Problem inserting XML data in customers table');
END Insert_Customer_in_XML;
@
393
E.3.3 Triggers
Example E-6 is the sample DB2 DDL statements for triggers
Example: E-6 DB2 DDL statements for triggers
CREATE OR REPLACE TRIGGER UPDATE_ACC_ON_NEW_EMPL
/*
||--------------------------------------------------------------------|| DESCRIPTION: Trigger to update accounts and employees tables
||
upon addition of new employee
||
|| DEMO PURPOSE: Showcase PL/SQL support in triggers
||
||--------------------------------------------------------------------*/
AFTER INSERT ON EMPLOYEES FOR EACH ROW
BEGIN
-- Add one to the number of employees in the project.
UPDATE ACCOUNTS
SET current_employees = current_employees + 1
WHERE dept_code = :new.dept_code
AND acct_id = :new.acct_id;
END UPDATE_ACC_ON_NEW_EMPL;
@
CREATE OR REPLACE TRIGGER UPDATE_DEPARTMENTS
/*
||-----------------------------------------------------------------------|| DESCRIPTION: Trigger to keep the entries in the managers, employees, and
||
accounts tables in sync.
||
When a record is inserted
||
|| DEMO PURPOSE: Showcase PL/SQL support in triggers
||
||-----------------------------------------------------------------------*/
AFTER INSERT OR DELETE OR UPDATE ON employees FOR EACH ROW
BEGIN
IF DELETING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects-:old.current_projects,
total_employees=total_employees-1
WHERE dept_code=:old.dept_code;
ELSIF INSERTING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects+:new.current_projects,
total_employees=total_employees+1
394
WHERE dept_code=:new.dept_code;
ELSIF UPDATING THEN
UPDATE DEPARTMENTS
SET total_projects=total_projects+:new.current_projects-:old.current_projects
WHERE dept_code IN (:old.dept_code, :new.dept_code);
END IF;
END UPDATE_DEPARTMENTS;
@
395
<code>M2H-2P7</code>
<building_address> <!-- Object, deep nested L3 -->
<entry>A1</entry>
<floor>2</floor>
<apartment_number>120</apartment_number>
</building_address>
</address>
</adresses>
</customer>
This code is organized in a standard Eclipse project, and the samples are driven
by few use cases that are backed by their positive path JUnit test cases.
The sections that follow present the content of the files in the project. You can
use the actual Eclipse project as a starting point for experimenting with the deep
nested objects feature.
file:DB2_10_deep_nested_objects_explorer/src/com/ibm/imte/db2/sample/de
epnested/ddl/database.create.ddl.sql:
create database testdb automatic storage yes pagesize 32 K;
file:DB2_10_deep_nested_objects_explorer/src/com/ibm/imte/db2/sample/de
epnested/ddl/schema_objects.create.ddl.sql:
CREATE OR REPLACE PACKAGE DNOBJ AS
-- modeling the "name" element of the XML schema
TYPE NAME_TYPE IS RECORD
(
FIRST_NAMEVARCHAR2(32),
396
LAST_NAME VARCHAR2(32)
);
-- adding the default object constructor
FUNCTION NAME (
FIRST_NAMEVARCHAR2(32),
LAST_NAME VARCHAR2(32)
) RETURN NAME_TYPE;
-- modeling the "info" element of the XML schema
TYPE INFO_TYPE IS RECORD
(
NAME NAME_TYPE, -- nesting a record type in another record type
BIRTH_DATEDATE
);
-- adding the default object constructor
FUNCTION INFO (
NAME NAME_TYPE,
BIRTH_DATEDATE
) RETURN INFO_TYPE;
-- modeling the "phone" element of the XML schema
TYPE PHONE_TYPE IS RECORD
(
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
);
-- adding the default object constructor
FUNCTION PHONE (
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
) RETURN PHONE_TYPE;
-- modeling the "phones" element of the XML schema using an array of
-- phone records
TYPE PHONE_ARRAY_TYPE IS TABLE OF PHONE_TYPE INDEX BY INTEGER;
-- adding the default object constructor
FUNCTION PHONES (
A1PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE;
FUNCTION PHONES (
A1PHONE_TYPE,
397
A2PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE;
FUNCTION PHONES (
A1PHONE_TYPE,
A2PHONE_TYPE,
A3PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE;
-- modeling the "buiding_address" element of the XML schema
TYPE BUILDING_ADDRESS_TYPE IS RECORD
(
ENTRY
VARCHAR2(8),
FLOOR
NUMBER(4),
APARTMENT_NUMBERNUMBER(3)
);
-- adding the default object constructor
FUNCTION BUILDING_ADDRESS (
ENTRY
VARCHAR2(8),
FLOOR
NUMBER(4),
APARTMENT_NUMBERNUMBER(3)
) RETURN BUILDING_ADDRESS_TYPE;
-- modeling the "address" element of the XML schema
TYPE ADDRESS_TYPE IS RECORD
(
PHONES PHONE_ARRAY_TYPE,
COUNTRYVARCHAR2(32),
COUNTRY_DIVVARCHAR2(32),
CITY
VARCHAR2(32),
STREET VARCHAR2(32),
CODE
VARCHAR2(32),
BUILDING_ADDRESSBUILDING_ADDRESS_TYPE
);
-- adding the default object constructor
FUNCTION ADDRESS (
PHONES PHONE_ARRAY_TYPE,
COUNTRYVARCHAR2(32),
COUNTRY_DIVVARCHAR2(32),
CITY
VARCHAR2(32),
STREET VARCHAR2(32),
CODE
VARCHAR2(32),
BUILDING_ADDRESSBUILDING_ADDRESS_TYPE
) RETURN ADDRESS_TYPE;
398
399
PHONE_MA_COUNT
IN INTEGER
);
END DEEP_NESTED_OBJECTS_SAMPLE;
/
CREATE OR REPLACE PACKAGE BODY DNOBJ AS
FUNCTION NAME (
FIRST_NAMEVARCHAR2(32),
LAST_NAME VARCHAR2(32)
) RETURN NAME_TYPE
IS
OBJ NAME_TYPE;
BEGIN
OBJ.FIRST_NAME:= FIRST_NAME;
OBJ.LAST_NAME:= LAST_NAME;
RETURN OBJ;
END NAME;
FUNCTION INFO (
NAME NAME_TYPE,
BIRTH_DATEDATE
) RETURN INFO_TYPE
IS
OBJ INFO_TYPE;
BEGIN
OBJ.NAME:= NAME;
OBJ.BIRTH_DATE:= BIRTH_DATE;
RETURN OBJ;
END INFO;
FUNCTION PHONE (
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
) RETURN PHONE_TYPE
IS
OBJ PHONE_TYPE;
BEGIN
OBJ.PHONE_PROVIDER:= PHONE_PROVIDER;
OBJ.PHONE_NUMBER:= PHONE_NUMBER;
RETURN OBJ;
END PHONE;
FUNCTION PHONES (
400
A1PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE
IS
OBJ PHONE_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
RETURN OBJ;
END PHONES;
FUNCTION PHONES (
A1PHONE_TYPE,
A2PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE
IS
OBJ PHONE_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
OBJ(2):=A2;
RETURN OBJ;
END PHONES;
FUNCTION PHONES (
A1PHONE_TYPE,
A2PHONE_TYPE,
A3PHONE_TYPE
) RETURN PHONE_ARRAY_TYPE
IS
OBJ PHONE_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
OBJ(2):=A2;
OBJ(3):=A3;
RETURN OBJ;
END PHONES;
FUNCTION BUILDING_ADDRESS (
ENTRY
VARCHAR2(8),
P_FLOORNUMBER(4),
APARTMENT_NUMBERNUMBER(3)
) RETURN BUILDING_ADDRESS_TYPE
IS
OBJ BUILDING_ADDRESS_TYPE;
BEGIN
OBJ.ENTRY:= ENTRY;
OBJ.FLOOR:= P_FLOOR;
OBJ.APARTMENT_NUMBER:= APARTMENT_NUMBER;
401
RETURN OBJ;
END BUILDING_ADDRESS;
FUNCTION ADDRESS (
PHONES PHONE_ARRAY_TYPE,
COUNTRYVARCHAR2(32),
COUNTRY_DIVVARCHAR2(32),
CITY
VARCHAR2(32),
STREET VARCHAR2(32),
CODE
VARCHAR2(32),
BUILDING_ADDRESSBUILDING_ADDRESS_TYPE
) RETURN ADDRESS_TYPE
IS
OBJ ADDRESS_TYPE;
BEGIN
OBJ.PHONES:= PHONES;
OBJ.COUNTRY:= COUNTRY;
OBJ.COUNTRY_DIV:= COUNTRY_DIV;
OBJ.CITY := CITY;
OBJ.STREET:= STREET;
OBJ.CODE := CODE;
OBJ.BUILDING_ADDRESS:= BUILDING_ADDRESS;
RETURN OBJ;
END ADDRESS;
FUNCTION ADDRESSES (
A1ADDRESS_TYPE
) RETURN ADDRESS_ARRAY_TYPE
IS
OBJ ADDRESS_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
RETURN OBJ;
END ADDRESSES;
FUNCTION ADDRESSES (
A1ADDRESS_TYPE,
A2ADDRESS_TYPE
) RETURN ADDRESS_ARRAY_TYPE
IS
OBJ ADDRESS_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
OBJ(2):=A2;
RETURN OBJ;
END ADDRESSES;
402
FUNCTION CUSTOMER (
KEY
INTEGER,
INFOINFO_TYPE,
ADDRESSESADDRESS_ARRAY_TYPE
) RETURN CUSTOMER_TYPE
IS
OBJ CUSTOMER_TYPE;
BEGIN
OBJ.KEY
:= KEY;
OBJ.INFO:= INFO;
OBJ.ADDRESSES:= ADDRESSES;
RETURN OBJ;
END NAME;
FUNCTION CUSTOMERS (
A1CUSTOMER_TYPE
) RETURN CUSTOMER_ARRAY_TYPE
IS
OBJ CUSTOMER_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
RETURN OBJ;
END CUSTOMERS;
FUNCTION CUSTOMERS (
A1CUSTOMER_TYPE,
A2CUSTOMER_TYPE
) RETURN CUSTOMER_ARRAY_TYPE
IS
OBJ CUSTOMER_ARRAY_TYPE;
BEGIN
OBJ(1):=A1;
OBJ(2):=A2;
RETURN OBJ;
END CUSTOMERS;
-- simple random generator or nested object tree
PROCEDURE
GENERATE_CUSTOMER_SAMPLE_OBJECT_ARRAY(
CUSTOMERS
OUT CUSTOMER_ARRAY_TYPE,
SEED
IN INTEGER,
CUSTOMER_COUNT
IN INTEGER,
ADDRESS_MAX_COUNT IN INTEGER,
PHONE_MAX_COUNT
IN INTEGER
) AS
403
BEGIN
if
SEED
= 1
and CUSTOMER_COUNT
= 1
and ADDRESS_MAX_COUNT = 1
and PHONE_MAX_COUNT
= 1
then
-- at this time we generate a simple constant deep nsted object
-- tree to showcase the usagage of the function based factories
-- to build nested trees which can be conpared with XML, JSON
etc
CUSTOMERS :=
CUSTOMERS(
CUSTOMER(
0, -- key
INFO(
NAME('John','Smith'),
'1967-02-23'
),
ADDRESSES(
ADDRESS(
PHONES(
PHONE(
'Rogers',
'000-111-2223'
)
), -- phone array
'Canada', -- country
'Ontario', -- country_div
'Toronto', -- city
'Warden Ave.', -- street
'M2H-2P7', -- code
BUILDING_ADDRESS(
'A1', -- entry
3, -- floor
120 -- appartment
) -- building_ddress
) -- address
)-- address array
)
);
else
-- TODO: implement random generator
customers := customers(cast(null as customer_type));
end if;
END;
404
END;
/
-------------------------------------------------------------------------The second case is when standalone types are to be converted
-The process is the same just that this tim there are no plsql
packages
-------------------------------------------------------------------------- modeling the "name" element of the XML schema
create or replace
TYPE NAME_TYPE AS ROW
(
FIRST_NAMEVARCHAR2(32),
LAST_NAME VARCHAR2(32)
)
/
create or replace
TYPE NAME_ARRAY_TYPE as NAME_TYPE ARRAY[]
/
-- modeling the "info" element of the XML schema
create or replace
TYPE INFO_TYPE AS ROW
(
NAME NAME_TYPE, -- nesting a record type in another record type
BIRTH_DATEDATE
)
/
create or replace
TYPE INFO_ARRAY_TYPE as INFO_TYPE ARRAY[]
/
-- modeling the "phone" element of the XML schema
create or replace
TYPE PHONE_TYPE AS ROW
(
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
)
/
405
406
)
/
-- provision to return multiple addresses in a single operation
create or replace
TYPE CUSTOMER_ARRAY_TYPE AS CUSTOMER_TYPE ARRAY[]
/
-- create supporting object instance function factories (constructors)
-- all those constructors can be automatically generated for the
existing
-- row types already defined in the database
-- the generator can also include the array types and their constructor
-- function counterparts
create or replace
FUNCTION NAME (
FIRST_NAMEVARCHAR2(32),
LAST_NAME VARCHAR2(32)
) RETURNS NAME_TYPE
LANGUAGE SQL
BEGIN
declare OBJ NAME_TYPE;
set OBJ.FIRST_NAME= FIRST_NAME;
set OBJ.LAST_NAME= LAST_NAME;
RETURN OBJ;
END
/
create or replace
FUNCTION INFO (
NAME NAME_TYPE,
BIRTH_DATEDATE
) RETURNS INFO_TYPE
LANGUAGE SQL
BEGIN
declare OBJ INFO_TYPE;
set OBJ.NAME= NAME;
set OBJ.BIRTH_DATE= BIRTH_DATE;
RETURN OBJ;
END
/
create or replace
FUNCTION PHONE (
PHONE_PROVIDERVARCHAR2(32),
PHONE_NUMBERVARCHAR2(32)
407
) RETURNS PHONE_TYPE
LANGUAGE SQL
BEGIN
declare OBJ PHONE_TYPE;
set OBJ.PHONE_PROVIDER= PHONE_PROVIDER;
set OBJ.PHONE_NUMBER= PHONE_NUMBER;
RETURN OBJ;
END
/
create or replace
FUNCTION PHONES (
a1PHONE_TYPE
) RETURNS PHONE_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ PHONE_ARRAY_TYPE;
set OBJ[1] = a1;
RETURN OBJ;
END
/
create or replace
FUNCTION PHONES (
a1PHONE_TYPE,
a2PHONE_TYPE
) RETURNS PHONE_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ PHONE_ARRAY_TYPE;
set OBJ[1] = a1;
set OBJ[2] = a2;
RETURN OBJ;
END
/
create or replace
FUNCTION PHONES (
a1PHONE_TYPE,
a2PHONE_TYPE,
a3PHONE_TYPE
) RETURNS PHONE_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ PHONE_ARRAY_TYPE;
set OBJ[1] = a1;
set OBJ[2] = a2;
408
409
a1ADDRESS_TYPE
) RETURNS ADDRESS_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ ADDRESS_ARRAY_TYPE;
set OBJ[1] = a1;
RETURN OBJ;
END
/
create or replace
FUNCTION ADDRESSES (
a1ADDRESS_TYPE,
a2ADDRESS_TYPE
) RETURNS ADDRESS_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ ADDRESS_ARRAY_TYPE;
set OBJ[1] = a1;
set OBJ[1] = a2;
RETURN OBJ;
END
/
create or replace
FUNCTION CUSTOMER (
KEY
INT,
INFOINFO_TYPE,
ADDRESSESADDRESS_ARRAY_TYPE
) RETURNS CUSTOMER_TYPE
LANGUAGE SQL
BEGIN
declare OBJ CUSTOMER_TYPE;
set OBJ.KEY
= KEY;
set OBJ.INFO = INFO;
set OBJ.ADDRESSES= ADDRESSES;
RETURN OBJ;
END
/
create or replace
FUNCTION CUSTOMERS (
a1CUSTOMER_TYPE
) RETURNS CUSTOMER_ARRAY_TYPE
LANGUAGE SQL
BEGIN
declare OBJ CUSTOMER_ARRAY_TYPE;
410
411
if
SEED
= 1
and CUSTOMER_COUNT
= 1
and ADDRESS_MAX_COUNT = 1
and PHONE_MAX_COUNT
= 1
then
CUSTOMERS :=
CUSTOMERS(
CUSTOMER(
0,-- key
INFO(
NAME('John','Smith'),
'1967-02-23'
),
ADDRESSES(
ADDRESS(
PHONES(
PHONE(
'Rogers',
'000-111-2223'
)
), -- phone array
'Canada', -- country
'Ontario', -- country_div
'Toronto', -- city
'Warden Ave.', -- street
'M2H-2P7', -- code
BUILDING_ADDRESS(
'A1', -- entry
3, -- floor
120 -- appartment
) -- building_ddress
) -- address
)-- address array
)
);
else
-- TODO: implement random generator
CUSTOMERS := customers(null);
end if;
END;
/
412
E.4.3 JUnit tests (class file excerpt that includes only relevant
information for the printed book)
Example E-10 shows the JUnit test case that runs the deep nested objects
function at the client side. The test code provides setup and tear down logic and
the following test cases:
testReadNestedObjectPackage
testReadNestedObjectStandalone
Both tests use stored procedures that generate pseudo-random deep nested
structures. These structures exercise object nesting procedures that use PL/SQL
stored procedures (testReadNestedObjectPackage) and native SQL PL
procedures (testReadNestedObjectStandalone). The test logic calls the stored
procedures and then converts the result in a textual format to be easier to read.
The test logic uses an expected string constant as a way to validate the test
result (string compare). The test case is derived from a TestBase class that
encapsulates all the common logic. The test case can be reused by the reader to
quickly build his own tests to exercise various scenarios with the database.
Example: E-10 The Java and JDBC JUnit test case file location and content
file:DB2_10_deep_nested_objects_explorer/src/com/ibm/imte/db2/sample/de
epnested/ReadNestedObjectTest.java
public class ReadNestedObjectTest extends TestBase {
@Override
protected void setUp() throws Exception {
assertEquals(
0,
create_test_initial_state("com/ibm/imte/db2/sample/deepnested/ddl/schem
a_objects.create.ddl.sql"));
}
@Override
protected void tearDown() throws Exception {
revert_test_state("com/ibm/imte/db2/sample/deepnested/ddl/schema_object
s.drop.ddl.sql");
closeConnection();
}
public void testReadNestedObjectPackage() throws SQLException,
ClassNotFoundException {
Connection con = getConnection();
413
414
}
}
}
public void testReadNestedObjectStandalone() throws SQLException,
ClassNotFoundException {
Connection con = getConnection();
CallableStatement cstmt = null;
try {
cstmt = con
.prepareCall("CALL
generate_customer_sample_object_array(?,?,?,?,?)");
cstmt.registerOutParameter(1, java.sql.Types.ARRAY);
cstmt.setInt(2, 1);// using same seed for random to keep the
test
// happy :)
cstmt.setInt(3, 1);
cstmt.setInt(4, 1);
cstmt.setInt(5, 1);
cstmt.execute();
// simply dump of the received data tree by walking
// the tree down for any "A" array or "S" structure
// entity detected, leaf field discrimination is positional
String result = sqlObjectToText("customers->",
cstmt.getArray(1));
// log the current result to be used in case of failure
logInfo("Result\n" + result);
// verify by comparing to the expected value
String expected = "customers->A[0]S[0]=0\n"
+ "customers->A[0]S[1]S[0]S[0]=John\n"
+ "customers->A[0]S[1]S[0]S[1]=Smith\n"
+ "customers->A[0]S[1]S[1]=1967-02-23 00:00:00.0\n"
+ "customers->A[0]S[2]A[0]S[0]A[0]S[0]=Rogers\n"
+ "customers->A[0]S[2]A[0]S[0]A[0]S[1]=000-111-2223\n"
+ "customers->A[0]S[2]A[0]S[1]=Canada\n"
+ "customers->A[0]S[2]A[0]S[2]=Ontario\n"
+ "customers->A[0]S[2]A[0]S[3]=Toronto\n"
+ "customers->A[0]S[2]A[0]S[4]=Warden Ave.\n"
+ "customers->A[0]S[2]A[0]S[5]=M2H-2P7\n"
+ "customers->A[0]S[2]A[0]S[6]S[0]=A1\n"
415
+ "customers->A[0]S[2]A[0]S[6]S[1]=3\n"
+ "customers->A[0]S[2]A[0]S[6]S[2]=120\n";
logInfo("Expected\n" + expected);
assertEquals(expected, result);
} finally {
if (cstmt != null) {
cstmt.close();
}
}
}
}
file:DB2_10_deep_nested_objects_explorer/src/com/ibm/imte/TestBase.java
public class TestBase extends TestCase {
public int create_test_initial_state(String id) throws SQLException,
IOException, ClassNotFoundException {
// id is the path from where we load all the DDL definitions and
initial
// table data
// in order to reproduce a given database state
return execBatch(getConnection(), id, getLogger());
}
public int revert_test_state(String id) throws SQLException,
IOException,
ClassNotFoundException {
return execBatch(getConnection(), id, getLogger());
}
public String sqlObjectToText(String name, Object obj) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bos);
if (obj instanceof java.sql.Array) {
try {
Object[] a = (Object[]) ((Array) obj).getArray();
for (int i = 0; i < a.length; ++i) {
out.print(sqlObjectToText(name + "A[" + i + "]", a[i]));
}
} catch (SQLException e) {
out.println(name + "Array:-exception->" + e.getMessage());
}
416
417
if (env == null) {
env = new Properties();
try {
InputStream fin = new FileInputStream("env.properties");
env.load(fin);
fin.close();
} catch (FileNotFoundException e) {
logger.info("No env.properties in cwd, using default");
} catch (IOException e) {
logger
.info("Unable to access env.properties in cwd, using
default");
}
if (!env.containsKey("db2.host")) {
env.setProperty("db2.host", host);
}
if (!env.containsKey("db2.port")) {
env.setProperty("db2.port", port);
}
if (!env.containsKey("db2.db")) {
env.setProperty("db2.db", database);
}
if (!env.containsKey("db2.user")) {
env.setProperty("db2.user", user);
}
if (!env.containsKey("db2.password")) {
env.setProperty("db2.password", password);
}
if (!env.containsKey("db2.traceLevel")) {
env.setProperty("db2.traceLevel", trace_level);
}
if (!env.containsKey("db2.traceFile")) {
env.setProperty("db2.traceFile", trace_file);
}
if (!env.containsKey("db2.traceDirectory")) {
env.setProperty("db2.traceDirectory", trace_dir);
}
if (!env.containsKey("db2.traceFileAppend")) {
env.setProperty("db2.traceFileAppend", trace_append);
}
}
return env;
}
private String getConnectionSting() {
418
getTestEnv();
String cstr = "jdbc:db2://"
+ env.getProperty("db2.host")
+ ":"
+ env.getProperty("db2.port")
+ "/"
+ env.getProperty("db2.db")
+ ":retrieveMessagesFromServerOnGetMessage=true;"
+ (env.getProperty("db2.traceLevel").equalsIgnoreCase("0")
? ""
: ("traceLevel=" + env.getProperty("db2.traceLevel")
+ ";traceFile="
+ env.getProperty("db2.traceFile")
+ ";traceDirectory="
+ env.getProperty("db2.traceDirectory")
+ ";traceFileAppend=" + env
.getProperty("db2.traceFileAppend")));
return cstr;
}
private Logger logger;
public Logger getLogger() {
if (logger == null) {
logger = getLogger(this.getClass().getCanonicalName(), this
.getClass().getCanonicalName()
+ ".log");
}
return logger;
}
public void logInfo(String msg) {
if (logger == null) {
logger = getLogger();
}
logger.info(msg);
}
public void log(Level level, String msg) {
if (logger == null) {
logger = getLogger(this.getClass().getCanonicalName(), this
.getClass().getCanonicalName()
+ ".log");
}
419
logger.log(level, msg);
}
DatabaseMetaData dbm;
Connection con;
protected Connection getConnection() throws SQLException,
ClassNotFoundException {
if (con == null) {
logInfo("Loading DB2 JCC 4 driver");
Class.forName("com.ibm.db2.jcc.DB2Driver");
logInfo("Connecting to the DB2 with user:" + user);
con = DriverManager.getConnection(getConnectionSting(), env
.getProperty("db2.user"),
env.getProperty("db2.password"));
logInfo("Connected");
con.setAutoCommit(true);
// dbm=con.getMetaData();
}
return con;
}
protected void closeConnection() {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
con = null;
}
}
public boolean canReachDBServer() {
try {
getTestEnv();
Socket sk = new Socket(env.getProperty("db2.host"), Integer
.parseInt(env.getProperty("db2.port")));
sk.close();
return true;
} catch (NumberFormatException e) {
420
421
while (rs.next()) {
for (int i = 0; i < ncols; i++) {
value = rs.getString(i + 1);
if (value == null) {
value = "NULL";
} else {
if (value.indexOf(',') >= 0 || value.indexOf('"') >= 0)
{
value = '"' + value.replace("\"", "\\\"") + '"';
}
}
out.print(value);
if (i < (ncols - 1)) {
out.print(",");
}
}
out.println();
}
out.flush();
}
public static String resultSetToCSV(ResultSet rs) throws
SQLException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bos);
resultSetToCSV(rs, out);
return bos.toString();
}
public static List<String> loadStatements(InputStream in, String
delim)
throws IOException {
List<String> stmts = new ArrayList<String>();
BufferedReader rd = new BufferedReader(new
InputStreamReader(in));
String line, stmt;
StringBuilder sb = new StringBuilder();
do {
line = rd.readLine();
if (line == null) {
break;
}
if (line.trim().equalsIgnoreCase(delim)) {
stmt = sb.toString().trim();
422
if (stmt.length() > 0) {
stmts.add(stmt);
sb = new StringBuilder();
}
} else {
sb.append(line);
sb.append("\n");
}
} while (line != null);
return stmts;
}
public static int execBatch(List<String> stmts, Connection con,
Logger logger) throws SQLException {
Statement st = con.createStatement();
int err_cnt = 0;
try {
for (Iterator<String> iterator = stmts.iterator(); iterator
.hasNext();) {
String stmt = (String) iterator.next();
logger.info(stmt);
try {
st.execute(stmt);
logger.info("Success");
} catch (SQLException e) {
++err_cnt;
logger.info("Exec error:" + e.getMessage());
}
}
} finally {
con.commit();
}
return err_cnt;
}
public static int execBatch(Connection con, String resource_id,
Logger logger) throws SQLException, IOException {
return execBatch(
loadStatements(Thread.currentThread().getContextClassLoader()
.getResourceAsStream(resource_id), "/"), con,
logger);
}
}
423
E.4.4 Cleaning up
The project provides an optional SQL batch file that selectively removes all the
objects that are created by the setup phase. For convenience, a pseudo-test
case provides a simple way to run it from inside the Eclipse Env (Example E-11).
Example: E-11 The selective schema cleanup logic
file:DB2_10_deep_nested_objects_explorer/src/com/ibm/imte/db2/sample/de
epnested/TestEnvCleanup.java
public class TestEnvCleanup extends TestBase {
@Override
protected void tearDown() throws Exception {
closeConnection();
}
public void testEnvCleanup() throws SQLException,
ClassNotFoundException {
try {
revert_test_state("com/ibm/imte/db2/sample/deepnested/ddl/schema_object
s.drop.ddl.sql");
} catch (IOException e) {
logInfo("Drop sql exception:" + e.getMessage());
}
}
}
424
Appendix F.
Additional material
This appendix refers to more material that can be downloaded from the Internet
as described in the following sections.
Code disclaimer
IBM does not warrant or represent that the code provided is complete or
up-to-date. IBM does not warrant, represent or imply reliability, serviceability, or
function of the code. IBM is under no obligation to update content nor provide
further support.
All code is provided as is, with no warranties or guarantees whatsoever. IBM
expressly disclaims to the fullest extent permitted by law all express, implied,
statutory, and other warranties, guarantees, or representations, including, without
limitation, the warranties of merchantability, fitness for a particular purpose, and
non-infringement of proprietary and intellectual property rights. You understand
and agree that you use these materials, information, products, software,
programs, and services, at your own discretion and risk and that you will be
solely responsible for any damages that may result, including loss of data or
damage to your computer system.
425
In no event will IBM be liable to any party for any direct, indirect, incidental,
special, exemplary, or consequential damages of any type whatsoever related to
or arising from use of the code found herein, without limitation, any lost profits,
business interruption, lost savings, loss of programs, or other data, even if IBM is
expressly advised of the possibility of such damages. This exclusion and waiver
of liability applies to all causes of action, whether based on contract, warranty,
tort, or any other legal theories.
426
0.5 MB minimum
Windows 2000/Linux SUSE or Red Hat
Intel 386 or higher
16 MB
427
428
Related publications
The publications that are listed in this section are considered suitable for a more
detailed discussion of the topics that are covered in this book.
Other publications
These publications are also relevant as further information sources:
Administrative API Reference, SC27-2435
Administrative Routines and Views, SC27-2436
Call Level Interface Guide and Reference, Volume 1, SC27-2437
429
430
Online resources
These websites are also relevant as further information sources:
DB2
Database and Information Management home page
http://www.ibm.com/software/data/
DB2 Application Development
http://www.ibm.com/software/data/db2/ad/
DB2 developerWorks
http://www.ibm.com/developerworks/db2/
DB2 Information Center
http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/index.jsp
DB2 for Linux
http://www.ibm.com/software/data/db2/linux/
Related publications
431
Other resources
Apache HTTP Server Project
http://httpd.apache.org
Comprehensive Perl Archive Network
http://www.cpan.org
http://www.cpan.org/modules/by-category/07_Database_Interfaces/DBI
DB2 Perl Database Interface
http://www.ibm.com/software/data/db2/perl
DBI.perl.org
http://dbi.perl.org
IBM Tivoli System Automation for Multiplatforms
http://publib.boulder.ibm.com/tividd/td/ITSAFL/SC33-8272-01/en_US/PD
F/HALBAU01.pdf
Perl.apache.org
http://perl.apache.org/docs/1.0/guide/
PHP scripting language
http://www.php.net/
432
(0.5 spine)
0.475<->0.875
250 <-> 459 pages
Back cover
INTERNATIONAL
TECHNICAL
SUPPORT
ORGANIZATION
BUILDING TECHNICAL
INFORMATION BASED ON
PRACTICAL EXPERIENCE
IBM Redbooks are developed by
the IBM International Technical
Support Organization. Experts
from IBM, Customers and
Partners from around the world
create timely technical
information based on realistic
scenarios. Specific
recommendations are provided
to help you implement IT
solutions more effectively in
your environment.
ISBN 0738438758