® ®
IBM DB2 Universal Database
Application Development Guide
Version 7
SC09-2949-00
® ®
IBM DB2 Universal Database
Application Development Guide
Version 7
SC09-2949-00
Before using this information and the product it supports, be sure to read the general information under
“Appendix G. Notices” on page 813.
This document contains proprietary information of IBM. It is provided under a license agreement and is protected by
copyright law. The information contained in this publication does not include any product warranties, and any
statements provided in this manual should not be interpreted as such.
Order publications through your IBM representative or the IBM branch office serving your locality or by calling
1-800-879-2755 in the United States or 1-800-IBM-4YOU in Canada.
When you send information to IBM, you grant IBM a nonexclusive right to use or distribute the information in any
way it believes appropriate without incurring any obligation to you.
© Copyright International Business Machines Corporation 1993, 2000. All rights reserved.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Part 1. DB2 Application Part 2. Embedding SQL in
Development Concepts . . . . . . 1 Applications . . . . . . . . . . 43
Chapter 1. Getting Started with DB2 Chapter 3. Embedded SQL Overview . . . 45
Application Development . . . . . . . 3 Embedding SQL Statements in a Host
About This Book . . . . . . . . . . . 3 Language . . . . . . . . . . . . . 45
Who Should Use This Book . . . . . . . 4 Creating and Preparing the Source Files. . . 47
How to Use This Book . . . . . . . . . 4 Creating Packages for Embedded SQL . . . 49
Conventions . . . . . . . . . . . 7 Precompiling. . . . . . . . . . . 49
Related Publications. . . . . . . . . 8 Compiling and Linking . . . . . . . 52
Binding . . . . . . . . . . . . 53
Chapter 2. Coding a DB2 Application . . . 9 Advantages of Deferred Binding . . . . 56
Prerequisites for Programming . . . . . . 9 DB2 Bind File Description Utility - db2bfd 56
DB2 Application Coding Overview . . . . 10 Application, Bind File, and Package
Declaring and Initializing Variables . . . 11 Relationships. . . . . . . . . . . 57
Connecting to the Database Server . . . 16 Timestamps . . . . . . . . . . . 58
Coding Transactions . . . . . . . . 17 Rebinding. . . . . . . . . . . . 58
Ending the Program . . . . . . . . 19
Implicitly Ending a Transaction . . . . 19 Chapter 4. Writing Static SQL Programs. . 61
Application Pseudocode Framework . . . 20 Characteristics and Reasons for Using Static
Designing an Application For DB2 . . . . 21 SQL . . . . . . . . . . . . . . . 61
Access to Data . . . . . . . . . . 23 Advantages of Static SQL . . . . . . . 62
Data Value Control. . . . . . . . . 25 Example: Static SQL Program . . . . . . 63
Data Relationship Control . . . . . . 27 How the Static Program Works . . . . 64
Application Logic at the Server . . . . 29 C Example: STATIC.SQC . . . . . . . 66
The IBM DB2 Universal Database Project Java Example: Static.sqlj . . . . . . . 67
Add-In for Microsoft Visual C++ . . . . 30 COBOL Example: STATIC.SQB . . . . . 69
Supported SQL Statements . . . . . . . 33 Coding SQL Statements to Retrieve and
Authorization Considerations . . . . . . 33 Manipulate Data . . . . . . . . . . 71
Dynamic SQL . . . . . . . . . . 34 Retrieving Data . . . . . . . . . . 71
Static SQL. . . . . . . . . . . . 35 Using Host Variables . . . . . . . . . 71
Using APIs . . . . . . . . . . . 35 Declaration Generator - db2dclgn . . . . 73
Example . . . . . . . . . . . . 35 Using Indicator Variables . . . . . . . 75
Database Manager APIs Used in Embedded Data Types . . . . . . . . . . . 77
SQL or DB2 CLI Programs . . . . . . . 36 Using an Indicator Variable in the STATIC
Setting Up the Testing Environment . . . . 36 program . . . . . . . . . . . . 80
Creating a Test Database . . . . . . . 37 Selecting Multiple Rows Using a Cursor . . 81
Creating Test Tables . . . . . . . . 37 Declaring and Using the Cursor . . . . 81
Generating Test Data . . . . . . . . 38 Cursors and Unit of Work Considerations 82
Running, Testing and Debugging Your Example: Cursor Program . . . . . . 84
Programs . . . . . . . . . . . . . 40 Updating and Deleting Retrieved Data . . . 92
Prototyping Your SQL Statements . . . . . 40 Updating Retrieved Data. . . . . . . 92
Deleting Retrieved Data . . . . . . . 92
Types of Cursors . . . . . . . . . 92
© Copyright IBM Corp. 1993, 2000 iii
Example: OPENFTCH Program . . . . 93 Advantages of Using DB2 CLI . . . . 171
Advanced Scrolling Techniques . . . . . 102 Deciding on Embedded SQL or DB2 CLI 173
Scrolling Through Data that has Already
Been Retrieved. . . . . . . . . . 102 Chapter 6. Common DB2 Application
Keeping a Copy of the Data . . . . . 102 Techniques . . . . . . . . . . . 175
Retrieving the Data a Second Time . . . 102 Generated Columns . . . . . . . . . 176
Establishing a Position at the End of a Identity Columns . . . . . . . . . . 176
Table . . . . . . . . . . . . . 104 Declared Temporary Tables . . . . . . 177
Updating Previously Retrieved Data . . 105 Controlling Transactions with Savepoints 179
Example: UPDAT Program. . . . . . 105 Savepoint Restrictions . . . . . . . 180
Diagnostic Handling and the SQLCA Savepoints and Data Definition Language
Structure . . . . . . . . . . . . . 115 (DDL). . . . . . . . . . . . . 181
Return Codes . . . . . . . . . . 115 Savepoints and Buffered Inserts . . . . 182
SQLCODE and SQLSTATE . . . . . . 115 Using Savepoints with Cursor Blocking 182
Token Truncation in SQLCA Structure . . 116 Savepoints and XA Compliant Transaction
Handling Errors using the WHENEVER Managers . . . . . . . . . . . 183
Statement . . . . . . . . . . . 116
Exception, Signal, Interrupt Handler
Part 3. Stored Procedures . . . . 185
Considerations . . . . . . . . . . 117
Exit List Routine Considerations . . . . 118
Using GET ERROR MESSAGE in Chapter 7. Stored Procedures . . . . . 187
Example Programs . . . . . . . . 118 Stored Procedure Overview . . . . . . 187
Advantages of Stored Procedures . . . . 188
Writing Stored Procedures . . . . . . . 190
Chapter 5. Writing Dynamic SQL
Client Application . . . . . . . . 191
Programs . . . . . . . . . . . . 127
Stored Procedures on the Server . . . . 192
Why Use Dynamic SQL? . . . . . . . 127
Writing OLE Automation Stored
Dynamic SQL Support Statements . . . 127
Procedures . . . . . . . . . . . 209
Comparing Dynamic SQL with Static SQL 128
Example OUT Parameter Stored
Using PREPARE, DESCRIBE, FETCH and
Procedure . . . . . . . . . . . 210
the SQLDA . . . . . . . . . . . . 131
Code Page Considerations . . . . . . 222
Declaring and Using Cursors . . . . . 131
C++ Consideration . . . . . . . . 222
Example: Dynamic SQL Program . . . 133
Graphic Host Variable Considerations . . 222
Declaring the SQLDA . . . . . . . 143
Multisite Update Consideration . . . . 223
Preparing the Statement Using the
NOT FENCED Stored Procedures . . . . 223
Minimum SQLDA Structure . . . . . 144
Returning Result Sets from Stored
Allocating an SQLDA with Sufficient
Procedures . . . . . . . . . . . . 225
SQLVAR Entries . . . . . . . . . 145
Example: Returning a Result Set from a
Describing the SELECT Statement . . . 146
Stored Procedure . . . . . . . . . 226
Acquiring Storage to Hold a Row . . . 146
Resolving Problems . . . . . . . . 236
Processing the Cursor . . . . . . . 147
Allocating an SQLDA Structure . . . . 147
Passing Data Using an SQLDA Structure 151 Chapter 8. Writing SQL Procedures . . . 239
Processing Interactive SQL Statements 152 Comparison of SQL Procedures and External
Saving SQL Requests from End Users . . 153 Procedures . . . . . . . . . . . . 239
Example: ADHOC Program . . . . . 154 Valid SQL Procedure Body Statements . . . 240
Variable Input to Dynamic SQL . . . . . 161 Issuing CREATE PROCEDURE Statements 242
Using Parameter Markers . . . . . . 161 Handling Conditions in SQL Procedures . . 243
Example: VARINP Program . . . . . 162 Declaring Condition Handlers . . . . 243
The DB2 Call Level Interface (CLI) . . . . 170 SIGNAL and RESIGNAL Statements . . 245
Comparing Embedded SQL and DB2 CLI 170
iv Application Development Guide
SQLCODE and SQLSTATE Variables in Manipulating Distinct Types . . . . . . 277
SQL Procedures . . . . . . . . . 246 Examples of Manipulating Distinct Types 277
Using Dynamic SQL in SQL Procedures . . 246 Example: Comparisons Between Distinct
Nested SQL Procedures . . . . . . . . 248 Types and Constants . . . . . . . . 277
Passing Parameters Between Nested SQL Example: Casting Between Distinct Types 278
Procedures . . . . . . . . . . . 248 Example: Comparisons Involving Distinct
Returning Result Sets From Nested SQL Types . . . . . . . . . . . . . 279
Procedures . . . . . . . . . . . 249 Example: Sourced UDFs Involving
Restrictions on Nested SQL Procedures 249 Distinct Types . . . . . . . . . . 280
Returning Result Sets From SQL Procedures 249 Example: Assignments Involving Distinct
Returning Result Sets to Caller or Client 250 Types . . . . . . . . . . . . . 280
Receiving Result Sets as a Caller . . . . 251 Example: Assignments in Dynamic SQL 280
Debugging SQL Procedures . . . . . . 252 Example: Assignments Involving Different
Displaying Error Messages for SQL Distinct Types . . . . . . . . . . 281
Procedures . . . . . . . . . . . 252 Example: Use of Distinct Types in
Debugging SQL Procedures Using UNION . . . . . . . . . . . . 282
Intermediate Files . . . . . . . . . 255
Examples of SQL Procedures . . . . . . 255 Chapter 12. Working with Complex
Objects: User-Defined Structured Types . 283
Chapter 9. IBM DB2 Stored Procedure Structured Types Overview . . . . . . 284
Builder . . . . . . . . . . . . . 261 Creating a Structured Type Hierarchy . . 285
What is Stored Procedure Builder? . . . . 261 Storing Objects in Typed Tables . . . . 291
Advantages of Using Stored Procedure Storing Objects in Columns . . . . . 293
Builder . . . . . . . . . . . . . 262 Additional Properties of Structured Types 295
Creating New Stored Procedures. . . . 262 Using Structured Types in Typed Tables . . 296
Working with Existing Stored Procedures 263 Creating a Typed Table . . . . . . . 296
Creating Stored Procedure Builder Populating a Typed Table . . . . . . 298
Projects . . . . . . . . . . . . 263 Using Reference Types . . . . . . . 300
Debugging Stored Procedures . . . . . 263 Comparing Reference Types . . . . . 300
Creating a Typed View . . . . . . . 303
Part 4. Object-Relational Dropping a User-Defined Type (UDT) or
Type Mapping . . . . . . . . . . 305
Programming . . . . . . . . . 265 Altering or Dropping a View . . . . . 306
Querying a Typed Table. . . . . . . 306
Chapter 10. Using the Object-Relational Queries that Dereference References . . 307
Capabilities . . . . . . . . . . . 267 Additional Query Specification
Why Use the DB2 Object Extensions? . . . 267 Techniques . . . . . . . . . . . 309
Object-Relational Features of DB2 . . . 267 Additional Hints and Tips . . . . . . 311
Creating and Using Structured Types as
Chapter 11. User-defined Distinct Types 273 Column Types . . . . . . . . . . . 313
Why Use Distinct Types? . . . . . . . 273 Inserting Structured Type Instances into a
Defining a Distinct Type . . . . . . . 274 Column . . . . . . . . . . . . 313
Resolving Unqualified Distinct Types . . . 274 Defining Tables with Structured Type
Examples of Using CREATE DISTINCT Columns . . . . . . . . . . . . 314
TYPE . . . . . . . . . . . . . . 275 Defining Types with Structured Type
Example: Money . . . . . . . . . 275 Attributes . . . . . . . . . . . 314
Example: Job Application . . . . . . 275 Inserting Rows that Contain Structured
Defining Tables with Distinct Types . . . . 275 Type Values . . . . . . . . . . . 314
Example: Sales . . . . . . . . . . 275 Retrieving and Modifying Structured
Example: Application Forms . . . . . 276 Type Values . . . . . . . . . . . 316
Contents v
Associating Transforms with a Type. . . 318 Example: AVG over a UDT . . . . . 375
Where Transform Groups Must Be Example: Counting . . . . . . . . 375
Specified . . . . . . . . . . . . 320 Example: Counting with an OLE
Creating the Mapping to the Host Automation Object . . . . . . . . 376
Language Program: Transform Functions . 321 Example: Table Function Returning
Working with Structured Type Host Document IDs . . . . . . . . . . 376
Variables . . . . . . . . . . . . 340 Using Functions and Methods . . . . . 377
Referring to Functions . . . . . . . 377
Chapter 13. Using Large Objects (LOBs) 341 Examples of Function Invocations . . . 378
What are LOBs? . . . . . . . . . . 341 Using Parameter Markers in Functions 379
Understanding Large Object Data Types Using Qualified Function Reference . . . 379
(BLOB, CLOB, DBCLOB) . . . . . . . 342 Using Unqualified Function Reference 380
Understanding Large Object Locators . . . 343 Summary of Function References . . . 380
Example: Using a Locator to Work With a
CLOB Value . . . . . . . . . . . 345 Chapter 15. Writing User-Defined
How the Sample LOBLOC Program Functions (UDFs) and Methods . . . . 385
Works. . . . . . . . . . . . . 345 Description . . . . . . . . . . . . 385
C Sample: LOBLOC.SQC . . . . . . 346 Interface between DB2 and a UDF . . . . 387
COBOL Sample: LOBLOC.SQB . . . . 348 The Arguments Passed from DB2 to a
Example: Deferring the Evaluation of a LOB UDF . . . . . . . . . . . . . 387
Expression . . . . . . . . . . . . 351 Summary of UDF Argument Use . . . 400
How the Sample LOBEVAL Program How the SQL Data Types are Passed to a
Works. . . . . . . . . . . . . 352 UDF . . . . . . . . . . . . . 402
C Sample: LOBEVAL.SQC . . . . . . 353 Writing Scratchpads on 32-bit and 64-bit
COBOL Sample: LOBEVAL.SQB . . . . 355 Platforms . . . . . . . . . . . 410
Indicator Variables and LOB Locators . . 358 The UDF Include File: sqludf.h . . . . 411
LOB File Reference Variables . . . . . . 358 Creating and Using Java User-Defined
Example: Extracting a Document To a File 360 Functions . . . . . . . . . . . . 412
How the Sample LOBFILE Program Coding a Java UDF . . . . . . . . 412
Works. . . . . . . . . . . . . 360 Changing How a Java UDF Runs . . . 414
C Sample: LOBFILE.SQC . . . . . . 361 Table Function Execution Model for Java 415
COBOL Sample: LOBFILE.SQB . . . . 362 Writing OLE Automation UDFs . . . . . 416
Example: Inserting Data Into a CLOB Creating and Registering OLE
Column . . . . . . . . . . . . . 364 Automation UDFs . . . . . . . . 417
Object Instance and Scratchpad
Chapter 14. User-Defined Functions Considerations . . . . . . . . . . 418
(UDFs) and Methods . . . . . . . . 365 How the SQL Data Types are Passed to
What are Functions and Methods? . . . . 365 an OLE Automation UDF . . . . . . 418
Why Use Functions and Methods? . . . . 366 Implementing OLE Automation UDFs in
UDF And Method Concepts . . . . . . 369 BASIC and C++ . . . . . . . . . 420
Implementing Functions and Methods . . . 370 OLE DB Table Functions . . . . . . . 423
Writing Functions and Methods . . . . . 371 Creating an OLE DB Table Function . . 424
Registering Functions and Methods . . . . 371 Fully Qualified Rowset Names . . . . 426
Examples of Registering UDFs and Methods 371 Defining a Server Name for an OLE DB
Example: Exponentiation . . . . . . 372 Provider . . . . . . . . . . . . 427
Example: String Search . . . . . . . 372 Defining a User Mapping . . . . . . 427
Example: BLOB String Search . . . . . 373 Supported OLE DB Data Types . . . . 428
Example: String Search over UDT . . . 373 Scratchpad Considerations . . . . . . . 430
Example: External Function with UDT Table Function Considerations . . . . . 432
Parameter . . . . . . . . . . . 374 Table Function Error Processing . . . . . 433
vi Application Development Guide
Scalar Function Error Processing . . . . . 433 Chapter 17. Programming in Complex
Using LOB Locators as UDF Parameters or Environments . . . . . . . . . . 493
Results . . . . . . . . . . . . . 434 National Language Support Considerations 493
Scenarios for Using LOB Locators . . . 438 Collating Sequence Overview . . . . . 494
Other Coding Considerations . . . . . . 438 Deriving Code Page Values . . . . . 499
Hints and Tips . . . . . . . . . . 439 Deriving Locales in Application Programs 500
UDF Restrictions and Caveats. . . . . 441 National Language Support Application
Examples of UDF Code . . . . . . . . 443 Development . . . . . . . . . . 501
Example: Integer Divide Operator . . . 443 DBCS Character Sets . . . . . . . . 508
Example: Fold the CLOB, Find the Vowel 447 Extended UNIX Code (EUC) Character
Example: Counter. . . . . . . . . 451 Sets . . . . . . . . . . . . . 509
Example: Weather Table Function . . . 453 Running CLI/ODBC/JDBC/SQLJ
Example: Function using LOB locators 461 Programs in a DBCS Environment . . . 510
Example: Counter OLE Automation UDF Japanese and Traditional Chinese EUC
in BASIC. . . . . . . . . . . . 464 and UCS-2 Code Set Considerations. . . 511
Example: Counter OLE Automation UDF Considerations for Multisite Updates . . . 525
in C++ . . . . . . . . . . . . 466 Remote Unit of Work . . . . . . . 525
Debugging your UDF . . . . . . . . 470 Multisite Update . . . . . . . . . 525
Accessing Host or AS/400 Servers . . . . 532
Chapter 16. Using Triggers in an Active Multiple Thread Database Access . . . . 533
DBMS . . . . . . . . . . . . . 473 Recommendations for Using Multiple
Why Use Triggers? . . . . . . . . . 473 Threads . . . . . . . . . . . . 534
Benefits of Triggers . . . . . . . . 474 Multithreaded UNIX Applications
Overview of a Trigger . . . . . . . . 475 Working with Code Page and Country
Trigger Event . . . . . . . . . . . 476 Code . . . . . . . . . . . . . 534
Set of Affected Rows . . . . . . . . . 477 Potential Pitfalls when Using Multiple
Trigger Granularity . . . . . . . . . 477 Threads . . . . . . . . . . . . 535
Trigger Activation Time . . . . . . . . 478 Concurrent Transactions . . . . . . . 537
Transition Variables . . . . . . . . . 479 Potential Pitfalls when Using Concurrent
Transition Tables . . . . . . . . . . 480 Transactions . . . . . . . . . . 537
Triggered Action . . . . . . . . . . 482 X/Open XA Interface Programming
Triggered Action Condition . . . . . 482 Considerations . . . . . . . . . . . 539
Triggered SQL Statements . . . . . . 483 Application Linkage . . . . . . . . 542
Functions Within SQL Triggered Working with Large Volumes of Data Across
Statement . . . . . . . . . . . 483 a Network . . . . . . . . . . . . 542
Trigger Cascading. . . . . . . . . . 484
Interactions with Referential Constraints . . 485 Chapter 18. Programming Considerations
Ordering of Multiple Triggers . . . . . . 485 in a Partitioned Environment . . . . . 545
Synergy Between Triggers, Constraints, Improving Performance . . . . . . . . 545
UDTs, UDFs, and LOBs . . . . . . . . 486 Using FOR READ ONLY Cursors . . . 545
Extracting Information . . . . . . . 486 Using Directed DSS and Local Bypass . . 545
Preventing Operations on Tables . . . . 487 Using Buffered Inserts . . . . . . . 547
Defining Business Rules. . . . . . . 487 Example: Extracting Large Volume of
Defining Actions . . . . . . . . . 488 Data (largevol.c) . . . . . . . . . 552
Creating a Test Environment . . . . . . 558
Part 5. DB2 Programming Error-Handling Considerations . . . . . 558
Severe Errors . . . . . . . . . . 559
Considerations . . . . . . . . 491 Merged Multiple SQLCA Structures. . . 559
Identifying the Partition that Returned
the Error . . . . . . . . . . . . 560
Contents vii
Debugging . . . . . . . . . . . . 560 Indicator Variables in C and C++ . . . 593
Diagnosing a Looping or Suspended Graphic Host Variable Declarations in C
application . . . . . . . . . . . 560 or C++ . . . . . . . . . . . . 593
LOB Data Declarations in C or C++. . . 596
Chapter 19. Writing Programs for DB2 LOB Locator Declarations in C or C++ 598
Federated Systems. . . . . . . . . 563 File Reference Declarations in C or C++ 599
Introduction to DB2 Federated Systems . . 563 Initializing Host Variables in C and C++ 600
Accessing Data Source Tables and Views . . 564 C Macro Expansion . . . . . . . . 600
Working with Nicknames . . . . . . 564 Host Structure Support in C and C++ . . 602
Using Isolation Levels to Maintain Data Indicator Tables in C and C++ . . . . 603
Integrity . . . . . . . . . . . . 568 Null-terminated Strings in C and C++ 604
Working with Data Type Mappings . . . . 569 Pointer Data Types in C and C++ . . . 606
How DB2 Determines What Data Types Using Class Data Members as Host
to Define Locally . . . . . . . . . 569 Variables in C and C++ . . . . . . . 607
Default Data Type Mappings . . . . . 569 Using Qualification and Member
How You Can Override Default Type Operators in C and C++ . . . . . . 608
Mappings and Create New Ones. . . . 570 Handling Graphic Host Variables in C
Using Distributed Requests to Query Data and C++ . . . . . . . . . . . . 609
Sources . . . . . . . . . . . . . 571 Japanese or Traditional Chinese EUC, and
Coding Distributed Requests . . . . . 571 UCS-2 Considerations in C and C++ . . 614
Using Server Options to Facilitate Supported SQL Data Types in C and C++ 615
Optimization . . . . . . . . . . 572 FOR BIT DATA in C and C++. . . . . 620
Invoking Data Source Functions . . . . . 574 SQLSTATE and SQLCODE Variables in C
Enabling DB2 to Invoke Data Source and C++ . . . . . . . . . . . . . 620
Functions . . . . . . . . . . . 574
Reducing the Overhead of Invoking a Chapter 21. Programming in Java . . . 623
Function . . . . . . . . . . . . 574 Programming Considerations for Java . . . 623
Specifying Function Names in the Comparison of SQLJ to JDBC . . . . . 623
CREATE FUNCTION MAPPING Advantages of Java over Other
Statement . . . . . . . . . . . 576 Languages . . . . . . . . . . . 624
Discontinuing Function Mappings . . . 576 SQL Security in Java . . . . . . . . 624
Using Pass-Through to Query Data Sources Source and Output Files for Java. . . . 624
Directly . . . . . . . . . . . . . 576 Java Class Libraries . . . . . . . . 625
SQL Processing in Pass-Through Sessions 576 Java Packages . . . . . . . . . . 625
Considerations and Restrictions . . . . 577 Supported SQL Data Types in Java . . . 625
SQLSTATE and SQLCODE Values in Java 627
Part 6. Language Considerations 579 Trace Facilities in Java . . . . . . . 627
Creating Java Applications and Applets 628
JDBC Programming . . . . . . . . . 630
Chapter 20. Programming in C and C++ 581
How the DB2Appl Program Works . . . 630
Programming Considerations for C and C++ 581
Distributing a JDBC Application . . . . 633
Language Restrictions for C and C++ . . . 581
Distributing and Running a JDBC Applet 633
Trigraph Sequences for C and C++ . . . 581
JDBC 2.0 . . . . . . . . . . . . 634
C++ Type Decoration Consideration . . 582
SQLJ Programming . . . . . . . . . 637
Input and Output Files for C and C++ . . . 582
DB2 SQLJ Support . . . . . . . . 637
Include Files for C and C++ . . . . . . 583
Embedding SQL Statements in Java . . . 639
Including Files in C and C++ . . . . . 585
Host Variables in Java . . . . . . . 646
Embedding SQL Statements in C and C++ 586
Calls to Stored Procedures and Functions
Host Variables in C and C++ . . . . . . 588
in SQLJ . . . . . . . . . . . . 646
Naming Host Variables in C and C++ . . 588
Compiling and Running SQLJ Programs 646
Declaring Host Variables in C and C++ 589
viii Application Development Guide
SQLJ Translator Options . . . . . . 648 Language Restrictions in FORTRAN . . . 687
Stored Procedures and UDFs in Java . . . 649 Call by Reference in FORTRAN . . . . 687
Where to Put Java Classes . . . . . . 650 Debugging and Comment Lines in
Updating Java Classes for Routines . . . 651 FORTRAN . . . . . . . . . . . 688
Debugging Stored Procedures in Java . . 651 Precompiling Considerations for
Java Stored Procedures and UDFs . . . 654 FORTRAN . . . . . . . . . . . 688
Using LOBs and Graphical Objects With Input and Output Files for FORTRAN . . . 688
JDBC 1.22 . . . . . . . . . . . . 657 Include Files for FORTRAN . . . . . . 688
JDBC and SQLJ Interoperability . . . . . 658 Including Files in FORTRAN . . . . . 691
Session Sharing . . . . . . . . . 659 Embedding SQL Statements in FORTRAN 691
Connection Resource Management in Java 659 Host Variables in FORTRAN . . . . . . 693
Naming Host Variables in FORTRAN . . 693
Chapter 22. Programming in Perl . . . . 661 Declaring Host Variables . . . . . . 693
Programming Considerations for Perl . . . 661 Indicator Variables in FORTRAN. . . . 696
Perl Restrictions . . . . . . . . . . 661 LOB Declarations in FORTRAN . . . . 696
Connecting to a Database Using Perl . . . 661 LOB Locator Declarations in FORTRAN 697
Fetching Results in Perl . . . . . . . . 662 File Reference Declarations in FORTRAN 697
Parameter Markers in Perl . . . . . . . 663 Supported SQL Data Types in FORTRAN 698
SQLSTATE and SQLCODE Variables in Perl 663 SQLSTATE and SQLCODE Variables in
Perl DB2 Application Example . . . . . 664 FORTRAN . . . . . . . . . . . . 700
Considerations for Multi-byte Character Sets
Chapter 23. Programming in COBOL . . 665 in FORTRAN . . . . . . . . . . . 700
Programming Considerations for COBOL 665 Japanese or Traditional Chinese EUC, and
Language Restrictions in COBOL . . . . 665 UCS-2 Considerations for FORTRAN . . . 701
Input and Output Files for COBOL . . . . 665
Include Files for COBOL . . . . . . . 665 Chapter 25. Programming in REXX . . . 703
Embedding SQL Statements in COBOL . . 668 Programming Considerations for REXX . . 703
Host Variables in COBOL . . . . . . . 671 Language Restrictions for REXX . . . . . 704
Naming Host Variables in COBOL . . . 671 Registering SQLEXEC, SQLDBS and
Declaring Host Variables . . . . . . 671 SQLDB2 in REXX . . . . . . . . . 704
Indicator Variables in COBOL. . . . . 675 Embedding SQL Statements in REXX . . . 705
LOB Declarations in COBOL . . . . . 675 Host Variables in REXX . . . . . . . . 707
LOB Locator Declarations in COBOL . . 676 Naming Host Variables in REXX . . . . 707
File Reference Declarations in COBOL 677 Referencing Host Variables in REXX . . 707
Host Structure Support in COBOL . . . 677 Indicator Variables in REXX . . . . . 708
Indicator Tables in COBOL. . . . . . 680 Predefined REXX Variables. . . . . . 708
Using REDEFINES in COBOL Group LOB Host Variables in REXX . . . . . 710
Data Items . . . . . . . . . . . 680 LOB Locator Declarations in REXX . . . 710
Using BINARY/COMP-4 COBOL Data LOB File Reference Declarations in REXX 711
Types . . . . . . . . . . . . . 681 Clearing LOB Host Variables in REXX 712
Supported SQL Data Types in COBOL . . . 681 Supported SQL Data Types in REXX . . . 712
FOR BIT DATA in COBOL . . . . . . 684 Using Cursors in REXX . . . . . . . 714
SQLSTATE and SQLCODE Variables in Execution Requirements for REXX . . . . 715
COBOL . . . . . . . . . . . . . 685 Bind Files for REXX . . . . . . . . 715
Japanese or Traditional Chinese EUC, and API Syntax for REXX . . . . . . . . 716
UCS-2 Considerations for COBOL . . . . 685 REXX Stored Procedures . . . . . . . 718
Object Oriented COBOL . . . . . . . 686 Calling Stored Procedures in REXX . . . 718
Japanese or Traditional Chinese EUC
Chapter 24. Programming in FORTRAN 687 Considerations for REXX . . . . . . . 720
Programming Considerations for FORTRAN 687
Contents ix
Part 7. Appendixes . . . . . . . 721 Large Object (LOB) Data Type . . . . 775
User Defined Types (UDTs) . . . . . 775
ROWID Data Type . . . . . . . . 776
Appendix A. Supported SQL Statements 723
64-bit Integer (BIGINT) data type . . . 776
Using Data Control Language (DCL) . . . 776
Appendix B. Sample Programs . . . . 729
Connecting and Disconnecting . . . . . 776
DB2 API Non-Embedded SQL Samples . . 733
Precompiling . . . . . . . . . . . 777
DB2 API Embedded SQL Samples . . . . 736
Blocking . . . . . . . . . . . . 777
Embedded SQL Samples With No DB2 APIs 738
Package Attributes . . . . . . . . 778
User-Defined Function Samples . . . . . 740
C Null-terminated Strings . . . . . . 779
DB2 Call Level Interface Samples . . . . 740
Standalone SQLCODE and SQLSTATE 779
Java Samples . . . . . . . . . . . 742
Defining a Sort Order . . . . . . . . 779
SQL Procedure Samples. . . . . . . . 744
Managing Referential Integrity . . . . . 779
ADO, RDO, and MTS Samples . . . . . 746
Locking . . . . . . . . . . . . . 780
Object Linking and Embedding Samples . . 747
Differences in SQLCODEs and SQLSTATEs 780
Command Line Processor Samples . . . . 748
Using System Catalogs . . . . . . . . 781
Log Management User Exit Samples . . . 749
Numeric Conversion Overflows on Retrieval
Assignments . . . . . . . . . . . 781
Appendix C. DB2DARI and DB2GENERAL Isolation Levels . . . . . . . . . . 781
Stored Procedures and UDFs . . . . . 751 Stored Procedures. . . . . . . . . . 782
DB2DARI Stored Procedures . . . . . . 751 Stored Procedure Builder . . . . . . 783
Using the SQLDA in a Client Application 751 NOT ATOMIC Compound SQL . . . . . 785
Using Host Variables in a DB2DARI Multisite Update with DB2 Connect. . . . 785
Client . . . . . . . . . . . . . 752 Host or AS/400 Server SQL Statements
Using the SQLDA in a Stored Procedure 752 Supported by DB2 Connect . . . . . . 786
Summary of Data Structure Usage . . . 753 Host or AS/400 Server SQL Statements
Input/Output SQLDA and SQLCA Rejected by DB2 Connect . . . . . . . 787
Structures . . . . . . . . . . . 754
Return Values for DB2DARI Stored
Appendix E. Simulating EBCDIC Binary
Procedures . . . . . . . . . . . 755
Collation . . . . . . . . . . . . 789
DB2GENERAL UDFs and Stored Procedures 755
Supported SQL Data Types . . . . . 756
Appendix F. Using the DB2 Library . . . 795
Classes for Java Stored Procedures and
DB2 PDF Files and Printed Books . . . . 795
UDFs . . . . . . . . . . . . . 757
DB2 Information . . . . . . . . . 795
NOT FENCED Stored Procedures . . . 763
Printing the PDF Books . . . . . . . 804
Example Input-SQLDA Programs . . . . 764
Ordering the Printed Books . . . . . 805
How the Example Input-SQLDA Client
DB2 Online Documentation . . . . . . 806
Application Works . . . . . . . . 765
Accessing Online Help . . . . . . . 806
C Example: V5SPCLI.SQC . . . . . . 767
Viewing Information Online . . . . . 808
How the Example Input-SQLDA Stored
Using DB2 Wizards . . . . . . . . 810
Procedure Works . . . . . . . . . 770
Setting Up a Document Server . . . . 811
C Example: V5SPSRV.SQC . . . . . . 771
Searching Information Online . . . . . 812
Appendix D. Programming in a Host or
Appendix G. Notices . . . . . . . . 813
AS/400 Environment . . . . . . . . 773
Trademarks . . . . . . . . . . . . 816
Using Data Definition Language (DDL) . . 774
Using Data Manipulation Language (DML) 775
Numeric Data Types . . . . . . . . 775 Index . . . . . . . . . . . . . 819
Mixed-Byte Data . . . . . . . . . 775
Long Fields . . . . . . . . . . . 775 Contacting IBM . . . . . . . . . . 847
Product Information . . . . . . . . . 847
x Application Development Guide
Part 1. DB2 Application Development Concepts
© Copyright IBM Corp. 1993, 2000 1
2 Application Development Guide
Chapter 1. Getting Started with DB2 Application
Development
About This Book . . . . . . . . . . . 3 Conventions . . . . . . . . . . . 7
Who Should Use This Book . . . . . . . 4 Related Publications. . . . . . . . . 8
How to Use This Book . . . . . . . . . 4
About This Book
This book discusses how to design and code application programs that access
DB2 databases. It presents detailed information on the use of Structured
Query Language (SQL) in supported host language programs. For information
on language support for your specific operating system, see the Application
Building Guide. This book also provides an overview of some of the DB2
utilities that you can use to help create DB2 applications. These utilities
include “The IBM DB2 Universal Database Project Add-In for Microsoft Visual
C++” on page 30 and “Chapter 9. IBM DB2 Stored Procedure Builder” on
page 261.
You can access data with:
v SQL statements embedded in a host language, including embedded SQL for
Java (SQLJ)
v dynamic APIs including Java Database Connectivity (JDBC), Perl DBI, and
DB2 Call Level Interface (DB2 CLI)
This book discusses all these ways to access data except DB2 CLI, which is
discussed in the CLI Guide and Reference. JDBC, SQLJ, and DB2 CLI provide
some data access capabilities that are not available through embedded SQL.
These capabilities include scrollable cursors and stored procedures that return
multiple result sets. See the discussion in “Access to Data” on page 23 to help
you decide which data access method to use.
To effectively use the information in this book to design, write, and test your
DB2 application programs, you need to refer to the SQL Reference along with
this book. If you are using the DB2 Call Level Interface (CLI) or Open
Database Connectivity (ODBC) interface in your applications to access DB2
databases, refer to the CLI Guide and Reference. To perform database manager
administration functions using the DB2 administration APIs in your
application programs, refer to the Administrative API Reference.
You can also develop applications where one part of the application runs on
the client and another part runs on the server. Version 7 of DB2 introduces
© Copyright IBM Corp. 1993, 2000 3
support for stored procedures with enhanced portability and scalability across
platforms. Stored procedures are discussed in “Chapter 7. Stored Procedures”
on page 187.
You can use object-based extensions to DB2 to make your DB2 application
programs more powerful, flexible, and active than traditional DB2
applications. The extensions include large objects (LOBs), distinct types,
structured types, user-defined functions (UDFs), and triggers. These features
of DB2 are described in:
v “Chapter 10. Using the Object-Relational Capabilities” on page 267
v “Chapter 11. User-defined Distinct Types” on page 273
v “Chapter 12. Working with Complex Objects: User-Defined Structured
Types” on page 283
v “Chapter 13. Using Large Objects (LOBs)” on page 341
v “Chapter 14. User-Defined Functions (UDFs) and Methods” on page 365
v “Chapter 15. Writing User-Defined Functions (UDFs) and Methods” on
page 385
v “Chapter 16. Using Triggers in an Active DBMS” on page 473
References to DB2 in this book should be understood to mean the DB2
Universal Database product on UNIX, Linux, OS/2, and Windows 32-bit
operating systems. References to DB2 on other platforms use a specific
product name and platform, such as DB2 Universal Database for AS/400.
Who Should Use This Book
This book is intended for programmers who are experienced with SQL and
with one or more of the supported programming languages.
How to Use This Book
This book is organized, by task, into the following parts, chapters, and
appendices:
v Part 1. DB2 Application Development Concepts contains information you
need to use this book and an overview of the methods you can use to
develop applications for DB2 Universal Database.
– Chapter 1. Getting Started with DB2 Application Development describes
the structure of this book and the conventions used in it.
– Chapter 2. Coding a DB2 Application introduces the overall application
development process using DB2. It discusses and compares the
important application design issues you need to consider prior to coding
4 Application Development Guide
your applications. This chapter concludes with information to help you
set up a test environment where you can begin to develop your
applications.
v Part 2. Embedding SQL in Applications describes how to embed static and
dynamic SQL in your applications. This information includes a description
of the utilities that you can use to help create your embedded SQL
applications.
– Embedding SQL Statements in a Host Language discusses the process of
creating a DB2 application by embedding SQL in host languages such as
C/C++, Java, and COBOL. It contains an overview of the DB2
precompiler, compiling and linking the application, and binding the
embedded SQL statements to the database.
– Chapter 4. Writing Static SQL Programs discusses the details of coding
your DB2 embedded SQL application using static SQL statements. It
contains detailed guidelines and considerations for using static SQL.
– Chapter 5. Writing Dynamic SQL Programs discusses the details of
coding your DB2 embedded SQL application using dynamic SQL
statements. It contains detailed guidelines and considerations for using
dynamic SQL.
– Chapter 6. Common DB2 Application Techniques discusses DB2 features
that help you with common application development problems. These
features include the ability to automatically create unique row identifiers,
to create columns that are dynamically derived from an expression, and
to create and use declared temporary tables.
v Part 3. Stored Procedures discusses how to use stored procedures to
improve the performance of database applications that run in client/server
environments.
– Chapter 7. Stored Procedures describes how to write stored procedures
and the client applications that call stored procedures using host
languages.
– Chapter 8. Writing SQL Procedures describes how to write stored
procedures in SQL by issuing a CREATE PROCEDURE statement. SQL
procedures encode their procedural logic using SQL in the body of the
CREATE PROCEDURE statement.
– Chapter 9. IBM DB2 Stored Procedure Builder describes the IBM DB2
Stored Procedure Builder, a graphical application that supports the rapid
development of stored procedures for DB2. Stored Procedure Builder
helps you create both SQL and Java stored procedures.
v Part 4. Object-Relational Programming describes how to use the
object-relational support provided by DB2. This information includes an
introduction to and detailed instructions on how to use large objects,
user-defined functions, user-defined distinct types, and triggers.
Chapter 1. Getting Started with DB2 Application Development 5
– Chapter 10. Using the Object-Relational Capabilities introduces the
object-oriented capabilities of DB2. It explains how to extend your
traditional application to one that takes advantage of DB2 capabilities
such as large objects, user-defined functions, and user-defined distinct
types in an object-oriented context.
– Chapter 11. User-defined Distinct Types describes how to create and use
your own data types in applications. It explains how to use distinct types
as a foundation for object-oriented extensions to the built-in data types.
– Chapter 12. Working with Complex Objects: User-Defined Structured
Types describes how to create and use structured types in applications. It
explains how to model objects as hierarchies of structured types, access
instances of structured types as rows or columns in tables, and bind
structured types into and out of your applications.
– Chapter 13. Using Large Objects (LOBs) describes how to define and use
data types that can store data objects as binary or text strings of up to
two gigabytes in size. It also explains how to efficiently use LOBs in a
networked environment.
– Chapter 14. User-Defined Functions (UDFs) and Methods describes how
to write your own extensions to SQL. It explains how to use UDFs to
express the behavior of your data objects.
– Chapter 15. Writing User-Defined Functions (UDFs) and Methods
describes how to write user-defined functions that extend your DB2
applications. Topics include the details of writing a user-defined
function, programming considerations for user-defined functions, and
several examples that show you how to exploit this important capability.
In addition, this chapter describes user-defined table functions, OLE DB
table functions, and OLE automation UDFs.
– Chapter 16. Using Triggers in an Active DBMS describes how to use
triggers to encapsulate and enforce business rules within all of your
database applications.
v Part 5. DB2 Programming Considerations contains information on special
application development considerations.
– Chapter 17. Programming in Complex Environments discusses advanced
programming topics such as national language support, dealing with
Extended UNIX® Code (EUC) code pages for databases and applications,
accessing multiple databases within a unit of work, and creating
multi-threaded applications.
– Chapter 18. Programming Considerations in a Partitioned Environment
describes programming considerations if you are developing applications
that run in a partitioned environment.
– Chapter 19. Writing Programs for DB2 Federated Systems describes how
to create applications that transparently access data from DB2 family and
Oracle data sources through a federated server.
6 Application Development Guide
v Part 6. Language Considerations contains specific information about the
programming languages that DB2 supports.
– Chapter 20. Programming in C and C++ discusses host language specific
information concerning database applications written in C and C++.
– Chapter 21. Programming in Java discusses host language specific
information concerning database applications written in Java using JDBC
or SQLJ.
– Chapter 22. Programming in Perl discusses host language specific
information concerning database applications written in Perl using the
DBD::DB2 database driver for the Perl Database Interface (DBI) Module.
– Chapter 23. Programming in COBOL discusses host language specific
information concerning database applications written in COBOL.
– Chapter 24. Programming in FORTRAN discusses host language specific
information concerning database applications written in FORTRAN.
– Chapter 25. Programming in REXX discusses host language specific
information concerning database applications written in REXX.
v The Appendices contain supplemental information to which you may need
to refer when developing DB2 applications.
– Appendix A. Supported SQL Statements lists the SQL statements
supported by DB2 Universal Database.
– Appendix B. Sample Programs contains information on supplied sample
programs for supported host languages and describes how they work.
– Appendix C. DB2DARI and DB2GENERAL Stored Procedures and UDFs
contains information you can use to create stored procedures and UDFs
that are compatible with previous versions of DB2 Universal Database.
– Appendix D. Programming in a Host or AS/400 Environment describes
programming considerations for DB2 Connect if you access host or
AS/400 database servers in your applications in a distributed
environment.
– Appendix E. Simulating EBCDIC Binary Collation describes how to
collate DB2 character strings according to an EBCDIC, or user-defined,
collating sequence.
– Appendix F. Using the DB2 Library shows you where you can get more
information for the DB2 Universal Database product.
Conventions
This book uses the following conventions:
Directories and Paths
This book uses the UNIX convention for delimiting directories, for
example: sqllib/samples/java. You can convert these paths to
Windows 32-bit operating system and OS/2 paths by changing the /
to a \ and prepending the appropriate installation drive and directory.
Chapter 1. Getting Started with DB2 Application Development 7
Italics Indicates one of the following:
v Introduction of a new term
v Variable names or values that are supplied by the user
v Reference to another source of information, for example, a book or
CD-ROM
v General emphasis
UPPERCASE
Indicates one of the following:
v Abbreviations
v Database manager data types
v SQL statements
Example
Indicates one of the following:
v Coding examples and code fragments
v Examples of output, similar to what is displayed by the system
v Examples of specific data values
v Examples of system messages
v File and directory names
v Information that you are instructed to type
v Java method names
v Function names
v API names
Bold Bold text emphasizes a point.
Related Publications
The following manuals describe how to develop applications for international
use and for specific countries:
Form Number Book Title
SE09-8001-03 National Language Design Guide, Volume 1
SE09-8002-03 NLS Reference Manual, Release 4
8 Application Development Guide
Chapter 2. Coding a DB2 Application
Prerequisites for Programming . . . . . . 9 Application Logic and Program Variable
DB2 Application Coding Overview . . . . 10 Types . . . . . . . . . . . . 27
Declaring and Initializing Variables . . . 11 Data Relationship Control . . . . . . 27
Declaring Variables that Interact with Referential Integrity Constraints . . . 28
the Database Manager. . . . . . . 11 Triggers . . . . . . . . . . . 28
Handling Errors and Warnings . . . . 14 Application Logic . . . . . . . . 29
Using Additional Nonexecutable Application Logic at the Server . . . . 29
Statements . . . . . . . . . . 16 Stored Procedures . . . . . . . . 29
Connecting to the Database Server . . . 16 User-Defined Functions . . . . . . 29
Coding Transactions . . . . . . . . 17 Triggers . . . . . . . . . . . 29
Beginning a Transaction . . . . . . 18 The IBM DB2 Universal Database Project
Ending a Transaction . . . . . . . 18 Add-In for Microsoft Visual C++ . . . . 30
Ending the Program . . . . . . . . 19 Activating the IBM DB2 Universal
Implicitly Ending a Transaction . . . . 19 Database Project Add-In for Microsoft
On Most Supported Operating Systems 20 Visual C++ . . . . . . . . . . 32
On Windows 32-bit Operating Systems 20 Activating the IBM DB2 Universal
When Using the DB2 Context APIs . . 20 Database Tools Add-In for Microsoft
Application Pseudocode Framework . . . 20 Visual C++ . . . . . . . . . . 32
Designing an Application For DB2 . . . . 21 Supported SQL Statements . . . . . . . 33
Access to Data . . . . . . . . . . 23 Authorization Considerations . . . . . . 33
Embedded SQL . . . . . . . . . 23 Dynamic SQL . . . . . . . . . . 34
DB2 Call Level Interface (DB2 CLI) and Static SQL. . . . . . . . . . . . 35
Open Database Connectivity (ODBC) . 24 Using APIs . . . . . . . . . . . 35
JDBC . . . . . . . . . . . . 24 Example . . . . . . . . . . . . 35
Microsoft Specifications . . . . . . 25 Database Manager APIs Used in Embedded
Perl DBI . . . . . . . . . . . 25 SQL or DB2 CLI Programs . . . . . . . 36
Query Products . . . . . . . . . 25 Setting Up the Testing Environment . . . . 36
Data Value Control. . . . . . . . . 25 Creating a Test Database . . . . . . . 37
Data Types . . . . . . . . . . 26 Creating Test Tables . . . . . . . . 37
Unique Constraints . . . . . . . 26 Generating Test Data . . . . . . . . 38
Table Check Constraints . . . . . . 26 Running, Testing and Debugging Your
Referential Integrity Constraints . . . 26 Programs . . . . . . . . . . . . . 40
Views with Check Option . . . . . 27 Prototyping Your SQL Statements . . . . . 40
Prerequisites for Programming
This chapter presents a model of the logical parts of a DB2 application and
discusses the individual strengths of the supported DB2 programming APIs.
Programmers who are new to developing a DB2 application should read the
entire chapter closely.
The application development process described in this book assumes that you
have established the appropriate operating environment. This means that the
following are properly installed and configured:
v A supported compiler or interpreter for developing your applications.
© Copyright IBM Corp. 1993, 2000 9
v DB2 Universal Database, either locally or remotely.
v DB2 Application Development Client.
For details on how to accomplish these tasks, refer to the Application Building
Guide and the Quick Beginnings books for your operating environment.
You can develop applications at a server, or on any client, that has the DB2
Application Development Client (DB2 Application Development Client)
installed. You can run applications with either the server, the DB2 Run-Time
Client, or the DB2 Administrative Client. You can also develop Java JDBC
programs on one of these clients, provided that you install the ″Java
Enablement″ component when you install the client. That means you can
execute any DB2 application on these clients. However, unless you also install
the DB2 Application Development Client with these clients, you can only
develop JDBC applications on them.
DB2 supports the C, C++, Java (SQLJ), COBOL, and FORTRAN programming
languages through its precompilers. In addition, DB2 provides support for the
Perl, Java (JDBC), and REXX dynamically interpreted languages. For
information on the specific precompilers provided by DB2, and the languages
supported on your platform, refer to the Application Building Guide.
Note: FORTRAN and REXX support stabilized in DB2 Version 5, and no
enhancements for FORTRAN or REXX support are planned for the
future.
DB2 provides a sample database which you require when running the
supplied sample programs. For information about the sample database and its
contents, refer to the SQL Reference.
DB2 Application Coding Overview
A DB2 application program consists of several parts:
1. Declaring and initializing variables
2. Connecting to the database
3. Performing one or more transactions
4. Disconnecting from the database
5. Ending the program
A transaction is a set of database operations that must conclude successfully
before being committed to the database. With embedded SQL, a transaction
begins implicitly and ends when the application executes either a COMMIT or
ROLLBACK statement. An example of a transaction is the entry of a
customer’s deposit, and the updating of the customer’s balance.
10 Application Development Guide
Certain SQL statements must appear at the beginning and end of the program
to handle the transition from the host language to the embedded SQL
statements.
The beginning of every program must contain:
v Declarations of all variables and data structures that the database manager
uses to interact with the host program
v SQL statements that provide for error handling by setting up the SQL
Communications Area (SQLCA)
Note that DB2 applications written in Java throw an SQLException, which
you handle in a catch block, rather than using the SQLCA.
The body of every program contains the SQL statements that access and
manage data. These statements constitute transactions. Transactions must
include the following statements:
v The CONNECT statement, which establishes a connection to a database
server
v One or more:
– Data manipulation statements (for example, the SELECT statement)
– Data definition statements (for example, the CREATE statement)
– Data control statements (for example, the GRANT statement)
v Either the COMMIT or ROLLBACK statement to end the transaction
The end of the application program typically contains SQL statements that:
v Release the program’s connection to the database server
v Clean up any resource
Declaring and Initializing Variables
To code a DB2 application, you must first declare:
v the variables that interact with the database manager
v the SQLCA, if applicable
Declaring Variables that Interact with the Database Manager
All variables that interact with the database manager must be declared in an
SQL declare section. You must code an SQL declare section with the following
structure:
1. the SQL statement BEGIN DECLARE SECTION
2. a group of one or more variable declarations
3. the SQL statement END DECLARE SECTION
Host program variables declared in an SQL declare section are called host
variables. You can use host variables in host-variable references in SQL
statements. Host-variable is a tag used in syntax diagrams in the SQL Reference.
A program may contain multiple SQL declare sections.
Chapter 2. Coding a DB2 Application 11
The attributes of each host variable depend on how the variable is used in the
SQL statement. For example, variables that receive data from or store data in
DB2 tables must have data type and length attributes compatible with the
column being accessed. To determine the data type for each variable, you
must be familiar with DB2 data types, which are explained in “Data Types”
on page 77.
Declaring Variables that Represent SQL Objects: For DB2 Version 7, the
names of tables, aliases, views, and correlations have a maximum length of
128 bytes. Column names have a maximum length of 30 bytes. In DB2 Version
7, schema names have a maximum length of 30 bytes. Future releases of DB2
may increase the lengths of column names and other identifiers of SQL objects
up to 128 bytes. If you declare variables that represent SQL objects with less
than 128 byte lengths, future increases in SQL object identifier lengths may
affect the stability of your applications. For example, if you declare the
variable char[9]schema_name in a C++ application to hold a schema name,
your application functions properly for the allowed schema names in DB2
Version 6, which have a maximum length of 8 bytes.
char[9] schema_name; /* holds null-delimited schema name of up to 8 bytes;
works for DB2 Version 6, but may truncate schema names in future releases */
However, if you migrate the database to DB2 Version 7, which accepts schema
names with a maximum length of 30 bytes, your application cannot
differentiate between the schema names LONGSCHEMA1 and LONGSCHEMA2. The
database manager truncates the schema names to their 8-byte limit of
LONGSCHE, and any statement in your application that depends on
differentiating the schema names fails. To increase the longevity of your
application, declare the schema name variable with a 128-byte length as
follows:
char[129] schema_name; /* holds null-delimited schema name of up to 128 bytes
good for DB2 Version 7 and beyond */
To improve the future operation of your application, consider declaring all of
the variables in your applications that represent SQL object names with
lengths of 128 bytes. You must weigh the advantage of improved
compatibility against the increased system resources that longer variables
require.
To ease the use of this coding practice and increase the clarity of your C/C++
application code, consider using C macro expansion to declare the lengths of
these SQL object identifiers. Since the include file sql.h declares
SQL_MAX_IDENT to be 128, you can easily declare SQL object identifiers
with the SQL_MAX_IDENT macro. For example:
12 Application Development Guide
#include <sql.h>
char[SQL_MAX_IDENT+1] schema_name;
char[SQL_MAX_IDENT+1] table_name;
char[SQL_MAX_IDENT+1] employee_column;
char[SQL_MAX_IDENT+1] manager_column;
For more information on C macro expansion, see “C Macro Expansion” on
page 600.
Relating Host Variables to an SQL Statement: You can use host variables to
receive data from the database manager or to transfer data to it from the host
program. Host variables that receive data from the database manager are
output host variables, while those that transfer data to it from the host program
are input host variables.
Consider the following SELECT INTO statement:
SELECT HIREDATE, EDLEVEL
INTO :hdate, :lvl
FROM EMPLOYEE
WHERE EMPNO = :idno
It contains two output host variables, hdate and lvl, and one input host
variable, idno. The database manager uses the data stored in the host variable
idno to determine the EMPNO of the row that is retrieved from the
EMPLOYEE table. If the database manager finds a row that meets the search
criteria, hdate and lvl receive the data stored in the columns HIREDATE and
EDLEVEL, respectively. This statement illustrates an interaction between the
host program and the database manager using columns of the EMPLOYEE
table.
Each column of a table is assigned a data type in the CREATE TABLE
definition. You must relate this data type to the host language data type
defined in the Supported SQL Data Types section of each language-specific
chapter in this document. For example, the INTEGER data type is a 32-bit
signed integer. This is equivalent to the following data description entries in
each of the host languages, respectively:
C/C++:
sqlint32 variable_name;
Java: int variable_name;
COBOL:
01 variable-name PICTURE S9(9) COMPUTATIONAL-5.
FORTRAN:
INTEGER*4 variable_name
Chapter 2. Coding a DB2 Application 13
For the list of supported SQL data types and the corresponding host language
data types, see the following:
v for C/C++, “Supported SQL Data Types in C and C++” on page 615
v for Java, “Supported SQL Data Types in Java” on page 625
v for COBOL, “Supported SQL Data Types in COBOL” on page 681
v for FORTRAN, “Supported SQL Data Types in FORTRAN” on page 698
v for REXX, “Supported SQL Data Types in REXX” on page 712
In order to determine exactly how to define the host variable for use with a
column, you need to find out the SQL data type for that column. Do this by
querying the system catalog, which is a set of views containing information
about all tables created in the database. The SQL Reference describes this
catalog.
After you have determined the data types, you can refer to the conversion
charts in the host language chapters and code the appropriate declarations.
The Declaration Generator utility (db2dclgn) is also available for generating
the appropriate declarations for a given table in a database. For more
information on db2dclgn, see “Declaration Generator - db2dclgn” on page 73
and refer to the Command Reference.
Table 4 on page 74 shows examples of declarations in the supported host
languages. Note that REXX applications do not need to declare host variables
except for LOB locators and file reference variables. The contents of the
variable determine other host variable data types and sizes at run time.
Table 4 also shows the BEGIN and END DECLARE SECTION statements.
Observe how the delimiters for SQL statements differ for each language. For
the exact rules of placement, continuation, and delimiting of these statements,
see the language-specific chapters of this book.
Handling Errors and Warnings
The SQL Communications Area (SQLCA) is discussed in detail later in this
chapter. This section presents an overview. To declare the SQLCA, code the
INCLUDE SQLCA statement in your program.
For C or C++ applications use:
EXEC SQL INCLUDE SQLCA;
For Java applications: You do not explicitly use the SQLCA in Java. Instead,
use the SQLException instance methods to get the SQLSTATE and SQLCODE
values. See “SQLSTATE and SQLCODE Values in Java” on page 627 for more
details.
For COBOL applications use:
EXEC SQL INCLUDE SQLCA END-EXEC.
14 Application Development Guide
For FORTRAN applications use:
EXEC SQL INCLUDE SQLCA
When you preprocess your program, the database manager inserts host
language variable declarations in place of the INCLUDE SQLCA statement.
The system communicates with your program using the variables for warning
flags, error codes, and diagnostic information.
After executing each SQL statement, the system returns a return code in both
SQLCODE and SQLSTATE. SQLCODE is an integer value that summarizes
the execution of the statement, and SQLSTATE is a character field that
provides common error codes across IBM’s relational database products.
SQLSTATE also conforms to the ISO/ANS SQL92 and FIPS 127-2 standard.
Note: FIPS 127-2 refers to Federal Information Processing Standards Publication
127-2 for Database Language SQL. ISO/ANS SQL92 refers to American
National Standard Database Language SQL X3.135-1992 and International
Standard ISO/IEC 9075:1992, Database Language SQL.
Note that if SQLCODE is less than 0, it means an error has occurred and the
statement has not been processed. If the SQLCODE is greater than 0, it means
a warning has been issued, but the statement is still processed. See the
Message Reference for a listing of SQLCODE and SQLSTATE error conditions.
If you want the system to control error checking after each SQL statement, use
the WHENEVER statement.
Note: Embedded SQL for Java (SQLJ) applications cannot use the
WHENEVER statement. Use the SQLException methods described in
“SQLSTATE and SQLCODE Values in Java” on page 627 to handle
errors returned by SQL statements.
The following WHENEVER statement indicates to the system what to do
when it encounters a negative SQLCODE:
WHENEVER SQLERROR GO TO errchk
That is, whenever an SQL error occurs, program control is transferred to code
that follows the label, such as errchk. This code should include logic to
analyze the error indicators in the SQLCA. Depending upon the ERRCHK
definition, action may be taken to execute the next sequential program
instruction, to perform some special functions, or as in most situations, to roll
back the current transaction and terminate the program. See “Coding
Transactions” on page 17 for more information on a transaction and
“Diagnostic Handling and the SQLCA Structure” on page 115 for more
information about how to control error checking in your application program.
Chapter 2. Coding a DB2 Application 15
Exercise caution when using the WHENEVER SQLERROR statement. If your
application’s error handling code contains SQL statements, and if these
statements result in an error while processing the original error, your
application may enter an infinite loop. This situation is difficult to
troubleshoot. The first statement in the destination of a WHENEVER
SQLERROR should be WHENEVER SQLERROR CONTINUE. This statement
resets the error handler. After this statement, you can safely use SQL
statements.
For a DB2 application written in C or C++, if the application is made up of
multiple source files, only one of the files should include the EXEC SQL
INCLUDE SQLCA statement to avoid multiple definitions of the SQLCA. The
remaining source files should use the following lines:
#include "sqlca.h"
extern struct sqlca sqlca;
If your application must be compliant with the ISO/ANS SQL92 or FIPS 127-2
standard, do not use the above statements or the INCLUDE SQLCA statement.
For more information on the ISO/ANS SQL92 and FIPS 127-2 standards, see
“Definition of FIPS 127-2 and ISO/ANS SQL92” on page 15. For the
alternative to coding the above statements, see the following:
v For C or C++ applications, see “SQLSTATE and SQLCODE Variables in C
and C++” on page 620
v For COBOL applications, “SQLSTATE and SQLCODE Variables in COBOL”
on page 685
v For FORTRAN applications, “SQLSTATE and SQLCODE Variables in
FORTRAN” on page 700
Using Additional Nonexecutable Statements
Generally, other nonexecutable SQL statements are also part of this section of
the program. Both the SQL Reference and subsequent chapters of this manual
discuss nonexecutable statements. Examples of nonexecutable statements are:
v INCLUDE text-file-name
v INCLUDE SQLDA
v DECLARE CURSOR
Connecting to the Database Server
Your program must establish a connection to the target database server before
it can run any executable SQL statements. This connection identifies both the
authorization ID of the user who is running the program, and the name of the
database server on which the program is run. Generally, your application
process can only connect to one database server at a time. This server is called
the current server. However, your application can connect to multiple database
servers within a multisite update environment. In this case, only one server
can be the current server. For more information on multisite updates, see
“Multisite Update” on page 525.
16 Application Development Guide
Your program can establish a connection to a database server either:
v explicitly, using the CONNECT statement
v implicitly, connecting to the default database server
v for Java applications, through a Connection instance
Refer to the SQL Reference for a discussion of connection states and how to use
the CONNECT statement. Upon initialization, the application requester
establishes a default database server. If implicit connects are enabled,
application processes started after initialization connect implicitly to the
default database server. It is good practice to use the CONNECT statement as
the first SQL statement executed by an application program. This avoids
accidentally executing SQL statements against the default database.
After the connection has been established, your program can issue SQL
statements that:
v Manipulate data
v Define and maintain database objects
v Initiate control operations, such as granting user authority, or committing
changes to the database
A connection lasts until a CONNECT RESET, CONNECT TO, or
DISCONNECT statement is issued. In a multisite update environment, a
connection also lasts until a DB2 RELEASE then DB2 COMMIT is issued. A
CONNECT TO statement does not terminate a connection when using
multisite update (see “Multisite Update” on page 525).
Coding Transactions
A transaction is a sequence of SQL statements (possibly with intervening host
language code) that the database manager treats as a whole. An alternative
term that is often used for transaction is unit of work.
To ensure the consistency of data at the transaction level, the system makes
sure that either all operations within a transaction are completed, or none are
completed. Suppose, for example, that the program is supposed to deduct
money from one account and add it to another. If you place both of these
updates in a single transaction, and a system failure occurs while they are in
progress, then when you restart the system, the database manager
automatically restores the data to the state it was in before the transaction
began. If a program error occurs, the database manager restores all changes
made by the statement in error. The database manager will not undo work
performed in the transaction prior to execution of the statement in error,
unless you specifically roll it back.
You can code one or more transactions within a single application program,
and it is possible to access more than one database from within a single
transaction. A transaction that accesses more than one database is called a
Chapter 2. Coding a DB2 Application 17
multisite update. For information on these topics, see “Remote Unit of Work”
on page 525 and “Multisite Update” on page 525.
Beginning a Transaction
A transaction begins implicitly with the first executable SQL statement and
ends with either a COMMIT or a ROLLBACK statement, or when the
program ends.
In contrast, the following six statements do not start a transaction because
they are not executable statements:
BEGIN DECLARE SECTION INCLUDE SQLCA
END DECLARE SECTION INCLUDE SQLDA
DECLARE CURSOR WHENEVER
An executable SQL statement always occurs within a transaction. If a program
contains an executable SQL statement after a transaction ends, it automatically
starts a new transaction.
Ending a Transaction
To end a transaction, you can use either:
v The COMMIT statement to save its changes
v The ROLLBACK statement to ensure that these changes are not saved
Using the COMMIT Statement: This statement ends the current transaction.
It makes the database changes performed during the current transaction
visible to other processes.
You should commit changes as soon as application requirements permit. In
particular, write your programs so that uncommitted changes are not held
while waiting for input from a terminal, as this can result in database
resources being held for a long time. Holding these resources prevents other
applications that need these resources from running.
The COMMIT statement has no effect on the contents of host variables.
Your application programs should explicitly end any transactions prior to
terminating. If you do not end transactions explicitly, DB2 automatically
commits all the changes made during the program’s pending transaction
when the program ends successfully, except on Windows 32-bit operating
systems. DB2 rolls back the changes under the following conditions:
v A log full condition
v Any other system condition that causes database manager processing to
end
On Windows 32-bit operating systems, if you do not explicitly commit the
transaction, the database manager always rolls back the changes.
18 Application Development Guide
For more information about program termination, see “Ending the Program”
and “Diagnostic Handling and the SQLCA Structure” on page 115.
Using the ROLLBACK Statement: This statement ends the current
transaction, and restores the data to the state it was in prior to beginning the
transaction.
The ROLLBACK statement has no effect on the contents of host variables.
If you use a ROLLBACK statement in a routine that was entered because of
an error or warning and you use the SQL WHENEVER statement, then you
should specify WHENEVER SQLERROR CONTINUE and WHENEVER
SQLWARNING CONTINUE before the ROLLBACK. This avoids a program
loop if the ROLLBACK fails with an error or warning.
In the event of a severe error, you will receive a message indicating that you
cannot issue a ROLLBACK statement. Do not issue a ROLLBACK statement if
a severe error occurs such as the loss of communications between the client
and server applications, or if the database gets corrupted. After a severe error,
the only statement you can issue is a CONNECT statement.
Ending the Program
To properly end your program:
1. End the current transaction (if one is in progress) by explicitly issuing
either a COMMIT statement or a ROLLBACK statement.
2. Release your connection to the database server by using the CONNECT
RESET statement.
3. Clean up resources used by the program. For example, free any temporary
storage or data structures that are used.
Note: If the current transaction is still active when the program terminates,
DB2 implicitly ends the transaction. Since DB2’s behavior when it
implicitly ends a transaction is platform specific, you should explicitly
end all transactions by issuing a COMMIT or a ROLLBACK statement
before the program terminates. See Implicitly Ending a Transaction for
details on how DB2 implicitly ends a transaction.
Implicitly Ending a Transaction
If your program terminates without ending the current transaction, DB2
implicitly ends the current transaction (see “Ending the Program” for details
on how to properly end your program). DB2 implicitly terminates the current
transaction by issuing either a COMMIT or a ROLLBACK statement when the
application ends. Whether DB2 issues a COMMIT or ROLLBACK depends on
factors such as:
v Whether the application terminated normally
v The platform on which the DB2 server runs
Chapter 2. Coding a DB2 Application 19
v Whether the application uses the context APIs (see “Multiple Thread
Database Access” on page 533)
On Most Supported Operating Systems
DB2 implicitly commits a transaction if the termination is normal, or implicitly
rolls back the transaction if it is abnormal. Note that what your program
considers to be an abnormal termination may not be considered abnormal by
the database manager. For example, you may code exit(-16) when your
application encounters an unexpected error and terminate your application
abruptly. The database manager considers this to be a normal termination and
commits the transaction. The database manager considers items such as an
exception or a segmentation violation as abnormal terminations.
On Windows 32-bit Operating Systems
DB2 always rolls back the transaction regardless of whether your application
terminates normally or abnormally, unless you explicitly commit the
transaction using the COMMIT statement.
When Using the DB2 Context APIs
Your application can use any of the DB2 APIs to set up and pass application
contexts between threads as described in “Multiple Thread Database Access”
on page 533. If your application uses these DB2 APIs, DB2 implicitly rolls
back the transaction regardless of whether your application terminates
normally or abnormally. Unless you explicitly commit the transaction using
the COMMIT statement, DB2 rolls back the transaction.
Application Pseudocode Framework
Pseudocode Framework for Coding Programs summarizes the general
framework for a DB2 application program in pseudocode format. You must, of
course, tailor this framework to suit your own program.
Start Program
EXEC SQL BEGIN DECLARE SECTION |
DECLARE USERID FIXED CHARACTER (8) |
DECLARE PW FIXED CHARACTER (8) |
| Application
(other host variable declarations) | Setup
|
EXEC SQL END DECLARE SECTION |
EXEC SQL INCLUDE SQLCA |
EXEC SQL WHENEVER SQLERROR GOTO ERRCHK |
(program logic)
EXEC SQL CONNECT TO database A USER :userid USING :pw |
EXEC SQL SELECT ... |
EXEC SQL INSERT ... | First Unit
(more SQL statements) | of Work
EXEC SQL COMMIT |
(more program logic)
20 Application Development Guide
EXEC SQL CONNECT TO database B USER :userid USING :pw |
EXEC SQL SELECT ... |
EXEC SQL DELETE ... | Second Unit
(more SQL statements) | of Work
EXEC SQL COMMIT |
(more program logic)
EXEC SQL CONNECT TO database A |
EXEC SQL SELECT ... |
EXEC SQL DELETE ... | Third Unit
(more SQL statements) | of Work
EXEC SQL COMMIT |
(more program logic)
EXEC SQL CONNECT RESET |
ERRCHK |
| Application
(check error information in SQLCA) | Cleanup
|
End Program
Designing an Application For DB2
DB2 provides you with a variety of application development capabilities that
you can use to supplement or extend the traditional capabilities of an
application. As an application designer, you must make the most fundamental
design decision: Which DB2 capabilities should I use in the design of my
application? In order to make appropriate choices, you need to consider both
the database design and target environments for your application. For
example, you can choose to enforce some business rules in your database
design instead of including the logic in your application.
The capabilities you use and the extent to which you use them can vary
greatly. This section is an overview of the capabilities available that can
significantly affect your design and provides some reasons for why you might
choose one over another. For more information and detail on any of the
capabilities described, a reference to more detail is provided.
The capabilities that you need to consider include:
v Accessing the data using:
– Embedded SQL, including embedded SQLJ for Java (SQLJ)
– DB2 Call Level Interface (DB2 CLI), Open Database Connectivity
(ODBC), and Java Database Connectivity (JDBC)
– Microsoft Specifications
– Perl DBI
– Query products
Chapter 2. Coding a DB2 Application 21
v Controlling data values using:
– Data types (built-in or user-defined)
– Table check constraints
– Referential integrity constraints
– Views using the CHECK OPTION
– Application logic and variable types
v Controlling the relationship between data values using:
– Referential integrity constraints
– Triggers
– Application logic
v Executing programs at the server using:
– Stored procedures
– User-defined functions
– Triggers
You will notice that this list mentions some capabilities more than once, such
as triggers. This reflects the flexibility of these capabilities to address more
than one design criteria.
Your first and most fundamental decision is whether or not to move the logic
to enforce application related rules about the data into the database.
The key advantage in transferring logic focussed on the data from the
application into the database is that your application becomes more
independent of the data. The logic surrounding your data is centralized in one
place, the database. This means that you can change data or data logic once
and affect all applications immediately.
This latter advantage is very powerful, but you must also consider that any
data logic put into the database affects all users of the data equally. You must
consider whether the rules and constraints that you wish to impose on the
data apply to all users of the data or just the users of your application.
Your application requirements may also affect whether to enforce rules at the
database or the application. For example, you may need to process validation
errors on data entry in a specific order. In general, you should do these types
of data validation in the application code.
You should also consider the computing environment where the application is
used. You need to consider the difference between performing logic on the
client machines against running the logic on the usually more powerful
database server machines using either stored procedures, UDFs, or a
combination of both.
22 Application Development Guide
In some cases, the correct answer is to include the enforcement in both the
application (perhaps due to application specific requirements) and in the
database (perhaps due to other interactive uses outside the application).
Access to Data
In a relational database, you must use SQL to access the desired data, but you
may choose how to integrate the SQL into your application. You can choose
from the following interfaces and their supported languages:
Embedded SQL
C/C++, COBOL, FORTRAN, Java (SQLJ), REXX
DB2 CLI and ODBC
C/C++, Java (JDBC)
Microsoft Specifications, including ADO, RDO, and OLE DB
Visual Basic, Visual C++
Perl DBI
Perl
Query Products
Lotus Approach, IBM Query Management Facility
Embedded SQL
Embedded SQL has the advantage that it can consist of either static or
dynamic SQL or a mixture of both types. If the content and format of your
SQL statements will be frozen when your application is in use, you should
consider using embedded static SQL in your application. With static SQL, the
person who executes the application temporarily inherit the privileges of the
user that bound the application to the database. Unless you bind the
application with the DYNAMICRULES BIND option, dynamic SQL uses the
privileges of the person who executes the application. In general, you should
use embedded dynamic SQL where the executable statements are determined
at run time. This creates a more secure application program that can handle a
greater variety of input.
Note: Embedded SQL for Java (SQLJ) applications can only embed static SQL
statements. However, you can use JDBC to make dynamic SQL calls in
SQLJ applications.
You must precompile embedded SQL applications to convert the SQL
statements into host language commands before using your programming
language compiler. In addition, you must bind the SQL in the application to
the database for the application to run.
For additional information on using embedded SQL, refer to “Chapter 4.
Writing Static SQL Programs” on page 61.
Chapter 2. Coding a DB2 Application 23
REXX Considerations: REXX applications use APIs which enable them to use
most of the features provided by database manager APIs and SQL. Unlike
applications written in a compiled language, REXX applications are not
precompiled. Instead, a dynamic SQL handler processes all SQL statements.
By combining REXX with these callable APIs, you have access to most of the
database manager capabilities. Although REXX does not directly support some
APIs using embedded SQL, they can be accessed using the DB2 Command
Line Processor from within the REXX application.
As REXX is an interpretive language, you may find it is easier to develop and
debug your application prototypes in REXX as compared to compiled host
languages. Note that while DB2 applications coded in REXX do not provide
the performance of DB2 applications that use compiled languages, they do
provide the ability to create DB2 applications without precompiling,
compiling, linking, or using additional software.
For details of coding and building DB2 applications using REXX, see
“Chapter 25. Programming in REXX” on page 703.
DB2 Call Level Interface (DB2 CLI) and Open Database Connectivity
(ODBC)
The DB2 Call Level Interface (DB2 CLI) is IBM’s callable SQL interface to the
DB2 family of database servers. It is a C and C++ application programming
interface for relational database access, and it uses function calls to pass
dynamic SQL statements as function arguments. A callable SQL interface is an
application program interface (API) for database access, which uses function
calls to invoke dynamic SQL statements. It is an alternative to embedded
dynamic SQL, but unlike embedded SQL, it does not require precompiling or
binding.
DB2 CLI is based on the Microsoft™ Open Database Connectivity (ODBC)
specification, and the X/Open® specifications. IBM chose these specifications
to follow industry standards, and to provide a shorter learning curve for DB2
application programmers who are familiar with either of these database
interfaces.
For more information on the ODBC support in DB2, see the CLI Guide and
Reference.
JDBC
DB2’s Java support includes JDBC, a vendor-neutral dynamic SQL interface
that provides data access to your application through standardized Java
methods. JDBC is similar to DB2 CLI in that you do not have to precompile or
bind a JDBC program. As a vendor-neutral standard, JDBC applications offer
increased portability.
24 Application Development Guide
An application written using JDBC uses only dynamic SQL. The JDBC
interface imposes additional processing overhead.
For additional information on JDBC, refer to “JDBC Programming” on
page 630.
Microsoft Specifications
You can write database applications that conform to the ActiveX Data Object
(ADO) in Microsoft Visual Basic™ or Visual C++™. ADO applications use the
OLE DB Bridge. You can write database applications that conform to the
Remote Data Object (RDO) specifications in Visual Basic. You can also define
OLE DB table functions that return data from OLE DB providers. For more
information on OLE DB table functions, see “OLE DB Table Functions” on
page 423.
This book does not attempt to provide a tutorial on writing applications that
conform to the ADO and RDO specifications. For full samples of DB2
applications that use the ADO and RDO specifications, refer to the following
directories:
v For samples written in Visual Basic, refer to sqllib\samples\VB
v For samples written in Visual C++, refer to sqllib\samples\VC
v For samples that use the RDO specification, refer to sqllib\samples\RDO
v For samples that use the Microsoft Transaction Server™, refer to
sqllib\samples\MTS
Perl DBI
DB2 supports the Perl Database Interface (DBI) specification for data access
through the DBD::DB2 driver. For more information on creating appliations
with the Perl DBI that access DB2 databases, see “Chapter 22. Programming in
Perl” on page 661. The DB2 Universal Database Perl DBI Web site at
http://www.ibm.com/software/data/db2/perl/ contains the latest DBD::DB2
driver and information on the support available for your platform.
Query Products
Query products including IBM Query Management Facility (QMF) and Lotus
Notes support query development and reporting. The products vary in how
SQL statements are developed and the degree of logic that can be introduced.
Depending on your needs, this approach may meet your requirements to
access data. This book does not provide further information on query
products.
Data Value Control
One traditional area of application logic is validating and protecting data
integrity by controlling the values allowed in the database. Applications have
logic that specifically checks data values as they are entered for validity. (For
example, checking that the department number is a valid number and that it
Chapter 2. Coding a DB2 Application 25
refers to an existing department.) There are several different ways of
providing these same capabilities in DB2, but from within the database.
Data Types
The database stores every data element in a column of a table, and defines
each column with a data type. This data type places certain limits on the
types of values for the column. For example, an integer must be a number
within a fixed range. The use of the column in SQL statements must conform
to certain behaviors; for instance, the database does not compare an integer to
a character string. DB2 includes a set of built-in data types with defined
characteristics and behaviors. DB2 also supports defining your own data
types, called user-defined distinct types, that are based on the built-in types but
do not automatically support all the behaviors of the built-in type. You can
also use data types, like binary large object (BLOB), to store data that may
consist of a set of related values, such as a data structure.
For additional information on data types, refer to the SQL Reference.
Unique Constraints
Unique constraints prevent occurrences of duplicate values in one or more
columns within a table. Unique and primary keys are the supported unique
constraints. For example, you can define a unique constraint on the DEPTNO
column in the DEPARTMENT table to ensure that the same department
number is not given to two departments.
Use unique constraints if you need to enforce a uniqueness rule for all
applications that use the data in a table. For additional information on unique
constraints, refer to the SQL Reference.
Table Check Constraints
You can use a table check constraint to define restrictions, beyond those of the
data type, on the values that are allowed for a column in the table. Table
check constraints take the form of range checks or checks against other values
in the same row of the same table.
If the rule applies for all applications that use the data, use a table check
constraint to enforce your restriction on the data allowed in the table. Table
check constraints make the restriction generally applicable and easier to
maintain.
For additional information on table check constraints, refer to the SQL
Reference.
Referential Integrity Constraints
Use referential integrity (RI) constraints if you must maintain value-based
relationships for all applications that use the data. For example, you can use
an RI constraint to ensure that the value of a DEPTNO column in an
26 Application Development Guide
EMPLOYEE table matches a value in the DEPARTMENT table. This constraint
prevents inserts, updates or deletes that would otherwise result in missing
DEPARTMENT information. By centralizing your rules in the database, RI
constraints make the rules generally applicable and easier to maintain.
See “Data Relationship Control” for further uses of RI constraints.
For additional information on referential integrity, refer to the SQL Reference.
Views with Check Option
If your application cannot define the desired rules as table check constraints,
or the rules do not apply to all uses of the data, there is another alternative to
placing the rules in the application logic. You can consider creating a view of
the table with the conditions on the data as part of the WHERE clause and the
WITH CHECK OPTION clause specified. This view definition restricts the
retrieval of data to the set that is valid for your application. Additionally, if
you can update the view, the WITH CHECK OPTION clause restricts updates,
inserts, and deletes to the rows applicable to your application.
For additional information on the WITH CHECK OPTION, refer to the SQL
Reference.
Application Logic and Program Variable Types
When you write your application logic in a programming language, you also
declare variables to provide some of the same restrictions on data that are
described above. In addition, you can choose to write code to enforce rules in
the application instead of the database. Place the logic in the application
server when:
v The rules are not generally applicable, except in the case of views noted in
“Views with Check Option”
v You do not have control over the definitions of the data in the database
v You believe the rule can be more effectively handled in the application logic
For example, processing errors on input data in the order that they are
entered may be required, but cannot be guaranteed from the order of
operations within the database.
Data Relationship Control
Another major area of focus in application logic is in the area of managing the
relationships between different logical entities in your system. For example, if
you add a new department, then you need to create a new account code. DB2
provides two methods of managing the relationships between different objects
in your database: referential integrity constraints and triggers.
Chapter 2. Coding a DB2 Application 27
Referential Integrity Constraints
Referential integrity (RI) constraints, considered from the perspective of data
relationship control, allow you to control the relationships between data in
more than one table. Use the CREATE TABLE or ALTER TABLE statements to
define the behavior of operations that affect the related primary key, such as
DELETE and UPDATE.
RI constraints enforce your rules on the data across one or more tables. If the
rules apply for all applications that use the data, then RI constraints centralize
the rules in the database. This makes the rules generally applicable and easier
to maintain.
For additional information on referential integrity, refer to the SQL Reference.
Triggers
You can use triggers before or after an update to support logic that can also
be performed in an application. If the rules or operations supported by the
triggers apply for all applications that use the data, then triggers centralize the
rules or operations in the database, making it generally applicable and easier
to maintain.
For additional information on triggers, see “Chapter 16. Using Triggers in an
Active DBMS” on page 473 and refer to the SQL Reference.
Using Triggers Before an Update: Using triggers that run before an update
or insert, values that are being updated or inserted can be modified before the
database is actually modified. These can be used to transform input from the
application (user view of the data) to an internal database format where
desired. These before triggers can also be used to cause other non-database
operations to be activated through user-defined functions.
Using Triggers After an Update: Triggers that run after an update, insert or
delete can be used in several ways:
v Triggers can update, insert, or delete data in the same or other tables. This
is useful to maintain relationships between data or to keep audit trail
information.
v Triggers can check data against values of data in the rest of the table or in
other tables. This is useful when you cannot use RI constraints or check
constraints because of references to data from other rows from this or other
tables.
v Triggers can use user-defined functions to activate non-database operations.
This is useful, for example, for issuing alerts or updating information
outside the database.
28 Application Development Guide
Application Logic
You may decide to write code to enforce rules or perform related operations
in the application instead of the database. You must do this for cases where
you cannot generally apply the rules to the database. You may also choose to
place the logic in the application when you do not have control over the
definitions of the data in the database or you believe the application logic can
handle the rules or operations more efficiently.
Application Logic at the Server
A final aspect of application design for which DB2 offers additional capability
is running some of your application logic at the database server. Usually you
will choose this design to improve performance, but you may also run
application logic at the server to support common functions.
Stored Procedures
A stored procedure is a routine for your application that is called from client
application logic but runs on the database server. The most common reason to
use a stored procedure is for database intensive processing that produces only
small amounts of result data. This can save a large amount of communications
across the network during the execution of the stored procedure. You may
also consider using a stored procedure for a set of operations that are
common to multiple applications. In this way, all the applications use the
same logic to perform the operation.
For additional information on Stored Procedures, refer to “Chapter 7. Stored
Procedures” on page 187.
User-Defined Functions
You can write a user-defined function (UDF) for use in performing operations
within an SQL statement to return:
v A single scalar value (scalar function)
v A table from a non-DB2 data source, for example, an ASCII file or a Web
page (table function)
A UDF cannot contain SQL statements. UDFs are useful for tasks like
transforming data values, performing calculations on one or more data values,
or extracting parts of a value (such as extracting parts of a large object).
For additional information on writing user-defined functions, refer to
“Chapter 15. Writing User-Defined Functions (UDFs) and Methods” on
page 385.
Triggers
In “Triggers” on page 28, it is noted that triggers can be used to invoke
user-defined functions. This is useful when you always want a certain
non-SQL operation performed when specific statements occur, or data values
Chapter 2. Coding a DB2 Application 29
are changed. Examples include such operations as issuing an electronic mail
message under specific circumstances or writing alert type information to a
file.
For additional information on triggers, refer to “Chapter 16. Using Triggers in
an Active DBMS” on page 473.
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++ is a
collection of management tools and wizards that plug into the Visual C++
component of Visual Studio IDE. The tools and wizards automate and
simplify the various tasks involved in developing applications for DB2 using
embedded SQL.
You can use the IBM DB2 Universal Database Project Add-In for Microsoft
Visual C++ to develop, package, and deploy:
v Stored procedures written in C/C++ for DB2 Universal Database on
Windows 32-bit operating systems
v Windows 32-bit C/C++ embedded SQL client applications that access DB2
Universal Database servers
v Windows 32-bit C/C++ client applications that invoke stored procedures
using C/C++ function call wrappers
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++
allows you to focus on the design and logic of your DB2 applications rather
than the actual building and deployment of it.
Some of the tasks performed by the IBM DB2 Universal Database Project
Add-In for Microsoft Visual C++ include:
v Creating a new embedded SQL module
v Inserting SQL statements into an embedded SQL module using SQL Assist
v Adding imported stored procedures
v Creating an exported stored procedure
v Packaging the DB2 Project
v Deploying the DB2 project from within Visual C++
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++ is
presented in the form of a toolbar. The toolbar buttons include:
DB2 Project Properties
Manages the project properties (development database and
code-generation options)
New DB2 Object
Adds a new embedded SQL module, imported stored procedure, or
exported stored procedure
30 Application Development Guide
DB2 Embedded SQL Modules
Manages the list of embedded SQL modules and their precompiler
options
DB2 Imported Stored Procedures
Manages the list of imported stored procedures
DB2 Exported Stored Procedures
Manages the list of exported stored procedures
Package DB2 Project
Packages the DB2 external project files
Deploy DB2 Project
Deploys the packaged DB2 external project files
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++
also has the following three hidden buttons that can be made visible using the
standard Visual C++ tools customization options:
New DB2 Embedded SQL Module
Adds a new C/C++ embedded SQL module
New DB2 Imported Stored Procedure
Imports a new database stored procedure
New DB2 Exported Stored Procedure
Exports a new database stored procedure
The IBM DB2 Universal Database Project Add-In for Microsoft Visual C++ can
automatically generate the following code elements:
v Skeletal embedded SQL module files with optional sample SQL statements
v Standard database connect and disconnect embedded SQL functions
v Imported stored procedure call wrapper functions
v Exported stored procedure function templates
v Exported stored procedure data definition language (DDL) files
Terminology associated with the IBM DB2 Universal Database Project
Add-In for Microsoft Visual C++:
IDE project
The standard Visual C++ project
DB2 project
The collection of DB2 project objects that are inserted into the IDE
project. DB2 project objects can be inserted into any Visual C++
project. The DB2 project allows you to manage the various DB2
Chapter 2. Coding a DB2 Application 31
objects such as embedded SQL modules, imported stored procedures,
and exported stored procedures. You can add, delete, and modify
these objects and their properties.
module
A C/C++ source code file that might contain SQL statements.
development database
The database that is used to compile embedded SQL modules. The
development database is also used to look up the list of importable
database stored procedure definitions.
embedded SQL module
A C/C++ source code file that contains embedded static or dynamic
SQL.
imported stored procedure
A stored procedure, already defined in the database, that the project
invokes.
exported stored procedure
A database stored procedure that is built and defined by the project.
Activating the IBM DB2 Universal Database Project Add-In for Microsoft
Visual C++
To activate the IBM DB2 Universal Database Project Add-In for Microsoft
Visual C++, perform the following steps:
Step 1. Register the add-in, if you have not already done so, by entering:
db2vccmd register
on the command line.
Step 2. Select Tools —> Customize. The Customize notebook opens.
Step 3. Select the Add-ins and Macro Files tab. The Add-ins and Macro Files
page opens.
Step 4. Select the IBM DB2 Project Add-In check box.
Step 5. Click OK. A floating toolbar will be created.
Note: If the toolbar is accidentally closed, you can either deactivate then
reactivate the add-in or use the Microsoft Visual C++ standard
customization options to redisplay the toolbar.
Activating the IBM DB2 Universal Database Tools Add-In for Microsoft
Visual C++
The DB2 Tools Add-In is a toolbar that enables the launch of some of the DB2
administration and development tools from within the Visual C++ integrated
development environment.
32 Application Development Guide
To activate the IBM DB2 Universal Database Tools Add-In for Microsoft Visual
C++, perform the following steps:
Step 1. Register the add-in, if you have not already done so, by entering:
db2vccmd register
on the command line.
Step 2. Select Tools —> Customize. The Customize notebook opens.
Step 3. Select the Add-ins and Macro Files tab.
Step 4. Select the IBM DB2 Tools Add-In check box.
Step 5. Click OK. A floating toolbar will be created.
Note: If the toolbar is accidentally closed, you can either deactivate then
reactivate the add-in or use the Visual C++ standard customization
options to redisplay the toolbar.
For more information on the IBM DB2 Universal Database Project Add-In for
Microsoft Visual C++, refer to:
v The online help for the IBM DB2 Universal Database Project Add-In for
Microsoft Visual C++.
v http://www.ibm.com/software/data/db2/udb/ide/index.html.
Supported SQL Statements
The SQL language provides for data definition, retrieval, update, and control
operations from within an application. Table 37 on page 723 shows the SQL
statements supported by the DB2 product and whether the statement is
supported dynamically, through the CLP, or through the DB2 CLI. You can
use Table 37 on page 723 as a quick reference aid. For a complete discussion of
all the statements, including their syntax, refer to the SQL Reference.
Authorization Considerations
An authorization allows a user or group to perform a general task such as
connecting to a database, creating tables, or administering a system. A privilege
gives a user or group the right to access one specific database object in a
specified way. DB2 uses a set of privileges to provide protection for the
information that you store in it. For more information about the different
privileges, refer to the Administration Guide: Planning.
Most SQL statements require some type of privilege on the database objects
which the statement utilizes. Most API calls usually do not require any
privilege on the database objects which the call utilizes, however, many APIs
require that you possess the necessary authority in order to invoke them. The
DB2 APIs enable you to perform the DB2 administrative functions from
Chapter 2. Coding a DB2 Application 33
within your application program. For example, to recreate a package stored in
the database without the need for a bind file, you can use the sqlarbnd (or
REBIND) API. For details on each DB2 API, refer to the Administrative API
Reference.
For information on the required privilege to issue each SQL statement, refer to
the SQL Reference. For information on the required privilege and authority to
issue each API call, refer to the Administrative API Reference.
When you design your application, consider the privileges your users will
need to run the application. The privileges required by your users depend on:
v whether your application uses dynamic SQL, including JDBC and DB2 CLI,
or static SQL
v which APIs the application uses
Dynamic SQL
To use dynamic SQL in a package bound with DYNAMICRULES RUN
(default), the person that runs a dynamic SQL application must have the
privileges necessary to issue each SQL request performed, as well as the
EXECUTE privilege on the package. The privileges may be granted to the
user’s authorization ID, to any group of which the user is a member, or to
PUBLIC.
If you bind the application with the DYNAMICRULES BIND option, DB2
associates your authorization ID with the application packages. This allows
any user that runs the application to inherit the privileges associated your
authorization ID.
The person binding the application (for embedded dynamic SQL applications)
only needs the BINDADD authority on the database, if the program contains
no static SQL. Again, this privilege can be granted to the user’s authorization
ID, to a group of which the user is a member, or to PUBLIC.
When you bind a dynamic SQL package with the DYNAMICRULES BIND
option, the user that runs the application only needs the EXECUTE privilege
on the package. To bind a dynamic SQL application with the
DYNAMICRULES BIND option, you must have the privileges necessary to
perform all the dynamic and static SQL statements in the application. If you
have SYSADM or DBADM authority and bind packages with
DYNAMICRULES BIND, consider using the OWNER BIND option to
designate a different authorization ID. OWNER BIND prevents the package
from automatically inheriting SYSADM or DBADM privileges on dynamic
SQL statements. For more information on DYNAMICRULES BIND and
OWNER BIND, refer to the BIND command in the Command Reference.
34 Application Development Guide
Static SQL
To use static SQL, the user running the application only needs the EXECUTE
privilege on the package. No privileges are required for each of the statements
that make up the package. The EXECUTE privilege may be granted to the
user’s authorization ID, to any group of which the user is a member, or to
PUBLIC.
Unless you specify the VALIDATE RUN option when binding the application,
the authorization ID you use to bind the application must have the privileges
necessary to perform all the statements in the application. If VALIDATE RUN
was specified at BIND time, all authorization failures for any static SQL
within this package will not cause the BIND to fail and those statements will
be revalidated at run time. The person binding the application must always
have BINDADD authority. The privileges needed to execute the statements
must be granted to the user’s authorization ID or to PUBLIC. Group
privileges are not used when binding static SQL statements. As with dynamic
SQL, the BINDADD privilege can be granted to the user authorization ID, to a
group of which the user is a member, or to PUBLIC.
These properties of static SQL give you very precise control over access to
information in DB2. See the example at the end of this section for a possible
application of this.
Using APIs
Most of the APIs provided by DB2 do not require the use of privileges,
however, many do require some kind of authority to invoke. For the APIs that
do require a privilege, the privilege must be granted to the user running the
application. The privilege may be granted to the user’s authorization ID, to
any group of which the user is a member, or to PUBLIC. For information on
the required privilege and authority to issue each API call, see the
Administrative API Reference.
Example
Consider two users, PAYROLL and BUDGET, who need to perform queries
against the STAFF table. PAYROLL is responsible for paying the employees of
the company, so it needs to issue a variety of SELECT statements when
issuing paychecks. PAYROLL needs to be able to access each employee’s
salary. BUDGET is responsible for determining how much money is needed to
pay the salaries. BUDGET should not, however, be able to see any particular
employee’s salary.
Since PAYROLL issues many different SELECT statements, the application you
design for PAYROLL could probably make good use of dynamic SQL. This
would require that PAYROLL have SELECT privilege on the STAFF table. This
is not a problem since PAYROLL needs full access to the table anyhow.
Chapter 2. Coding a DB2 Application 35
BUDGET, on the other hand, should not have access to each employee’s
salary. This means that you should not grant SELECT privilege on the STAFF
table to BUDGET. Since BUDGET does need access to the total of all the
salaries in the STAFF table, you could build a static SQL application to
execute a SELECT SUM(SALARY) FROM STAFF, bind the application and
grant the EXECUTE privilege on your application’s package to BUDGET. This
lets BUDGET get the needed information without exposing the information
that BUDGET should not see.
Database Manager APIs Used in Embedded SQL or DB2 CLI Programs
Your application can use APIs to access database manager facilities that are
not available using SQL statements. For complete details on the APIs available
with the database manager and how to call them, refer to the examples in the
Administrative API Reference.
You can use the DB2 APIs to:
v Manipulate the database manager environment, which includes cataloging
and uncataloging databases and nodes, and scanning database and node
directories. You can also use APIs to create, delete, and migrate databases
v Provide facilities to import and export data, and administer, backup, and
restore the database
v Manipulate the database manager configuration file and the database
configuration files
v Provide operations specific to the client/server environment
v Provide the run-time interface for precompiled SQL statements. These APIs
are not usually called directly by the programmer. Instead, they are inserted
into the modified source file by the precompiler after processing.
The database manager includes APIs for language vendors who want to write
their own precompiler, and other APIs useful for developing applications.
For complete details on the APIs available with the database manager and
how to call them, see the examples in the Administrative API Reference.
Setting Up the Testing Environment
In order to perform many of the tasks described in the following sections, you
should set up a test environment. For example, you need a database to test
your application’s SQL code.
A testing environment should include the following:
36 Application Development Guide
v A test database. If your application updates, inserts, or deletes data from
tables and views, use test data to verify its execution. If it only retrieves
data from tables and views, consider using production-level data when
testing it.
v Test input data. The input data used to test an application should be valid
data that represents all possible input conditions. If the application verifies
that input data is valid, include both valid and invalid data to verify that
the valid data is processed and the invalid data is flagged.
Creating a Test Database
If you must create a test database, write a small server application that calls
the CREATE DATABASE API, or use the command line processor. Refer to the
Command Reference for information about the command line processor, or the
Administrative API Reference for information about the CREATE DATABASE
API.
Creating Test Tables
To design the test tables and views needed, first analyze the data needs of the
application. To create a table, you need the CREATETAB authority and the
CREATEIN privilege on the schema. Refer to the information on the CREATE
TABLE statement in the SQL Reference for alternative authorities.
List the data the application accesses and describe how each data item is
accessed. For example, suppose the application being developed accesses the
TEST.TEMPL, TEST.TDEPT, and TEST.TPROJ tables. You could record the type
of accesses as shown in Table 1.
Table 1. Description of the Application Data
Table or View Insert Delete Column Name Data Type Update
Name Rows Rows Access
TEST.TEMPL No No EMPNO CHAR(6) Yes
LASTNAME VARCHAR(15) Yes
WORKDEPT CHAR(3) Yes
PHONENO CHAR(4)
JOBCODE DECIMAL(3)
TEST.TDEPT No No DEPTNO CHAR(3)
MGRNO CHAR(6)
TEST.TPROJ Yes Yes PROJNO CHAR(6) Yes
DEPTNO CHAR(3) Yes
RESPEMP CHAR(6) Yes
PRSTAFF DECIMAL(5,2) Yes
PRSTDATE DECIMAL(6) Yes
PRENDATE DECIMAL(6)
Chapter 2. Coding a DB2 Application 37
When the description of the application data access is complete, construct the
test tables and views that are needed to test the application:
v Create a test table when the application modifies data in a table or a view.
Create the following test tables using the CREATE TABLE SQL statement:
– TEMPL
– TPROJ
v Create a test view when the application does not modify data in the
production database.
In this example, create a test view of the TDEPT table using the CREATE
VIEW SQL statement.
If the database schema is being developed along with the application, the
definitions of the test tables might be refined repeatedly during the
development process. Usually, the primary application cannot both create the
tables and access them because the database manager cannot bind statements
that refer to tables and views that do not exist. To make the process of
creating and changing tables less time-consuming, consider developing a
separate application to create the tables. Of course you can always create test
tables interactively using the Command Line Processor (CLP).
Generating Test Data
Use any of the following methods to insert data into a table:
v INSERT...VALUES (an SQL statement) puts one or more rows into a table
each time the command is issued.
v INSERT...SELECT obtains data from an existing table (based on a SELECT
clause) and puts it into the table identified with the INSERT statement.
v The IMPORT or LOAD utility inserts large amounts of new or existing data
from a defined source.
v The RESTORE utility can be used to duplicate the contents of an existing
database into an identical test database by using a BACKUP copy of the
original database.
For information about the INSERT statement, refer to the SQL Reference. For
information about the IMPORT, LOAD, and RESTORE utilities, refer to the
Administration Guide.
The following SQL statements demonstrate a technique you can use to
populate your tables with randomly generated test data. Suppose the table
EMP contains four columns, ENO (employee number), LASTNAME (last
name), HIREDATE (date of hire) and SALARY (employee’s salary) as in the
following CREATE TABLE statement:
CREATE TABLE EMP (ENO INTEGER, LASTNAME VARCHAR(30),
HIREDATE DATE, SALARY INTEGER);
38 Application Development Guide
Suppose you want to populate this table with employee numbers from 1 to a
number, say 100, with random data for the rest of the columns. You can do
this using the following SQL statement:
INSERT INTO EMP
-- generate 100 records
WITH DT(ENO) AS (VALUES(1) UNION ALL
SELECT ENO+1 FROM DT WHERE ENO < 100 ) 1
-- Now, use the generated records in DT to create other columns
-- of the employee record.
SELECT ENO, 2
TRANSLATE(CHAR(INTEGER(RAND()*1000000)), 3
CASE MOD(ENO,4) WHEN 0 THEN 'aeiou' || 'bcdfg'
WHEN 1 THEN 'aeiou' || 'hjklm'
WHEN 2 THEN 'aeiou' || 'npqrs'
ELSE 'aeiou' || 'twxyz' END,
'1234567890') AS LASTNAME,
CURRENT DATE - (RAND()*10957) DAYS AS HIREDATE, 4
INTEGER(10000+RAND()*200000) AS SALARY 5
FROM DT;
SELECT * FROM EMP;
The following is an explanation of the above statement:
1. The first part of the INSERT statement generates 100 records for the first
100 employees using a recursive subquery to generate the employee
numbers. Each record contains the employee number. To change the
number of employees, use a number other than 100.
2. The SELECT statement generates the LASTNAME column. It begins by
generating a random integer up to 6 digits long using the RAND function.
It then converts the integer to its numeric character format using the
CHAR function.
3. To convert the numeric characters to alphabet characters, the statement
uses the TRANSLATE function to convert the ten numeric characters (0
through 9) to alphabet characters. Since there are more than 10 alphabet
characters, the statement selects from five different translations. This
results in names having enough random vowels to be pronounceable and
so the vowels are included in each translation.
4. The statement generates a random HIREDATE value. The value of
HIREDATE ranges back from the current date to 30 years ago. HIREDATE
is calculated by subtracting a random number of days between 0 and
10 957 from the current date. (10 957 is the number of days in 30 years.)
5. Finally, the statement randomly generates the SALARY. The minimum
salary is 10 000, to which a random number from 0 to 200 000 is added.
For sample programs that are helpful in generating random test data, please
see the fillcli.sqc and fillsrv.sqc sample programs in the
sqllib/samples/c subdirectory.
Chapter 2. Coding a DB2 Application 39
You may also want to consider prototyping any user-defined functions (UDF)
you are developing against the test data. For more information on why and
how you write UDFs, see “Chapter 15. Writing User-Defined Functions (UDFs)
and Methods” on page 385 and “Chapter 14. User-Defined Functions (UDFs)
and Methods” on page 365.
Running, Testing and Debugging Your Programs
The Application Building Guide tells you how to run your program in your
environment. You can do the following to help you during the testing and
debugging of your code:
v Use the same techniques discussed in “Prototyping Your SQL Statements”.
These include using the command line processor, the Explain facility,
analyzing the system catalog views for information about the tables and
databases your program is manipulating, and updating certain system
catalog statistics to simulate production conditions.
v Use the database system monitor to capture certain optimizing information
for analysis. See the System Monitor Guide and Reference.
v Use the flagger facility to check the syntax of SQL statements in
applications being developed for DB2 Universal Database for OS/390, or
for conformance to the SQL92 Entry Level standard. This facility is invoked
during precompilation. For information about how to do this, see
“Precompiling” on page 49, towards the end of the section.
v Make full use of the error-handling APIs. For example, you can use
error-handling APIs to print all messages during the testing phase. For
more information about error-handling APIs, see the Administrative API
Reference.
Prototyping Your SQL Statements
As you design and code your application, you can take advantage of certain
database manager features and utilities to prototype portions of your SQL
code, and to improve performance. For example, you can do the following:
v Use the Command Center or the command line processor (CLP) to test
many SQL statements before you attempt to compile and link a complete
program.
This allows you to define and manipulate information stored in a database
table, index, or view. You can add, delete, or update information as well as
generate reports from the contents of tables. Note that you have to
minimally change the syntax for some SQL statements in order to use host
variables in your embedded SQL program. Host variables are used to store
data that is output to your screen. In addition, some embedded SQL
statements (such as BEGIN DECLARE SECTION) are not supported by the
40 Application Development Guide
Command Center or CLP as they are not relevant to that environment. See
Table 37 on page 723 to see which SQL statements are not supported by the
CLP.
You can also redirect the input and output of command line processor
requests. For example, you could create one or more files containing SQL
statements you need as input into a command line processor request, to
save retyping the statement.
For information about the command line processor, refer to the Command
Reference. For information about the Command Center, refer to the
Administration Guide.
v Use the Explain facility to get an idea of the estimated costs of the DELETE,
INSERT, UPDATE, or SELECT statements you plan to use in your program.
The Explain facility places the information about the structure and the
estimated costs of the subject statement into user supplied tables. You can
view this information using Visual Explain or the db2exfmt utility.
For information about how to use the Explain facility, refer to the
Administration Guide: Implementation.
v Use the system catalog views to easily retrieve information about existing
databases. The database manager creates and maintains the system catalog
tables on which the views are based during normal operation as databases
are created, altered, and updated. These views contain data about each
database, including authorities granted, column names, data types, indexes,
package dependencies, referential constraints, table names, views, and so
on. Data in the system catalog views is available through normal SQL query
facilities.
You can update some system catalog views containing statistical
information used by the SQL optimizer. You may change some columns in
these views to influence the optimizer or to investigate the performance of
hypothetical databases. You can use this method to simulate a production
system on your development or test system and analyze how queries
perform.
For a complete description of each system catalog view, refer to the
appendix in the SQL Reference. For information about system catalog
statistics and which ones you can change, refer to the Administration Guide:
Implementation.
Chapter 2. Coding a DB2 Application 41
42 Application Development Guide
Part 2. Embedding SQL in Applications
© Copyright IBM Corp. 1993, 2000 43
44 Application Development Guide
Chapter 3. Embedded SQL Overview
Embedding SQL Statements in a Host Binding Dynamic Statements . . . . 54
Language . . . . . . . . . . . . . 45 Resolving Unqualified Table Names . . 54
Creating and Preparing the Source Files. . . 47 Other Binding Considerations . . . . 55
Creating Packages for Embedded SQL . . . 49 Advantages of Deferred Binding . . . . 56
Precompiling. . . . . . . . . . . 49 DB2 Bind File Description Utility - db2bfd 56
Source File Requirements . . . . . 51 Application, Bind File, and Package
Compiling and Linking . . . . . . . 52 Relationships. . . . . . . . . . . 57
Binding . . . . . . . . . . . . 53 Timestamps . . . . . . . . . . . 58
Renaming Packages . . . . . . . 53 Rebinding. . . . . . . . . . . . 58
Embedding SQL Statements in a Host Language
You can write applications with SQL statements embedded within a host
language. The SQL statements provide the database interface, while the host
language provides the remaining support needed for the application to
execute.
Table 2 shows an SQL statement embedded in a host language application. In
the example, the application checks the SQLCODE field of the SQLCA structure to
determine whether the update was successful.
Table 2. Embedding SQL Statements in a Host Language
Language Sample Source Code
C/C++ EXEC SQL UPDATE staff SET job = 'Clerk' WHERE job = 'Mgr';
if ( SQLCODE < 0 )
printf( "Update Error: SQLCODE = %ld \n", SQLCODE );
Java (SQLJ) try {
#sql { UPDATE staff SET job = 'Clerk' WHERE job = 'Mgr' };
}
catch (SQLException e) {
println( "Update Error: SQLCODE = " + e.getErrorCode() );
}
COBOL EXEC SQL UPDATE staff SET job = 'Clerk' WHERE job = 'Mgr' END_EXEC.
IF SQLCODE LESS THAN 0
DISPLAY 'UPDATE ERROR: SQLCODE = ', SQLCODE.
FORTRAN EXEC SQL UPDATE staff SET job = 'Clerk' WHERE job = 'Mgr'
if ( sqlcode .lt. 0 ) THEN
write(*,*) 'Update error: sqlcode = ', sqlcode
© Copyright IBM Corp. 1993, 2000 45
SQL statements placed in an application are not specific to the host language.
The database manager provides a way to convert the SQL syntax for
processing by the host language.
For the C, C++, COBOL or FORTRAN languages, this conversion is handled
by the DB2 precompiler. The DB2 precompiler is invoked using the PREP
command. The precompiler converts embedded SQL statements directly into
DB2 run-time services API calls.
For the Java language, the SQLJ translator converts SQLJ clauses into JDBC
statements. The SQLJ translator is invoked with the SQLJ command.
When the precompiler processes a source file, it specifically looks for SQL
statements and avoids the non-SQL host language. It can find SQL statements
because they are surrounded by special delimiters. For the syntax information
necessary to embed SQL statements in the language you are using, see the
following:
v for C/C++, “Embedding SQL Statements in C and C++” on page 586
v for Java (SQLJ), “Embedding SQL Statements in Java” on page 639
v for COBOL, “Embedding SQL Statements in COBOL” on page 668
v for FORTRAN, “Embedding SQL Statements in FORTRAN” on page 691
v for REXX, “Embedding SQL Statements in REXX” on page 705
Table 3 shows how to use delimiters and comments to create valid embedded
SQL statements in the supported compiled host languages.
Table 3. Embedding SQL Statements in a Host Language
Language Sample Source Code
C/C++ /* Only C or C++ comments allowed here */
EXEC SQL
-- SQL comments or
/* C comments or */
// C++ comments allowed here
DECLARE C1 CURSOR FOR sname;
/* Only C or C++ comments allowed here */
SQLJ /* Only Java comments allowed here */
#sql c1 = {
-- SQL comments or
/* Java comments or */
// Java comments allowed here
SELECT name FROM employee
};
/* Only Java comments allowed here */
46 Application Development Guide
Table 3. Embedding SQL Statements in a Host Language (continued)
Language Sample Source Code
COBOL * See COBOL documentation for comment rules
* Only COBOL comments are allowed here
EXEC SQL
-- SQL comments or
* full-line COBOL comments are allowed here
DECLARE C1 CURSOR FOR sname END-EXEC.
* Only COBOL comments are allowed here
FORTRAN C Only FORTRAN comments are allowed here
EXEC SQL
+ -- SQL comments, and
C full-line FORTRAN comment are allowed here
+ DECLARE C1 CURSOR FOR sname
I=7 ! End of line FORTRAN comments allowed here
C Only FORTRAN comments are allowed here
Creating and Preparing the Source Files
You can create the source code in a standard ASCII file, called a source file,
using a text editor. The source file must have the proper extension for the host
language in which you write your code. See Table 38 on page 731 to find out
the required file extension for the host language you are using.
Note: Not all platforms support all host languages. See the Application
Building Guide for specific information.
For this discussion, assume that you have already written the source code.
If you have written your application using a compiled host language, you
must follow additional steps to build your application. Along with compiling
and linking your program, you must precompile and bind it.
Simply stated, precompiling converts embedded SQL statements into DB2
run-time API calls that a host compiler can process, and creates a bind file.
The bind file contains information on the SQL statements in the application
program. The BIND command creates a package in the database. Optionally, the
precompiler can perform the bind step at precompile time.
Binding is the process of creating a package from a bind file and storing it in a
database. If your application accesses more than one database, you must
create a package for each database.
Figure 1 on page 48 shows the order of these steps, along with the various
modules of a typical compiled DB2 application. You may wish to refer to it as
Chapter 3. Embedded SQL Overview 47
you read through the following sections about what happens at each stage of
program preparation.
Source Files
1 With SQL
Statements
PACKAGE BINDFILE
Precompiler
2 Create a Create a
(db2 PREP)
Package Bind File
Source Files
Modified
Without SQL
Source Files
Statements
3 Host Language Compiler
Object
Libraries
Files
4 Host Language Linker
Executable Bind
6
Program File
Binder
5
(db2 BIND)
Database Manager Package (Package)
Figure 1. Preparing Programs Written in Compiled Host Languages
48 Application Development Guide
Creating Packages for Embedded SQL
To run applications written in compiled host languages, you must create the
packages needed by the database manager at execution time. This involves
the following steps as shown in Figure 1 on page 48:
v Precompiling (step 2), to convert embedded SQL source statements into a
form the database manager can use,
v Compiling and Linking (steps 3 and 4), to create the required object
modules, and,
v Binding (step 5), to create the package to be used by the database manager
when the program is run.
Other topics discussed in this section include:
v Application, Bind File, and Package Relationships, and,
v Rebinding, which describes when and how to rebind packages.
To create the packages needed by SQLJ applications, you need to use both the
SQLJ translator and db2profc command. For more information on using the
SQLJ translator, see “SQLJ Programming” on page 637.
Precompiling
After you create the source files, you must precompile each host language file
containing SQL statements with the PREP command for host language source
files. The precompiler converts SQL statements contained in the source file to
comments, and generates the DB2 run-time API calls for those statements.
Before precompiling an application you must connect to a server, either
implicitly or explicitly. Although you precompile application programs at the
client workstation and the precompiler generates modified source and
messages on the client, the precompiler uses the server connection to perform
some of the validation.
The precompiler also creates the information the database manager needs to
process the SQL statements against a database. This information is stored in a
package, in a bind file, or in both, depending on the precompiler options
selected.
A typical example of using the precompiler follows. To precompile a C
embedded SQL source file called filename.sqc, you can issue the following
command to create a C source file with the default name filename.c and a
bind file with the default name filename.bnd:
DB2 PREP filename.sqc BINDFILE
For detailed information on precompiler syntax and options, see the Command
Reference.
Chapter 3. Embedded SQL Overview 49
The precompiler generates up to four types of output:
v Modified source
v Package
v Bind file
v Message file
Modified Source
This file is the new version of the original source file after the
precompiler converts the SQL statements into DB2 run-time
API calls. It is given the appropriate host language extension.
Package If you use the PACKAGE option (the default), or do not
specify any of the BINDFILE, SYNTAX, or SQLFLAG options,
the package is stored in the connected database. The package
contains all the information required to execute the static SQL
statements of a particular source file against this database
only. Unless you specify a different name with the PACKAGE
USING option, the precompiler forms the package name from
the first 8 characters of the source file name.
With the PACKAGE option, the database used during the
precompile process must contain all of the database objects
referenced by the static SQL statements in the source file. For
example, you cannot precompile a SELECT statement unless
the table it references exists in the database.
Bind File If you use the BINDFILE option, the precompiler creates a
bind file (with extension .bnd) that contains the data required
to create a package. This file can be used later with the BIND
command to bind the application to one or more databases. If
you specify BINDFILE and do not specify the PACKAGE
option, binding is deferred until you invoke the BIND
command. Note that for the Command Line Processor (CLP),
the default for PREP does not specify the BINDFILE option.
Thus, if you are using the CLP and want the binding to be
deferred, you need to specify the BINDFILE option.
If you request a bind file at precompile time but do not
specify the PACKAGE, that is, you do not create a package,
certain object existence and authorization SQLCODEs are
treated as warnings instead of errors. This enables you to
precompile a program and create a bind file without requiring
that the referenced objects be present, or requiring that you
possess the authority to execute the SQL statements being
precompiled. For a list of the specific SQLCODEs that are
treated as warnings instead of errors refer to the Command
Reference.
50 Application Development Guide
Message File If you use the MESSAGES option, the precompiler redirects
messages to the indicated file. These messages include
warnings and error messages that describe problems
encountered during precompilation. If the source file does not
precompile successfully, use the warning and error messages
to determine the problem, correct the source file, and then
attempt to precompile the source file again. If you do not use
the MESSAGES option, precompilation messages are written
to the standard output.
Source File Requirements
You must always precompile a source file against a specific database, even if
eventually you do not use the database with the application. In practice, you
can use a test database for development, and after you fully test the
application, you can bind its bind file to one or more production databases.
See “Advantages of Deferred Binding” on page 56 for other ways to use this
feature.
If your application uses a code page that is not the same as your database
code page, you need to consider which code page to use when precompiling.
See “Conversion Between Different Code Pages” on page 504.
If your application uses user-defined functions (UDFs) or user-defined distinct
types (UDTs), you may need to use the FUNCPATH option when you
precompile your application. This option specifies the function path that is
used to resolve UDFs and UDTs for applications containing static SQL. If
FUNCPATH is not specified, the default function path is SYSIBM, SYSFUN,
USER, where USER refers to the current user ID. For more information on
bind options refer to the Command Reference.
To precompile an application program that accesses more than one server, you
can do one of the following:
v Split the SQL statements for each database into separate source files. Do not
mix SQL statements for different databases in the same file. Each source file
can be precompiled against the appropriate database. This is the
recommended method.
v Code your application using dynamic SQL statements only, and bind
against each database your program will access.
v If all the databases look the same, that is, they have the same definition,
you can group the SQL statements together into one source file.
The same procedures apply if your application will access a host or AS/400
application server through DB2 Connect. Precompile it against the server to
which it will be connecting, using the PREP options available for that server.
Chapter 3. Embedded SQL Overview 51
If you are precompiling an application that will run on DB2 Universal
Database for OS/390, consider using the flagger facility to check the syntax of
the SQL statements. The flagger indicates SQL syntax that is supported by
DB2 Universal Database, but not supported by DB2 Universal Database for
OS/390. You can also use the flagger to check that your SQL syntax conforms
to the SQL92 Entry Level syntax. You can use the SQLFLAG option on the
PREP command to invoke it and to specify the version of DB2 Universal
Database for OS/390 SQL syntax to be used for comparison. The flagger
facility will not enforce any changes in SQL use; it only issues informational
and warning messages regarding syntax incompatibilities, and does not
terminate preprocessing abnormally.
For details about the PREP command, refer to the Command Reference.
Compiling and Linking
Compile the modified source files and any additional source files that do not
contain SQL statements using the appropriate host language compiler. The
language compiler converts each modified source file into an object module.
Refer to the Application Building Guide or other programming documentation
for your operating platform for any exceptions to the default compile options.
Refer to your compiler’s documentation for a complete description of
available compile options.
The host language linker creates an executable application. For example:
v On OS/2 and Windows 32-bit operating systems, the application can be an
executable file or a dynamic link library (DLL).
v On UNIX-based systems, the application can be an executable load module
or a shared library.
Note: Although applications can be DLLs on Windows 32-bit operating
systems, the DLLs are loaded directly by the application and not by the
DB2 database manager. On Windows 32-bit operating systems, the
database manager can load DLLs. Stored procedures are normally built
as DLLs or shared libraries. For information on using stored
procedures, see “Chapter 7. Stored Procedures” on page 187.
For information on creating executable files on other platforms supported by
DB2, refer to the Application Building Guide.
To create the executable file, link the following:
v User object modules, generated by the language compiler from the
modified source files and other files not containing SQL statements
v Host language library APIs, supplied with the language compiler
52 Application Development Guide
v The database manager library containing the database manager APIs for
your operating environment. Refer to the Application Building Guide or other
programming documentation for your operating platform for the specific
name of the database manager library you need for your database manager
APIs.
Binding
Binding is the process that creates the package the database manager needs in
order to access the database when the application is executed. Binding can be
done implicitly by specifying the PACKAGE option during precompilation, or
explicitly by using the BIND command against the bind file created during
precompilation.
A typical example of using the BIND command follows. To bind a bind file
named filename.bnd to the database, you can issue the following command:
DB2 BIND filename.bnd
For detailed information on BIND command syntax and options, refer to the
Command Reference.
One package is created for each separately precompiled source code module.
If an application has five source files, of which three require precompilation,
three packages or bind files are created. By default, each package is given a
name that is the same as the name of the source module from which the .bnd
file originated, but truncated to 8 characters. If the name of this newly created
package is the same as a package that currently exists in the target database,
the new package replaces the previously existing package. To explicitly specify
a different package name, you must use the PACKAGE USING option on the
PREP command. See the Command Reference for details.
Renaming Packages
When creating multiple versions of an application, you should avoid
conflicting names by renaming your package. For example, if you have an
application called foo (compiled from foo.sqc), you precompile it and send it
to all the users of your application. The users bind the application to the
database, and then run the application. To make subsequent changes, create a
new version of foo and send this application and its bind file to the users that
require the new version. The new users bind foo.bnd and the new application
runs without any problem. However, when users attempt to run the old
version of the application, they receive a timestamp conflict on the FOO
package (which indicates that the package in the database does not match the
application being run) so they rebind the client. (See “Timestamps” on page 58
for more information on package timestamps.) Now the users of the new
application receive a timestamp conflict. This problem is caused because both
applications use packages with the same name.
Chapter 3. Embedded SQL Overview 53
The solution is to use package renaming. When you build the first version of
FOO, you precompile it with the command:
DB2 PREP FOO.SQC BINDFILE PACKAGE USING FOO1
After you distribute this application, users can bind and run it without any
problem. When you build the new version, you precompile it with the
command:
DB2 PREP FOO.SQC BINDFILE PACKAGE USING FOO2
After you distribute the new application, it will also bind and run without
any problem. Since the package name for the new version is FOO2 and the
package name for the first version is FOO1, there is no naming conflict and
both versions of the application can be used.
Binding Dynamic Statements
For dynamically prepared statements, the values of a number of special
registers determine the statement compilation environment:
v The CURRENT QUERY OPTIMIZATION special register determines which
optimization class is used.
v The CURRENT FUNCTION PATH special register determines the function
path used for UDF and UDT resolution.
v The CURRENT EXPLAIN SNAPSHOT register determines whether explain
snapshot information is captured.
v The CURRENT EXPLAIN MODE register determines whether explain table
information is captured, for any eligible dynamic SQL statement. The
default values for these special registers are the same defaults used for the
related bind options. For information on special registers and their
interaction with BIND options, refer to the appendix of the SQL Reference.
Resolving Unqualified Table Names
You can handle unqualified table names in your application by using one of
the following methods:
v For each user, bind the package with different COLLECTION parameters
from different authorization identifiers by using the following commands:
CONNECT TO db_name USER user_name
BIND file_name COLLECTION schema_name
In the above example, db_name is the name of the database, user_name is the
name of the user, and file_name is the name of the application that will be
bound. Note that user_name and schema_name are usually the same value.
Then use the SET CURRENT PACKAGESET statement to specify which
package to use, and therefore, which qualifiers will be used. The default
qualifier is the authorization identifier that is used when binding the
package. For an example of how to use the SET CURRENT PACKAGESET
statement, refer to the SQL Reference.
54 Application Development Guide
v Create views for each user with the same name as the table so the
unqualified table names resolve correctly. (Note that the QUALIFIER option
is DB2 Connect only, meaning that it can only be used when using a host
server.)
v Create an alias for each user to point to the desired table.
Other Binding Considerations
If your application code page uses a different code page from your database
code page, you may need to consider which code page to use when binding.
See “Conversion Between Different Code Pages” on page 504.
If your application issues calls to any of the database manager utility APIs,
such as IMPORT or EXPORT, you must bind the supplied utility bind files to
the database. For details, refer to the Quick Beginnings guide for your
platform.
You can use bind options to control certain operations that occur during
binding, as in the following examples:
v The QUERYOPT bind option takes advantage of a specific optimization
class when binding.
v The EXPLSNAP bind option stores Explain Snapshot information for
eligible SQL statements in the Explain tables.
v The FUNCPATH bind option properly resolves user-defined distinct types
and user-defined functions in static SQL.
For information on bind options, refer to the section on the BIND command in
the Command Reference.
If the bind process starts but never returns, it may be that other applications
connected to the database hold locks that you require. In this case, ensure that
no applications are connected to the database. If they are, disconnect all
applications on the server and the bind process will continue.
If your application will access a server using DB2 Connect, you can use the
BIND options available for that server. For details on the BIND command and
its options, refer to the Command Reference.
Bind files are not backward compatible with previous versions of DB2
Universal Database. In mixed-level environments, DB2 can only use the
functions available to the lowest level of the database environment. For
example, if a V5.2 client connects to a V5.0 server, the client will only be able
to use V5.0 functions. As bind files express the functionality of the database,
they are subject to the mixed-level restriction.
If you need to rebind higher-level bind files on lower-level systems, you can:
Chapter 3. Embedded SQL Overview 55
v Use a lower-level DB2 Application Development Client to connect to the
higher-level server and create bind files which can be shipped and bound
to the lower-level DB2 Universal Database environment.
v Use a higher-level DB2 client in the lower-level production environment to
bind the higher-level bind files that were created in the test environment.
The higher-level client passes only the options that apply to the lower-level
server.
Advantages of Deferred Binding
Precompiling with binding enabled allows an application to access only the
database used during the precompile process. Precompiling with binding
deferred, however, allows an application to access many databases, because
you can bind the BIND file against each one. This method of application
development is inherently more flexible in that applications are precompiled
only once, but the application can be bound to a database at any time.
Using the BIND API during execution allows an application to bind itself,
perhaps as part of an installation procedure or before an associated module is
executed. For example, an application can perform several tasks, only one of
which requires the use of SQL statements. You can design the application to
bind itself to a database only when the application calls the task requiring
SQL statements, and only if an associated package does not already exist.
Another advantage of the deferred binding method is that it lets you create
packages without providing source code to end users. You can ship the
associated bind files with the application.
DB2 Bind File Description Utility - db2bfd
With the DB2 Bind File Description (db2bfd) utility, you can easily display the
contents of a bind file to examine and verify the SQL statements within it, as
well as display the precompile options used to create the bind file. This may
be useful in problem determination related to your application’s bind file.
The db2bfd utility is located in the bin subdirectory of the sqllib directory of
the instance.
Its syntax is:
56 Application Development Guide
(1) (5)
WW db2bfd X -h filespec WY
(2)
-b
(3)
-s
(4)
-v
Notes:
1 Display the help information.
2 Display bind file header.
3 Display SQL statements.
4 Display host variable declarations
5 The name of the bind file.
For more information on db2bfd, refer to the Command Reference.
Application, Bind File, and Package Relationships
A package is an object stored in the database that includes information
needed to execute specific SQL statements in a single source file. A database
application uses one package for every precompiled source file used to build
the application. Each package is a separate entity, and has no relationship to
any other packages used by the same or other applications. Packages are
created by running the precompiler against a source file with binding enabled,
or by running the binder at a later time with one or more bind files.
Database applications use packages for some of the same reasons that
applications are compiled: improved performance and compactness. By
precompiling an SQL statement, the statement is compiled into the package
when the application is built, instead of at run time. Each statement is parsed,
and a more efficiently interpreted operand string is stored in the package. At
run time, the code generated by the precompiler calls run-time services
database manager APIs with any variable information required for input or
output data, and the information stored in the package is executed.
The advantages of precompilation apply only to static SQL statements. SQL
statements that are executed dynamically (using PREPARE and EXECUTE or
EXECUTE IMMEDIATE) are not precompiled; therefore, they must go through
the entire set of processing steps at run time.
Note: Do not assume that a static version of an SQL statement automatically
executes faster than the same statement processed dynamically. In some
Chapter 3. Embedded SQL Overview 57
cases, static SQL is faster because of the overhead required to prepare
the dynamic statement. In other cases, the same statement prepared
dynamically executes faster, because the optimizer can make use of
current database statistics, rather than the database statistics available
at an earlier bind time. Note that if your transaction takes less than a
couple of seconds to complete, static SQL will generally be faster. To
choose which method to use, you should prototype both forms of
binding. For a detailed comparison of static and dynamic SQL, see
“Comparing Dynamic SQL with Static SQL” on page 128.
Timestamps
When generating a package or a bind file, the precompiler generates a
timestamp. The timestamp is stored in the bind file or package and in the
modified source file.
When an application is precompiled with binding enabled, the package and
modified source file are generated with timestamps that match. When the
application is run, the timestamps are checked for equality. An application
and an associated package must have matching timestamps for the application
to run, or an SQL0818N error is returned to the application.
Remember that when you bind an application to a database, the first eight
characters of the application name are used as the package name unless you
override the default by using the PACKAGE USING option on the PREP command.
This means that if you precompile and bind two programs using the same
name, the second will override the package of the first. When you run the
first program, you will get a timestamp error because the timestamp for the
modified source file no longer matches that of the package in the database.
When an application is precompiled with binding deferred, one or more bind
files and modified source files are generated with matching timestamps. To
run the application, the bind files produced by the application modules can
execute. The binding process must be done for each bind file as discussed in
“Binding” on page 53.
The application and package timestamps match because the bind file contains
the same timestamp as the one that was stored in the modified source file
during precompilation.
Rebinding
Rebinding is the process of recreating a package for an application program
that was previously bound. You must rebind packages if they have been
marked invalid or inoperative. In some situations, however, you may want to
rebind packages that are valid. For example, you may want to take advantage
of a newly created index, or make use of updated statistics after executing the
RUNSTATS command.
58 Application Development Guide
Packages can be dependent on certain types of database objects such as tables,
views, aliases, indexes, triggers, referential constraints and table check
constraints. If a package is dependent on a database object (such as a table,
view, trigger, and so on), and that object is dropped, the package is placed
into an invalid state. If the object that is dropped is a UDF, the package is
placed into an inoperative state. For more information, refer to the
Administration Guide: Planning.
Invalid packages are implicitly (or automatically) rebound by the database
manager when they are executed. Inoperative packages must be explicitly
rebound by executing either the BIND command or the REBIND command. Note
that implicit rebinding can cause unexpected errors if the implicit rebind fails.
That is, the implicit rebind error is returned on the statement being executed
which may not be the statement that is actually in error. If an attempt is made
to execute an inoperative package, an error occurs. You may decide to
explicitly rebind invalid packages rather than have the system automatically
rebind them. This enables you to control when the rebinding occurs.
The choice of which command to use to explicitly rebind a package depends
on the circumstances. You must use the BIND command to rebind a package
for a program which has been modified to include more, fewer, or changed
SQL statements. You must also use the BIND command if you need to change
any bind options from the values with which the package was originally
bound. In all other cases, use either the BIND or REBIND command. You should
use REBIND whenever your situation does not specifically require the use of
BIND, as the performance of REBIND is significantly better than that of BIND.
For details on the REBIND command, refer to the Command Reference.
Chapter 3. Embedded SQL Overview 59
60 Application Development Guide
Chapter 4. Writing Static SQL Programs
Characteristics and Reasons for Using Static Advanced Scrolling Techniques . . . . . 102
SQL . . . . . . . . . . . . . . . 61 Scrolling Through Data that has Already
Advantages of Static SQL . . . . . . . 62 Been Retrieved. . . . . . . . . . 102
Example: Static SQL Program . . . . . . 63 Keeping a Copy of the Data . . . . . 102
How the Static Program Works . . . . 64 Retrieving the Data a Second Time . . . 102
C Example: STATIC.SQC . . . . . . . 66 Retrieving from the Beginning . . . 103
Java Example: Static.sqlj . . . . . . . 67 Retrieving from the Middle . . . . 103
COBOL Example: STATIC.SQB . . . . . 69 Order of Rows in the Second Result
Coding SQL Statements to Retrieve and Table . . . . . . . . . . . . 103
Manipulate Data . . . . . . . . . . 71 Retrieving in Reverse Order . . . . 104
Retrieving Data . . . . . . . . . . 71 Establishing a Position at the End of a
Using Host Variables . . . . . . . . . 71 Table . . . . . . . . . . . . . 104
Declaration Generator - db2dclgn . . . . 73 Updating Previously Retrieved Data . . 105
Using Indicator Variables . . . . . . . 75 Example: UPDAT Program. . . . . . 105
Data Types . . . . . . . . . . . 77 How the UPDAT Program Works . . 105
Using an Indicator Variable in the STATIC C Example: UPDAT.SQC . . . . . 107
program . . . . . . . . . . . . 80 Java Example: Updat.sqlj . . . . . 109
Selecting Multiple Rows Using a Cursor . . 81 COBOL Example: UPDAT.SQB . . . 111
Declaring and Using the Cursor . . . . 81 REXX Example: UPDAT.CMD. . . . 113
Cursors and Unit of Work Considerations 82 Diagnostic Handling and the SQLCA
Read Only Cursors. . . . . . . . 82 Structure . . . . . . . . . . . . . 115
WITH HOLD Option . . . . . . . 82 Return Codes . . . . . . . . . . 115
Example: Cursor Program . . . . . . 84 SQLCODE and SQLSTATE . . . . . . 115
How the Cursor Program Works . . . 84 Token Truncation in SQLCA Structure . . 116
C Example: CURSOR.SQC . . . . . 86 Handling Errors using the WHENEVER
Java Example: Cursor.sqlj . . . . . 88 Statement . . . . . . . . . . . 116
COBOL Example: CURSOR.SQB . . . 90 Exception, Signal, Interrupt Handler
Updating and Deleting Retrieved Data . . . 92 Considerations . . . . . . . . . . 117
Updating Retrieved Data. . . . . . . 92 Exit List Routine Considerations . . . . 118
Deleting Retrieved Data . . . . . . . 92 Using GET ERROR MESSAGE in
Types of Cursors . . . . . . . . . 92 Example Programs . . . . . . . . 118
Example: OPENFTCH Program . . . . 93 C Example: UTILAPI.C . . . . . . 119
How the OPENFTCH Program Works 93 Java Example: Catching SQLException 121
C Example: OPENFTCH.SQC . . . . 95 COBOL Example: CHECKERR.CBL 122
Java Example: Openftch.sqlj . . . . . 97 REXX Example: CHECKERR
COBOL Example: OPENFTCH.SQB 100 Procedure . . . . . . . . . . 124
Characteristics and Reasons for Using Static SQL
When the syntax of embedded SQL statements is fully known at precompile
time, the statements are referred to as static SQL. This is in contrast to dynamic
SQL statements whose syntax is not known until run time.
Note: Static SQL is not supported in interpreted languages, such as REXX.
© Copyright IBM Corp. 1993, 2000 61
The structure of an SQL statement must be completely specified in order for a
statement to be considered static. For example, the names for the columns and
tables referenced in a statement must be fully known at precompile time. The
only information that can be specified at run time are values for any host
variables referenced by the statement. However, host variable information,
such as data types, must still be precompiled.
When a static SQL statement is prepared, an executable form of the statement
is created and stored in the package in the database. The executable form can
be constructed either at precompile time, or at a later bind time. In either case,
preparation occurs before run time. The authorization of the person binding
the application is used, and optimization is based upon database statistics and
configuration parameters that may not be current when the application runs.
Advantages of Static SQL
Programming using static SQL requires less effort than using embedded
dynamic SQL. Static SQL statements are simply embedded into the host
language source file, and the precompiler handles the necessary conversion to
database manager run-time services API calls that the host language compiler
can process.
Because the authorization of the person binding the application is used, the
end user does not require direct privileges to execute the statements in the
package. For example, an application could allow a user to update parts of a
table without granting an update privilege on the entire table. This can be
achieved by restricting the static SQL statements to allow updates only to
certain columns or a range of values.
Static SQL statements are persistent, meaning that the statements last for as
long as the package exists. Dynamic SQL statements are cached until they are
either invalidated, freed for space management reasons, or the database is
shut down. If required, the dynamic SQL statements are recompiled implicitly
by the DB2 SQL compiler whenever a cached statement becomes invalid. For
information on caching and the reasons for invalidation of a cached statement,
refer to the SQL Reference.
The key advantage of static SQL, with respect to persistence, is that the static
statements exist after a particular database is shut down, whereas dynamic
SQL statements cease to exist when this occurs. In addition, static SQL does
not have to be compiled by the DB2 SQL compiler at run time, while dynamic
SQL must be explicitly compiled at run time (for example, by using the
PREPARE statement). Because DB2 caches dynamic SQL statements, the
statements do not need to be compiled often by DB2, but they must be
compiled at least once when you execute the application.
62 Application Development Guide
There can be performance advantages to static SQL. For simple, short-running
SQL programs, a static SQL statement executes faster than the same statement
processed dynamically since the overhead of preparing an executable form of
the statement is done at precompile time instead of at run time.
Note: The performance of static SQL depends on the statistics of the database
the last time the application was bound. However, if these statistics
change, the performance of equivalent dynamic SQL can be very
different. If, for example, an index is added to a database at a later
time, an application using static SQL cannot take advantage of the
index unless it is re-bound to the database. In addition, if you are using
host variables in a static SQL statement, the optimizer will not be able
to take advantage of any distribution statistics for the table.
Example: Static SQL Program
This sample program shows examples of static SQL statements and database
manager API calls in the following supported languages:
C static.sqc
Java Static.sqlj
COBOL static.sqb
The REXX language does not support static SQL, so a sample is not provided.
This sample program contains a query that selects a single row. Such a query
can be performed using the SELECT INTO statement.
The SELECT INTO statement selects one row of data from tables in a
database, and the values in this row are assigned to host variables specified in
the statement. Host variables are discussed in detail in “Using Host Variables”
on page 71. For example, the following statement will deliver the salary of
the employee with the last name of 'HAAS' into the host variable empsal:
SELECT SALARY
INTO :empsal
FROM EMPLOYEE
WHERE LASTNAME='HAAS'
A SELECT INTO statement must be specified to return only one or zero rows.
Finding more than one row results in an error, SQLCODE -811 (SQLSTATE
21000). If several rows can be the result of a query, a cursor must be used to
process the rows. See “Selecting Multiple Rows Using a Cursor” on page 81
for more information.
For more details on the SELECT INTO statement, refer to the SQL Reference.
Chapter 4. Writing Static SQL Programs 63
For an introductory discussion on how to write SELECT statements, see
“Coding SQL Statements to Retrieve and Manipulate Data” on page 71.
How the Static Program Works
1. Include the SQLCA. The INCLUDE SQLCA statement defines and
declares the SQLCA structure, and defines SQLCODE and SQLSTATE as
elements within the structure. The SQLCODE field of the SQLCA structure
is updated with diagnostic information by the database manager after
every execution of SQL statements or database manager API calls.
2. Declare host variables. The SQL BEGIN DECLARE SECTION and END
DECLARE SECTION statements delimit the host variable declarations.
These are variables that can be referenced in SQL statements. Host
variables are used to pass data to the database manager or to hold data
returned by it. They are prefixed with a colon (:) when referenced in an
SQL statement. For more information, see “Using Host Variables” on
page 71.
3. Connect to database. The program connects to the sample database, and
requests shared access to it. (It is assumed that a START DATABASE
MANAGER API call or db2start command has been issued.) Other
programs that connect to the same database using shared access are also
granted access.
4. Retrieve data. The SELECT INTO statement retrieves a single value based
upon a query. This example retrieves the FIRSTNME column from the
EMPLOYEE table where the value of the LASTNAME column is JOHNSON. The
value SYBIL is returned and placed in the host variable firstname. The
sample tables supplied with DB2 are listed in the appendix of the SQL
Reference.
5. Process errors. The CHECKERR macro/function is an error checking utility
which is external to the program. The location of this error checking utility
depends upon the programming language used:
C For C programs that call DB2 APIs, the sqlInfoPrint
function in utilapi.c is redefined as API_SQL_CHECK in
utilapi.h. For C embedded SQL programs, the
sqlInfoPrint function in utilemb.sqc is redefined as
EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled
in the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for
the source code for this error checking utility.
64 Application Development Guide
6. Disconnect from database. The program disconnects from the database by
executing the CONNECT RESET statement. Note that SQLJ programs
automatically close the database connection when the program returns.
Chapter 4. Writing Static SQL Programs 65
C Example: STATIC.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA; 1
int main(int argc, char *argv[])
{ int rc = 0;
char dbAlias[15] ;
char user[15] ;
char pswd[15] ;
EXEC SQL BEGIN DECLARE SECTION; 2
char firstname[13];
EXEC SQL END DECLARE SECTION;
/* checks the command line arguments */
rc = CmdLineArgsCheck1( argc, argv, dbAlias, user, pswd ); 3
if ( rc != 0 ) return( rc ) ;
printf("\n\nSample C program: STATIC\n");
/* initialize the embedded application */
rc = EmbAppInit( dbAlias, user, pswd);
if ( rc != 0 ) return( rc ) ;
EXEC SQL SELECT FIRSTNME INTO :firstname 4
FROM employee
WHERE LASTNAME = 'JOHNSON';
EMB_SQL_CHECK("SELECT statement"); 5
printf( "First name = %s\n", firstname );
/* terminate the embedded application */
rc = EmbAppTerm( dbAlias);
return( rc ) ;
}
/* end of program : STATIC.SQC */
66 Application Development Guide
Java Example: Static.sqlj
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
class Static
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println (" Java Static Sample");
String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname
Connection con = null;
// Set the connection 3
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception("\nUsage: java Static [username password]\n");
}
// Set the default context
DefaultContext ctx = new DefaultContext(con);
DefaultContext.setDefaultContext(ctx);
String firstname = null;
#sql { SELECT FIRSTNME INTO :firstname
FROM employee
WHERE LASTNAME = 'JOHNSON' } ; 4
System.out.println ("First name = " + firstname);
}
catch( Exception e ) 5
Chapter 4. Writing Static SQL Programs 67
{
System.out.println (e);
}
}
}
68 Application Development Guide
COBOL Example: STATIC.SQB
Identification Division.
Program-ID. "static".
Data Division.
Working-Storage Section.
copy "sql.cbl".
copy "sqlca.cbl". 1
EXEC SQL BEGIN DECLARE SECTION END-EXEC. 2
01 firstname pic x(12).
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
EXEC SQL END DECLARE SECTION END-EXEC.
77 errloc pic x(80).
Procedure Division.
Main Section.
display "Sample COBOL program: STATIC".
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd 3
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL SELECT FIRSTNME INTO :firstname 4
FROM EMPLOYEE
WHERE LASTNAME = 'JOHNSON' END-EXEC.
move "SELECT" to errloc.
call "checkerr" using SQLCA errloc. 5
display "First name = ", firstname.
EXEC SQL CONNECT RESET END-EXEC. 6
move "CONNECT RESET" to errloc.
Chapter 4. Writing Static SQL Programs 69
call "checkerr" using SQLCA errloc.
End-Prog.
stop run.
70 Application Development Guide
Coding SQL Statements to Retrieve and Manipulate Data
The database manager provides application programmers with statements for
retrieving and manipulating data; the coding task consists of embedding these
statements into the host language code. This section shows how to code
statements that will retrieve and manipulate data for one or more rows of
data in DB2 tables. (It does not go into the details of the different host
languages.) For the exact rules of placement, continuation, and delimiting SQL
statements, see:
v “Chapter 20. Programming in C and C++” on page 581
v “Chapter 21. Programming in Java” on page 623
v “Chapter 23. Programming in COBOL” on page 665
v “Chapter 24. Programming in FORTRAN” on page 687
v “Chapter 25. Programming in REXX” on page 703.
Retrieving Data
One of the most common tasks of an SQL application program is to retrieve
data. This is done using the select-statement, which is a form of query that
searches for rows of tables in the database that meet specified search
conditions. If such rows exist, the data is retrieved and put into specified
variables in the host program, where it can be used for whatever it was
designed to do.
After you have written a select-statement, you code the SQL statements that
define how information will be passed to your application.
You can think of the result of a select-statement as being a table having rows
and columns, much like a table in the database. If only one row is returned,
you can deliver the results directly into host variables specified by the
SELECT INTO statement.
If more than one row is returned, you must use a cursor to fetch them one at a
time. A cursor is a named control structure used by an application program to
point to a specific row within an ordered set of rows. For information about
how to code and use cursors, see the following sections:
v “Declaring and Using the Cursor” on page 81,
v “Selecting Multiple Rows Using a Cursor” on page 81,
v “Example: Cursor Program” on page 84.
Using Host Variables
Host variables are variables referenced by embedded SQL statements. They
transmit data between the database manager and an application program.
When you use a host variable in an SQL statement, you must prefix its name
with a colon, (:). When you use a host variable in a host language statement,
omit the colon.
Chapter 4. Writing Static SQL Programs 71
Host variables are declared in compiled host languages, and are delimited by
BEGIN DECLARE SECTION and END DECLARE SECTION statements.
These statements enable the precompiler to find the declarations.
Note: Java JDBC and SQLJ programs do not use declare sections. Host
variables in Java follow the normal Java variable declaration syntax.
Host variables are declared using a subset of the host language. For a
description of the supported syntax for your host language, see:
v “Chapter 20. Programming in C and C++” on page 581
v “Chapter 21. Programming in Java” on page 623
v “Chapter 23. Programming in COBOL” on page 665
v “Chapter 24. Programming in FORTRAN” on page 687
v “Chapter 25. Programming in REXX” on page 703.
The following rules apply to host variable declaration sections:
v All host variables must be declared in the source file before they are
referenced, except for host variables referring to SQLDA structures.
v Multiple declare sections may be used in one source file.
v The precompiler is unaware of host language variable scoping rules.
With respect to SQL statements, all host variables have a global scope
regardless of where they are actually declared in a single source file.
Therefore, host variable names must be unique within a source file.
This does not mean that the DB2 precompiler changes the scope of host
variables to global so that they can be accessed outside the scope in which
they are defined. Consider the following example:
foo1(){
.
.
.
BEGIN SQL DECLARE SECTION;
int x;
END SQL DECLARE SECTION;
x=10;
.
.
.
}
foo2(){
.
.
.
y=x;
.
.
.
}
72 Application Development Guide
Depending on the language, the above example will either fail to compile
because variable x is not declared in function foo2() or the value of x
would not be set to 10 in foo2(). To avoid this problem, you must either
declare x as a global variable, or pass x as a parameter to function foo2()
as follows:
foo1(){
.
.
.
BEGIN SQL DECLARE SECTION;
int x;
END SQL DECLARE SECTION;
x=10;
foo2(x);
.
.
.
}
foo2(int x){
.
.
.
y=x;
.
.
.
}
For further information on declaring host variables, see:
v “Declaration Generator - db2dclgn” to generate host variable declaration
source code automatically with the db2dclgn tool
v Table 4 on page 74 for examples of how host variables appear in source
code
v Table 5 on page 75 for examples of how to reference host variables in the
supported host languages
v “Naming Host Variables in REXX” on page 707 and “Referencing Host
Variables in REXX” on page 707 for information on naming and referencing
host variables in REXX
Declaration Generator - db2dclgn
The Declaration Generator speeds application development by generating
declarations for a given table in a database. It creates embedded SQL
declaration source files which you can easily insert into your applications.
db2dclgn supports the C/C++, Java, COBOL, and FORTRAN languages.
To generate declaration files, enter the db2dclgn command in the following
format:
Chapter 4. Writing Static SQL Programs 73
db2dclgn -d database-name -t table-name [options]
For example, to generate the declarations for the STAFF table in the SAMPLE
database in C in the output file staff.h, issue the following command:
db2dclgn -d sample -t staff -l C
The resulting staff.h file contains:
struct
{
short id;
struct
{
short length;
char data[9];
} name;
short dept;
char job[5];
short years;
double salary;
double comm;
} staff;
For detailed information on db2dclgn, refer to the Command Reference.
Table 4. Declaring Host Variables
Language Example Source Code
C/C++ EXEC SQL BEGIN DECLARE SECTION;
short dept=38, age=26;
double salary;
char CH;
char name1[9], NAME2[9];
/* C comment */
short nul_ind;
EXEC SQL END DECLARE SECTION;
Java // Note that Java host variable declarations follow
// normal Java variable declaration rules, and have
// no equivalent of a DECLARE SECTION
short dept=38, age=26;
double salary;
char CH;
String name1[9], NAME2[9];
/* Java comment */
short nul_ind;
74 Application Development Guide
Table 4. Declaring Host Variables (continued)
Language Example Source Code
COBOL EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 age PIC S9(4) COMP-5 VALUE 26.
01 DEPT PIC S9(9) COMP-5 VALUE 38.
01 salary PIC S9(6)V9(3) COMP-3.
01 CH PIC X(1).
01 name1 PIC X(8).
01 NAME2 PIC X(8).
* COBOL comment
01 nul-ind PIC S9(4) COMP-5.
EXEC SQL END DECLARE SECTION END-EXEC.
FORTRAN EXEC SQL BEGIN DECLARE SECTION
integer*2 age /26/
integer*4 dept /38/
real*8 salary
character ch
character*8 name1,NAME2
C FORTRAN comment
integer*2 nul_ind
EXEC SQL END DECLARE SECTION
Table 5. Referencing Host Variables
Language Example Source Code
C/C++ EXEC SQL FETCH C1 INTO :cm;
printf( "Commission = %f\n", cm );
JAVA (SQLJ) #SQL { FETCH :c1 INTO :cm };
System.out.println("Commission = " + cm);
COBOL EXEC SQL FETCH C1 INTO :cm END-EXEC
DISPLAY 'Commission = ' cm
FORTRAN EXEC SQL FETCH C1 INTO :cm
WRITE(*,*) 'Commission = ', cm
Using Indicator Variables
Applications written in languages other than Java must prepare for receiving
null values by associating an indicator variable with any host variable that can
receive a null. Java applications compare the value of the host variable with
Java null to determine whether the received value is null. An indicator
variable is shared by both the database manager and the host application;
therefore, the indicator variable must be declared in the application as a host
variable. This host variable corresponds to the SQL data type SMALLINT.
Chapter 4. Writing Static SQL Programs 75
An indicator variable is placed in an SQL statement immediately after the
host variable, and is prefixed with a colon. A space can separate the indicator
variable from the host variable, but is not required. However, do not put a
comma between the host variable and the indicator variable. You can also
specify an indicator variable by using the optional INDICATOR keyword,
which you place between the host variable and its indicator.
Indicator Variables shows indicator variable usage in the supported host
languages using the INDICATOR keyword.
Language
Example Source Code
C/C++
EXEC SQL FETCH C1 INTO :cm INDICATOR :cmind;
if ( cmind < 0 )
printf( "Commission is NULL\n" );
Java (SQLJ)
#SQL { FETCH :c1 INTO :cm };
if ( cm == null )
System.out.println( "Commission is NULL\n" );
COBOL
EXEC SQL FETCH C1 INTO :cm INDICATOR :cmind END-EXEC
IF cmind LESS THAN 0
DISPLAY 'Commission is NULL'
FORTRAN
EXEC SQL FETCH C1 INTO :cm INDICATOR :cmind
IF ( cmind .LT. 0 ) THEN
WRITE(*,*) 'Commission is NULL'
ENDIF
In the figure, cmind is examined for a negative value. If it is not negative, the
application can use the returned value of cm. If it is negative, the fetched
value is NULL and cm should not be used. The database manager does not
change the value of the host variable in this case.
Note: If the database configuration parameter DFT_SQLMATHWARN is set to
’YES’, the value of cmind may be -2. This indicates a NULL that was
caused by evaluating an expression with an arithmetic error or by an
overflow while attempting to convert the numeric result value to the
host variable.
If the data type can handle NULLs, the application must provide a NULL
indicator. Otherwise, an error may occur. If a NULL indicator is not used, an
SQLCODE -305 (SQLSTATE 22002) is returned.
76 Application Development Guide
If the SQLCA structure indicates a truncation warning, the indicator variables
can be examined for truncation. If an indicator variable has a positive value, a
truncation occurred.
v If the seconds portion of a TIME data type is truncated, the indicator value
contains the seconds portion of the truncated data.
v For all other string data types, except large objects (LOB), the indicator
value represents the actual length of the data returned. User-defined
distinct types (UDT) are handled in the same way as their base type.
When processing INSERT or UPDATE statements, the database manager
checks the indicator variable if one exists. If the indicator variable is negative,
the database manager sets the target column value to NULL if NULLs are
allowed. If the indicator variable is zero or positive, the database manager
uses the value of the associated host variable.
The SQLWARN1 field in the SQLCA structure may contain an ’X’ or ’W’ if the
value of a string column is truncated when it is assigned to a host variable. It
contains an ’N’ if a null terminator is truncated.
A value of ’X’ is returned by the database manager only if all of the following
conditions are met:
v A mixed code page connection exists where conversion of character string
data from the database code page to the application code page involves a
change in the length of the data.
v A cursor is blocked.
v An indicator variable is provided by your application.
The value returned in the indicator variable will be the length of the resultant
character string in the application’s code page.
In all other cases involving data truncation, (as opposed to NULL terminator
truncation), the database manager returns a ’W’. In this case, the database
manager returns a value in the indicator variable to the application that is the
length of the resultant character string in the code page of the select list item
(either the application code page, the data base code page, or nothing). For
related information, refer to the SQL Reference.
Data Types
Each column of every DB2 table is given an SQL data type when the column is
created. For information about how these types are assigned to columns, refer
to the CREATE TABLE statement in the SQL Reference. The database manager
supports the following column data types:
SMALLINT
16-bit signed integer.
Chapter 4. Writing Static SQL Programs 77
INTEGER
32-bit signed integer. INT can be used as a synonym for this type.
BIGINT
64-bit signed integer.
DOUBLE
Double-precision floating point. DOUBLE PRECISION and
FLOAT(n) (where n is greater than 24) are synonyms for this type.
REAL Single-precision floating point. FLOAT(n) (where n is less than 24) is a
synonym for this type.
DECIMAL
Packed decimal. DEC, NUMERIC, and NUM are synonyms for this
type.
CHAR
Fixed-length character string of length 1 byte to 254 bytes.
CHARACTER can be used as a synonym for this type.
VARCHAR
Variable-length character string of length 1 byte to 32672 bytes.
CHARACTER VARYING and CHAR VARYING are synonyms for
this type.
LONG VARCHAR
Long variable-length character string of length 1 byte to 32 700 bytes.
CLOB Large object variable-length character string of length 1 byte to 2
gigabytes.
BLOB Large object variable-length binary string of length 1 byte to 2
gigabytes.
DATE Character string of length 10 representing a date.
TIME Character string of length 8 representing a time.
TIMESTAMP
Character string of length 26 representing a timestamp.
The following data types are supported only in double-byte character set
(DBCS) and Extended UNIX Code (EUC) character set environments:
GRAPHIC
Fixed-length graphic string of length 1 to 127 double-byte characters.
VARGRAPHIC
Variable-length graphic string of length 1 to 16336 double-byte
characters.
78 Application Development Guide
LONG VARGRAPHIC
Long variable-length graphic string of length 1 to 16 350 double-byte
characters.
DBCLOB
Large object variable-length graphic string of length 1 to 1 073 741 823
double-byte characters.
Notes:
1. Every supported data type can have the NOT NULL attribute. This is
treated as another type.
2. The above set of data types can be extended by defining user-defined
distinct types (UDT). UDTs are separate data types which use the
representation of one of the built-in SQL types.
Supported host languages have data types that correspond to the majority of
the database manager data types. Only these host language data types can be
used in host variable declarations. When the precompiler finds a host variable
declaration, it determines the appropriate SQL data type value. The database
manager uses this value to convert the data exchanged between itself and the
application.
As the application programmer, it is important for you to understand how the
database manager handles comparisons and assignments between different
data types. Simply put, data types must be compatible with each other during
assignment and comparison operations, whether the database manager is
working with two SQL column data types, two host-language data types, or
one of each.
The general rule for data type compatibility is that all supported host-language
numeric data types are comparable and assignable with all database manager
numeric data types, and all host-language character types are compatible with
all database manager character types; numeric types are incompatible with
character types. However, there are also some exceptions to this general rule
depending on host language idiosyncrasies and limitations imposed when
working with large objects.
Within SQL statements, DB2 provides conversions between compatible data
types. For example, in the following SELECT statement, SALARY and BONUS
are DECIMAL columns; however, each employee’s total compensation is
returned as DOUBLE data:
SELECT EMPNO, DOUBLE(SALARY+BONUS) FROM EMPLOYEE
Note that the execution of the above statement includes conversion between
DECIMAL and DOUBLE data types. To make the query results more readable
on your screen, you could use the following SELECT statement:
Chapter 4. Writing Static SQL Programs 79
SELECT EMPNO, DIGIT(SALARY+BONUS) FROM EMPLOYEE
To convert data within your application, contact your compiler vendor for
additional routines, classes, built-in types, or APIs that supports this
conversion.
Character data types may also be subject to character conversion. If your
application code page is not the same as your database code page, see
“Conversion Between Different Code Pages” on page 504.
For the list of supported SQL data types and the corresponding host language
data types, see the following:
v for C/C++, “Supported SQL Data Types in C and C++” on page 615
v for Java, “Supported SQL Data Types in Java” on page 625
v for COBOL, “Supported SQL Data Types in COBOL” on page 681
v for FORTRAN, “Supported SQL Data Types in FORTRAN” on page 698
v for REXX, “Supported SQL Data Types in REXX” on page 712.
For more information about SQL data types, the rules of assignments and
comparisons, and data conversion and conversion errors, refer to the SQL
Reference.
Using an Indicator Variable in the STATIC program
The following code segments show the modification to the corresponding
segments in the C version of the sample STATIC program, listed in “C
Example: STATIC.SQC” on page 66. They show the implementation of
indicator variables on data columns that are nullable. In this example, the
STATIC program is extended to select another column, WORKDEPT. This column
can have a null value. An indicator variable needs to be declared as a host
variable before being used.
..
.
EXEC SQL BEGIN DECLARE SECTION;
char wd[3];
short wd_ind;
char firstname[13];
..
.
EXEC SQL END DECLARE SECTION;
..
.
/* CONNECT TO SAMPLE DATABASE */
..
.
80 Application Development Guide
EXEC SQL SELECT FIRSTNME, WORKDEPT INTO :firstname, :wd:wdind
FROM EMPLOYEE
WHERE LASTNAME = 'JOHNSON';
..
.
Selecting Multiple Rows Using a Cursor
To allow an application to retrieve a set of rows, SQL uses a mechanism called
a cursor.
To help understand the concept of a cursor, assume that the database manager
builds a result table to hold all the rows retrieved by executing a SELECT
statement. A cursor makes rows from the result table available to an
application, by identifying or pointing to a current row of this table. When a
cursor is used, an application can retrieve each row sequentially from the
result table until an end of data condition, that is, the NOT FOUND
condition, SQLCODE +100 (SQLSTATE 02000) is reached. The set of rows
obtained as a result of executing the SELECT statement can consist of zero,
one, or more rows, depending on the number of rows that satisfy the search
condition.
The steps involved in processing a cursor are as follows:
1. Specify the cursor using a DECLARE CURSOR statement.
2. Perform the query and build the result table using the OPEN statement.
3. Retrieve rows one at a time using the FETCH statement.
4. Process rows with the DELETE or UPDATE statements (if required).
5. Terminate the cursor using the CLOSE statement.
An application can use several cursors concurrently. Each cursor requires its
own set of DECLARE CURSOR, OPEN, CLOSE, and FETCH statements.
See “Example: Cursor Program” on page 84 for an example of how an
application can select a set of rows and, using a cursor, process the set one
row at a time.
Declaring and Using the Cursor
The DECLARE CURSOR statement defines and names the cursor, identifying
the set of rows to be retrieved using a SELECT statement.
The application assigns a name for the cursor. This name is referred to in
subsequent OPEN, FETCH, and CLOSE statements. The query is any valid
select statement.
Declare Cursor Statement shows a DECLARE statement associated with a
static SELECT statement.
Chapter 4. Writing Static SQL Programs 81
Language
Example Source Code
C/C++
EXEC SQL DECLARE C1 CURSOR FOR
SELECT PNAME, DEPT FROM STAFF
WHERE JOB=:host_var;
Java (SQLJ)
#sql iterator cursor1(host_var data type);
#sql cursor1 = { SELECT PNAME, DEPT FROM STAFF
WHERE JOB=:host_var };
COBOL
EXEC SQL DECLARE C1 CURSOR FOR
SELECT NAME, DEPT FROM STAFF
WHERE JOB=:host-var END-EXEC.
FORTRAN
EXEC SQL DECLARE C1 CURSOR FOR
+ SELECT NAME, DEPT FROM STAFF
+ WHERE JOB=:host_var
Note: The placement of the DECLARE statement is arbitrary, but it must be
placed above the first use of the cursor.
Cursors and Unit of Work Considerations
The actions of a COMMIT or ROLLBACK operation vary for cursors,
depending on how the cursors are declared.
Read Only Cursors
If a cursor is determined to be read only and uses a repeatable read isolation
level, repeatable read locks are still gathered and maintained on system tables
needed by the unit of work. Therefore, it is important for applications to
periodically issue COMMIT statements, even for read only cursors.
WITH HOLD Option
If an application completes a unit of work by issuing a COMMIT statement,
all open cursors, except those declared using the WITH HOLD option, are
automatically closed by the database manager.
A cursor that is declared WITH HOLD maintains the resources it accesses
across multiple units of work. The exact effect of declaring a cursor WITH
HOLD depends on how the unit of work ends.
If the unit of work ends with a COMMIT statement, open cursors defined
WITH HOLD remain OPEN. The cursor is positioned before the next logical
row of the result table. In addition, prepared statements referencing OPEN
cursors defined WITH HOLD are retained. Only FETCH and CLOSE requests
82 Application Development Guide
associated with a particular cursor are valid immediately following the
COMMIT. UPDATE WHERE CURRENT OF and DELETE WHERE CURRENT
OF statements are valid only for rows fetched within the same unit of work. If
a package is rebound during a unit of work, all held cursors are closed.
If the unit of work ends with a ROLLBACK statement, all open cursors are
closed, all locks acquired during the unit of work are released, and all
prepared statements that are dependent on work done in that unit are
dropped.
For example, suppose that the TEMPL table contains 1000 entries. You want to
update the salary column for all employees, and you expect to issue a
COMMIT statement every time you update 100 rows.
1. Declare the cursor using the WITH HOLD option:
EXEC SQL DECLARE EMPLUPDT CURSOR WITH HOLD FOR
SELECT EMPNO, LASTNAME, PHONENO, JOBCODE, SALARY
FROM TEMPL FOR UPDATE OF SALARY
2. Open the cursor and fetch data from the result table one row at a time:
EXEC SQL OPEN EMPLUPDT
.
.
.
EXEC SQL FETCH EMPLUPDT
INTO :upd_emp, :upd_lname, :upd_tele, :upd_jobcd, :upd_wage,
3. When you want to update or delete a row, use an UPDATE or DELETE
statement using the WHERE CURRENT OF option. For example, to
update the current row, your program can issue:
EXEC SQL UPDATE TEMPL SET SALARY = :newsalary
WHERE CURRENT OF EMPLUPDT
4. After a COMMIT is issued, you must issue a FETCH before you can
update another row.
You should include code in your application to detect and handle an
SQLCODE -501 (SQLSTATE 24501), which can be returned on a FETCH or
CLOSE statement if your application either:
v Uses cursors declared WITH HOLD
v Executes more than one unit of work and leaves a WITH HOLD cursor
open across the unit of work boundary (COMMIT WORK).
If an application invalidates its package by dropping a table on which it is
dependent, the package gets rebound dynamically. If this is the case, an
SQLCODE -501 (SQLSTATE 24501) is returned for a FETCH or CLOSE
statement because the database manager closes the cursor. The way to handle
Chapter 4. Writing Static SQL Programs 83
an SQLCODE -501 (SQLSTATE 24501) in this situation depends on whether
you want to fetch rows from the cursor.
v If you want to fetch rows from the cursor, open the cursor, then run the
FETCH statement. Note, however, that the OPEN statement repositions the
cursor to the start. The previous position held at the COMMIT WORK
statement is lost.
v If you do not want to fetch rows from the cursor, do not issue any more
SQL requests against the cursor.
WITH RELEASE Option: When an application closes a cursor using the
WITH RELEASE option, DB2 attempts to release all READ locks that the
cursor still holds. The cursor will only continue to hold WRITE locks. If the
application closes the cursor without using the RELEASE option, the READ
and WRITE locks will be released when the unit of work completes.
Example: Cursor Program
This sample program shows the SQL statements that define and use a cursor.
The cursor is processed using static SQL. The sample is available in the
following programming languages:
C cursor.sqc
Java Cursor.sqlj
COBOL cursor.sqb
Since REXX does not support static SQL, a sample is not provided. See
“Example: Dynamic SQL Program” on page 133 for a REXX example that
processes a cursor dynamically.
How the Cursor Program Works
1. Declare the cursor. The DECLARE CURSOR statement associates the
cursor c1 to a query. The query identifies the rows that the application
retrieves using the FETCH statement. The job field of staff is defined to
be updatable, even though it is not specified in the result table.
2. Open the cursor. The cursor c1 is opened, causing the database manager
to perform the query and build a result table. The cursor is positioned
before the first row.
3. Retrieve a row. The FETCH statement positions the cursor at the next row
and moves the contents of the row into the host variables. This row
becomes the current row.
4. Close the cursor. The CLOSE statement is issued, releasing the resources
associated with the cursor. The cursor can be opened again, however.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
84 Application Development Guide
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl
FORTRAN CHECKERR is a subroutine located in the util.f file.
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
Chapter 4. Writing Static SQL Programs 85
C Example: CURSOR.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
int main(int argc, char *argv[])
{
EXEC SQL BEGIN DECLARE SECTION;
char pname[10];
short dept;
char userid[9];
char passwd[19];
EXEC SQL END DECLARE SECTION;
printf( "Sample C program: CURSOR \n" );
if (argc == 1)
{
EXEC SQL CONNECT TO sample;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else if (argc == 3)
{
strcpy (userid, argv[1]);
strcpy (passwd, argv[2]);
EXEC SQL CONNECT TO sample USER :userid USING :passwd;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else
{
printf ("\nUSAGE: cursor [userid passwd]\n\n");
return 1;
} /* endif */
EXEC SQL DECLARE c1 CURSOR FOR 1
SELECT name, dept FROM staff WHERE job='Mgr'
FOR UPDATE OF job;
EXEC SQL OPEN c1; 2
EMB_SQL_CHECK("OPEN CURSOR");
do
{
EXEC SQL FETCH c1 INTO :pname, :dept; 3
if (SQLCODE != 0) break;
printf( "%-10.10s in dept. %2d will be demoted to Clerk\n",
pname, dept );
} while ( 1 );
EXEC SQL CLOSE c1; 4
86 Application Development Guide
EMB_SQL_CHECK("CLOSE CURSOR");
EXEC SQL ROLLBACK;
EMB_SQL_CHECK("ROLLBACK");
printf( "\nOn second thought -- changes rolled back.\n" );
EXEC SQL CONNECT RESET;
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
/* end of program : CURSOR.SQC */
Chapter 4. Writing Static SQL Programs 87
Java Example: Cursor.sqlj
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
#sql iterator CursorByName(String name, short dept) ;
#sql iterator CursorByPos(String, short ) ;
class Cursor
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println (" Java Cursor Sample");
String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname
Connection con = null;
// Set the connection
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception("\nUsage: java Cursor [username password]\n");
}
// Set the default context
DefaultContext ctx = new DefaultContext(con);
DefaultContext.setDefaultContext(ctx);
// Enable transactions
con.setAutoCommit(false);
// Using cursors
try
{ CursorByName cursorByName;
CursorByPos cursorByPos;
88 Application Development Guide
String name = null;
short dept=0;
// Using the JDBC ResultSet cursor method
System.out.println("\nUsing the JDBC ResultSet cursor method");
System.out.println(" with a 'bind by name' cursor ...\n");
#sql cursorByName = {
SELECT name, dept FROM staff WHERE job='Mgr' }; 1
while (cursorByName.next()) 2
{ name = cursorByName.name(); 3
dept = cursorByName.dept();
System.out.print (" name= " + name);
System.out.print (" dept= " + dept);
System.out.print ("\n");
}
cursorByName.close(); 4
// Using the SQLJ iterator cursor method
System.out.println("\nUsing the SQLJ iterator cursor method");
System.out.println(" with a 'bind by position' cursor ...\n");
#sql cursorByPos = {
SELECT name, dept FROM staff WHERE job='Mgr' }; 1 2
while (true)
{ #sql { FETCH :cursorByPos INTO :name, :dept }; 3
if (cursorByPos.endFetch()) break;
System.out.print (" name= " + name);
System.out.print (" dept= " + dept);
System.out.print ("\n");
}
cursorByPos.close(); 4
}
catch( Exception e )
{ throw e;
}
finally
{ // Rollback the transaction
System.out.println("\nRollback the transaction...");
#sql { ROLLBACK };
System.out.println("Rollback done.");
}
}
catch( Exception e )
{ System.out.println (e);
}
}
}
Chapter 4. Writing Static SQL Programs 89
COBOL Example: CURSOR.SQB
Identification Division.
Program-ID. "cursor".
Data Division.
Working-Storage Section.
copy "sqlenv.cbl".
copy "sql.cbl".
copy "sqlca.cbl".
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 pname pic x(10).
77 dept pic s9(4) comp-5.
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
EXEC SQL END DECLARE SECTION END-EXEC.
77 errloc pic x(80).
Procedure Division.
Main Section.
display "Sample COBOL program: CURSOR".
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL DECLARE c1 CURSOR FOR 1
SELECT name, dept FROM staff
WHERE job='Mgr'
FOR UPDATE OF job END-EXEC.
EXEC SQL OPEN c1 END-EXEC. 2
move "OPEN CURSOR" to errloc.
call "checkerr" using SQLCA errloc.
90 Application Development Guide
perform Fetch-Loop thru End-Fetch-Loop
until SQLCODE not equal 0.
EXEC SQL CLOSE c1 END-EXEC. 4
move "CLOSE CURSOR" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL ROLLBACK END-EXEC.
move "ROLLBACK" to errloc.
call "checkerr" using SQLCA errloc.
DISPLAY "On second thought -- changes rolled back.".
EXEC SQL CONNECT RESET END-EXEC.
move "CONNECT RESET" to errloc.
call "checkerr" using SQLCA errloc.
End-Main.
go to End-Prog.
Fetch-Loop Section.
EXEC SQL FETCH c1 INTO :PNAME, :DEPT END-EXEC. 3
if SQLCODE not equal 0
go to End-Fetch-Loop.
display pname, " in dept. ", dept,
" will be demoted to Clerk".
End-Fetch-Loop. exit.
End-Prog.
stop run.
Chapter 4. Writing Static SQL Programs 91
Updating and Deleting Retrieved Data
It is possible to update and delete the row referenced by a cursor. For a row
to be updatable, the query corresponding to the cursor must not be read-only.
For a description of what makes a query updatable or deletable, refer to the
SQL Reference.
Updating Retrieved Data
To update with a cursor, use the WHERE CURRENT OF clause in an
UPDATE statement. Use the FOR UPDATE clause to tell the system that you
want to update some columns of the result table. You can specify a column in
the FOR UPDATE without it being in the fullselect; therefore, you can update
columns that are not explicitly retrieved by the cursor. If the FOR UPDATE
clause is specified without column names, all columns of the table or view
identified in the first FROM clause of the outer fullselect are considered to be
updatable. Do not name more columns than you need in the FOR UPDATE
clause. In some cases, naming extra columns in the FOR UPDATE clause can
cause DB2 to be less efficient in accessing the data.
Deleting Retrieved Data
Deletion with a cursor is done using the WHERE CURRENT OF clause in a
DELETE statement. In general, the FOR UPDATE clause is not required for
deletion of the current row of a cursor. The only exception occurs when using
dynamic SQL (see “Chapter 5. Writing Dynamic SQL Programs” on page 127
for information on dynamic SQL) for either the SELECT statement or the
DELETE statement in an application which has been precompiled with
LANGLEVEL set to SAA1, and bound with BLOCKING ALL. In this case, a
FOR UPDATE clause is necessary in the SELECT statement. Refer to the
Command Reference for information on the precompiler options.
The DELETE statement causes the row being referenced by the cursor to be
deleted. This leaves the cursor positioned before the next row and a FETCH
statement must be issued before additional WHERE CURRENT OF operations
may be performed against the cursor.
Types of Cursors
Cursors fall into three categories:
Read only
The rows in the cursor can only be read, not updated. Read-only
cursors are used when an application will only read data, not modify
it. A cursor is considered read only if it is based on a read-only
select-statement. See the rules in “Updating Retrieved Data” for
select-statements which define non-updatable result tables.
There can be performance advantages for read-only cursors. For more
information on read-only cursors, refer to the Administration Guide:
Implementation.
92 Application Development Guide
Updatable
The rows in the cursor can be updated. Updatable cursors are used
when an application modifies data as the rows in the cursor are
fetched. The specified query can only refer to one table or view. The
query must also include the FOR UPDATE clause, naming each
column that will be updated (unless the LANGLEVEL MIA
precompile option is used).
Ambiguous
The cursor cannot be determined to be updatable or read only from
its definition or context. This can happen when a dynamic SQL
statement is encountered that could be used to change a cursor that
would otherwise be considered read-only.
An ambiguous cursor is treated as read only if the BLOCKING ALL
option is specified when precompiling or binding. Otherwise, it is
considered updatable.
Note: Cursors processed dynamically are always ambiguous.
For a complete list of criteria used to determine whether a cursor is read-only,
updatable, or ambiguous, refer to the SQL Reference.
Example: OPENFTCH Program
This example selects from a table using a cursor, opens the cursor, and fetches
rows from the table. For each row fetched, it decides if the row should be
deleted or updated (based on a simple criteria). The sample is available in the
following programming languages:
C openftch.sqc
Java Openftch.sqlj and OpF_Curs.sqlj
COBOL openftch.sqb
The REXX language does not support static SQL, so a sample is not provided.
How the OPENFTCH Program Works
1. Declare the cursor. The DECLARE CURSOR statement associates the
cursor c1 to a query. The query identifies the rows that the application
retrieves using the FETCH statement. The job field of staff is defined to
be updatable, even though it is not specified in the result table.
2. Open the cursor. The cursor c1 is opened, causing the database manager
to perform the query and build a result table. The cursor is positioned
before the first row.
3. Retrieve a row. The FETCH statement positions the cursor at the next row
and moves the contents of the row into the host variables. This row
becomes the current row.
Chapter 4. Writing Static SQL Programs 93
4. Update OR Delete the current row. The current row is either updated or
deleted, depending upon the value of dept returned with the FETCH
statement.
If an UPDATE is performed, the position of the cursor remains on this row
because the UPDATE statement does not change the position of the
current row.
If a DELETE statement is performed, a different situation arises, because
the current row is deleted. This is equivalent to being positioned before the
next row, and a FETCH statement must be issued before additional
WHERE CURRENT OF operations are performed.
5. Close the cursor. The CLOSE statement is issued, releasing the resources
associated with the cursor. The cursor can be opened again, however.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl.
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
94 Application Development Guide
C Example: OPENFTCH.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
int main(int argc, char *argv[])
{
EXEC SQL BEGIN DECLARE SECTION;
char pname[10];
short dept;
char userid[9];
char passwd[19];
EXEC SQL END DECLARE SECTION;
printf( "Sample C program: OPENFTCH\n" );
if (argc == 1)
{
EXEC SQL CONNECT TO sample;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else if (argc == 3)
{
strcpy (userid, argv[1]);
strcpy (passwd, argv[2]);
EXEC SQL CONNECT TO sample USER :userid USING :passwd;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else
{
printf ("\nUSAGE: openftch [userid passwd]\n\n");
return 1;
} /* endif */
EXEC SQL DECLARE c1 CURSOR FOR 1
SELECT name, dept FROM staff WHERE job='Mgr'
FOR UPDATE OF job;
EXEC SQL OPEN c1; 2
EMB_SQL_CHECK("OPEN CURSOR");
do
{
EXEC SQL FETCH c1 INTO :pname, :dept; 3
if (SQLCODE != 0) break;
if (dept > 40)
{
printf( "%-10.10s in dept. %2d will be demoted to Clerk\n",
pname, dept );
EXEC SQL UPDATE staff SET job = 'Clerk' 4
Chapter 4. Writing Static SQL Programs 95
WHERE CURRENT OF c1;
EMB_SQL_CHECK("UPDATE STAFF");
}
else
{
printf ("%-10.10s in dept. %2d will be DELETED!\n",
pname, dept);
EXEC SQL DELETE FROM staff WHERE CURRENT OF c1;
EMB_SQL_CHECK("DELETE");
} /* endif */
} while ( 1 );
EXEC SQL CLOSE c1; 5
EMB_SQL_CHECK("CLOSE CURSOR");
EXEC SQL ROLLBACK;
EMB_SQL_CHECK("ROLLBACK");
printf( "\nOn second thought -- changes rolled back.\n" );
EXEC SQL CONNECT RESET;
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
/* end of program : OPENFTCH.SQC */
96 Application Development Guide
Java Example: Openftch.sqlj
OpF_Curs.sqlj
// PURPOSE : This file, named OpF_Curs.sqlj, contains the definition
// of the class OpF_Curs used in the sample program Openftch.
import sqlj.runtime.ForUpdate;
#sql public iterator OpF_Curs implements ForUpdate (String, short);
Openftch.sqlj
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
class Openftch
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println (" Java Openftch Sample");
String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname
Connection con = null;
// Set the connection
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception(
"\nUsage: java Openftch [username password]\n");
} // if - else if - else
// Set the default context
DefaultContext ctx = new DefaultContext(con);
DefaultContext.setDefaultContext(ctx);
// Enable transactions
Chapter 4. Writing Static SQL Programs 97
con.setAutoCommit(false);
// Executing SQLJ positioned update/delete statements.
try
{ OpF_Curs forUpdateCursor;
String name = null;
short dept=0;
#sql forUpdateCursor =
{ SELECT name, dept
FROM staff
WHERE job='Mgr'
}; // #sql 12
while (true)
{ #sql
{ FETCH :forUpdateCursor
INTO :name, :dept
}; // #sql 3
if (forUpdateCursor.endFetch()) break;
if (dept > 40)
{ System.out.println (
name + " in dept. "
+ dept + " will be demoted to Clerk");
#sql
{ UPDATE staff SET job = 'Clerk'
WHERE CURRENT OF :forUpdateCursor
}; // #sql 4
}
else
{ System.out.println (
name + " in dept. " + dept
+ " will be DELETED!");
#sql
{ DELETE FROM staff
WHERE CURRENT OF :forUpdateCursor
}; // #sql
} // if - else
}
forUpdateCursor.close(); 5
}
catch( Exception e )
{ throw e;
}
finally
{ // Rollback the transaction
System.out.println("\nRollback the transaction...");
#sql { ROLLBACK };
System.out.println("Rollback done.");
} // try - catch - finally
}
catch( Exception e )
98 Application Development Guide
{ System.out.println (e);
} // try - catch
} // main
} // class Openftch
Chapter 4. Writing Static SQL Programs 99
COBOL Example: OPENFTCH.SQB
Identification Division.
Program-ID. "openftch".
Data Division.
Working-Storage Section.
copy "sqlca.cbl".
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 pname pic x(10).
01 dept pic s9(4) comp-5.
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
EXEC SQL END DECLARE SECTION END-EXEC.
77 errloc pic x(80).
Procedure Division.
Main Section.
display "Sample COBOL program: OPENFTCH".
* Get database connection information.
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL DECLARE c1 CURSOR FOR 1
SELECT name, dept FROM staff
WHERE job='Mgr'
FOR UPDATE OF job END-EXEC.
EXEC SQL OPEN c1 END-EXEC 2
move "OPEN" to errloc.
call "checkerr" using SQLCA errloc.
* call the FETCH and UPDATE/DELETE loop.
100 Application Development Guide
perform Fetch-Loop thru End-Fetch-Loop
until SQLCODE not equal 0.
EXEC SQL CLOSE c1 END-EXEC. 5
move "CLOSE" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL ROLLBACK END-EXEC.
move "ROLLBACK" to errloc.
call "checkerr" using SQLCA errloc.
display "On second thought -- changes rolled back.".
EXEC SQL CONNECT RESET END-EXEC.
move "CONNECT RESET" to errloc.
call "checkerr" using SQLCA errloc.
End-Main.
go to End-Prog.
Fetch-Loop Section.
EXEC SQL FETCH c1 INTO :pname, :dept END-EXEC. 3
if SQLCODE not equal 0
go to End-Fetch-Loop.
if dept greater than 40
go to Update-Staff.
Delete-Staff.
display pname, " in dept. ", dept,
" will be DELETED!".
EXEC SQL DELETE FROM staff WHERE CURRENT OF c1 END-EXEC.
move "DELETE" to errloc.
call "checkerr" using SQLCA errloc.
go to End-Fetch-Loop.
Update-Staff.
display pname, " in dept. ", dept,
" will be demoted to Clerk".
EXEC SQL UPDATE staff SET job = 'Clerk' 4
WHERE CURRENT OF c1 END-EXEC.
move "UPDATE" to errloc.
call "checkerr" using SQLCA errloc.
End-Fetch-Loop. exit.
End-Prog.
stop run.
Chapter 4. Writing Static SQL Programs 101
Advanced Scrolling Techniques
The following topics on advanced scrolling techniques are discussed in this
section:
v Scrolling Through Data that has Already Been Retrieved
v Keeping a Copy of the Data
v Retrieving the Data a Second Time
v Establishing a Position at the End of a Table
v Updating Previously Retrieved Data
Scrolling Through Data that has Already Been Retrieved
When an application retrieves data from the database, the FETCH statement
allows it to scroll forward through the data, however, the database manager
has no embedded SQL statement that allows it scroll backwards through the
data, (equivalent to a backward FETCH). DB2 CLI and Java, however, do
support a backward FETCH through read-only scrollable cursors. Refer to the
CLI Guide and Reference and see “Creating Java Applications and Applets” on
page 628 for more information on scrollable cursors. For embedded SQL
applications, you can use the following techniques to scroll through data that
has been retrieved:
1. Keep a copy of the data that has been fetched and scroll through it by
some programming technique.
2. Use SQL to retrieve the data again, typically by a second SELECT
statement.
These options are discussed in more detail in:
v Keeping a Copy of the Data
v Retrieving the Data a Second Time
Keeping a Copy of the Data
An application can save fetched data in virtual storage. If the data does not fit
in virtual storage, the application can write the data to a temporary file. One
effect of this approach is that a user, scrolling backward, always sees exactly
the same data that was fetched, even if the data in the database was changed
in the interim by a transaction.
Using an isolation level of repeatable read, the data you retrieve from a
transaction can be retrieved again by closing and opening a cursor. Other
applications are prevented from updating the data in your result set. Isolation
levels and locking can affect how users update data.
Retrieving the Data a Second Time
This technique depends on the order in which you want to see the data again:
v Retrieving from the Beginning
v Retrieving from the Middle
v Order of Rows in the Second Result Table
v Retrieving in Reverse Order
102 Application Development Guide
Retrieving from the Beginning
To retrieve the data again from the beginning, merely close the active cursor
and reopen it. This action positions the cursor at the beginning of the result
table. But, unless the application holds locks on the table, others may have
changed it, so what had been the first row of the result table may no longer
be.
Retrieving from the Middle
To retrieve data a second time from somewhere in the middle of the result
table, execute a second SELECT statement and declare a second cursor on the
statement. For example, suppose the first SELECT statement was:
SELECT * FROM DEPARTMENT
WHERE LOCATION = 'CALIFORNIA'
ORDER BY DEPTNO
Now, suppose that you want to return to the rows that start with
DEPTNO = 'M95' and fetch sequentially from that point. Code the following:
SELECT * FROM DEPARTMENT
WHERE LOCATION = 'CALIFORNIA'
AND DEPTNO >= 'M95'
ORDER BY DEPTNO
This statement positions the cursor where you want it.
Order of Rows in the Second Result Table
The rows of the second result table may not be displayed in the same order as
in the first. The database manager does not consider the order of rows as
significant unless the SELECT statement uses ORDER BY. Thus, if there are
several rows with the same DEPTNO value, the second SELECT statement may
retrieve them in a different order from the first. The only guarantee is that
they will all be in order by department number, as demanded by the clause
ORDER BY DEPTNO.
The difference in ordering could occur even if you were to execute the same
SQL statement, with the same host variables, a second time. For example, the
statistics in the catalog could be updated between executions, or indexes
could be created or dropped. You could then execute the SELECT statement
again.
The ordering is more likely to change if the second SELECT has a predicate
that the first did not have; the database manager could choose to use an index
on the new predicate. For example, it could choose an index on LOCATION for
the first statement in our example and an index on DEPTNO for the second.
Because rows are fetched in order by the index key, the second order need not
be the same as the first.
Chapter 4. Writing Static SQL Programs 103
Again, executing two similar SELECT statements can produce a different
ordering of rows, even if no statistics change and no indexes are created or
dropped. In the example, if there are many different values of LOCATION, the
database manager could choose an index on LOCATION for both statements. Yet
changing the value of DEPTNO in the second statement to the following, could
cause the database manager to choose an index on DEPTNO:
SELECT * FROM DEPARTMENT
WHERE LOCATION = 'CALIFORNIA'
AND DEPTNO >= 'Z98'
ORDER BY DEPTNO
Because of the subtle relationships between the form of an SQL statement and
the values in this statement, never assume that two different SQL statements
will return rows in the same order unless the order is uniquely determined by
an ORDER BY clause.
Retrieving in Reverse Order
Ascending ordering of rows is the default. If there is only one row for each
value of DEPTNO, then the following statement specifies a unique ascending
ordering of rows:
SELECT * FROM DEPARTMENT
WHERE LOCATION = 'CALIFORNIA'
ORDER BY DEPTNO
To retrieve the same rows in reverse order, specify that the order is
descending, as in the following statement:
SELECT * FROM DEPARTMENT
WHERE LOCATION = 'CALIFORNIA'
ORDER BY DEPTNO DESC
A cursor on the second statement retrieves rows in exactly the opposite order
from a cursor on the first statement. Order of retrieval is guaranteed only if
the first statement specifies a unique ordering sequence.
For retrieving rows in reverse order, it can be useful to have two indexes on
the DEPTNO column, one in ascending order and the other in descending order.
Establishing a Position at the End of a Table
The database manager does not guarantee an order to data stored in a table;
therefore, the end of a table is not defined. However, order is defined on the
result of an SQL statement:
SELECT * FROM DEPARTMENT
ORDER BY DEPTNO DESC
For this example, the following statement positions the cursor at the row with
the highest DEPTNO value:
104 Application Development Guide
SELECT * FROM DEPARTMENT
WHERE DEPTNO =
(SELECT MAX(DEPTNO) FROM DEPARTMENT)
Note, however, that if several rows have the same value, the cursor is
positioned on the first of them.
Updating Previously Retrieved Data
To scroll backward and update data that was retrieved previously, you can
use a combination of the techniques discussed in “Scrolling Through Data that
has Already Been Retrieved” on page 102 and “Updating Retrieved Data” on
page 92. You can do one of two things:
1. If you have a second cursor on the data to be updated and if the SELECT
statement uses none of the restricted elements, you can use a
cursor-controlled UPDATE statement. Name the second cursor in the
WHERE CURRENT OF clause.
2. In other cases, use UPDATE with a WHERE clause that names all the
values in the row or specifies the primary key of the table. You can
execute one statement many times with different values of the variables.
Example: UPDAT Program
The UPDAT program uses dynamic SQL to access the STAFF table in the
SAMPLE database and changes all managers to clerks. Then the program
reverses the changes by rolling back the unit of work. The sample is available
in the following programming languages:
C updat.sqc
Java Updat.sqlj
COBOL updat.sqb
REXX updat.cmd
How the UPDAT Program Works
1. Define an SQLCA structure. The INCLUDE SQLCA statement defines and
declares an SQLCA structure, and defines SQLCODE as an element within
the structure. The SQLCODE field of the SQLCA structure is updated with
error information by the database manager after execution of SQL
statements and database manager API calls.
Java applications access SQLCODE and SQLSTATE through the methods
defined for the SQLException object, and therefore do not need an
equivalent ″include SQLCA″ statement.
REXX applications have one occurrence of an SQLCA structure, named
SQLCA, predefined for application use. It can be referenced without
application definition.
2. Declare host variables. The BEGIN DECLARE SECTION and END
DECLARE SECTION statements delimit the host variable declarations.
Chapter 4. Writing Static SQL Programs 105
Host variables are used to pass data to and from the database manager.
They are prefixed with a colon (:) when referenced in an SQL statement.
Java and REXX applications do not need to declare host variables, except
(for REXX) in the case of LOB file reference variables and locators. Host
variable data types and sizes are determined at run time when the
variables are referenced.
3. Connect to database. The program connects to the sample database, and
requests shared access to it. (It is assumed that a START DATABASE
MANAGER API call or db2start command has been issued.) Other
programs that connect to the same database using shared access are also
granted access.
4. Execute the UPDATE SQL statement. The SQL statement is executed
statically with the use of a host variable. The job column of the staff
tables is set to the value of the host variable, where the job column has a
value of Mgr.
5. Execute the DELETE SQL statement The SQL statement is executed
statically with the use of a host variable. All rows that have a job column
value equal to that of the specified host variable, (jobUpdate/job-
update/job_update) are deleted.
6. Execute the INSERT SQL statement A row is inserted into the STAFF table.
This insertion implements the use of a host variable which was set prior to
the execution of this SQL statement.
7. End the transaction. End the unit of work with a ROLLBACK statement.
The result of the SQL statement executed previously can be either made
permanent using the COMMIT statement, or undone using the
ROLLBACK statement. All SQL statements within the unit of work are
affected.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl.
REXX CHECKERR is a procedure located at bottom of the current
program.
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
106 Application Development Guide
C Example: UPDAT.SQC
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlenv.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA; 1
int main(int argc, char *argv[])
{
EXEC SQL BEGIN DECLARE SECTION; 2
char statement[256];
char userid[9];
char passwd[19];
char jobUpdate[6];
EXEC SQL END DECLARE SECTION;
printf( "\nSample C program: UPDAT \n");
if (argc == 1)
{
EXEC SQL CONNECT TO sample;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else if (argc == 3)
{
strcpy (userid, argv[1]);
strcpy (passwd, argv[2]);
EXEC SQL CONNECT TO sample USER :userid USING :passwd; 3
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else
{
printf ("\nUSAGE: updat [userid passwd]\n\n");
return 1;
} /* endif */
strcpy (jobUpdate, "Clerk");
EXEC SQL UPDATE staff SET job = :jobUpdate WHERE job = 'Mgr'; 4
EMB_SQL_CHECK("UPDATE STAFF");
printf ("All 'Mgr' have been demoted to 'Clerk'!\n" );
strcpy (jobUpdate, "Sales");
EXEC SQL DELETE FROM staff WHERE job = :jobUpdate; 5
EMB_SQL_CHECK("DELETE FROM STAFF");
printf ("All 'Sales' people have been deleted!\n");
EXEC SQL INSERT INTO staff
VALUES (999, 'Testing', 99, :jobUpdate, 0, 0, 0); 6
EMB_SQL_CHECK("INSERT INTO STAFF");
printf ("New data has been inserted\n");
EXEC SQL ROLLBACK; 7
Chapter 4. Writing Static SQL Programs 107
EMB_SQL_CHECK("ROLLBACK");
printf( "On second thought -- changes rolled back.\n" );
EXEC SQL CONNECT RESET;
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
/* end of program : UPDAT.SQC */
108 Application Development Guide
Java Example: Updat.sqlj
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
class Updat
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println ("\n Java Updat Sample");
String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname
Connection con = null;
// Set the connection 3
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception("\nUsage: java Updat [username password]\n");
}
// Set the default context
DefaultContext ctx = new DefaultContext(con);
DefaultContext.setDefaultContext(ctx);
// Enable transactions
con.setAutoCommit(false);
// UPDATE/DELETE/INSERT
try
{ String jobUpdate = null;
jobUpdate="Clerk";
#sql {UPDATE staff SET job = :jobUpdate WHERE job = 'Mgr'}; 4
Chapter 4. Writing Static SQL Programs 109
System.out.println("\nAll 'Mgr' have been demoted to 'Clerk'!");
jobUpdate="Sales";
#sql {DELETE FROM staff WHERE job = :jobUpdate};
System.out.println("All 'Sales' people have been deleted!"); 5
#sql {INSERT INTO staff
VALUES (999, 'Testing', 99, :jobUpdate, 0, 0, 0)}; 6
System.out.println("New data has been inserted");
}
catch( Exception e )
{ throw e;
}
finally
{ // Rollback the transaction
System.out.println("\nRollback the transaction...");
#sql { ROLLBACK }; 7
System.out.println("Rollback done.");
}
}
catch (Exception e)
{ System.out.println (e);
}
}
}
110 Application Development Guide
COBOL Example: UPDAT.SQB
Identification Division.
Program-ID. "updat".
Data Division.
Working-Storage Section.
copy "sql.cbl".
copy "sqlenv.cbl".
copy "sqlca.cbl". 1
EXEC SQL BEGIN DECLARE SECTION END-EXEC. 2
01 statement pic x(80).
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
01 job-update pic x(5).
EXEC SQL END DECLARE SECTION END-EXEC.
* Local variables
77 errloc pic x(80).
77 error-rc pic s9(9) comp-5.
77 state-rc pic s9(9) comp-5.
* Variables for the GET ERROR MESSAGE API
* Use application specific bound instead of BUFFER-SZ
77 buffer-size pic s9(4) comp-5 value 1024.
77 line-width pic s9(4) comp-5 value 80.
77 error-buffer pic x(1024).
77 state-buffer pic x(1024).
Procedure Division.
Main Section.
display "Sample COBOL program: UPDAT".
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd 3
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
Chapter 4. Writing Static SQL Programs 111
move "Clerk" to job-update.
EXEC SQL UPDATE staff SET job=:job-update 4
WHERE job='Mgr' END-EXEC.
move "UPDATE STAFF" to errloc.
call "checkerr" using SQLCA errloc.
display "All 'Mgr' have been demoted to 'Clerk'!".
move "Sales" to job-update.
EXEC SQL DELETE FROM staff WHERE job=:job-update END-EXEC. 5
move "DELETE FROM STAFF" to errloc.
call "checkerr" using SQLCA errloc.
display "All 'Sales' people have been deleted!".
EXEC SQL INSERT INTO staff VALUES (999, 'Testing', 99, 6
:job-update, 0, 0, 0) END-EXEC.
move "INSERT INTO STAFF" to errloc.
call "checkerr" using SQLCA errloc.
display "New data has been inserted".
EXEC SQL ROLLBACK END-EXEC. 7
move "ROLLBACK" to errloc.
call "checkerr" using SQLCA errloc.
DISPLAY "On second thought -- changes rolled back."
EXEC SQL CONNECT RESET END-EXEC.
move "CONNECT RESET" to errloc.
call "checkerr" using SQLCA errloc.
End-Prog.
stop run.
112 Application Development Guide
REXX Example: UPDAT.CMD
Note: REXX programs cannot contain static SQL. This program is written
with dynamic SQL.
/* REXX program UPDAT.CMD */
parse version rexxType .
parse source platform .
if platform == 'AIX/6000' & rexxType == 'REXXSAA' then
do
rcy = SysAddFuncPkg("db2rexx")
end
else
do
if RxFuncQuery('SQLDBS') <> 0 then
rcy = RxFuncAdd( 'SQLDBS', 'db2ar', 'SQLDBS' )
if RxFuncQuery('SQLEXEC') <> 0 then
rcy = RxFuncAdd( 'SQLEXEC', 'db2ar', 'SQLEXEC' )
end
/* pull in command line arguments */
parse arg userid passwd .
/* check to see if the proper number of arguments have been passed in */
PARSE ARG dbname userid password .
if ((dbname = "" ) | ,
(userid <> "" & password = "") ,
) then do
SAY "USAGE: updat.cmd <dbname> [<userid> <password>]"
exit -1
end
/* connect to database */
SAY
SAY 'Connect to' dbname
IF password= "" THEN
CALL SQLEXEC 'CONNECT TO' dbname
ELSE
CALL SQLEXEC 'CONNECT TO' dbname 'USER' userid 'USING' password
CALL CHECKERR 'Connect to '
SAY "Connected"
say 'Sample REXX program: UPDAT.CMD'
jobupdate = "'Clerk'"
st = "UPDATE staff SET job =" jobupdate "WHERE job = 'Mgr'"
call SQLEXEC 'EXECUTE IMMEDIATE :st' 4
call CHECKERR 'UPDATE'
say "All 'Mgr' have been demoted to 'Clerk'!"
Chapter 4. Writing Static SQL Programs 113
jobupdate = "'Sales'"
st = "DELETE FROM staff WHERE job =" jobupdate
call SQLEXEC 'EXECUTE IMMEDIATE :st' 5
call CHECKERR 'DELETE'
say "All 'Sales' people have been deleted!"
st = "INSERT INTO staff VALUES (999, 'Testing', 99," jobupdate ", 0, 0, 0)"
call SQLEXEC 'EXECUTE IMMEDIATE :st' 6
call CHECKERR 'INSERT'
say 'New data has been inserted'
call SQLEXEC 'ROLLBACK' 7
call CHECKERR 'ROLLBACK'
say 'On second thought...changes rolled back.'
call SQLEXEC 'CONNECT RESET'
call CHECKERR 'CONNECT RESET'
CHECKERR:
arg errloc
if ( SQLCA.SQLCODE = 0 ) then
return 0
else do
say '--- error report ---'
say 'ERROR occurred :' errloc
say 'SQLCODE :' SQLCA.SQLCODE
/******************************\
* GET ERROR MESSAGE API called *
\******************************/
call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80'
say errmsg
say '--- end error report ---'
if (SQLCA.SQLCODE < 0 ) then
exit
else do
say 'WARNING - CONTINUING PROGRAM WITH ERRORS'
return 0
end
end
return 0
114 Application Development Guide
Diagnostic Handling and the SQLCA Structure
Applications issuing SQL statements and calling database manager APIs must
properly check for error conditions by examining return codes and the
SQLCA structure.
Return Codes
Most database manager APIs pass back a zero return code when successful. In
general, a non-zero return code indicates that the secondary error handling
mechanism, the SQLCA structure, may be corrupt. In this case, the called API
is not executed. A possible cause for a corrupt SQLCA structure is passing an
invalid address for the structure.
SQLCODE and SQLSTATE
Error information is returned in the SQLCODE and SQLSTATE fields of the
SQLCA structure, which is updated after every executable SQL statement and
most database manager API calls.
A source file containing executable SQL statements can provide at least one
SQLCA structure with the name sqlca. The SQLCA structure is defined in the
SQLCA include file. Source files without embedded SQL statements, but
calling database manager APIs, can also provide one or more SQLCA
structures, but their names are arbitrary.
If your application is compliant with the FIPS 127-2 standard, you can declare
the SQLSTATE and SQLCODE as host variables instead of using the SQLCA
structure. For information on how to do this, see “SQLSTATE and SQLCODE
Variables in C and C++” on page 620 for C or C++ applications, “SQLSTATE
and SQLCODE Variables in COBOL” on page 685 for COBOL applications, or
“SQLSTATE and SQLCODE Variables in FORTRAN” on page 700 for
FORTRAN applications.
An SQLCODE value of 0 means successful execution (with possible
SQLWARN warning conditions). A positive value means that the statement
was successfully executed but with a warning, as with truncation of a host
variable. A negative value means that an error condition occurred.
An additional field, SQLSTATE, contains a standardized error code consistent
across other IBM database products and across SQL92 conformant database
managers. Practically speaking, you should use SQLSTATEs when you are
concerned about portability since SQLSTATEs are common across many
database managers.
The SQLWARN field contains an array of warning indicators, even if
SQLCODE is zero. The first element of the SQLWARN array, SQLWARN0,
contains a blank if all other elements are blank. SQLWARN0 contains a W if at
least one other element contains a warning character.
Chapter 4. Writing Static SQL Programs 115
Refer to the Administrative API Reference for more information about the
SQLCA structure, and the Message Reference for a listing of SQLCODE and
SQLSTATE error conditions.
Note: If you want to develop applications that access various IBM RDBMS
servers you should:
v Where possible, have your applications check the SQLSTATE rather
than the SQLCODE.
v If your applications will use DB2 Connect, consider using the
mapping facility provided by DB2 Connect to map SQLCODE
conversions between unlike databases.
Token Truncation in SQLCA Structure
Since tokens may be truncated in the SQLCA structure, you should not use
the token info for diagnostic purposes. While you can define table and
column names with lengths of up to 128 bytes, the SQLCA tokens will be
truncated to 17 bytes plus a truncation terminator (>). Application logic
should not depend on actual values of the sqlerrmc field. Refer to the SQL
Reference for a description of the SQLCA structure, and a discussion of token
truncation.
Handling Errors using the WHENEVER Statement
The WHENEVER statement causes the precompiler to generate source code
that directs the application to go to a specified label if an error, warning, or if
no rows are found during execution. The WHENEVER statement affects all
subsequent executable SQL statements until another WHENEVER statement
alters the situation.
The WHENEVER statement has three basic forms:
EXEC SQL WHENEVER SQLERROR action
EXEC SQL WHENEVER SQLWARNING action
EXEC SQL WHENEVER NOT FOUND action
In the above statements:
SQLERROR
Identifies any condition where SQLCODE < 0.
SQLWARNING
Identifies any condition where SQLWARN(0) = W or SQLCODE > 0
but is not equal to 100.
NOT FOUND
Identifies any condition where SQLCODE = 100.
In each case, the action can be either of the following:
116 Application Development Guide
CONTINUE
Indicates to continue with the next instruction in the application.
GO TO label
Indicates to go to the statement immediately following the label
specified after GO TO. (GO TO can be two words, or one word,
GOTO.)
If the WHENEVER statement is not used, the default action is to continue
processing if an error, warning, or exception condition occurs during
execution.
The WHENEVER statement must appear before the SQL statements you want
to affect. Otherwise, the precompiler does not know that additional
error-handling code should be generated for the executable SQL statements.
You can have any combination of the three basic forms active at any time. The
order in which you declare the three forms is not significant. To avoid an
infinite looping situation, ensure that you undo the WHENEVER handling
before any SQL statements are executed inside the handler. You can do this
using the WHENEVER SQLERROR CONTINUE statement.
For a complete description of the WHENEVER statement, refer to the SQL
Reference.
Exception, Signal, Interrupt Handler Considerations
An exception, signal, or interrupt handler is a routine that gets control when
an exception, signal, or interrupt occurs. The type of handler applicable is
determined by your operating environment, as shown in the following:
Windows 32-bit Operating Systems
Pressing Ctrl-C or Ctrl-Break generates an interrupt.
OS/2 Pressing Ctrl-C or Ctrl-Break generates an operating system
exception.
UNIX Usually, pressing Ctrl-C generates the SIGINT interrupt signal. Note
that keyboards can easily be redefined so SIGINT may be generated
by a different key sequence on your machine.
For other operating systems that are not in the above list, refer to the
Application Building Guide.
Do not put SQL statements (other than COMMIT or ROLLBACK) in
exception, signal, and interrupt handlers. With these kinds of error conditions,
you normally want to do a ROLLBACK to avoid the risk of inconsistent data.
Note that you should exercise caution when coding a COMMIT and
ROLLBACK in exception/signal/interrupt handlers. If you call either of these
Chapter 4. Writing Static SQL Programs 117
statements by themselves, the COMMIT or ROLLBACK is not executed until
the current SQL statement is complete, if one is running. This is not the
behavior desired from a Ctrl-C handler.
The solution is to call the INTERRUPT API (sqleintr/sqlgintr) before
issuing a ROLLBACK. This interrupts the current SQL query (if the
application is executing one) and lets the ROLLBACK begin immediately. If
you are going to perform a COMMIT rather than a ROLLBACK, you do not
want to interrupt the current command.
When using APPC to access a remote database server (DB2 for AIX or host
database system using DB2 Connect), the application may receive a SIGUSR1
signal. This signal is generated by SNA Services/6000 when an unrecoverable
error occurs and the SNA connection is stopped. You may want to install a
signal handler in your application to handle SIGUSR1.
Refer to your platform documentation for specific details on the various
handler considerations.
Exit List Routine Considerations
Do not use SQL or DB2 API calls in exit list routines. Note that you cannot
disconnect from a database in an exit routine.
Using GET ERROR MESSAGE in Example Programs
The code clips shown in “C Example: UTILAPI.C” on page 119 and “COBOL
Example: CHECKERR.CBL” on page 122 demonstrate the use of the GET
ERROR MESSAGE API to obtain the corresponding information related to the
SQLCA passed in.
You can find information on building these examples in the README files, or in
the header section of these sample programs.
118 Application Development Guide
C Example: UTILAPI.C
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlenv.h>
#include <sqlda.h>
#include <sqlca.h>
#include <string.h>
#include <ctype.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
/*#############################################################################
** 1. SQL_CHECK section
**
** 1.1 - SqlInfoPrint - prints on the screen everything that
** goes unexpected.
** 1.2 - TransRollback - rolls back the transaction
#############################################################################*/
/******************************************************************************
** 1.1 - SqlInfoPrint - prints on the screen everything that
** goes unexpected.
******************************************************************************/
int SqlInfoPrint( char * appMsg,
struct sqlca * pSqlca,
int line,
char * file )
{ int rc = 0;
char sqlInfo[1024];
char sqlInfoToken[1024];
char sqlstateMsg[1024];
char errorMsg[1024];
if (pSqlca->sqlcode != 0 && pSqlca->sqlcode != 100)
{ strcpy(sqlInfo, "");
if( pSqlca->sqlcode < 0)
{ sprintf( sqlInfoToken, "\n---- error report ----\n");
strcat( sqlInfo, sqlInfoToken);
}
else
{ sprintf( sqlInfoToken, "\n---- warning report ----\n");
strcat( sqlInfo, sqlInfoToken);
} /* endif */
sprintf( sqlInfoToken, " app. message = %s\n", appMsg);
strcat( sqlInfo, sqlInfoToken);
sprintf( sqlInfoToken, " line = %d\n", line);
strcat( sqlInfo, sqlInfoToken);
sprintf( sqlInfoToken, " file = %s\n", file);
Chapter 4. Writing Static SQL Programs 119
strcat( sqlInfo, sqlInfoToken);
sprintf( sqlInfoToken, " SQLCODE = %ld\n", pSqlca->sqlcode);
strcat( sqlInfo, sqlInfoToken);
/* get error message */
rc = sqlaintp( errorMsg, 1024, 80, pSqlca);
/* return code is the length of the errorMsg string */
if( rc > 0)
{ sprintf( sqlInfoToken, "%s\n", errorMsg);
strcat( sqlInfo, sqlInfoToken);
}
/* get SQLSTATE message */
rc = sqlogstt( sqlstateMsg, 1024, 80, pSqlca->sqlstate);
if (rc == 0)
{ sprintf( sqlInfoToken, "%s\n", sqlstateMsg);
strcat( sqlInfo, sqlInfoToken);
}
if( pSqlca->sqlcode < 0)
{ sprintf( sqlInfoToken, "--- end error report ---\n");
strcat( sqlInfo, sqlInfoToken);
printf("%s", sqlInfo);
return 1;
}
else
{ sprintf( sqlInfoToken, "--- end warning report ---\n");
strcat( sqlInfo, sqlInfoToken);
printf("%s", sqlInfo);
return 0;
} /* endif */
} /* endif */
return 0;
}
/******************************************************************************
** 1.2 - TransRollback - rolls back the transaction
******************************************************************************/
void TransRollback( )
{ int rc = 0;
/* rollback the transaction */
printf( "\nRolling back the transaction ...\n") ;
EXEC SQL ROLLBACK;
rc = SqlInfoPrint( "ROLLBACK", &sqlca, __LINE__, __FILE__);
if( rc == 0)
{ printf( "The transaction was rolled back.\n") ;
}
120 Application Development Guide
Java Example: Catching SQLException
JDBC and SQLJ applications throw an SQLException when an error occurs
during SQL processing. Your applications can catch and display an
SQLException with the following code:
try {
Statement stmt = connection.createStatement();
int rowsDeleted = stmt.executeUpdate(
"DELETE FROM employee WHERE empno = '000010'");
System.out.println( rowsDeleted + " rows were deleted");
}
catch (SQLException sqle) {
System.out.println(sqle);
}
For more information on handling SQLExceptions, see “SQLSTATE and
SQLCODE Values in Java” on page 627.
Chapter 4. Writing Static SQL Programs 121
COBOL Example: CHECKERR.CBL
Identification Division.
Program-ID. "checkerr".
Data Division.
Working-Storage Section.
copy "sql.cbl".
* Local variables
77 error-rc pic s9(9) comp-5.
77 state-rc pic s9(9) comp-5.
* Variables for the GET ERROR MESSAGE API
* Use application specific bound instead of BUFFER-SZ
* 77 buffer-size pic s9(4) comp-5 value BUFFER-SZ.
* 77 error-buffer pic x(BUFFER-SZ).
* 77 state-buffer pic x(BUFFER-SZ).
77 buffer-size pic s9(4) comp-5 value 1024.
77 line-width pic s9(4) comp-5 value 80.
77 error-buffer pic x(1024).
77 state-buffer pic x(1024).
Linkage Section.
copy "sqlca.cbl" replacing ==VALUE "SQLCA "== by == ==
==VALUE 136== by == ==.
01 errloc pic x(80).
Procedure Division using sqlca errloc.
Checkerr Section.
if SQLCODE equal 0
go to End-Checkerr.
display "--- error report ---".
display "ERROR occurred : ", errloc.
display "SQLCODE : ", SQLCODE.
********************************
* GET ERROR MESSAGE API called *
********************************
call "sqlgintp" using
by value buffer-size
by value line-width
by reference sqlca
by reference error-buffer
returning error-rc.
************************
* GET SQLSTATE MESSAGE *
************************
call "sqlggstt" using
by value buffer-size
by value line-width
by reference sqlstate
by reference state-buffer
122 Application Development Guide
returning state-rc.
if error-rc is greater than 0
display error-buffer.
if state-rc is greater than 0
display state-buffer.
if state-rc is less than 0
display "return code from GET SQLSTATE =" state-rc.
if SQLCODE is less than 0
display "--- end error report ---"
go to End-Prog.
display "--- end error report ---"
display "CONTINUING PROGRAM WITH WARNINGS!".
End-Checkerr. exit program.
End-Prog. stop run.
Chapter 4. Writing Static SQL Programs 123
REXX Example: CHECKERR Procedure
parse version rexxType .
parse source platform .
if platform == 'AIX/6000' & rexxType == 'REXXSAA' then
do
rcy = SysAddFuncPkg("db2rexx")
end
else
do
if RxFuncQuery('SQLDBS') <> 0 then
rcy = RxFuncAdd( 'SQLDBS', 'db2ar', 'SQLDBS' )
if RxFuncQuery('SQLEXEC') <> 0 then
rcy = RxFuncAdd( 'SQLEXEC', 'db2ar', 'SQLEXEC' )
end
..
.
call CHECKERR 'INSERT'
..
.
CHECKERR:
arg errloc
if ( SQLCA.SQLCODE = 0 ) then
return 0
else do
say '--- error report ---'
say 'ERROR occurred :' errloc
say 'SQLCODE :' SQLCA.SQLCODE
/******************************\
* GET ERROR MESSAGE API called *
\******************************/
call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80'
say errmsg
say '--- end error report ---'
if (SQLCA.SQLCODE < 0 ) then
exit
else do
say 'WARNING - CONTINUING PROGRAM WITH ERRORS'
return 0
end
end
return 0
/* this variable (SYSTEM) must be user defined */
SYSTEM = AIX
if SYSTEM = OS2 then do
if RxFuncQuery('SQLDBS') <> 0 then
rcy = RxFuncAdd( 'SQLDBS', 'DB2AR', 'SQLDBS' )
124 Application Development Guide
if RxFuncQuery('SQLEXEC') <> 0 then
rcy = RxFuncAdd( 'SQLEXEC', 'DB2AR', 'SQLEXEC' )
end
if SYSTEM = AIX then
rcy = SysAddFuncPkg("db2rexx")
..
.
call CHECKERR 'INSERT'
..
.
CHECKERR:
arg errloc
if ( SQLCA.SQLCODE = 0 ) then
return 0
else do
say '--- error report ---'
say 'ERROR occurred :' errloc
say 'SQLCODE :' SQLCA.SQLCODE
/******************************\
* GET ERROR MESSAGE API called *
\******************************/
call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80'
say errmsg
say '--- end error report ---'
if (SQLCA.SQLCODE < 0 ) then
exit
else do
say 'WARNING - CONTINUING PROGRAM WITH ERRORS'
return 0
end
end
return 0
Chapter 4. Writing Static SQL Programs 125
126 Application Development Guide
Chapter 5. Writing Dynamic SQL Programs
Why Use Dynamic SQL? . . . . . . . 127 Allocating an SQLDA Structure . . . . 147
Dynamic SQL Support Statements . . . 127 Passing Data Using an SQLDA Structure 151
Comparing Dynamic SQL with Static SQL 128 Processing Interactive SQL Statements 152
Using PREPARE, DESCRIBE, FETCH and Determining Statement Type . . . . 152
the SQLDA . . . . . . . . . . . . 131 Varying-List SELECT Statement . . . 153
Declaring and Using Cursors . . . . . 131 Saving SQL Requests from End Users . . 153
Example: Dynamic SQL Program . . . 133 Example: ADHOC Program . . . . . 154
How the Dynamic Program Works . . 133 How the ADHOC Program Works . . 154
C Example: DYNAMIC.SQC . . . . 135 C Example: ADHOC.SQC . . . . . 157
Java Example: Dynamic.java . . . . 137 Variable Input to Dynamic SQL . . . . . 161
COBOL Example: DYNAMIC.SQB . . 139 Using Parameter Markers . . . . . . 161
REXX Example: DYNAMIC.CMD . . 141 Example: VARINP Program . . . . . 162
Declaring the SQLDA . . . . . . . 143 How the VARINP Program Works . . 162
Preparing the Statement Using the C Example: VARINP.SQC . . . . . 164
Minimum SQLDA Structure . . . . . 144 Java Example: Varinp.java . . . . . 166
Allocating an SQLDA with Sufficient COBOL Example: VARINP.SQB . . . 168
SQLVAR Entries . . . . . . . . . 145 The DB2 Call Level Interface (CLI) . . . . 170
Describing the SELECT Statement . . . 146 Comparing Embedded SQL and DB2 CLI 170
Acquiring Storage to Hold a Row . . . 146 Advantages of Using DB2 CLI . . . . 171
Processing the Cursor . . . . . . . 147 Deciding on Embedded SQL or DB2 CLI 173
Why Use Dynamic SQL?
You may want to use dynamic SQL when:
v You need all or part of the SQL statement to be generated during
application execution.
v The objects referenced by the SQL statement do not exist at precompile
time.
v You want the statement to always use the most optimal access path, based
on current database statistics.
v You want to modify the compilation environment of the statement, that is,
experiment with the special registers.
Dynamic SQL Support Statements
The dynamic SQL support statements accept a character-string host variable
and a statement name as arguments. The host variable contains the SQL
statement to be processed dynamically in text form. The statement text is not
processed when an application is precompiled. In fact, the statement text does
not have to exist at the time the application is precompiled. Instead, the SQL
statement is treated as a host variable for precompilation purposes and the
variable is referenced during application execution. These SQL statements are
referred to as dynamic SQL.
© Copyright IBM Corp. 1993, 2000 127
Dynamic SQL support statements are required to transform the host variable
containing SQL text into an executable form and operate on it by referencing
the statement name. These statements are:
EXECUTE IMMEDIATE
Prepares and executes a statement that does not use any host
variables. All EXECUTE IMMEDIATE statements in an application are
cached in the same place at run time, so only the last statement is
known. Use this statement as an alternative to the PREPARE and
EXECUTE statements.
PREPARE
Turns the character string form of the SQL statement into an
executable form of the statement, assigns a statement name, and
optionally places information about the statement in an SQLDA
structure.
EXECUTE
Executes a previously prepared SQL statement. The statement can be
executed repeatedly within a connection.
DESCRIBE
Places information about a prepared statement into an SQLDA.
An application can execute most SQL statements dynamically. See Table 37 on
page 723 for the complete list of supported SQL statements.
Note: The content of dynamic SQL statements follows the same syntax as
static SQL statements, but with the following exceptions:
v Comments are not allowed.
v The statement cannot begin with EXEC SQL.
v The statement cannot end with the statement terminator. An
exception to this is the CREATE TRIGGER statement which can
contain a semicolon (;).
Comparing Dynamic SQL with Static SQL
The question of whether to use static or dynamic SQL for performance is
usually of great interest to programmers. The answer, of course, is that it all
depends on your situation. Refer to Table 6 on page 129 to help you decide
whether to use static or dynamic SQL. There may be certain considerations
such as security which dictate static SQL, or your environment (such as
whether you are using DB2 CLI or the CLP) which dictates dynamic SQL.
When making your decision, consider the following recommendations on
whether to choose static or dynamic SQL in a particular situation. In the
following table, 'either' means that there is no advantage to either static or
dynamic SQL. Note that these are general recommendations only. Your
specific application, its intended usage, and working environment dictate the
128 Application Development Guide
actual choice. When in doubt, prototyping your statements as static SQL, then
as dynamic SQL, and then comparing the differences is the best approach.
Table 6. Comparing Static and Dynamic SQL
Consideration Likely Best
Choice
Time to run the SQL statement:
v Less than 2 seconds v Static
v 2 to 10 seconds v either
v More than 10 seconds v Dynamic
Data Uniformity
v Uniform data distribution v Static
v Slight non-uniformity v either
v Highly non-uniform distribution v Dynamic
Range (<,>,BETWEEN,LIKE) Predicates
v Very Infrequent v Static
v Occasional v either
v Frequent v Dynamic
Repetitious Execution
v Runs many times (10 or more times) v either
v Runs a few times (less than 10 times) v either
v Runs once v Static
Nature of Query
v Random v Dynamic
v Permanent v either
Run Time Environment (DML/DDL)
v Transaction Processing (DML Only) v either
v Mixed (DML and DDL - DDL affects packages) v Dynamic
v Mixed (DML and DDL - DDL does not affect packages) v either
Frequency of RUNSTATS
v Very infrequently v Static
v Regularly v either
v Frequently v Dynamic
In general, an application using dynamic SQL has a higher start-up (or initial)
cost per SQL statement due to the need to compile the SQL statements prior
to using them. Once compiled, the execution time for dynamic SQL compared
to static SQL should be equivalent and, in some cases, faster due to better
access plans being chosen by the optimizer. Each time a dynamic statement is
executed, the initial compilation cost becomes less of a factor. If multiple users
are running the same dynamic application with the same statements, only the
first application to issue the statement realizes the cost of statement
compilation.
Chapter 5. Writing Dynamic SQL Programs 129
In a mixed DML and DDL environment, the compilation cost for a dynamic
SQL statement may vary as the statement may be implicitly recompiled by the
system while the application is running. In a mixed environment, the choice
between static and dynamic SQL must also factor in the frequency in which
packages are invalidated. If the DDL does invalidate packages, dynamic SQL
may be more efficient as only those queries executed are recompiled when
they are next used. Others are not recompiled. For static SQL, the entire
package is rebound once it has been invalidated.
Now suppose your particular application contains a mixture of the above
characteristics and some of these characteristics suggest that you use static
while others suggest dynamic. In this case, there is no clear cut decision and
you should probably use whichever method you have the most experience
with, and with which you feel most comfortable. Note that the considerations
in the above table are listed roughly in order of importance.
Note: Static and dynamic SQL each come in two types that make a difference
to the DB2 optimizer. These are:
1. Static SQL containing no host variables
This is an unlikely situation which you may see only for:
v Initialization code
v Novice training examples
This is actually the best combination from a performance perspective in
that there is no run-time performance overhead and yet the DB2
optimizer’s capabilities can be fully realized.
2. Static SQL containing host variables
This is the traditional legacy style of DB2 applications. It avoids the run
time overhead of a PREPARE and catalog locks acquired during statement
compilation. Unfortunately, the full power of the optimizer cannot be
harnessed since it does not know the entire SQL statement. A particular
problem exists with highly non-uniform data distributions.
3. Dynamic SQL containing no parameter markers
This is the typical style for random query interfaces (such as the CLP) and
is the optimizer’s preferred flavor of SQL. For complex queries, the
overhead of the PREPARE statement is usually worthwhile due to
improved execution time. For more information on parameter markers, see
“Using Parameter Markers” on page 161.
4. Dynamic SQL containing parameter markers
This is the most common type of SQL for CLI applications. The key benefit
is that the presence of parameter markers allows the cost of the PREPARE
to be amortized over the repeated executions of the statement, typically a
select or insert. This amortization is true for all repetitive dynamic SQL
applications. Unfortunately, just like static SQL with host variables, parts
130 Application Development Guide
of the DB2 optimizer will not work since complete information is
unavailable. The recommendation is to use static SQL with host variables or
dynamic SQL without parameter markers as the most efficient options.
Using PREPARE, DESCRIBE, FETCH and the SQLDA
With static SQL, host variables used in embedded SQL statements are known
at application compile time. With dynamic SQL, the embedded SQL
statements and consequently the host variables are not known until
application run time. Thus, for dynamic SQL applications, you need to deal
with the list of host variables that are used in your application. You can use
the DESCRIBE statement to obtain host variable information for any SELECT
statement that has been prepared (using PREPARE), and store that
information into the SQL descriptor area (SQLDA).
Note: Java applications do not use the SQLDA structure, and therefore do not
use the PREPARE or DESCRIBE statements. In JDBC applications you
can use a PreparedStatement object and the executeQuery() method to
generate a ResultSet object, which is the equivalent of a host language
cursor. In SQLJ applications you can also declare an SQLJ iterator
object with a CursorByPos or CursorByName cursor to return data from
FETCH statements.
When the DESCRIBE statement gets executed in your application, the
database manager defines your host variables in an SQLDA. Once the host
variables are defined in the SQLDA, you can use the FETCH statement to
assign values to the host variables, using a cursor.
For complete information on the PREPARE, DESCRIBE, and FETCH
statements, and a description of the SQLDA, refer to the SQL Reference.
For an example of a simple dynamic SQL program that uses the PREPARE,
DESCRIBE, and FETCH statements without using an SQLDA, see “Example:
Dynamic SQL Program” on page 133. For an example of a dynamic SQL
program that uses the PREPARE, DESCRIBE, and FETCH statements and an
SQLDA to process interactive SQL statements, see “Example: ADHOC
Program” on page 154.
Declaring and Using Cursors
Processing a cursor dynamically is nearly identical to processing it using static
SQL. When a cursor is declared, it is associated with a query.
In the static SQL case, the query is a SELECT statement in text form, as
shown in “Declare Cursor Statement” on page 82.
Chapter 5. Writing Dynamic SQL Programs 131
In the dynamic SQL case, the query is associated with a statement name
assigned in a PREPARE statement. Any referenced host variables are
represented by parameter markers. Table 7 shows a DECLARE statement
associated with a dynamic SELECT statement.
Table 7. Declare Statement Associated with a Dynamic SELECT
Language Example Source Code
C/C++ strcpy( prep_string, "SELECT tabname FROM syscat.tables"
"WHERE tabschema = ?" );
EXEC SQL PREPARE s1 FROM :prep_string;
EXEC SQL DECLARE c1 CURSOR FOR s1;
EXEC SQL OPEN c1 USING :host_var;
Java (JDBC) PreparedStatement prep_string = ("SELECT tabname FROM syscat.tables
WHERE tabschema = ?" );
prep_string.setCursor("c1");
prep_string.setString(1, host_var);
ResultSet rs = prep_string.executeQuery();
COBOL MOVE "SELECT TABNAME FROM SYSCAT.TABLES WHERE TABSCHEMA = ?"
TO PREP-STRING.
EXEC SQL PREPARE S1 FROM :PREP-STRING END-EXEC.
EXEC SQL DECLARE C1 CURSOR FOR S1 END-EXEC.
EXEC SQL OPEN C1 USING :host-var END-EXEC.
FORTRAN prep_string = 'SELECT tabname FROM syscat.tables WHERE tabschema = ?'
EXEC SQL PREPARE s1 FROM :prep_string
EXEC SQL DECLARE c1 CURSOR FOR s1
EXEC SQL OPEN c1 USING :host_var
The main difference between a static and a dynamic cursor is that a static
cursor is prepared at precompile time, and a dynamic cursor is prepared at
run time. Additionally, host variables referenced in the query are represented
by parameter markers, which are replaced by run-time host variables when
the cursor is opened.
For more information about how to use cursors, see the following sections:
v “Selecting Multiple Rows Using a Cursor” on page 81
v “Example: Cursor Program” on page 84
v “Using Cursors in REXX” on page 714
132 Application Development Guide
Example: Dynamic SQL Program
This sample program shows the processing of a cursor based upon a dynamic
SQL statement. It lists all the tables in SYSCAT.TABLES except for the tables
with the value STAFF in the name column. The sample is available in the
following programming languages:
C dynamic.sqc
Java Dynamic.java
COBOL dynamic.sqb
REXX dynamic.cmd
How the Dynamic Program Works
1. Declare host variables. This section includes declarations of three host
variables:
table_name
Used to hold the data returned during the FETCH statement
st Used to hold the dynamic SQL statement in text form
parm_var
Supplies a data value to replace the parameter marker in st.
2. Prepare the statement. An SQL statement with one parameter marker
(indicated by '?') is copied to the host variable. This host variable is passed
to the PREPARE statement for validation. The PREPARE statement parses
the SQL text and prepares an access section for the package in the same
way that the precompiler or binder does, only it happens at run time
instead of during preprocessing.
3. Declare the cursor. The DECLARE statement associates a cursor with a
dynamically prepared SQL statement. If the prepared SQL statement is a
SELECT statement, a cursor is necessary to retrieve the rows from the
result table.
4. Open the cursor. The OPEN statement initializes the cursor declared
earlier to point before the first row of the result table. The USING clause
specifies a host variable to replace the parameter marker in the prepared
SQL statement. The data type and length of the host variable must be
compatible with the associated column type and length.
5. Retrieve the data. The FETCH statement is used to move the NAME
column from the result table into the table_name host variable. The host
variable is printed before the program loops back to fetch another row.
6. Close the cursor. The CLOSE statement closes the cursor and releases the
resources associated with it.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
Chapter 5. Writing Dynamic SQL Programs 133
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl.
REXX CHECKERR is a procedure located at bottom of the current
program.
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
134 Application Development Guide
C Example: DYNAMIC.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
int main(int argc, char *argv[]) {
EXEC SQL BEGIN DECLARE SECTION;
char table_name[19];
char st[80]; 1
char parm_var[19];
char userid[9];
char passwd[19];
EXEC SQL END DECLARE SECTION;
printf( "Sample C program: DYNAMIC\n" );
if (argc == 1) {
EXEC SQL CONNECT TO sample;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else if (argc == 3) {
strcpy (userid, argv[1]);
strcpy (passwd, argv[2]);
EXEC SQL CONNECT TO sample USER :userid USING :passwd;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else {
printf ("\nUSAGE: dynamic [userid passwd]\n\n");
return 1;
} /* endif */
strcpy( st, "SELECT tabname FROM syscat.tables" );
strcat( st, " WHERE tabname <> ?" );
EXEC SQL PREPARE s1 FROM :st; 2
EMB_SQL_CHECK("PREPARE");
EXEC SQL DECLARE c1 CURSOR FOR s1; 3
strcpy( parm_var, "STAFF" );
EXEC SQL OPEN c1 USING :parm_var; 4
EMB_SQL_CHECK("OPEN");
do {
EXEC SQL FETCH c1 INTO :table_name; 5
if (SQLCODE != 0) break;
printf( "Table = %s\n", table_name );
} while ( 1 );
EXEC SQL CLOSE c1; 6
EMB_SQL_CHECK("CLOSE");
Chapter 5. Writing Dynamic SQL Programs 135
EXEC SQL COMMIT;
EMB_SQL_CHECK("COMMIT");
EXEC SQL CONNECT RESET;
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
/* end of program : DYNAMIC.SQC */
136 Application Development Guide
Java Example: Dynamic.java
import java.sql.*;
class Dynamic
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println (" Java Dynamic Sample");
// Connect to Sample database
Connection con = null;
// URL is jdbc:db2:dbname
String url = "jdbc:db2:sample";
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception("\nUsage: java Dynamic [username password]\n");
}
// Enable transactions
con.setAutoCommit(false);
// Perform dynamic SQL SELECT using JDBC
try
{ PreparedStatement pstmt1 = con.prepareStatement(
"SELECT tabname FROM syscat.tables " +
"WHERE tabname <> ? " +
"ORDER BY 1"); 2
// set cursor name for the positioned update statement
pstmt1.setCursorName("c1"); 3
pstmt1.setString(1, "STAFF");
ResultSet rs = pstmt1.executeQuery(); 4
System.out.print("\n");
while( rs.next() ) 5
Chapter 5. Writing Dynamic SQL Programs 137
{ String tableName = rs.getString("tabname");
System.out.println("Table = " + tableName);
};
rs.close();
pstmt1.close(); 7
}
catch( Exception e )
{ throw e;
}
finally
{ // Rollback the transaction
System.out.println("\nRollback the transaction...");
con.rollback();
System.out.println("Rollback done.");
}
}
catch( Exception e )
{ System.out.println(e);
}
}
}
138 Application Development Guide
COBOL Example: DYNAMIC.SQB
Identification Division.
Program-ID. "dynamic".
Data Division.
Working-Storage Section.
copy "sqlenv.cbl".
copy "sql.cbl".
copy "sqlca.cbl".
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 table-name pic x(20).
01 st pic x(80). 1
01 parm-var pic x(18).
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
EXEC SQL END DECLARE SECTION END-EXEC.
77 errloc pic x(80).
Procedure Division.
Main Section.
display "Sample COBOL program: DYNAMIC".
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
move "SELECT TABNAME FROM SYSCAT.TABLES
- " ORDER BY 1
- " WHERE TABNAME <> ?" to st.
EXEC SQL PREPARE s1 FROM :st END-EXEC. 2
move "PREPARE" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL DECLARE c1 CURSOR FOR s1 END-EXEC. 3
Chapter 5. Writing Dynamic SQL Programs 139
move "STAFF" to parm-var.
EXEC SQL OPEN c1 USING :parm-var END-EXEC. 4
move "OPEN" to errloc.
call "checkerr" using SQLCA errloc.
perform Fetch-Loop thru End-Fetch-Loop
until SQLCODE not equal 0.
EXEC SQL CLOSE c1 END-EXEC. 6
move "CLOSE" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL COMMIT END-EXEC.
move "COMMIT" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL CONNECT RESET END-EXEC.
move "CONNECT RESET" to errloc.
call "checkerr" using SQLCA errloc.
End-Main.
go to End-Prog.
Fetch-Loop Section.
EXEC SQL FETCH c1 INTO :table-name END-EXEC. 5
if SQLCODE not equal 0
go to End-Fetch-Loop.
display "TABLE = ", table-name.
End-Fetch-Loop. exit.
End-Prog.
stop run.
140 Application Development Guide
REXX Example: DYNAMIC.CMD
/* REXX DYNAMIC.CMD */
parse version rexxType .
parse source platform .
if platform == 'AIX/6000' & rexxType == 'REXXSAA' then
do
rcy = SysAddFuncPkg("db2rexx")
end
else
do
if RxFuncQuery('SQLDBS') <> 0 then
rcy = RxFuncAdd( 'SQLDBS', 'db2ar', 'SQLDBS' )
if RxFuncQuery('SQLEXEC') <> 0 then
rcy = RxFuncAdd( 'SQLEXEC', 'db2ar', 'SQLEXEC' )
end
/* pull in command line arguments */
parse arg userid passwd .
/* check to see if the proper number of arguments have been passed in */
PARSE ARG dbname userid password .
if ((dbname = "" ) | ,
(userid <> "" & password = "") ,
) then do
SAY "USAGE: dynamic.cmd <dbname> [<userid> <password>]"
exit -1
end
/* connect to database */
SAY
SAY 'Connect to' dbname
IF password= "" THEN
CALL SQLEXEC 'CONNECT TO' dbname
ELSE
CALL SQLEXEC 'CONNECT TO' dbname 'USER' userid 'USING' password
CALL CHECKERR 'Connect to '
SAY "Connected"
say 'Sample REXX program: DYNAMIC'
st = "SELECT tabname FROM syscat.tables WHERE tabname <> ? ORDER BY 1"
call SQLEXEC 'PREPARE s1 FROM :st' 2
call CHECKERR 'PREPARE'
call SQLEXEC 'DECLARE c1 CURSOR FOR s1' 3
call CHECKERR 'DECLARE'
parm_var = "STAFF"
call SQLEXEC 'OPEN c1 USING :parm_var' 4
Chapter 5. Writing Dynamic SQL Programs 141
do while ( SQLCA.SQLCODE = 0 )
call SQLEXEC 'FETCH c1 INTO :table_name' 5
if (SQLCA.SQLCODE = 0) then
say 'Table = ' table_name
end
call SQLEXEC 'CLOSE c1' 6
call CHECKERR 'CLOSE'
call SQLEXEC 'CONNECT RESET'
call CHECKERR 'CONNECT RESET'
CHECKERR:
arg errloc
if ( SQLCA.SQLCODE = 0 ) then
return 0
else do
say '--- error report ---'
say 'ERROR occurred :' errloc
say 'SQLCODE :' SQLCA.SQLCODE
/******************************\
* GET ERROR MESSAGE API called *
\******************************/
call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80'
say errmsg
say '--- end error report ---'
if (SQLCA.SQLCODE < 0 ) then
exit
else do
say 'WARNING - CONTINUING PROGRAM WITH ERRORS'
return 0
end
end
return 0
142 Application Development Guide
Declaring the SQLDA
An SQLDA contains a variable number of occurrences of SQLVAR entries,
each of which contains a set of fields that describe one column in a row of
data as shown in Figure 2. There are two types of SQLVAR entries: base
SQLVARs, and secondary SQLVARs. For information about the two types,
refer to the SQL Reference.
sqldaid CHAR sqldabc INTEGER
HEADER
sqln SMALLINT sqld SMALLINT
sqltype SMALLINT sqllen SMALLINT
SQLVAR
(1 per field) sqldata POINTER sqlind POINTER
sqlname VARCHAR (30)
OTHER SQLVARs
Figure 2. The SQL Descriptor Area (SQLDA)
Since the number of SQLVAR entries required depends on the number of
columns in the result table, an application must be able to allocate an
appropriate number of SQLVAR elements when needed. Two methods are
available as discussed below. For information about the fields of the SQLDA
that are mentioned, refer to the SQL Reference.
v Provide the largest SQLDA (that is, the one with the greatest number of
SQLVAR entries) that is needed. The maximum number of columns that can
be returned in a result table is 255. If any of the columns being returned is
either a LOB type or a distinct type, the value in SQLN is doubled, and the
number of SQLVARs needed to hold the information is doubled to 510.
However, as most SELECT statements do not even retrieve 255 columns,
most of the allocated space is unused.
v Provide a smaller SQLDA with fewer SQLVAR entries. In this case, if there
are more columns in the result than SQLVAR entries allowed for in the
SQLDA, then no descriptions are returned. Instead, the database manager
returns the number of select list items detected in the SELECT statement.
The application allocates an SQLDA with the required number of SQLVAR
entries, and then uses the DESCRIBE statement to acquire the column
Chapter 5. Writing Dynamic SQL Programs 143
descriptions. More details on this method are provided in “Preparing the
Statement Using the Minimum SQLDA Structure”.
For the above methods, the question arises as to how many initial SQLVAR
entries you should allocate. Each SQLVAR element uses up 44 bytes of storage
(not counting storage allocated for the SQLDATA and SQLIND fields). If
memory is plentiful, the first method of providing an SQLDA of maximum
size is easier to implement.
The second method of allocating a smaller SQLDA is only applicable to
programming languages such as C and C++ that support the dynamic
allocation of memory. For languages such as COBOL and FORTRAN that do
not support the dynamic allocation of memory, you have to use the first
method.
Preparing the Statement Using the Minimum SQLDA Structure
Suppose an application declares an SQLDA structure named minsqlda that
contains no SQLVAR entries. The SQLN field of the SQLDA describes the
number of SQLVAR entries that are allocated. In this case, SQLN must be set
to 0. Next, to prepare a statement from the character string dstring and to
enter its description into minsqlda, issue the following SQL statement
(assuming C syntax, and assuming that minsqlda is declared as a pointer to an
SQLDA structure):
EXEC SQL
PREPARE STMT INTO :*minsqlda FROM :dstring;
Suppose that the statement contained in dstring was a SELECT statement
that returns 20 columns in each row. After the PREPARE statement (or a
DESCRIBE statement), the SQLD field of the SQLDA contains the number of
columns of the result table for the prepared SELECT statement.
The SQLVARs in the SQLDA are set in the following cases:
v SQLN >= SQLD and no column is either a LOB or a distinct type
The first SQLD SQLVAR entries are set and SQLDOUBLED is set to blank.
v SQLN >= 2*SQLD and at least one column is a LOB or a distinct type
2* SQLD SQLVAR entries are set and SQLDOUBLED is set to 2.
v SQLD <= SQLN < 2*SQLD and at least one column is a distinct type but
there are no LOB columns
The first SQLD SQLVAR entries are set and SQLDOUBLED is set to blank.
If the SQLWARN bind option is YES, a warning SQLCODE +237
(SQLSTATE 01594) is issued.
The SQLVARs in the SQLDA are NOT set (requiring allocation of additional
space and another DESCRIBE) in the following cases:
144 Application Development Guide
v SQLN < SQLD and no column is either a LOB or distinct type
No SQLVAR entries are set and SQLDOUBLED is set to blank. If the
SQLWARN bind option is YES, a warning SQLCODE +236 (SQLSTATE
01005) is issued.
Allocate SQLD SQLVARs for a successful DESCRIBE.
v SQLN < SQLD and at least one column is a distinct type but there are no
LOB columns
No SQLVAR entries are set and SQLDOUBLED is set to blank. If the
SQLWARN bind option is YES, a warning SQLCODE +239 (SQLSTATE
01005) is issued.
Allocate 2*SQLD SQLVARs for a successful DESCRIBE including the names
of the distinct types.
v SQLN < 2*SQLD and at least one column is a LOB
No SQLVAR entries are set and SQLDOUBLED is set to blank. A warning
SQLCODE +238 (SQLSTATE 01005) is issued (regardless of the setting of
the SQLWARN bind option).
Allocate 2*SQLD SQLVARs for a successful DESCRIBE.
The SQLWARN option of the BIND command is used to control whether the
DESCRIBE (or PREPARE...INTO) will return the following warnings:
v SQLCODE +236 (SQLSTATE 01005)
v SQLCODE +237 (SQLSTATE 01594)
v SQLCODE +239 (SQLSTATE 01005).
It is recommended that your application code always consider that these
SQLCODEs could be returned. The warning SQLCODE +238 (SQLSTATE
01005) is always returned when there are LOB columns in the select list and
there are insufficient SQLVARs in the SQLDA. This is the only way the
application can know that the number of SQLVARs must be doubled because
of a LOB column in the result set.
Allocating an SQLDA with Sufficient SQLVAR Entries
After the number of columns in the result table is determined, storage can be
allocated for a second, full-size SQLDA. For example, if the result table
contains 20 columns (none of which are LOB columns), a second SQLDA
structure, fulsqlda, must be allocated with at least 20 SQLVAR elements (or
40 elements if the result table contains any LOBs or distinct types). For the
rest of this example, assume that no LOBs or distinct types are in the result
table.
The storage requirements for SQLDA structures consist of the following:
v A fixed-length header, 16 bytes in length, containing fields such as SQLN
and SQLD
Chapter 5. Writing Dynamic SQL Programs 145
v A varying-length array of SQLVAR entries, of which each element is 44
bytes in length on 32-bit platforms, and 56 bytes in length on 64-bit
platforms.
The number of SQLVAR entries needed for fulsqlda was specified in the
SQLD field of minsqlda. This value was 20. Therefore, the storage allocation
required for fulsqlda used in this example is:
16 + (20 * sizeof(struct sqlvar))
Note: On 64-bit platforms, sizeof(struct sqlvar) and
sizeof(struct sqlvar2) returns 56. On 32-bit platforms,
sizeof(struct sqlvar) and sizeof(struct sqlvar2) returns 44.
This value represents the size of the header plus 20 times the size of each
SQLVAR entry, giving a total of 896 bytes.
You can use the SQLDASIZE macro to avoid doing your own calculations and
to avoid any version-specific dependencies.
Describing the SELECT Statement
Having allocated sufficient space for fulsqlda, an application must take the
following steps:
1. Store the value 20 in the SQLN field of fulsqlda.
2. Obtain information about the SELECT statement using the second SQLDA
structure, fulsqlda. Two methods are available:
v Use another PREPARE statement specifying fulsqlda instead of
minsqlda.
v Use the DESCRIBE statement specifying fulsqlda.
Using the DESCRIBE statement is preferred because the costs of preparing the
statement a second time are avoided. The DESCRIBE statement simply reuses
information previously obtained during the prepare operation to fill in the
new SQLDA structure. The following statement can be issued:
EXEC SQL DESCRIBE STMT INTO :fulsqlda
After this statement is executed, each SQLVAR element contains a description
of one column of the result table.
Acquiring Storage to Hold a Row
Before fetching any rows of the result table using an SQLDA structure, an
application must do the following:
1. Analyze each SQLVAR description to determine how much space is
required for the value of that column.
Note that for Large Object (LOB) values, when the SELECT is described,
the data type given in the SQLVAR is SQL_TYP_xLOB. This data type
146 Application Development Guide
corresponds to a plain LOB host variable, that is, the whole LOB will be
stored in memory at one time. This will work for small LOBs (up to a few
MB), but you cannot use this data type for large LOBs (say 1 GB). It will
be necessary for your application to change its column definition in the
SQLVAR to be either SQL_TYP_xLOB_LOCATOR or
SQL_TYPE_xLOB_FILE. (Note that changing the SQLTYPE field of the
SQLVAR also necessitates changing the SQLLEN field.) After changing the
column definition in the SQLVAR, your application can then allocate the
correct amount of storage for the new type. For more information on
LOBs, see “Chapter 10. Using the Object-Relational Capabilities” on
page 267.
2. Allocate storage for the value of that column.
3. Store the address of the allocated storage in the SQLDATA field of the
SQLDA structure.
These steps are accomplished by analyzing the description of each column
and replacing the content of each SQLDATA field with the address of a
storage area large enough to hold any values from that column. The length
attribute is determined from the SQLLEN field of each SQLVAR entry for data
items that are not of a LOB type. For items with a type of BLOB, CLOB, or
DBCLOB, the length attribute is determined from the SQLLONGLEN field of
the secondary SQLVAR entry.
In addition, if the specified column allows nulls, then the application must
replace the content of the SQLIND field with the address of an indicator
variable for the column.
Processing the Cursor
After the SQLDA structure is properly allocated, the cursor associated with
the SELECT statement can be opened and rows can be fetched by specifying
the USING DESCRIPTOR clause of the FETCH statement.
When finished, the cursor should be closed and any dynamically allocated
memory should be released.
Allocating an SQLDA Structure
To create an SQLDA structure with C, either embed the INCLUDE SQLDA
statement in the host language or include the SQLDA include file to get the
structure definition. Then, because the size of an SQLDA is not fixed, the
application must declare a pointer to an SQLDA structure and allocate storage
for it. The actual size of the SQLDA structure depends on the number of
distinct data items being passed using the SQLDA. (For an example of how to
code an application to process the SQLDA, see “Example: ADHOC Program”
on page 154.)
Chapter 5. Writing Dynamic SQL Programs 147
In the C/C++ programming language, a macro is provided to facilitate
SQLDA allocation. With the exception of the HP-UX platform, this macro has
the following format:
#define SQLDASIZE(n) (offsetof(struct sqlda, sqlvar) \
+ (n) × sizeof(struct sqlvar))
On the HP-UX platform, the macro has the following format:
#define SQLDASIZE(n) (sizeof(struct sqlda) \
+ (n−1) × sizeof(struct sqlvar))
The effect of this macro is to calculate the required storage for an SQLDA
with n SQLVAR elements.
To create an SQLDA structure with COBOL, you can either embed an
INCLUDE SQLDA statement or use the COPY statement. Use the COPY
statement when you want to control the maximum number of SQLVARs and
hence the amount of storage that the SQLDA uses. For example, to change the
default number of SQLVARs from 1489 to 1, use the following COPY
statement:
COPY "sqlda.cbl"
replacing --1489--
by --1--.
The FORTRAN language does not directly support self-defining data
structures or dynamic allocation. No SQLDA include file is provided for
FORTRAN, because it is not possible to support the SQLDA as a data
structure in FORTRAN. The precompiler will ignore the INCLUDE SQLDA
statement in a FORTRAN program.
However, you can create something similar to a static SQLDA structure in a
FORTRAN program, and use this structure wherever an SQLDA can be used.
The file sqldact.f contains constants that help in declaring an SQLDA
structure in FORTRAN.
Execute calls to SQLGADDR to assign pointer values to the SQLDA elements
that require them.
The following table shows the declaration and use of an SQLDA structure
with one SQLVAR element.
148 Application Development Guide
Language Example Source Code
C/C++ #include <sqlda.h>
struct sqlda *outda = (struct sqlda *)malloc(SQLDASIZE(1));
/* DECLARE LOCAL VARIABLES FOR HOLDING ACTUAL DATA */
double sal;
short salind;
/* INITIALIZE ONE ELEMENT OF SQLDA */
memcpy( outda->sqldaid,"SQLDA ",sizeof(outda->sqldaid));
outda->sqln = outda->sqld = 1;
outda->sqlvar[0].sqltype = SQL_TYP_NFLOAT;
outda->sqlvar[0].sqllen = sizeof( double );.
outda->sqlvar[0].sqldata = (unsigned char *)&sal;
outda->sqlvar[0].sqlind = (short *)&salind;
COBOL WORKING-STORAGE SECTION.
77 SALARY PIC S99999V99 COMP-3.
77 SAL-IND PIC S9(4) COMP-5.
EXEC SQL INCLUDE SQLDA END-EXEC
* Or code a useful way to save unused SQLVAR entries.
* COPY "sqlda.cbl" REPLACING --1489-- BY --1--.
01 decimal-sqllen pic s9(4) comp-5.
01 decimal-parts redefines decimal-sqllen.
05 precision pic x.
05 scale pic x.
* Initialize one element of output SQLDA
MOVE 1 TO SQLN
MOVE 1 TO SQLD
MOVE SQL-TYP-NDECIMAL TO SQLTYPE(1)
* Length = 7 digits precision and 2 digits scale
MOVE x"07" TO PRECISION.
MOVE x"02" TO SCALE.
MOVE DECIMAL-SQLLEN TO O-SQLLEN(1).
SET SQLDATA(1) TO ADDRESS OF SALARY
SET SQLIND(1) TO ADDRESS OF SAL-IND
Chapter 5. Writing Dynamic SQL Programs 149
Language Example Source Code
FORTRAN include 'sqldact.f'
integer*2 sqlvar1
parameter ( sqlvar1 = sqlda_header_sz + 0*sqlvar_struct_sz )
C Declare an Output SQLDA -- 1 Variable
character out_sqlda(sqlda_header_sz + 1*sqlvar_struct_sz)
character*8 out_sqldaid ! Header
integer*4 out_sqldabc
integer*2 out_sqln
integer*2 out_sqld
integer*2 out_sqltype1 ! First Variable
integer*2 out_sqllen1
integer*4 out_sqldata1
integer*4 out_sqlind1
integer*2 out_sqlnamel1
character*30 out_sqlnamec1
equivalence( out_sqlda(sqlda_sqldaid_ofs), out_sqldaid )
equivalence( out_sqlda(sqlda_sqldabc_ofs), out_sqldabc )
equivalence( out_sqlda(sqlda_sqln_ofs), out_sqln )
equivalence( out_sqlda(sqlda_sqld_ofs), out_sqld )
equivalence( out_sqlda(sqlvar1+sqlvar_type_ofs), out_sqltype1 )
equivalence( out_sqlda(sqlvar1+sqlvar_len_ofs), out_sqllen1 )
equivalence( out_sqlda(sqlvar1+sqlvar_data_ofs), out_sqldata1 )
equivalence( out_sqlda(sqlvar1+sqlvar_ind_ofs), out_sqlind1 )
equivalence( out_sqlda(sqlvar1+sqlvar_name_length_ofs),
+ out_sqlnamel1 )
equivalence( out_sqlda(sqlvar1+sqlvar_name_data_ofs),
+ out_sqlnamec1 )
C Declare Local Variables for Holding Returned Data.
real*8 salary
integer*2 sal_ind
C Initialize the Output SQLDA (Header)
out_sqldaid = 'OUT_SQLDA'
out_sqldabc = sqlda_header_sz + 1*sqlvar_struct_sz
out_sqln = 1
out_sqld = 1
C Initialize VAR1
out_sqltype1 = SQL_TYP_NFLOAT
out_sqllen1 = 8
rc = sqlgaddr( %ref(salary), %ref(out_sqldata1) )
rc = sqlgaddr( %ref(sal_ind), %ref(out_sqlind1) )
150 Application Development Guide
In languages not supporting dynamic memory allocation, an SQLDA with the
desired number of SQLVAR elements must be explicitly declared in the host
language. Be sure to declare enough SQLVAR elements as determined by the
needs of the application.
Passing Data Using an SQLDA Structure
Greater flexibility is available when passing data using an SQLDA than is
available using lists of host variables. For example, an SQLDA can be used to
transfer data that has no native host language equivalent, such as DECIMAL
data in the C language. The sample program called ADHOC is an example
using this technique. (See “Example: ADHOC Program” on page 154.) See
Table 8 for a convenient cross-reference listing showing how the numeric
values and symbolic names are related.
Table 8. DB2 V2 SQLDA SQL Types. Numeric Values and Corresponding Symbolic Names
SQL Column Type SQLTYPE numeric SQLTYPE symbolic name1
value
DATE 384/385 SQL_TYP_DATE / SQL_TYP_NDATE
TIME 388/389 SQL_TYP_TIME / SQL_TYP_NTIME
TIMESTAMP 392/393 SQL_TYP_STAMP / SQL_TYP_NSTAMP
n/a 2
400/401 SQL_TYP_CGSTR / SQL_TYP_NCGSTR
BLOB 404/405 SQL_TYP_BLOB / SQL_TYP_NBLOB
CLOB 408/409 SQL_TYP_CLOB / SQL_TYP_NCLOB
DBCLOB 412/413 SQL_TYP_DBCLOB / SQL_TYP_NDBCLOB
VARCHAR 448/449 SQL_TYP_VARCHAR / SQL_TYP_NVARCHAR
CHAR 452/453 SQL_TYP_CHAR / SQL_TYP_NCHAR
LONG VARCHAR 456/457 SQL_TYP_LONG / SQL_TYP_NLONG
n/a 3
460/461 SQL_TYP_CSTR / SQL_TYP_NCSTR
VARGRAPHIC 464/465 SQL_TYP_VARGRAPH / SQL_TYP_NVARGRAPH
GRAPHIC 468/469 SQL_TYP_GRAPHIC / SQL_TYP_NGRAPHIC
LONG VARGRAPHIC 472/473 SQL_TYP_LONGRAPH / SQL_TYP_NLONGRAPH
FLOAT 480/481 SQL_TYP_FLOAT / SQL_TYP_NFLOAT
REAL4 480/481 SQL_TYP_FLOAT / SQL_TYP_NFLOAT
DECIMAL5 484/485 SQL_TYP_DECIMAL / SQL_TYP_DECIMAL
INTEGER 496/497 SQL_TYP_INTEGER / SQL_TYP_NINTEGER
SMALLINT 500/501 SQL_TYP_SMALL / SQL_TYP_NSMALL
n/a 804/805 SQL_TYP_BLOB_FILE / SQL_TYPE_NBLOB_FILE
n/a 808/809 SQL_TYP_CLOB_FILE / SQL_TYPE_NCLOB_FILE
n/a 812/813 SQL_TYP_DBCLOB_FILE / SQL_TYPE_NDBCLOB_FILE
Chapter 5. Writing Dynamic SQL Programs 151
Table 8. DB2 V2 SQLDA SQL Types (continued). Numeric Values and Corresponding Symbolic Names
SQL Column Type SQLTYPE numeric SQLTYPE symbolic name1
value
n/a 960/961 SQL_TYP_BLOB_LOCATOR / SQL_TYP_NBLOB_LOCATOR
n/a 964/965 SQL_TYP_CLOB_LOCATOR / SQL_TYP_NCLOB_LOCATOR
n/a 968/969 SQL_TYP_DBCLOB_LOCATOR / SQL_TYP_NDBCLOB_LOCATOR
Note: These defined types can be found in the sql.h include file located in the include sub-directory of
the sqllib directory. (For example, sqllib/include/sql.h for the C programming language.)
1. For the COBOL programming language, the SQLTYPE name does not use underscore (_) but uses a
hyphen (-) instead.
2. This is a null-terminated graphic string.
3. This is a null-terminated character string.
4. The difference between REAL and DOUBLE in the SQLDA is the length value (4 or 8).
5. Precision is in the first byte. Scale is in the second byte.
Processing Interactive SQL Statements
An application using dynamic SQL can be written to process arbitrary SQL
statements. For example, if an application accepts SQL statements from a user,
the application must be able to execute the statements without any prior
knowledge of the statements.
By using the PREPARE and DESCRIBE statements with an SQLDA structure,
an application can determine the type of SQL statement being executed, and
act accordingly.
For an example of a program that processes interactive SQL statements, see
“Example: ADHOC Program” on page 154.
Determining Statement Type
When an SQL statement is prepared, information concerning the type of
statement can be determined by examining the SQLDA structure. This
information is placed in the SQLDA structure either at statement preparation
time with the INTO clause, or by issuing a DESCRIBE statement against a
previously prepared statement.
In either case, the database manager places a value in the SQLD field of the
SQLDA structure, indicating the number of columns in the result table
generated by the SQL statement. If the SQLD field contains a zero (0), the
statement is not a SELECT statement. Since the statement is already prepared,
it can immediately be executed using the EXECUTE statement.
If the statement contains parameter markers, the USING clause must be
specified as described in the SQL Reference. The USING clause can specify
either a list of host variables or an SQLDA structure.
152 Application Development Guide
If the SQLD field is greater than zero, the statement is a SELECT statement
and must be processed as described in the following sections.
Varying-List SELECT Statement
A varying-list SELECT statement is one in which the number and types of
columns that are to be returned are not known at precompilation time. In this
case, the application does not know in advance the exact host variables that
need to be declared to hold a row of the result table.
To process a varying-list SELECT statement, an application can do the
following:
1. Declare an SQLDA. An SQLDA structure must be used to process
varying-list SELECT statements.
2. PREPARE the statement using the INTO clause. The application then
determines whether the SQLDA structure declared has enough SQLVAR
elements. If it does not, the application allocates another SQLDA structure
with the required number of SQLVAR elements, and issues an additional
DESCRIBE statement using the new SQLDA.
3. Allocate the SQLVAR elements. Allocate storage for the host variables
and indicators needed for each SQLVAR. This step involves placing the
allocated addresses for the data and indicator variables in each SQLVAR
element.
4. Process the SELECT statement. A cursor is associated with the prepared
statement, opened, and rows are fetched using the properly allocated
SQLDA structure.
These steps are described in detail in the following sections:
v “Declaring the SQLDA” on page 143
v “Preparing the Statement Using the Minimum SQLDA Structure” on
page 144
v “Allocating an SQLDA with Sufficient SQLVAR Entries” on page 145
v “Describing the SELECT Statement” on page 146
v “Acquiring Storage to Hold a Row” on page 146
v “Processing the Cursor” on page 147.
Saving SQL Requests from End Users
If your application allows users to save arbitrary SQL statements, you can
save them in a table with a column having a data type of VARCHAR, LONG
VARCHAR, CLOB, VARGRAPHIC, LONG VARGRAPHIC or DBCLOB. Note
that the VARGRAPHIC, LONG VARGRAPHIC, and DBCLOB data types are
only available in Double Byte Character Support (DBCS) and Extended UNIX
Code (EUC) environments.
You must save the source SQL statements, not the prepared versions. This
means that you must retrieve and then prepare each statement before
Chapter 5. Writing Dynamic SQL Programs 153
executing the version stored in the table. In essence, your application prepares
an SQL statement from a character string and executes this statement
dynamically.
Example: ADHOC Program
This sample program shows how the SQLDA is used to process interactive
SQL statements.
Note: The example adhoc.sqc exists for C only.
How the ADHOC Program Works
1. Define an SQLDA structure. The INCLUDE SQLDA statement defines
and declares an SQLDA structure, which is used to pass data from the
database manager to the program and back.
2. Define an SQLCA structure. The INCLUDE SQLCA statement defines an
SQLCA structure, and defines SQLCODE as an element within the structure.
The SQLCODE field of the SQLCA structure is updated with diagnostic
information by the database manager after execution of SQL statements.
3. Declare host variables. The BEGIN DECLARE SECTION and END
DECLARE SECTION statements delimit the host variable declarations.
Host variables are prefixed with a colon (:) when referenced in an SQL
statement.
4. Connect to database. The program connects to the database specified by
the user, and requests shared access to it. (It is assumed that a START
DATABASE MANAGER API call or db2start command has been issued.)
Other programs that attempt to connect to the same database in share
mode are also granted access.
5. Check completion. The SQLCA structure is checked for successful
completion of the CONNECT TO statement. An SQLCODE value of 0
indicates that the connection was successful.
6. Interactive prompt. SQL statements are entered in through the prompt
and then are sent to the process_statement function for further
processing.
7. End the transaction - COMMIT. The unit of work is ended with a
COMMIT if so chosen by the user. All changes requested by the SQL
statements entered since this last COMMIT are saved in the database.
8. End the transaction - ROLLBACK. The unit of work is ended with a
ROLLBACK if so chosen by the user. All changes requested by the SQL
statements entered since the last COMMIT or the start of the program,
are undone.
9. Disconnect from the database. The program disconnects from the
database by executing the CONNECT RESET statement. Upon return, the
SQLCA is checked for successful completion.
154 Application Development Guide
10. Copy SQL statement text to host variable. The statement text is copied
into the data area specified by the host variable st.
11. Prepare the SQLDA for processing. An initial SQLDA structure is
declared and memory is allocated through the init_da procedure to
determine what type of output the SQL statement could generate. The
SQLDA returned from this PREPARE statement reports the number of
columns that will be returned from the SQL statement.
12. SQLDA reports output columns exist. The SQL statement is a SELECT
statement. The SQLDA is initialized through the init_da procedure to
allocate memory space for the prepared SQL statement to reside in.
13. SQLDA reports no output columns. There are no columns to be
returned. The SQL statement is executed dynamically using the EXECUTE
statement.
14. Preparing memory space for the SQLDA. Memory is allocated to reflect
the column structures in the SQLDA. The required amount of memory is
selected by the SQLTYPE and the SQLLEN of the column structure in the
SQLDA.
15. Declare and open a cursor. The DECLARE statement associates the
cursor pcurs with the dynamically prepared SQL statement in
sqlStatement and the cursor is opened.
16. Retrieve a row. The FETCH statement positions the cursor at the next
row and moves the contents of the row into the SQLDA.
17. Display the column titles. The first row that is fetched is the column title
information.
18. Display the row information. The rows of information collected from
each consecutive FETCH is displayed.
19. Close the cursor. The CLOSE statement is closes the cursor, and releases
the resources associated with it.
The EMB_SQL_CHECK macro/function is an error checking utility which is
external to this program. For C programs that call DB2 APIs, the sqlInfoPrint
function in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in utilemb.sqc is
redefined as EMB_SQL_CHECK in utilemb.h. See “Using GET ERROR MESSAGE
in Example Programs” on page 118 for the source code for this error checking
utility.
Note that this example uses a number of additional procedures that are
provided as utilities in the file utilemb.sqc. These include:
init_da
Allocates memory for a prepared SQL statement. An internally
described function called SQLDASIZE is used to calculate the proper
amount of memory.
Chapter 5. Writing Dynamic SQL Programs 155
alloc_host_vars
Allocates memory for data from an SQLDA pointer.
free_da
Frees up the memory that has been allocated to use an SQLDA data
structure.
print_var
Prints out the SQLDA SQLVAR variables. This procedure first
determines data type then calls the appropriate subroutines that are
required to print out the data.
display_da
Displays the output of a pointer that has been passed through. All
pertinent information on the structure of the output data is available
from this pointer, as examined in the procedure print_var.
156 Application Development Guide
C Example: ADHOC.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlenv.h>
#include <sqlcodes.h>
#include <sqlda.h> 1
#include "utilemb.h"
#ifdef DB268K
/* Need to include ASLM for 68K applications */
#include <LibraryManager.h>
#endif
EXEC SQL INCLUDE SQLCA ; 2
#define SQLSTATE sqlca.sqlstate
int process_statement( char * ) ;
int main( int argc, char *argv[] ) {
int rc ;
char sqlInput[256] ;
char st[1024] ;
EXEC SQL BEGIN DECLARE SECTION ; 3
char userid[9] ;
char passwd[19] ;
EXEC SQL END DECLARE SECTION ;
#ifdef DB268K
/*
Before making any API calls for 68K environment,
need to initial the Library Manager
*/
InitLibraryManager(0,kCurrentZone,kNormalMemory) ;
atexit(CleanupLibraryManager) ;
#endif
printf( "Sample C program : ADHOC interactive SQL\n" ) ;
/* Initialize the connection to a database. */
if ( argc == 1 ) {
EXEC SQL CONNECT TO sample ;
EMB_SQL_CHECK( "CONNECT TO SAMPLE" ) ;
}
else if ( argc == 3 ) {
strcpy( userid, argv[1] ) ;
strcpy( passwd, argv[2] ) ;
EXEC SQL CONNECT TO sample USER :userid USING :passwd ; 4
EMB_SQL_CHECK( "CONNECT TO SAMPLE" ) ; 5
}
else {
printf( "\nUSAGE: adhoc [userid passwd]\n\n" ) ;
Chapter 5. Writing Dynamic SQL Programs 157
return( 1 ) ;
} /* endif */
printf( "Connected to database SAMPLE\n" ) ;
/* Enter the continuous command line loop. */
*sqlInput = '\0' ;
while ( ( *sqlInput != 'q' ) && ( *sqlInput != 'Q' ) ) { 6
printf( "Enter an SQL statement or 'quit' to Quit :\n" ) ;
gets( sqlInput ) ;
if ( ( *sqlInput == 'q' ) || ( *sqlInput == 'Q' ) ) break ;
if ( *sqlInput == '\0' ) { /* Don't process the statement */
printf( "No characters entered.\n" ) ;
continue ;
}
strcpy( st, sqlInput ) ;
while ( sqlInput[strlen( sqlInput ) - 1] == '\\' ) {
st[strlen( st ) - 1] = '\0' ;
gets( sqlInput ) ;
strcat( st, sqlInput ) ;
}
/* Process the statement. */
rc = process_statement( st ) ;
printf( "Enter 'c' to COMMIT or Any Other key to ROLLBACK the transaction :\n" ) ;
gets( sqlInput ) ;
if ( ( *sqlInput == 'c' ) || ( *sqlInput == 'C' ) ) {
printf( "COMMITING the transactions.\n" ) ;
EXEC SQL COMMIT ; 7
EMB_SQL_CHECK( "COMMIT" ) ;
}
else { /* assume that the transaction is to be rolled back */
printf( "ROLLING BACK the transactions.\n" ) ;
EXEC SQL ROLLBACK ; 8
EMB_SQL_CHECK( "ROLLBACK" ) ;
}
EXEC SQL CONNECT RESET ; 9
EMB_SQL_CHECK( "CONNECT RESET" ) ;
return( 0 ) ;
/******************************************************************************
* FUNCTION : process_statement
* This function processes the inputted statement and then prepares the
* procedural SQL implementation to take place.
158 Application Development Guide
******************************************************************************/
int process_statement ( char * sqlInput ) {
int counter = 0 ;
struct sqlda * sqldaPointer ;
short sqlda_d ;
EXEC SQL BEGIN DECLARE SECTION ; 3
char st[1024] ;
EXEC SQL END DECLARE SECTION ;
strcpy( st, sqlInput ) ; 10
/* allocate an initial SQLDA temp pointer to obtain information
about the inputted "st" */
init_da( &sqldaPointer, 1 ) ; 11
EXEC SQL PREPARE statement1 from :st ;
/* EMB_SQL_CHECK( "PREPARE" ) ; */
EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer ;
/* Expecting a return code of 0 or SQL_RC_W236,
SQL_RC_W237, SQL_RC_W238, SQL_RC_W239 for cases
where this statement is a SELECT statment. */
if ( SQLCODE != 0 &&
SQLCODE != SQL_RC_W236 &&
SQLCODE != SQL_RC_W237 &&
SQLCODE != SQL_RC_W238 &&
SQLCODE != SQL_RC_W239
) {
/* An unexpected warning/error has occurred. Check the SQLCA. */
EMB_SQL_CHECK( "DESCRIBE" ) ;
} /* end if */
sqlda_d = sqldaPointer->sqld ;
free( sqldaPointer ) ;
if ( sqlda_d > 0 ) { 12
/* this is a SELECT statement, a number of columns
are present in the SQLDA */
if ( SQLCODE == SQL_RC_W236 || SQLCODE == 0)
/* this out only needs a SINGLE SQLDA */
init_da( &sqldaPointer, sqlda_d ) ;
if ( SQLCODE == SQL_RC_W237 ||
SQLCODE == SQL_RC_W238 ||
SQLCODE == SQL_RC_W239 )
/* this output contains columns that need a DOUBLED SQLDA */
init_da( &sqldaPointer, sqlda_d * 2 ) ;
/* need to reassign the SQLDA with the correct number
of columns to the SQL statement */
Chapter 5. Writing Dynamic SQL Programs 159
EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer ;
EMB_SQL_CHECK( "DESCRIBE" ) ;
/* allocating the proper amount of memory
space needed for the variables */
alloc_host_vars( sqldaPointer ) ; 14
/* Don't need to check the SQLCODE for declaration of cursors */
EXEC SQL DECLARE pcurs CURSOR FOR statement1 ; 15
EXEC SQL OPEN pcurs ; 15
EMB_SQL_CHECK( "OPEN" ) ;
EXEC SQL FETCH pcurs USING DESCRIPTOR :*sqldaPointer; 16
EMB_SQL_CHECK( "FETCH" ) ;
/* if the FETCH is successful, obtain data from SQLDA */
/* display the column titles */
display_col_titles( sqldaPointer ) ; 17
/* display the rows that are fetched */
while ( SQLCODE == 0 ) {
counter++ ;
display_da( sqldaPointer ) ; 18
EXEC SQL FETCH pcurs USING DESCRIPTOR :*sqldaPointer ;
} /* endwhile */
EXEC SQL CLOSE pcurs ; 19
EMB_SQL_CHECK( "CLOSE CURSOR" ) ;
printf( "\n %d record(s) selected\n\n", counter ) ;
/* Free the memory allocated to this SQLDA. */
free_da( sqldaPointer ) ;
} else { /* this is not a SELECT statement, execute SQL statement */ 13
EXEC SQL EXECUTE statement1 ;
EMB_SQL_CHECK( "Executing the SQL statement" ) ;
} /* end if */
return( 0 ) ;
} /* end of program : ADHOC.SQC */
160 Application Development Guide
Variable Input to Dynamic SQL
This section shows you how to use parameter markers in your dynamic SQL
applications to represent host variable information. It includes:
v Using Parameter Markers
v Example: VARINP Program
Using Parameter Markers
A dynamic SQL statement cannot contain host variables, because host variable
information (data type and length) is available only during application
precompilation. At execution time, the host variable information is not
present. Therefore, a new method is needed to represent application variables.
Host variables are represented by a question mark (?) which is called a
parameter marker. Parameter markers indicate the places in which a host
variable is to be substituted inside of an SQL statement. The parameter
marker takes on an assumed data type and length that is dependent on the
context of its use inside the SQL statement.
If the data type of a parameter marker is not obvious from the context of the
statement in which it is used, the type can be specified using a CAST. Such a
parameter marker is considered a typed parameter marker. Typed parameter
markers will be treated like a host variable of the given type. For example, the
statement SELECT ? FROM SYSCAT.TABLES is invalid because DB2 does not
know the type of the result column. However, the statement
SELECT CAST(? AS INTEGER) FROM SYSCAT.TABLES, is valid because the cast
promises that the parameter marker represents an INTEGER, so DB2 knows the
type of the result column.
A character string containing a parameter marker might look like the
following:
DELETE FROM TEMPL WHERE EMPNO = ?
When this statement is executed, a host variable or SQLDA structure is
specified by the USING clause of the EXECUTE statement. The contents of the
host variable are used when the statement executes.
If the SQL statement contains more than one parameter marker, then the
USING clause of the EXECUTE statement must either specify a list of host
variables (one for each parameter marker), or it must identify an SQLDA that
has an SQLVAR entry for each parameter marker. (Note that for LOBs, there
are two SQLVARs per parameter marker.) The host variable list or SQLVAR
entries are matched according to the order of the parameter markers in the
statement, and they must have compatible data types.
Chapter 5. Writing Dynamic SQL Programs 161
Note that using a parameter marker with dynamic SQL is like using host
variables with static SQL. In either case, the optimizer does not use
distribution statistics, and possibly may not choose the best access plan.
The rules that apply to parameter markers are listed under the PREPARE
statement in the SQL Reference.
Example: VARINP Program
This is an example of an UPDATE that uses a parameter marker in the search
and update conditions. The sample is available in the following programming
languages:
C varinp.sqc
Java Varinp.java
COBOL varinp.sqb
How the VARINP Program Works
1. Prepare the SELECT SQL statement The PREPARE statement is called to
dynamically prepare an SQL statement. In this SQL statement, parameter
markers are denoted by the ?. The job field of staff is defined to be
updatable, even though it is not specified in the result table.
2. Declare the cursor. The DECLARE CURSOR statement associates the
cursor c1 to the query that was prepared in 1.
3. Open the cursor. The cursor c1 is opened, causing the database manager
to perform the query and build a result table. The cursor is positioned
before the first row.
4. Prepare the UPDATE SQL statement The PREPARE statement is called to
dynamically prepare an SQL statement. The parameter marker in this
statement is set to be Clerk but can be changed dynamically to anything,
as long as it conforms to the column data type it is being updated into.
5. Retrieve a row. The FETCH statement positions the cursor at the next row
and moves the contents of the row into the host variables. This row
becomes the CURRENT row.
6. Update the current row. The current row and specified column, job, is
updated with the content of the passed parameter parm_var.
7. Close the cursor. The CLOSE statement is issued, releasing the resources
associated with the cursor. The cursor can be opened again, however.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
162 Application Development Guide
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
Chapter 5. Writing Dynamic SQL Programs 163
C Example: VARINP.SQC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
int main(int argc, char *argv[])
{
EXEC SQL BEGIN DECLARE SECTION;
char pname[10];
short dept;
char userid[9];
char passwd[19];
char st[255];
char parm_var[6];
EXEC SQL END DECLARE SECTION;
printf( "Sample C program: VARINP \n" );
if (argc == 1)
{
EXEC SQL CONNECT TO sample;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else if (argc == 3)
{
strcpy (userid, argv[1]);
strcpy (passwd, argv[2]);
EXEC SQL CONNECT TO sample USER :userid USING :passwd;
EMB_SQL_CHECK("CONNECT TO SAMPLE");
}
else
{
printf ("\nUSAGE: varinp [userid passwd]\n\n");
return 1;
} /* endif */
strcpy (st, "SELECT name, dept FROM staff ");
strcat (st, "WHERE job = ? FOR UPDATE OF job");
EXEC SQL PREPARE s1 FROM :st; 1
EMB_SQL_CHECK("PREPARE");
EXEC SQL DECLARE c1 CURSOR FOR s1; 2
strcpy (parm_var, "Mgr");
EXEC SQL OPEN c1 USING :parm_var; 3
EMB_SQL_CHECK("OPEN");
strcpy (parm_var, "Clerk");
strcpy (st, "UPDATE staff SET job = ? WHERE CURRENT OF c1");
EXEC SQL PREPARE s2 from :st; 4
164 Application Development Guide
do
{
EXEC SQL FETCH c1 INTO :pname, :dept; 5
if (SQLCODE != 0) break;
printf( "%-10.10s in dept. %2d will be demoted to Clerk\n",
pname, dept );
EXEC SQL EXECUTE s2 USING :parm_var; 6
EMB_SQL_CHECK("EXECUTE");
} while ( 1 );
EXEC SQL CLOSE c1; 7
EMB_SQL_CHECK("CLOSE CURSOR");
EXEC SQL ROLLBACK;
EMB_SQL_CHECK("ROLLBACK");
printf( "\nOn second thought -- changes rolled back.\n" );
EXEC SQL CONNECT RESET;
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
/* end of program : VARINP.SQC */
Chapter 5. Writing Dynamic SQL Programs 165
Java Example: Varinp.java
import java.sql.*;
class Varinp
{ static
{ try
{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
}
catch (Exception e)
{ System.out.println ("\n Error loading DB2 Driver...\n");
System.out.println (e);
System.exit(1);
}
}
public static void main(String argv[])
{ try
{ System.out.println (" Java Varinp Sample");
// Connect to Sample database
Connection con = null;
// URL is jdbc:db2:dbname
String url = "jdbc:db2:sample";
if (argv.length == 0)
{ // connect with default id/password
con = DriverManager.getConnection(url);
}
else if (argv.length == 2)
{ String userid = argv[0];
String passwd = argv[1];
// connect with user-provided username and password
con = DriverManager.getConnection(url, userid, passwd);
}
else
{ throw new Exception("\nUsage: java Varinp [username password]\n");
}
// Enable transactions
con.setAutoCommit(false);
// Perform dynamic SQL using JDBC
try
{ PreparedStatement pstmt1 = con.prepareStatement(
"SELECT name, dept FROM staff WHERE job = ? FOR UPDATE OF job"); 1
// set cursor name for the positioned update statement
pstmt1.setCursorName("c1"); 2
pstmt1.setString(1, "Mgr");
ResultSet rs = pstmt1.executeQuery(); 3
PreparedStatement pstmt2 = con.prepareStatement(
"UPDATE staff SET job = ? WHERE CURRENT OF c1"); 4
pstmt2.setString(1, "Clerk");
166 Application Development Guide
System.out.print("\n");
while( rs.next() ) 5
{ String name = rs.getString("name");
short dept = rs.getShort("dept");
System.out.println(name + " in dept. " + dept
+ " will be demoted to Clerk");
pstmt2.executeUpdate(); 6
};
rs.close();
pstmt1.close(); 7
pstmt2.close();
}
catch( Exception e )
{ throw e;
}
finally
{ // Rollback the transaction
System.out.println("\nRollback the transaction...");
con.rollback();
System.out.println("Rollback done.");
}
}
catch( Exception e )
{ System.out.println(e);
}
}
}
Chapter 5. Writing Dynamic SQL Programs 167
COBOL Example: VARINP.SQB
Identification Division.
Program-ID. "varinp".
Data Division.
Working-Storage Section.
copy "sqlca.cbl".
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 pname pic x(10).
01 dept pic s9(4) comp-5.
01 st pic x(127).
01 parm-var pic x(5).
01 userid pic x(8).
01 passwd.
49 passwd-length pic s9(4) comp-5 value 0.
49 passwd-name pic x(18).
EXEC SQL END DECLARE SECTION END-EXEC.
77 errloc pic x(80).
Procedure Division.
Main Section.
display "Sample COBOL program: VARINP".
* Get database connection information.
display "Enter your user id (default none): "
with no advancing.
accept userid.
if userid = spaces
EXEC SQL CONNECT TO sample END-EXEC
else
display "Enter your password : " with no advancing
accept passwd-name.
* Passwords in a CONNECT statement must be entered in a VARCHAR format
* with the length of the input string.
inspect passwd-name tallying passwd-length for characters
before initial " ".
EXEC SQL CONNECT TO sample USER :userid USING :passwd
END-EXEC.
move "CONNECT TO" to errloc.
call "checkerr" using SQLCA errloc.
move "SELECT name, dept FROM staff
- " WHERE job = ? FOR UPDATE OF job" to st.
EXEC SQL PREPARE s1 FROM :st END-EXEC. 1
move "PREPARE" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL DECLARE c1 CURSOR FOR s1 END-EXEC. 2
168 Application Development Guide
move "Mgr" to parm-var.
EXEC SQL OPEN c1 USING :parm-var END-EXEC 3
move "OPEN" to errloc.
call "checkerr" using SQLCA errloc.
move "Clerk" to parm-var.
move "UPDATE staff SET job = ? WHERE CURRENT OF c1" to st.
EXEC SQL PREPARE s2 from :st END-EXEC. 4
move "PREPARE S2" to errloc.
call "checkerr" using SQLCA errloc.
* call the FETCH and UPDATE loop.
perform Fetch-Loop thru End-Fetch-Loop
until SQLCODE not equal 0.
EXEC SQL CLOSE c1 END-EXEC. 7
move "CLOSE" to errloc.
call "checkerr" using SQLCA errloc.
EXEC SQL ROLLBACK END-EXEC.
move "ROLLBACK" to errloc.
call "checkerr" using SQLCA errloc.
DISPLAY "On second thought -- changes rolled back.".
EXEC SQL CONNECT RESET END-EXEC.
move "CONNECT RESET" to errloc.
call "checkerr" using SQLCA errloc.
End-Main.
go to End-Prog.
Fetch-Loop Section.
EXEC SQL FETCH c1 INTO :pname, :dept END-EXEC. 5
if SQLCODE not equal 0
go to End-Fetch-Loop.
display pname, " in dept. ", dept,
" will be demoted to Clerk".
EXEC SQL EXECUTE s2 USING :parm-var END-EXEC. 6
move "EXECUTE" to errloc.
call "checkerr" using SQLCA errloc.
End-Fetch-Loop. exit.
End-Prog.
stop run.
Chapter 5. Writing Dynamic SQL Programs 169
The DB2 Call Level Interface (CLI)
An application that uses an embedded SQL interface requires a precompiler to
convert the SQL statements into code, which is then compiled, bound to the
database, and executed. In contrast, a DB2 CLI application does not have to
be precompiled or bound, but instead uses a standard set of functions to
execute SQL statements and related services at run time.
This difference is important because, traditionally, precompilers have been
specific to each database product, which effectively ties your applications to
that product. DB2 CLI enables you to write portable applications that are
independent of any particular database product. This independence means
DB2 CLI applications do not have to be recompiled or rebound to access
different DB2 databases, including DRDA databases. They just connect to the
appropriate database at run time.
Comparing Embedded SQL and DB2 CLI
DB2 CLI and embedded SQL also differ in the following ways:
v DB2 CLI does not require the explicit declaration of cursors. DB2 CLI has a
supply of cursors that get used as needed. The application can then use the
generated cursor in the normal cursor fetch model for multiple row SELECT
statements and positioned UPDATE and DELETE statements.
v The OPEN statement is not used in DB2 CLI. Instead, the execution of a
SELECT automatically causes a cursor to be opened.
v Unlike embedded SQL, DB2 CLI allows the use of parameter markers on
the equivalent of the EXECUTE IMMEDIATE statement (the SQLExecDirect()
function).
v A COMMIT or ROLLBACK in DB2 CLI is issued via the SQLEndTran() function
call rather than by passing it as an SQL statement.
v DB2 CLI manages statement related information on behalf of the
application, and provides a statement handle to refer to it as an abstract
object. This handle eliminates the need for the application to use product
specific data structures.
v Similar to the statement handle, the environment handle and connection handle
provide a means to refer to all global variables and connection specific
information. The descriptor handle describes either the parameters of an SQL
statement or the columns of a result set.
v DB2 CLI uses the SQLSTATE values defined by the X/Open SQL CAE
specification. Although the format and most of the values are consistent
with values used by the IBM relational database products, there are
differences. (There are also differences between ODBC SQLSTATES and the
X/Open defined SQLSTATES).
v DB2 CLI supports scrollable cursors. With scrollable cursors, you can scroll
through a static cursor as follows:
– Forward by one or more rows
170 Application Development Guide
– Backward by one or more rows
– From the first row by one or more rows
– From the last row by one or more rows.
Despite these differences, there is an important common concept between
embedded SQL and DB2 CLI: DB2 CLI can execute any SQL statement that can
be prepared dynamically in embedded SQL.
Note: DB2 CLI can also accept some SQL statements that cannot be prepared
dynamically, such as compound SQL statements.
Table 37 on page 723 lists each SQL statement, and indicates whether or not it
can be executed using DB2 CLI. The table also indicates if the command line
processor can be used to execute the statement interactively, (useful for
prototyping SQL statements).
Each DBMS may have additional statements that you can dynamically
prepare. In this case, DB2 CLI passes the statements to the DBMS. There is
one exception: the COMMIT and ROLLBACK statement can be dynamically
prepared by some DBMSs but are not passed. In this case, use the
SQLEndTran() function to specify either the COMMIT or ROLLBACK
statement.
Advantages of Using DB2 CLI
The DB2 CLI interface has several key advantages over embedded SQL.
v It is ideally suited for a client-server environment, in which the target
database is not known when the application is built. It provides a consistent
interface for executing SQL statements, regardless of which database server
the application is connected to.
v It increases the portability of applications by removing the dependence on
precompilers.
v Individual DB2 CLI applications do not need to be bound to each database,
only bind files shipped with DB2 CLI need to be bound once for all DB2
CLI applications. This can significantly reduce the amount of management
required for the application once it is in general use.
v DB2 CLI applications can connect to multiple databases, including multiple
connections to the same database, all from the same application. Each
connection has its own commit scope. This is much simpler using CLI than
using embedded SQL where the application must make use of
multi-threading to achieve the same result.
v DB2 CLI eliminates the need for application controlled, often complex data
areas, such as the SQLDA and SQLCA, typically associated with embedded
SQL applications. Instead, DB2 CLI allocates and controls the necessary
data structures, and provides a handle for the application to reference them.
Chapter 5. Writing Dynamic SQL Programs 171
v DB2 CLI enables the development of multi-threaded thread-safe
applications where each thread can have its own connection and a separate
commit scope from the rest. DB2 CLI achieves this by eliminating the data
areas described above, and associating all such data structures that are
accessible to the application with a specific handle. Unlike embedded SQL,
a multi-threaded CLI application does not need to call any of the context
management DB2 APIs; this is handled by the DB2 CLI driver
automatically.
v DB2 CLI provides enhanced parameter input and fetching capability,
allowing arrays of data to be specified on input, retrieving multiple rows of
a result set directly into an array, and executing statements that generate
multiple result sets.
v DB2 CLI provides a consistent interface to query catalog (Tables, Columns,
Foreign Keys, Primary Keys, etc.) information contained in the various
DBMS catalog tables. The result sets returned are consistent across DBMSs.
This shields the application from catalog changes across releases of database
servers, as well as catalog differences amongst different database servers;
thereby saving applications from writing version specific and server specific
catalog queries.
v Extended data conversion is also provided by DB2 CLI, requiring less
application code when converting information between various SQL and C
data types.
v DB2 CLI incorporates both the ODBC and X/Open CLI functions, both of
which are accepted industry specifications. DB2 CLI is also aligned with the
emerging ISO CLI standard. Knowledge that application developers invest
in these specifications can be applied directly to DB2 CLI development, and
vice versa. This interface is intuitive to grasp for those programmers who
are familiar with function libraries but know little about product specific
methods of embedding SQL statements into a host language.
v DB2 CLI provides the ability to retrieve multiple rows and result sets
generated from a stored procedure residing on a DB2 Universal Database
(or DB2 for MVS/ESA version 5 or later) server. However, note that this
capability exists for Version 5 DB2 Universal Database clients using
embedded SQL if the stored procedure resides on a server accessible from a
DataJoiner Version 2 server.
v DB2 CLI supports server-side scrollable cursors that can be used in
conjunction with array output. This is useful in GUI applications that
display database information in scroll boxes that make use of the Page Up,
Page Down, Home and End keys. You can declare a read-only cursor as
scrollable then move forward or backward through the result set by one or
more rows. You can also fetch rows by specifying an offset from:
– The current row
– The beginning or end of the result set
– A specific row you have previously set with a bookmark.
172 Application Development Guide
v DB2 CLI applications can dynamically describe parameters in an SQL
statement the same way that CLI and Embedded SQL applications describe
result sets. This enables CLI applications to dynamically process SQL
statements that contain parameter markers without knowing the data type
of those parameter markers in advance. When the SQL statement is
prepared, describe information is returned detailing the data types of the
parameters.
Deciding on Embedded SQL or DB2 CLI
Which interface you choose depends on your application.
DB2 CLI is ideally suited for query-based graphical user interface (GUI)
applications that require portability. The advantages listed above, may make
using DB2 CLI seem like the obvious choice for any application. There is
however, one factor that must be considered, the comparison between static
and dynamic SQL. It is much easier to use static SQL in embedded
applications.
For more information on using static SQL in CLI applications, refer to the Web
page at:
http://www.ibm.com/software/data/db2/udb/staticcli
Static SQL has several advantages:
v Performance
Dynamic SQL is prepared at run time, static SQL is prepared at precompile
time. As well as requiring more processing, the preparation step may incur
additional network-traffic at run time. This additional step (and
network-traffic), however, will not be required if the DB2 CLI application
makes use of deferred prepare.
It is important to note that static SQL will not always have better
performance than dynamic SQL. Dynamic SQL can make use of changes to
the database, such as new indexes, and can use current database statistics to
choose the optimal access plan. In addition, precompilation of statements
can be avoided if they are cached.
v Encapsulation and Security
In static SQL, the authorizations to objects (such as a table, view) are
associated with a package and are validated at package binding time. This
means that database administrators need only to grant execute on a
particular package to a set of users (thus encapsulating their privileges in
the package) without having to grant them explicit access to each database
object. In dynamic SQL, the authorizations are validated at run time on a
per statement basis; therefore, users must be granted explicit access to each
database object. This permits these users access to parts of the object that
they do not have a need to access.
v Embedded SQL is supported in languages other than C or C++.
Chapter 5. Writing Dynamic SQL Programs 173
v For fixed query selects, embedded SQL is simpler.
If an application requires the advantages of both interfaces, it is possible to
make use of static SQL within a DB2 CLI application by creating a stored
procedure that contains the static SQL. The stored procedure is called from
within a DB2 CLI application and is executed on the server. Once the stored
procedure is created, any DB2 CLI or ODBC application can call it. For more
information, refer to the CLI Guide and Reference.
For more information on using static SQL in CLI applications, refer to the Web
page at:
http://www.ibm.com/software/data/db2/udb/staticcli
It is also possible to write a mixed application that uses both DB2 CLI and
embedded SQL, taking advantage of their respective benefits. In this case,
DB2 CLI is used to provide the base application, with key modules written
using static SQL for performance or security reasons. This complicates the
application design, and should only be used if stored procedures do not meet
the applications requirements. For more information, refer to the section on
Mixing Embedded SQL and DB2 CLI in the CLI Guide and Reference.
Ultimately, the decision on when to use each interface, will be based on
individual preferences and previous experience rather than on any one factor.
174 Application Development Guide
Chapter 6. Common DB2 Application Techniques
Generated Columns . . . . . . . . . 176 Savepoints and Buffered Inserts . . . . 182
Identity Columns . . . . . . . . . . 176 Using Savepoints with Cursor Blocking 182
Declared Temporary Tables . . . . . . 177 Savepoints and XA Compliant Transaction
Controlling Transactions with Savepoints 179 Managers . . . . . . . . . . . 183
Savepoint Restrictions . . . . . . . 180
Savepoints and Data Definition Language
(DDL). . . . . . . . . . . . . 181
DB2 enables you to use embedded SQL to handle common database
application development problems.
Generated columns
Rather than using cumbersome insert and update triggers, DB2
enables you to include generated columns in your tables using the
GENERATED ALWAYS AS clause. Generated columns provide
automatically updated values derived from an SQL expression.
Identity columns
DB2 application developers often need to create a primary key for
every row in a table. If you create a table that uses an identity column
for the primary key, DB2 automatically inserts a unique value. When
you use identity columns, your applications can benefit from
increased performance due to a reduction in lock contention.
Declared temporary tables
Declared temporary tables are similar to regular tables, but persist
only as long as the database connection and are not subject to locking
or logging. If your application creates tables to process large amounts
of data and drops those tables once the application has finished
manipulating that data, consider using declared temporary tables.
Declared temporary tables can increase the performance of your
application and, for applications designed for concurrent users,
simplify your application development effort.
External savepoints
While COMMIT and ROLLBACK statements enable you to control the
behavior of an entire transaction, savepoints enable you to exercise
more granular control within transactions. Savepoint blocks group
several SQL statements together. If one of the sub-statements in the
savepoint block results in an error, you can roll back just the failing
sub-statement and complete the work of the other sub-statements.
© Copyright IBM Corp. 1993, 2000 175
Generated Columns
A generated column is a column that derives the values for each row from an
expression, rather than from an insert or update operation. While combining
an update trigger and an insert trigger can achieve a similar effect, using a
generated column guarantees that the derived value is consistent with the
expression.
To create a generated column in a table, use the GENERATED ALWAYS AS
clause for the column and include the expression from which the value for the
column will be derived. You can include the GENERATED ALWAYS AS clause
in ALTER TABLE or CREATE TABLE statements. The following example
creates a table with two regular columns, “c1” and “c2”, and two generated
columns, “c3” and “c4”, that are derived from the regular columns of the
table.
CREATE TABLE T1(c1 INT, c2 DOUBLE,
c3 DOUBLE GENERATED ALWAYS AS (c1 + c2),
c4 GENERATED ALWAYS AS
(CASE
WHEN c1 > c2 THEN 1
ELSE NULL
END)
);
For more information on using generated columns to improve the
performance of your applications, refer to the Administration Guide. For more
information on creating generated columns, refer to the CREATE TABLE
statement syntax in the SQL Reference.
Identity Columns
Identity columns provide DB2 application developers with an easy way of
automatically generating a unique primary key value for every row in a table.
To create an identity column, include the IDENTITY clause in your CREATE
TABLE or ALTER TABLE statement.
Use identity columns in your applications to avoid the concurrency and
performance problems that can occur when an application generates its own
unique counter outside the database. When you do not use identity columns
to automatically generate unique primary keys, a common design is to store a
counter in a table with a single row. Each transaction then locks this table,
increments the number, and then commits the transaction to unlock the
counter. Unfortunately, this design only allows a single transaction to
increment the counter at a time.
In contrast, if you use an identity column to automatically generate primary
keys, the application can achieve much higher levels of concurrency. With
176 Application Development Guide
identity columns, DB2 maintains the counter so that transactions do not have
to lock the counter. Applications that use identity columns can perform better
because an uncommitted transaction that has incremented the counter does
not prevent other subsequent transactions from also incrementing the counter.
The counter for the identity column is incremented or decremented
independently of the transaction. If a given transaction increments an identity
counter two times, that transaction may see a gap in the two numbers that are
generated because there may be other transactions concurrently incrementing
the same identity counter.
An identity column may appear to have generated gaps in the counter, as the
result of a transaction that was rolled back, or because the database cached a
range of values that have been deactivated (normally or abnormally) before all
the cached values were assigned.
For more information on identity columns, refer to the Administration Guide.
For more information on the IDENTITY clause of the CREATE TABLE and
ALTER TABLE stataments, refer to the SQL Reference.
Declared Temporary Tables
A declared temporary table is a temporary table that is only accessible to SQL
statements that are issued by the application which created the temporary
table. A declared temporary table does not persist beyond the duration of the
connection of the application to the database.
Use declared temporary tables to potentially improve the performance of your
applications. When you create a declared temporary table, DB2 does not insert
an entry into the system catalog tables, and therefore your server does not
suffer from catalog contention issues. In comparison to regular tables, DB2
does not lock declared temporary tables or their rows, and does not log
declared temporary tables or their contents. If your current application creates
tables to process large amounts of data and drops those tables once the
application has finished manipulating that data, consider using declared
temporary tables instead of regular tables.
If you develop applications written for concurrent users, your applications can
take advantage of declared temporary tables. Unlike regular tables, declared
temporary tables are not subject to name collision. For each instance of the
application, DB2 can create a declared temporary table with an identical
name. For example, to write an application for concurrent users that uses
regular tables to process large amounts of temporary data, you must ensure
that each instance of the application uses a unique name for the regular table
that holds the temporary data. Typically, you would create another table that
tracks the names of the tables that are in use at any given time. With declared
Chapter 6. Common DB2 Application Techniques 177
temporary tables, simply specify one declared temporary table name for your
temporary data. DB2 guarantees that each instance of the application uses a
unique table.
To use a declared temporary table, perform the following steps:
Step 1. Ensure that a USER TEMPORARY TABLESPACE exists. If a USER
TEMPORARY TABLESPACE does not exist, issue a CREATE USER
TEMPORARY TABLESPACE statement.
Step 2. Issue a DECLARE GLOBAL TEMPORARY TABLE statement in your
application.
The schema for declared temporary tables is always SESSION. To use the
declared temporary table in your SQL statements, you must refer to the table
using the SESSION schema qualifier either explicitly or by using a DEFAULT
schema of SESSION to qualify any unqualified references. In the following
example, the table name is always qualified by the schema name SESSION
when you create a declared temporary table named TT1 with the following
statement:
DECLARE GLOBAL TEMPORARY TABLE TT1
To select the contents of the column1 column from the declared temporary
table created in the previous example, use the following statement:
SELECT column1 FROM SESSION.TT1;
Note that DB2 also enables you to create persistent tables with the SESSION
schema. If you create a persistent table with the qualified name SESSION.TT3,
you can then create a declared temporary table with the qualified name
SESSION.TT3. In this situation, DB2 always resolves references to persistent
and declared temporary tables with identical qualified names to the declared
temporary table. To avoid confusing persistent tables with declared temporary
tables, you should not create persistent tables using the SESSION schema.
If you create an application that includes a static SQL reference to a table,
view, or alias qualified with the SESSION schema, the DB2 precompiler does
not compile that statement at bind time and marks the statement as “needing
compilation”. At run time, DB2 compiles the statement. This behavior is called
incremental binding. DB2 automatically performs incremental binding for static
SQL references to tables, views, and aliases qualified with the SESSION
schema. You do not need to specify the VALIDATE RUN option on the BIND
or PRECOMPILE command to enable incremental binding for these
statements.
If you issue a ROLLBACK statement for a transaction that includes a
DECLARE GLOBAL TEMPORARY TABLE statement, DB2 drops the declared
temporary table. If you issue a DROP TABLE statement for a declared
temporary table, issuing a ROLLBACK statement for that transaction only
178 Application Development Guide
restores an empty declared temporary table. A ROLLBACK of a DROP TABLE
statement does not restore the rows that existed in the declared temporary
table.
The default behavior of a declared temporary table is to delete all rows from
the table when you commit a transaction. However, if one or more WITH
HOLD cursors are still open on the declared temporary table, DB2 does not
delete the rows from the table when you commit a transaction. To avoid
deleting all rows when you commit a transaction, create the temporary table
using the ON COMMIT PRESERVE ROWS clause in the DECLARE GLOBAL
TEMPORARY TABLE statement.
If you modify the contents of a declared temporary table using an INSERT,
UPDATE, or DELETE statement within a transaction, and roll back that
transaction, DB2 deletes all of the rows of the declared temporary table. If you
attempt to modify the contents of a declared temporary table using an
INSERT, UPDATE, or DELETE statement, and the statement fails, DB2 deletes
all of the rows of the declared temporary table.
In a partitioned environment, when a node failure is encountered, all declared
temporary tables that have a partition on the failed node become unusable.
Any subsequent access to those unusable declared temporary tables returns an
error (SQL1477N). When your application encounters an unusable declared
temporary table the application can either drop the table or recreate the table
by specifying the WITH REPLACE clause in the DECLARE GLOBAL
TEMPORARY TABLE statement.
Declared temporary tables are subject to a number of restrictions. For
example, you cannot define indexes, aliases, or views for declared temporary
tables. You cannot use IMPORT and LOAD to populate declared temporary
tables. For the complete syntax of the DECLARE GLOBAL TEMPORARY
TABLE statement, and a complete list of the restrictions on declared
temporary tables, refer to the SQL Reference.
Controlling Transactions with Savepoints
A savepoint is a mechanism of undoing work done by the DBMS when a
database request fails. Savepoints make non-atomic database requests behave
atomically. If an error occurs during execution, the savepoint can be used to
undo changes made by the transaction between the time the savepoint was
started and the time the savepoint rollback is requested.
A savepoint is similar to a compound SQL statement. It allows you to group
several SQL statements into a single executable block. Before the first
sub-statement of the block is executed, a savepoint request to start a savepoint
block is required. If any of the sub-statements end in an error, only that
Chapter 6. Common DB2 Application Techniques 179
sub-statement will be rolled back. This provides more granularity than a
compound SQL statement, in which a single error causes the entire block to
end in an error and rolls back the entire compound SQL statement. At the end
of a savepoint block of statements, you can either release the savepoint, or
rollback to the savepoint.
The following SQL statements enable you to create and control savepoints:
SAVEPOINT
To set a savepoint, issue a SAVEPOINT SQL statement. To improve
the clarity of your code, you can choose a meaningful name for the
savepoint. For example:
SAVEPOINT savepoint1 ON ROLLBACK RETAIN CURSORS
RELEASE SAVEPOINT
To release a savepoint, issue a RELEASE SAVEPOINT SQL statement.
If you do not explicitly release a savepoint with a RELEASE
SAVEPOINT SQL statement, it is released at the end of the
transaction. For example:
RELEASE SAVEPOINT savepoint1
ROLLBACK TO SAVEPOINT
To rollback to a savepoint, issue a ROLLBACK TO SAVEPOINT SQL
statement. For example:
ROLLBACK TO SAVEPOINT
For the complete syntax of the SAVEPOINT, RELEASE SAVEPOINT, and
ROLLBACK TO SAVEPOINT statements, refer to the SQL Reference.
Savepoint Restrictions
DB2 Universal Database places the following restrictions on your use of
savepoints in applications:
Atomic compound SQL
DB2 does not enable you to use savepoints within atomic compound
SQL. You cannot use atomic compound SQL within a savepoint.
Nested Savepoints
DB2 does not support the use of a savepoint within another
savepoint.
Triggers
DB2 does not support the use of savepoints in triggers.
Quantity of savepoints within a transaction
DB2 enables you to set and use as many savepoints as you require
within a transaction.
SET INTEGRITY statement
Within a savepoint, DB2 treats SET INTEGRITY statements as DDL
180 Application Development Guide
statements. For more information on using DDL in savepoints, see
“Savepoints and Data Definition Language (DDL)”.
Savepoints and Data Definition Language (DDL)
DB2 enables you to include DDL statements within a savepoint. If the
application successfully releases a savepoint that executes DDL statements, the
application can continue to use the SQL objects created by the DDL. However,
if the application issues a ROLLBACK TO SAVEPOINT statement for a
savepoint that executes DDL statements, DB2 marks any cursors that depend
on the effects of those DDL statements as invalid.
In the following example, the application attempts to fetch from three
previously opened cursors after issuing a ROLLBACK TO SAVEPOINT
statement:
SAVEPOINT savepoint_name;
PREPARE s1 FROM 'SELECT FROM t1';
--issue DDL statement for t1
ALTER TABLE t1 ADD COLUMN...
PREPARE s2 FROM 'SELECT FROM t2';
--issue DDL statement for t3
ALTER TABLE t3 ADD COLUMN...
PREPARE s3 FROM 'SELECT FROM t3';
OPEN c1 USING s1;
OPEN c2 USING s2;
OPEN c3 USING s3;
ROLLBACK TO SAVEPOINT
FETCH c1; --invalid (SQLCODE −910)
FETCH c2; --successful
FETCH c3; --invalid (SQLCODE −910)
At the ROLLBACK TO SAVEPOINT statement, DB2 marks cursors “c1” and
“c3” as invalid because the SQL objects on which they depend have been
manipulated by DDL statements within the savepoint. However, a FETCH
using cursor “c2” from the example is successful after the ROLLBACK TO
SAVEPOINT statement.
You can issue a CLOSE statement to close invalid cursors. If you issue a
FETCH against an invalid cursor, DB2 returns SQLCODE −910. If you issue an
OPEN statement against an invalid cursor, DB2 returns SQLCODE −502. If
you issue an UPDATE or DELETE WHERE CURRENT OF statement against
an invalid cursor, DB2 returns SQLCODE −910.
Within savepoints, DB2 treats tables with the NOT LOGGED INITIALLY
property and temporary tables as follows:
NOT LOGGED INITIALLY tables
Within a savepoint, you can create a table with the NOT LOGGED
INITIALLY property, or alter a table to have the NOT LOGGED
INITIALLY property. For these savepoints, however, DB2 treats
Chapter 6. Common DB2 Application Techniques 181
ROLLBACK TO SAVEPOINT statements as ROLLBACK WORK
statements and rolls back the entire transaction.
DECLARE TEMPORARY TABLE inside savepoint
If a temporary table is declared within a savepoint, a ROLLBACK TO
SAVEPOINT statement drops the temporary table.
DECLARE TEMPORARY TABLE outside savepoint
If a temporary table is declared outside a savepoint, a ROLLBACK TO
SAVEPOINT statement does not drop the temporary table.
Savepoints and Buffered Inserts
To improve the performance of DB2 applications, you can use buffered inserts
in your applications by precompiling or binding with the INSERT BUF option.
If your application takes advantage of both buffered inserts and savepoints,
DB2 flushes the buffer before executing SAVEPOINT, RELEASE SAVEPOINT,
OR ROLLBACK TO SAVEPOINT statements.
For more information on using buffered inserts in an application, see “Using
Buffered Inserts” on page 547. For more information on precompiling and
binding applications, refer to the Command Reference.
Using Savepoints with Cursor Blocking
If your application uses savepoints, consider preventing cursor clocking by
precompiling or binding the application with the precompile option
BLOCKING NO. While blocking cursors can improve the performance of your
application by pre-fetching multiple rows, the data returned by an application
that uses savepoints and blocking cursors may not reflect data that has been
committed to the database.
If you do not precompile the application using BLOCKING NO, and your
application issues a FETCH statement after a ROLLBACK TO SAVEPOINT
has occurred, the FETCH statement may retrieve deleted data. For example,
assume that the application containing the following SQL is precompiled
without the BLOCKING NO option:
CREATE TABLE t1(c1 INTEGER);
DECLARE CURSOR c1 AS 'SELECT c1 FROM t1 ORDER BY c1';
INSERT INTO t1 VALUES (1);
SAVEPOINT showFetchDelete;
INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (3);
OPEN CURSOR c1;
FETCH c1; --get first value and cursor block
ALTER TABLE t1... --add constraint
ROLLBACK TO SAVEPOINT;
FETCH c1; --retrieves second value from cursor block
When your application issues the first FETCH on table “t1”, the DB2 server
sends a block of column values (1, 2 and 3) to the client application. These
182 Application Development Guide
column values are stored locally by the client. When your application issues
the ROLLBACK TO SAVEPOINT SQL statement, column values '2' and '3' are
deleted from the table. After the ROLLBACK TO SAVEPOINT statement, the
next FETCH from the table returns column value '2' even though that value
no longer exists in the table. The application receives this value because it
takes advantage of the cursor blocking option to improve performance and
accesses the data that it has stored locally.
For more information on precompiling and binding applications, refer to the
Command Reference.
Savepoints and XA Compliant Transaction Managers
If there are any active savepoints in an application when an XA compliant
transaction manager issues an XA_END request, DB2 issues a RELEASE
SAVEPOINT statement.
Chapter 6. Common DB2 Application Techniques 183
184 Application Development Guide
Part 3. Stored Procedures
© Copyright IBM Corp. 1993, 2000 185
186 Application Development Guide
Chapter 7. Stored Procedures
Stored Procedure Overview . . . . . . 187 Example OUT Client Application: C 216
Advantages of Stored Procedures . . . . 188 OUT Stored Procedure Description . . 217
Writing Stored Procedures . . . . . . . 190 Example OUT Parameter Stored
Client Application . . . . . . . . 191 Procedure: Java . . . . . . . . 218
Allocating Host Variables . . . . . 192 Example OUT Parameter Stored
Calling Stored Procedures . . . . . 192 Procedure: C . . . . . . . . . 220
Running the Client Application . . . 192 Code Page Considerations . . . . . . 222
Stored Procedures on the Server . . . . 192 C++ Consideration . . . . . . . . 222
Registering Stored Procedures. . . . 193 Graphic Host Variable Considerations . . 222
Variable Declaration and CREATE Multisite Update Consideration . . . . 223
PROCEDURE Examples. . . . . . 206 NOT FENCED Stored Procedures . . . . 223
SQL Statements in Stored Procedures 207 Returning Result Sets from Stored
Nested Stored Procedures . . . . . 208 Procedures . . . . . . . . . . . . 225
Restrictions . . . . . . . . . . 209 Example: Returning a Result Set from a
Writing OLE Automation Stored Stored Procedure . . . . . . . . . 226
Procedures . . . . . . . . . . . 209 C Example: SPSERVER.SQC
Example OUT Parameter Stored (one_result_set_to_client) . . . . . 228
Procedure . . . . . . . . . . . 210 Java Example: Spserver.java
OUT Client Description . . . . . . 212 (resultSetToClient) . . . . . . . 229
Example OUT Client Application: Java 214 Resolving Problems . . . . . . . . 236
Stored Procedure Overview
Use stored procedures to improve the performance of your client/server
applications. A stored procedure is a function in a shared library accessible to
the database server. Stored procedures access the database locally and return
information to client applications. A stored procedure saves the overhead of
having a remote application pass multiple SQL statements to the server. With
a single CALL statement, a client application invokes the stored procedure,
which then performs the database access work and returns the results to the
client application.
You can write stored procedures using SQL, called SQL procedures. For more
information on writing SQL procedures, see “Chapter 8. Writing SQL
Procedures” on page 239. You can also write stored procedures using
languages such as C or Java. You do not have to write client applications in
the same language as the stored procedure. When the language of the client
application and the stored procedure differ, DB2 transparently passes the
values between the client and the stored procedure.
You can use the DB2 Stored Procedure Builder (SPB) to help develop Java or
SQL stored procedures. You can integrate SPB with popular application
development tools, including Microsoft Visual Studio and IBM Visual Age for
Java, or you can use it as a standalone utility. To help you create your stored
© Copyright IBM Corp. 1993, 2000 187
procedures, SPB provides design assistants that guide you through basic
design patterns, help you create SQL queries, and estimate the performance
cost of invoking a stored procedure.
For more information on the DB2 Stored Procedure Builder, see “Chapter 9.
IBM DB2 Stored Procedure Builder” on page 261.
Advantages of Stored Procedures
Figure 3 shows how a normal database manager application accesses a
database located on a database server.
Database
Client
Client
Application
Database
Server
DB2 Client DB2
Network
Database
Figure 3. Application Accessing a Database on a Server
All database access must go across the network which, in some cases, results
in poor performance.
Using stored procedures allows a client application to pass control to a stored
procedure on the database server. This allows the stored procedure to perform
intermediate processing on the database server, without transmitting
unnecessary data across the network. Only those records that are actually
required at the client need to be transmitted. This can result in reduced
network traffic and better overall performance. Figure 4 shows this feature.
188 Application Development Guide
Database Database
Client Server
Client
Application DB2
Stored
Procedure
DB2 Client
Database
Figure 4. Application Using a Stored Procedure
Applications using stored procedures have the following advantages:
v Reduced network traffic
A properly designed application that processes large amounts of data using
stored procedures returns only the data that is needed by the client. This
reduces the amount of data transmitted across the network.
v Improved performance of server intensive work
The more SQL statements that are grouped together for execution, the
larger the savings in network traffic. A typical application requires two trips
across the network for each SQL statement, whereas an application using
the stored procedure technique requires two trips across the network for
each group of SQL statements. This reduces the number of trips, resulting in
a savings from the overhead associated with each trip.
v Access to features that exist only on the database server, including:
– Commands to list directories on the server (such as LIST DATABASE
DIRECTORY and LIST NODE DIRECTORY) can only run on the server.
– The stored procedure may have the advantage of increased memory and
disk space if the server computer is so equipped.
– Additional software installed only on the database server could be
accessed by the stored procedure.
Chapter 7. Stored Procedures 189
Writing Stored Procedures
An application design that includes a stored procedure consists of separate
client and server applications. The server application, called the stored
procedure, is contained in a shared library or class library on the server. You
must compile and access the stored procedure on the server instance where
the database resides. The client application contains a CALL statement to the
stored procedure. The CALL statement can pass parameters to and return
parameters from the stored procedure. You can write the stored procedure
and the client application using different languages. The client application can
be executed on on a platform different from the stored procedure.
The client application performs the following tasks:
1. Declares, allocates, and initializes storage for the optional data structures
and host variables.
2. Connects to a database by executing the CONNECT TO statement, or by
doing an implicit connect. Refer to the SQL Reference for details.
3. Invokes the stored procedure through the SQL CALL statement.
4. Issues a COMMIT or ROLLBACK to the database.
Note: While the stored procedure can issue COMMIT or ROLLBACK
statements, the recommended practice is to have the client
application issue to issue the COMMIT or ROLLBACK. This enables
your client application to evaluate the data returned by the stored
procedure and to decide whether to commit the transaction or roll it
back.
5. Disconnects from the database.
Note that you can code SQL statements in any of the above steps.
When invoked, the stored procedure performs the following tasks:
1. Accepts the parameters from the client application.
2. Executes on the database server under the same transaction as the client
application.
3. Optionally, issues one or more COMMIT or ROLLBACK statements.
Note: While the stored procedure can issue COMMIT or ROLLBACK
statements, the recommended practice is to have the client
application issue the COMMIT or ROLLBACK statements. This
enables your client application to evaluate the data returned by the
stored procedure and to decide whether to commit the transaction
or roll it back.
4. Returns SQLCA information and optional output data to the client
application.
190 Application Development Guide
The stored procedure executes when called by the client application. Control
is returned to the client when the server procedure finishes processing. You
can put several stored procedures into one library.
This chapter describes how to write stored procedures with the following
parameter styles:
DB2SQL The stored procedure receives parameters that you declare in
the CREATE PROCEDURE statement as host variables from
the CALL statement in the client application. DB2 allocates
additional parameters for DB2SQL stored procedures.
GENERAL The stored procedure receives parameters as host variables
from the CALL statement in the client application. The stored
procedure does not directly pass null indicators to the client
application. GENERAL is the equivalent of SIMPLE stored
procedures for DB2 Universal Database for OS/390.
GENERAL WITH NULLS
For each parameter declared by the user, DB2 allocates a
corresponding INOUT parameter null indicator. Like
GENERAL, parameters are passed as host variables.
GENERAL WITH NULLS is the equivalent of SIMPLE WITH
NULLS stored procedures for DB2 Universal Database for
OS/390.
JAVA The stored procedure uses a parameter passing convention
that conforms to the SQLJ Routines specification. The stored
procedure receives IN parameters as host variables, and
receives OUT and INOUT parameters as single entry arrays.
You must register each stored procedure for the previously listed parameter
styles with a CREATE PROCEDURE statement. The CREATE PROCEDURE
statement specifies the procedure name, arguments, location, and parameter
style of each stored procedure. These parameter styles offer increased
portability and scalability of your stored procedure code across the DB2
family.
For information on using the only styles of stored procedures supported by
versions of DB2 prior to DB2 Universal Database Version 6, that is, the
DB2DARI and DB2GENERAL parameter styles, see “Appendix C. DB2DARI
and DB2GENERAL Stored Procedures and UDFs” on page 751.
Client Application
The client application performs several steps before calling the stored
procedure. It must be connected to a database, and it must declare, allocate,
and initialize host variables or an SQLDA structure. The SQL CALL statement
can accept a series of host variables, or an SQLDA structure. Refer to the SQL
Chapter 7. Stored Procedures 191
Reference for descriptions of the SQL CALL statement and the SQLDA
structure. For information on using the SQLDA structure in a client
application, see “Appendix C. DB2DARI and DB2GENERAL Stored
Procedures and UDFs” on page 751.
Allocating Host Variables
Use the following steps to allocate the necessary input host variables on the
client side of a stored procedure:
1. Declare enough host variables for all input variables that will be passed to
the stored procedure.
2. Determine which input host variables can also be used to return values
back from the stored procedure to the client.
3. Declare host variables for any additional values returned from the stored
procedure to the client.
When writing the client portion of your stored procedure, you should attempt
to overload as many of the host variables as possible by using them for both
input and output. This will increase the efficiency of handling multiple host
variables. For example, when returning an SQLCODE to the client from the
stored procedure, try to use an input host variable that is declared as an
INTEGER to return the SQLCODE.
Note: Do not allocate storage for these structures on the database server. The
database manager automatically allocates duplicate storage based upon
the storage allocated by the client application. Do not alter any storage
pointers for the input/output parameters on the stored procedure side.
Attempting to replace a pointer with a locally created storage pointer
will cause an error with SQLCODE -1133 (SQLSTATE 39502).
Calling Stored Procedures
You can invoke a stored procedure stored at the location of the database by
using the SQL CALL statement. Refer to the SQL Reference for a complete
description of the CALL statement. Using the CALL statement is the
recommended method of invoking stored procedures.
Running the Client Application
The client application must ensure that a database connection has been made
before invoking the stored procedure, or an error is returned. After the
database connection and data structure initialization, the client application
calls the stored procedure and passes any required data. The application
disconnects from the database. Note that you can code SQL statements in any
of the above steps.
Stored Procedures on the Server
The stored procedure is invoked by the SQL CALL statement and executes
using data passed to it by the client application. The parameter style with
which you register the stored procedure in the database manager with the
192 Application Development Guide
CREATE PROCEDURE statement determines how the stored procedure
receives data from the client application.
Registering Stored Procedures
To use the CREATE PROCEDURE statement, you must declare the following:
v Procedure name
v Mode, name, and SQL data type of each parameter
v EXTERNAL name and location
v PARAMETER STYLE
Your CREATE PROCEDURE should also declare the following:
v Whether it runs FENCED or NOT FENCED
v The type of SQL statements contained in the procedure body, if any
You can find more information on the CREATE PROCEDURE statement,
including its full syntax and options for DB2 family compatibility, in the SQL
Reference. Descriptions of typical usages of the CREATE PROCEDURE
statement follow.
Procedure Names: You can overload stored procedures only by using the
same name for procedures that accept a unique number of parameters. Since
DB2 does not distinguish between data types, you cannot overload stored
procedures based on parameter data types.
For example, issuing the following CREATE PROCEDURE statements will
work because they accept one and two parameters, respectively:
CREATE PROCEDURE OVERLOAD (IN VAR1 INTEGER) ...
CREATE PROCEDURE OVERLOAD (IN VAR1 INTEGER, IN VAR2 INTEGER) ...
However, DB2 will fail to register the second stored procedure in the
following example because it has the same number of parameters as the first
stored procedure with the same name:
CREATE PROCEDURE OVERLOADFAIL (IN VAR1 INTEGER) ...
CREATE PROCEDURE OVERLOADFAIL (IN VAR2 VARCHAR(15)) ...
Parameter Modes: An explicit parameter is a parameter that you explicitly
declare in the parameter list of the CREATE PROCEDURE statement. An
implicit parameter is a parameter that is automatically supplied by DB2; for
example, a PARAMETER STYLE GENERAL WITH NULLS stored procedure
automatically supplies an array of null indicators for the explicit parameters.
When you write a stored procedure, you must consider both the explicit and
implicit parameters for your stored procedure. When you write a client
application, you only have to handle the explicit parameters for the stored
procedure. You must declare every explicit parameter as either an IN, OUT, or
Chapter 7. Stored Procedures 193
INOUT parameter with a name and SQL data type. For examples of CREATE
PROCEDURE statements, see “Variable Declaration and CREATE
PROCEDURE Examples” on page 206.
IN Passes a value to the stored procedure from the client application, but
returns no value to the client application when control returns to the
client application
OUT Stores a value that is passed to the client application when the stored
procedure terminates
INOUT
Passes a value to the stored procedure from the client application, and
returns a value to the client application when the stored procedure
terminates
Location: The EXTERNAL clause of the CREATE PROCEDURE statement
tells the database manager the location of the library that contains the stored
procedure. If you do not specify an absolute path for the library, or a jar name
for Java stored procedures, the database manager searches the function
directory. The function directory is a directory defined for your operating system
as follows:
Unix operating systems
sqllib/function
OS/2 or Windows 32-bit operating systems
instance_name\function, where instance_name represents the value of
the DB2INSTPROF instance-specific registry setting. If DB2INSTPROF
is not set, instance_name represents the value of the %DB2PATH%
environment variable. The default value of the %DB2PATH%
environment variable is the path in which you installed DB2.
If DB2 does not find the stored procedure in instance_name\function,
DB2 searches the directories defined by the PATH and LIBPATH
environment variables.
For example, the function directory for a Windows 32-bit operating
system server with DB2 installed in the C:\sqllib directory, where you
have not set the DB2INSTPROF registry setting, is:
C:\sqllib\function
Note: You should give your library a name that is different than the stored
procedure name. If DB2 locates the library in the search path, DB2
executes any stored procedure with the same name as the library which
contains the stored procedure as a FENCED DB2DARI procedure.
For LANGUAGE C stored procedures, specify:
v The library name, taking the form of either:
194 Application Development Guide
– A library found in the function directory
– An absolute path including the library name
v The entry point for the stored procedure in the library. If you do not specify
an entry point, the database manager will use the default entry point. The
IBM XLC compiler on AIX allows you to specify any exported function
name in the library as the default entry point. This is the function that is
called if only the library name is specified in a stored procedure call or
CREATE FUNCTION statement. To specify a default entry point, use the -e
option in the link step. For example: -e funcname makes funcname the
default entry point. On other UNIX platforms, no such mechanism exists,
so the default entry point is assumed by DB2 to be the same name as the
library itself.
On a UNIX-based system, for example, mymod!proc8 directs the database
manager to the sqllib/function/mymod library and to use entry point proc8
within that library. On Windows 32-bit and OS/2 operating systems
mymod!proc8 directs the database manager to load the mymod.dll file from the
function directory and call the proc8() procedure in the dynamic link library
(DLL).
For LANGUAGE JAVA stored procedures, use the following syntax:
[<jar-file-name>:]<class-name>.<method-name>
The following list defines the EXTERNAL keywords for Java stored
procedures:
jar-file-name
If a jar file installed in the database contains the stored procedure
method, you must include this value. The keyword represents the
name of the jar file, and is delimitied by a colon (:). If you do not
specify a jar file name, the database manager looks for the class in the
function directory. For more information on installing jar files, see
“Java Stored Procedures and UDFs” on page 654.
class-name
The name of the class that contains the stored procedure method. If
the class is part of a package, you must include the complete package
name as a prefix.
method-name
The name of the stored procedure method.
For example, if you specify MyPackage.MyClass.myMethod, the database
manager uses the myMethod method in the MyClass class, within the MyPackage
package. DB2 recognizes that MyPackage refers to a package rather than a jar
file because it uses a period (.) delimiter instead of a colon (:) delimiter. DB2
searches the function directory for the MyPackage package.
Chapter 7. Stored Procedures 195
For more information on the function directory, see “Location” on page 194.
LANGUAGE: For C/C++, declare LANGUAGE C in your CREATE
PROCEDURE statement. For Java stored procedures, declare LANGUAGE
JAVA. For OLE stored procedures on Windows 32-bit operating systems,
declare LANGUAGE OLE. For COBOL stored procedures, declare
LANGUAGE COBOL. For Fortran or REXX stored procedures, you must write
the stored procedure as a DB2DARI stored procedure. For more information
on writing DB2DARI stored procedures, see “Appendix C. DB2DARI and
DB2GENERAL Stored Procedures and UDFs” on page 751.
LANGUAGE C
The database manager calls the stored procedure using ANSI C calling
and linkage conventions. Use this option for most C/C++ stored
procedures.
LANGUAGE JAVA
The database manager calls the stored procedure as a method in a
Java class. Use this option for any Java stored procedure.
LANGUAGE OLE
The database manager calls the stored procedure as a OLE function.
Use this option for any OLE stored procedure on Windows 32-bit
operating systems. Before issuing the CREATE PROCEDURE
statement, you must register the DLL that contains the OLE stored
procedure using the REGSVR32 command. OLE stored procedures
must run in FENCED mode. For more information on using OLE
stored procedures, refer to the Application Building Guide.
LANGUAGE COBOL
The database manager calls the stored procedure using COBOL calling
and linkage conventions. Use this option for COBOL stored
procedures.
Passing Parameters as Subroutines: C stored procedures of PROGRAM
TYPE SUB accept arguments as subroutines. Pass numeric data type
parameters as pointers. Pass character data types as arrays of the appropriate
length. For example, the following C stored procedure signature accepts
parameters of type INTEGER, SMALLINT, and CHAR(3):
int storproc (sqlint32 *arg1, short *arg2, char arg[4])
Java stored procedures can only accept arguments as subroutines. Pass IN
parameters as simple arguments. Pass OUT and INOUT parameters as arrays
with a single element. For example, the following Java stored procedure
signature accepts an IN parameter of type INTEGER, an OUT parameter of
type SMALLINT, and an INOUT parameter of type CHAR(3):
int storproc (int arg1, short arg2[], String arg[])
196 Application Development Guide
Passing Parameters as main Functions: To write a stored procedure that
accepts arguments like a main function in a C program, specify PROGRAM
TYPE MAIN in the CREATE PROCEDURE statement. You must write stored
procedures of PROGRAM TYPE MAIN to conform to the following
specifications:
v DB2 sets the value of the first element in the parameter array to the stored
procedure name
v the stored procedure accepts parameters through two arguments:
– a parameter counter variable; for example, argc
– an array containing the parameters; for example, argv[]
v the stored procedure must be built as a shared library
In PROGRAM TYPE MAIN stored procedures, DB2 sets the value of the first
element in the argv array, (argv[0]), to the name of the stored procedure. The
remaining elements of the argv array correspond to the parameters declared in
the CREATE PROCEDURE statement for the stored procedure. For example,
the following embedded C stored procedure passes in one IN parameter as
argv[1] and returns two OUT parameters as argv[2] and argv[3].
The CREATE PROCEDURE statement for the PROGRAM TYPE MAIN
example is as follows:
CREATE PROCEDURE MAIN_EXAMPLE (IN job CHAR(8),
OUT salary DOUBLE, OUT errorcode INTEGER)
DYNAMIC RESULT SETS 0
LANGUAGE C
PARAMETER STYLE GENERAL
NO DBINFO
FENCED
READS SQL DATA
PROGRAM TYPE MAIN
EXTERNAL NAME 'spserver!mainexample'
The following code for the stored procedure copies the value of argv[1] into
the CHAR(8) host variable injob, then copies the value of the DOUBLE host
variable outsalary into argv[2] and returns the SQLCODE as argv[3]:
EXEC SQL BEGIN DECLARE SECTION;
char injob[9];
double outsalary;
EXEC SQL END DECLARE SECTION;
SQL_API_RC SQL_API_FN main_example (int argc, char **argv)
{
EXEC SQL INCLUDE SQLCA;
/* argv[0] contains the procedure name, so parameters start at argv[1] */
strcpy (injob, (char *)argv[1]);
EXEC SQL SELECT AVG(salary)
INTO :outsalary
Chapter 7. Stored Procedures 197
FROM employee
WHERE job = :injob;
memcpy ((double *)argv[2], (double *)&outsalary, sizeof(double));
memcpy ((sqlint32 *)argv[3], (sqlint32 *)&SQLCODE, sizeof(sqlint32));
return (0);
} /* end main_example function */
PARAMETER STYLE: Table 9 summarizes the combinations of PARAMETER
STYLE (horizontal axis) and LANGUAGE (vertical axis) allowed in CREATE
PROCEDURE statements for DB2 Version 7.
Table 9. CREATE PROCEDURE: Valid Combinations of PARAMETER STYLE and LANGUAGE
GENERAL, JAVA DB2SQL DB2DARI DB2GENERAL
GENERAL
WITH NULLS
LANGUAGE C Y N Y Y N
LANGUAGE N Y N N Y
JAVA
LANGUAGE N N Y N N
OLE
LANGUAGE Y N Y N N
COBOL
GENERAL
The stored procedure receives parameters as host variables from the
CALL statement in the client application. The stored procedure does
not directly pass null indicators to the client application. You can only
use GENERAL when you also specify the LANGUAGE C or
LANGUAGE COBOL option.
DB2 Universal Database for OS/390 compatibility note: GENERAL is
the equivalent of SIMPLE.
PARAMETER STYLE GENERAL stored procedures accept parameters
in the manner indicated by the value of the PROGRAM TYPE clause.
The following example demonstrates a PARAMETER STYLE
GENERAL stored procedure that accepts two parameters using
PROGRAM TYPE SUBROUTINE:
SQL_API_RC SQL_API_FN one_result_set_to_client
(double *insalary, sqlint32 *out_sqlerror)
{
EXEC SQL INCLUDE SQLCA;
EXEC SQL WHENEVER SQLERROR GOTO return_error;
198 Application Development Guide
EXEC SQL BEGIN DECLARE SECTION;
double l_insalary;
EXEC SQL END DECLARE SECTION;
l_insalary = *insalary;
*out_sqlerror = 0;
EXEC SQL DECLARE c3 CURSOR FOR
SELECT name, job, CAST(salary AS INTEGER)
FROM staff
WHERE salary > :l_insalary
ORDER BY salary;
EXEC SQL OPEN c3;
/* Leave cursor open to return result set */
return (0);
/* Copy SQLCODE to OUT parameter if SQL error occurs */
return_error:
{
*out_sqlerror = SQLCODE;
EXEC SQL WHENEVER SQLERROR CONTINUE;
return (0);
}
} /* end one_result_set_to_client function */
GENERAL WITH NULLS
For each parameter declared by the user, DB2 allocates a
corresponding INOUT parameter null indicator. Like GENERAL,
parameters are passed as host variables. You can only use GENERAL
WITH NULLS when you also specify the LANGUAGE C or
LANGUAGE COBOL option.
DB2 Universal Database for OS/390 compatibility note: GENERAL
WITH NULLS is the equivalent of SIMPLE WITH NULLS.
PARAMETER STYLE GENERAL WITH NULLS stored procedures
accept parameters in the manner indicated by the value of the
PROGRAM TYPE clause, and allocate an array of null indicators with
one element per declared parameter. The following SQL registers a
PARAMETER STYLE GENERAL WITH NULLS stored procedure that
passes one INOUT parameter and two OUT parameters using
PROGRAM TYPE SUB:
CREATE PROCEDURE INOUT_PARAM (INOUT medianSalary DOUBLE,
OUT errorCode INTEGER, OUT errorLabel CHAR(32))
DYNAMIC RESULT SETS 0
LANGUAGE C
PARAMETER STYLE GENERAL WITH NULLS
NO DBINFO
FENCED
Chapter 7. Stored Procedures 199
MODIFIES SQL DATA
PROGRAM TYPE SUB
EXTERNAL NAME 'spserver!inout_param'
The following C code demonstrates how to declare and use the null
indicators required by a GENERAL WITH NULLS stored procedure:
SQL_API_RC SQL_API_FN inout_param (double *inoutMedian,
sqlint32 *out_sqlerror, char buffer[33], sqlint16 nullinds[3])
{
EXEC SQL INCLUDE SQLCA;
EXEC SQL WHENEVER SQLERROR GOTO return_error;
if (nullinds[0] < 0)
{
/* NULL value was received as input, so return NULL output */
nullinds[0] = -1;
nullinds[1] = -1;
nullinds[2] = -1;
}
else
{
int counter = 0;
*out_sqlerror = 0;
medianSalary = *inoutMedian;
strcpy(buffer, "DECLARE inout CURSOR");
EXEC SQL DECLARE inout CURSOR FOR
SELECT CAST(salary AS DOUBLE) FROM staff
WHERE salary > :medianSalary
ORDER BY salary;
nullinds[1] = 0;
nullinds[2] = 0;
strcpy(buffer, "SELECT COUNT INTO numRecords");
EXEC SQL SELECT COUNT(*) INTO :numRecords
FROM staff
WHERE salary > :medianSalary;
if (numRecords != 0)
/* At least one record was found */
{
strcpy(buffer, "OPEN inout");
EXEC SQL OPEN inout USING :medianSalary;
strcpy(buffer, "FETCH inout");
while (counter < (numRecords / 2 + 1)) {
EXEC SQL FETCH inout INTO :medianSalary;
*inoutMedian = medianSalary;
counter = counter + 1;
}
200 Application Development Guide
strcpy(buffer, "CLOSE inout");
EXEC SQL CLOSE inout;
}
else /* No records were found */
{
/* Return 100 to indicate NOT FOUND error */
*out_sqlerror = 100;
}
}
return (0);
/* Copy SQLCODE to OUT parameter if SQL error occurs */
return_error:
{
*out_sqlerror = SQLCODE;
EXEC SQL WHENEVER SQLERROR CONTINUE;
return (0);
}
} /* end inout_param function */
JAVA The stored procedure uses a parameter passing convention that
conforms to the SQLJ Routines specification. The stored procedure
receives IN parameters as host variables, and receives OUT and
INOUT parameters as single entry arrays. You can only use JAVA
when you also specify the LANGUAGE JAVA option.
DB2SQL
Your C function definition for a DB2SQL stored procedure must
append the following implicit parameters to the definition for the
parameters declared in the CREATE PROCEDURE statement:
sqlint16 nullinds[n], 1
char sqlst[6], 2
char qualname[28], 3
char specname[19], 4
char diagmsg[71], 5
DB2 passes the following arguments to the stored procedure:
1. DB2 allocates an array of implicit SMALLINT INOUT parameters
as null indicators for the explicit parameters. The array is of size n,
where n represents the number of explicit parameters.
2. An implicit CHAR(5) OUT parameter for an SQLSTATE value.
3. An implicit CHAR(27) IN parameter for the qualified stored
procedure name.
4. An implicit CHAR(18) IN parameter for the specific name of the
stored procedure.
5. An implicit CHAR(70) OUT parameter for an SQL diagnostic
string.
Chapter 7. Stored Procedures 201
You can only specify DB2SQL when you also specify the LANGUAGE
C or LANGUAGE COBOL option. For example, the following
CREATE PROCEDURE statement registers a PARAMETER STYLE
DB2SQL stored procedure:
CREATE PROCEDURE DB2SQL_EXAMPLE (IN job CHAR(8), OUT salary DOUBLE)
DYNAMIC RESULT SETS 0
LANGUAGE C
PARAMETER STYLE DB2SQL
NO DBINFO
FENCED
READS SQL DATA
PROGRAM TYPE SUB
EXTERNAL NAME 'spserver!db2sqlexample'
Write the stored procedure using the following conventions:
v PARAMETER STYLE DB2SQL stored procedures pass an array of
null indicators with one element for each explicit parameter. A
negative value of the null indicator element for an IN or INOUT
parameter indicates that the client application passed in a null
value for that parameter. To indicate that an output parameter is
not NULL, set the value of the null indicator element for the OUT
or INOUT parameter to 0. To indicate that an output parameter is
NULL, set the value of the null indicator element for the OUT or
INOUT parameter to -1.
v Append the arguments in the stored procedure signature for the
DB2SQL parameters, as previously described.
v You can set the value of the DB2SQL SQLSTATE (CHAR(5) and
diagnostic message (null-terminated CHAR(70)) parameters to
return a customized value in the SQLCA to the client.
For example, the following embedded C stored procedure
demonstrates the coding style for PARAMETER STYLE DB2SQL
stored procedures:
SQL_API_RC SQL_API_FN db2sql_example (
char injob[9], /* Input - CHAR(8) */
double *salary, /* Output - DOUBLE */
sqlint16 nullinds[2],
char sqlst[6],
char qualname[28],
char specname[19],
char diagmsg[71]
)
{
EXEC SQL INCLUDE SQLCA;
if (nullinds[0] < 0)
{
/* NULL value was received as input, so return NULL output */
nullinds[1] = -1;
202 Application Development Guide
/* Set custom SQLSTATE to return to client. */
strcpy(sqlst, "38100");
/* Set custom message to return to client. */
strcpy(diagmsg, "Received null input on call to DB2SQL_EXAMPLE.");
}
else
{
EXEC SQL SELECT (CAST(AVG(salary) AS DOUBLE))
INTO :outsalary INDICATOR :outsalaryind
FROM employee
WHERE job = :injob;
*salary = outsalary;
nullinds[1] = outsalaryind;
}
return (0);
} /* end db2sql_example function */
The following embedded C client application demonstrates how to
issue a CALL statement that invokes the DB2SQL_EXAMPLE stored
procedure. Note that the example includes null indicators for each
parameter in the CALL statement. The example sets the null indicator
in_jobind to 0 to indicate that a non-NULL value is being passed to the
stored procedure for the IN parameter represented by the host
variable in_job. The null indicators for the OUT parameters are set to
-1 to indicate that no input is being passed to the stored procedure for
those parameters.
int db2sqlparm(char out_lang[9], char job_name[9])
{
int testlang;
EXEC SQL BEGIN DECLARE SECTION;
/* Declare host variables for passing data to DB2SQL_EXAMPLE */
char in_job[9];
sqlint16 in_jobind;
double out_salary = 0;
sqlint16 out_salaryind;
EXEC SQL END DECLARE SECTION;
/********************************************************\
* Call DB2SQL_EXAMPLE stored procedure *
\********************************************************/
testlang = strncmp(out_lang, "C", 1);
if (testlang != 0) {
/* Only LANGUAGE C procedures can be PARAMETER STYLE DB2SQL,
so do not call the DB2SQL_EXAMPLE stored procedure */
printf("\nStored procedures are not implemented in C.\n"
"Skipping the call to DB2SQL_EXAMPLE.\n");
}
else {
strcpy(procname, "DB2SQL_EXAMPLE");
Chapter 7. Stored Procedures 203
printf("\nCALL stored procedure named %s\n", procname);
/* out_salary is an OUT parameter, so set the
null indicator to -1 to indicate no input value */
out_salaryind = -1;
strcpy(in_job, job_name);
/* in_job is an IN parameter, so check to
see if there is any input value */
if (strlen(in_job) == 0)
{
/* in_job is null, so set the null indicator
to -1 to indicate there is no input value */
in_jobind = -1;
printf("with NULL input, to return a custom
SQLSTATE and diagnostic message\n");
}
else
{
/* in_job is not null, so set the null indicator
to 0 to indicate there is an input value */
in_jobind = 0;
}
/* DB2SQL_EXAMPLE is PS DB2SQL, so pass
a null indicator for each parameter */
EXEC SQL CALL :procname (:in_job:in_jobind,
:out_salary:out_salaryind);
/* DB2SQL stored procedures can return a custom
SQLSTATE and diagnostic message, so instead of
using the EMB_SQL_CHECK macro to check the value
of the returned SQLCODE, check the SQLCA structure for
the value of the SQLSTATE and the diagnostic message */
/* Check value of returned SQLSTATE */
if (strncmp(sqlca.sqlstate, "00000", 5) == 0) {
printf("Stored procedure returned successfully.\n");
printf("Average salary for job %s = %9.2f\n",
in_job, out_salary);
}
else {
printf("Stored procedure failed with SQLSTATE %s.\n",
sqlca.sqlstate);
printf("Stored procedure returned the following
diagnostic message:\n");
printf(" \"%s\"\n", sqlca.sqlerrmc);
}
}
return 0;
}
204 Application Development Guide
DB2GENERAL
The stored procedure uses a parameter passing convention that is
only supported by DB2 Java stored procedures. You can only use
DB2GENERAL when you also specify the LANGUAGE JAVA option.
For increased portability, you should write Java stored procedures
using the PARAMETER STYLE JAVA conventions. See “Appendix C.
DB2DARI and DB2GENERAL Stored Procedures and UDFs” on
page 751 for more information on writing DB2GENERAL parameter
style stored procedures.
DB2DARI
The stored procedure uses a parameter passing convention that
conforms with C language calling and linkage conventions. This
option is only supported by DB2 Universal Database, and can only be
used when you also specify the LANGUAGE C option.
To increase portability across the DB2 family, you should write your
LANGUAGE C stored procedures using the GENERAL or GENERAL
WITH NULLS parameter styles. If you want to write DB2DARI
parameter style stored procedures, see “Appendix C. DB2DARI and
DB2GENERAL Stored Procedures and UDFs” on page 751.
Passing a DBINFO Structure: For LANGUAGE C stored procedures with a
PARAMETER TYPE of GENERAL, GENERAL WITH NULLS, or DB2SQL,
you have the option of writing your stored procedure to accept an additional
parameter. You can specify DBINFO in the CREATE PROCEDURE statement
to instruct the client application to pass a DBINFO structure containing
information about the DB2 client to the stored procedure, along with the call
parameters. The DBINFO structure contains the following values:
Database name
The name of the database to which the client is connected.
Application authorization ID
The application run-time authorization ID.
Code page
The code page of the database.
Schema name
Not applicable to stored procedures.
Table name
Not applicable to stored procedures.
Column name
Not applicable to stored procedures.
Chapter 7. Stored Procedures 205
Database version and release
The version, release, and modification level of the database server
invoking the stored procedure.
Platform
The platform of the database server.
Table function result column numbers
Not applicable to stored procedures.
For more information on the DBINFO structure, see “DBINFO Structure” on
page 396.
Variable Declaration and CREATE PROCEDURE Examples
The following examples demonstrate the stored procedure source code and
CREATE PROCEDURE statements you would use in hypothetical scenarios
with the SAMPLE database.
Using IN and OUT Parameters: Assume that you want to create a Java
stored procedure GET_LASTNAME that, given empno (SQL type VARCHAR),
returns lastname (SQL type CHAR) from the EMPLOYEE table in the SAMPLE
database. You will create the procedure as the getname method of the Java
class StoredProcedure, contained in the JAR installed as myJar. Finally, you
will call the stored procedure with a client application coded in C.
1. Declare two host variables in your stored procedure source code:
String empid;
String name;
...
#sql { SELECT lastname INTO :empid FROM employee WHERE empno=:empid }
2. Register the stored procedure with the following CREATE PROCEDURE
statement:
CREATE PROCEDURE GET_LASTNAME (IN EMPID CHAR(6), OUT NAME VARCHAR(15))
EXTERNAL NAME 'myJar:StoredProcedure.getname'
LANGUAGE JAVA PARAMETER STYLE JAVA FENCED
READS SQL DATA
3. Call the stored procedure from your client application written in C:
EXEC SQL BEGIN DECLARE SECTION;
struct name { short int; char[15] }
char[7] empid;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL CALL GET_LASTNAME (:empid, :name);
Using INOUT Parameters: For the following example, assume that you want
to create a C stored procedure GET_MANAGER that, given deptnumb (SQL
type SMALLINT), returns manager (SQL type SMALLINT) from the ORG table
in the SAMPLE database.
206 Application Development Guide
1. Since deptnumb and manager are both of SQL data type SMALLINT, you
can declare a single variable onevar in your stored procedure that receives
a value from and returns a value to the client application:
EXEC SQL BEGIN DECLARE SECTION;
short onevar = 0;
EXEC SQL END DECLARE SECTION;
2. Register the stored procedure with the following CREATE PROCEDURE
statement:
CREATE PROCEDURE GET_MANAGER (INOUT onevar SMALLINT)
EXTERNAL NAME 'stplib!getman'
LANGUAGE C PARAMETER STYLE GENERAL FENCED
READS SQL DATA
3. Call the stored procedure from your client application written in Java:
short onevar = 0;
...
#SQL { CALL GET_MANAGER (:INOUT onevar) };
SQL Statements in Stored Procedures
Stored procedures can contain SQL statements. When you issue the CREATE
PROCEDURE statement, you should specify the type of SQL statements the
stored procedure contains, if any. If you do not specify a value when you
register the stored procedure, the database manager uses MODIFIES SQL
DATA. To restrict the type of SQL used in the stored procedure, you can use
one of the following four options:
NO SQL
Indicates that the stored procedure cannot execute any SQL
statements. If the stored procedure attempts to execute an SQL
statement, the statement returns SQLSTATE 38001.
CONTAINS SQL
Indicates that SQL statements that neither read nor modify SQL data
can be executed by the stored procedure. If the stored procedure
attempts to execute an SQL statement that reads or modifies SQL
data, the statement returns SQLSTATE 38004. Statements that are not
supported in any stored procedure return SQLSTATE 38003.
READS SQL DATA
Indicates that some SQL statements that do not modify SQL data can
be executed by the stored procedure. If the stored procedure attempts
to execute an SQL statement that modifies data, the statement returns
SQLSTATE 38002. Statements that are not supported in any stored
procedure return SQLSTATE 38003.
MODIFIES SQL DATA
Indicates that the stored procedure can execute any SQL statement
except statements that are not supported in stored procedures. If the
Chapter 7. Stored Procedures 207
stored procedure attempts to execute an SQL statement that is not
supported in a stored procedure, the statement returns SQLSTATE
38003.
For more information on the CREATE PROCEDURE statement, refer to the
SQL Reference.
Nested Stored Procedures
Nested stored procedures are stored procedures that call another stored
procedure. You can use this technique in your DB2 applications under the
following restrictions:
v the stored procedures must be cataloged as LANGUAGE C or LANGUAGE
SQL.
v the calling stored procedure can only call a stored procedure that is
cataloged using the same LANGUAGE clause. For nested calls only,
LANGUAGE C and LANGUAGE SQL are considered the same language.
For example, a LANGUAGE C stored procedure can call an SQL procedure.
v the calling stored procedure cannot call a stored procedure that is cataloged
with a higher SQL data access level. For example, a stored procedure
cataloged with CONTAINS SQL data access can call a stored procedure
cataloged with NO SQL or CONTAINS SQL data access, but cannot call a
stored procedure cataloged with READS SQL DATA or MODIFIES SQL
DATA.
v up to 16 levels of nested stored procedure calls are supported. For example,
a scenario where stored procedure PROC1 calls PROC2, and PROC2 calls
PROC3 represents three levels of nested stored procedures.
v the calling and called stored procedures at all levels of nesting cannot be
cataloged as NOT FENCED
Nested SQL procedures can return one or more result sets to the client
application or to the calling stored procedure. To return a result set from an
SQL procedure to the client application, issue the DECLARE CURSOR
statement using the WITH RETURN TO CLIENT clause. To return a result set
from an SQL procedure to the caller, where the caller is either a client
application or a calling stored procedure, issue the DECLARE CURSOR
statement using the WITH RETURN TO CALLER clause.
Nested embedded SQL stored procedures written in C and nested CLI stored
procedures cannot return result sets to the client application or calling stored
procedure. If a nested embedded SQL stored procedure or a nested CLI stored
procedure leaves cursors open when the stored procedure exits, DB2 closes
the cursors. For more information on returning result sets from stored
procedures, see “Returning Result Sets from Stored Procedures” on page 225.
208 Application Development Guide
Restrictions
When you create a stored procedure, you must observe the following
restrictions:
v Do not use the standard I/O streams, for example, calls to
System.out.println() in Java, printf() in C/C++, or display in COBOL.
Stored procedures run in the background, so you cannot write to the screen.
However, you can write to a file.
v Include only the SQL statements allowed by the CREATE PROCEDURE
statement with which you register the stored procedure. For information on
using the NO SQL, READS SQL DATA, CONTAINS SQL, or MODIFIES
SQL DATA clauses to catalog your stored procedure, see “SQL Statements
in Stored Procedures” on page 207.
v You cannot use COMMIT statements in stored procedures when either or
both of the following conditions is true:
– you catalog the stored procedure using the NO SQL clause
– the stored procedure is called from an application performing a multisite
update
v You cannot execute any connection-related statements or commands in
stored procedures, including:
– BACKUP
– CONNECT
– CONNECT TO
– CONNECT RESET
– CREATE DATABASE
– DROP DATABASE
– FORWARD RECOVERY
– RESTORE
v On UNIX-based systems, NOT FENCED stored procedures run under the
user ID of the DB2 Agent Process. FENCED stored procedures run under
the user ID of the db2dari executable, which is set to the owner of the
.fenced file in sqllib/adm. This user ID controls the system resources
available to the stored procedure. For information on the db2dari
executable, refer to the Quick Beginnings book for your platform.
v You cannot overload stored procedures that accept the same number of
parameters, even if the parameters are of different SQL data types.
v Stored procedures cannot contain commands that would terminate the
current process. A stored procedure should always return control to the
client without terminating the current process.
Writing OLE Automation Stored Procedures
OLE (Object Linking and Embedding) automation is part of the OLE 2.0
architecture from Microsoft Corporation. DB2 can invoke methods of OLE
Chapter 7. Stored Procedures 209
automation objects as external stored procedures. For an overview of OLE
automation, see “Writing OLE Automation UDFs” on page 416.
After you code an OLE automation object, you must register the methods of
the object as stored procedures using the CREATE PROCEDURE statement. To
register an OLE automation stored procedure, issue a CREATE PROCEDURE
statement with the LANGUAGE OLE clause. The external name consists of
the OLE progID identifying the OLE automation object and the method name
separated by ! (exclamation mark).
The following CREATE PROCEDURE statement registers an OLE automation
stored procedure called “median” for the “median” method of the OLE
automation object “db2smpl.salary”:
CREATE PROCEDURE median (INOUT sal DOUBLE)
EXTERNAL NAME 'db2smpl.salary!median'
LANGUAGE OLE
FENCED
PARAMETER STYLE DB2SQL
The calling conventions for OLE method implementations are identical to the
conventions for procedures written in C or C++.
DB2 automatically handles the type conversions between SQL types and OLE
automation types. For a list of the DB2 mappings between supported OLE
automation types and SQL types, see Table 16 on page 419. For a list of the
DB2 mappings between SQL types and the data types of the OLE
programming language, such as BASIC or C/C++, see Table 17 on page 420.
Data passed between DB2 and OLE automation stored procedures is passed as
call by reference. DB2 does not support SQL types such as DECIMAL or
LOCATORS, or OLE automation types such as boolean or CURRENCY, that
are not listed in the previously referenced tables. Character and graphic data
mapped to BSTR is converted from the database code page to UCS-2 (also
known as Unicode, IBM code page 13488) scheme. Upon return, the data is
converted back to the database code page. These conversions occur regardless
of the database code page. If code page conversion tables to convert from the
database code page to UCS-2 and from UCS-2 to the database code page are
not installed, you receive an SQLCODE -332 (SQLSTATE 57017).
Example OUT Parameter Stored Procedure
Following is a sample program demonstrating the use of an OUT host
variable. The client application invokes a stored procedure that determines the
median salary for employees in the SAMPLE database. (The definition of the
median is that half the values lie above it, and half below it.) The median
salary is then passed back to the client application using an OUT host
variable.
210 Application Development Guide
This sample program calculates the median salary of all employees in the
SAMPLE database. Since there is no existing SQL column function to calculate
medians, the median salary can be found iteratively by the following
algorithm:
1. Determine the number of records, n, in the table.
2. Order the records based upon salary.
3. Fetch records until the record in row position n ⁄ 2 + 1 is found.
4. Read the median salary from this record.
An application that uses neither the stored procedures technique, nor blocking
cursors, must FETCH each salary across the network as shown in Figure 5.
Client Database
Workstation Server
Send request to Retrieve all the
SELECT SALARY salaries from the
FROM STAFF table.
ORDER BY SALARY. 11508.60
12258.50
12508.20
FETCH all of the data 12954.75 Return all of the
back, one record at 12954.75 data to the client.
a time.
Read the median
salary from the data
returned.
Figure 5. Median Sample Without a Stored Procedure
Since only the salary at row n ⁄ 2 + 1 is needed, the application discards all
the additional data, but only after it is transmitted across the network.
You can design an application using the stored procedures technique that
allows the stored procedure to process and discard the unnecessary data,
returning only the median salary to the client application. Figure 6 shows this
feature.
Chapter 7. Stored Procedures 211
Client Database
Workstation Server
Call Server Retrieve all the
Procedure stored salaries from the
on the Database. table.
Determine the
median salary.
Read the median Return the median
salary from the data salary to the client.
item returned.
17654.50
Figure 6. OUT Parameter Sample Using a Stored Procedure
“OUT Client Description” shows a sample OUT host variable client
application and stored procedure. The sample programs are available in Java
as:
Client application Outcli.java
Stored procedure Outsrv.sqlj
The sample programs are available in C as:
Client application spclient.sqc
Stored procedure spserver.sqc
OUT Client Description
1. Include Files. The C client applications include the following files:
SQL Defines the symbol SQL_TYP_FLOAT
SQLDA Defines the descriptor area
SQLCA Defines the communication area for error handling
The JDBC client application imports the following packages:
java.sql.* JDBC classes from the Java implementation on your client
java.math.BigDecimal
Provides Java support for the DB2 DECIMAL data type
212 Application Development Guide
2. Connect to Database. The application must connect to the database before
calling the stored procedure.
3. Turn off Autocommit. The client application explicitly disables
autocommit before calling the stored procedure. Disabling autocommit
allows the client application to control whether the work performed by the
stored procedure control is rolled back or committed. The stored procedure
for this example returns an OUT parameter containing an SQLCODE value
so that client applications can easily use condition statements to commit or
roll back the work performed by the stored procedure.
4. Declare and Initialize the Host Variable. This step declares and initializes
the host variable. Java programs must register the data type of each
INOUT or OUT parameter and initialize the value of every parameter
before invoking the stored procedure.
5. Call the Stored Procedure. The client application calls the stored
procedure OUTPARAM for the database SAMPLE using a CALL statement
with three parameters.
6. Retrieve the Output Parameters. JDBC client applications must explicitly
retrieve the values of the output parameters returned by the stored
procedure. For C/C++ client applications, DB2 updates the value of the
host variables used in the CALL statement when the client application
executes the CALL statement.
7. Check the Value of the Returned SQLCODE. The client application
checks the value of the OUT parameter containing the SQLCODE to
determine whether to roll back or commit the transaction.
8. Disconnect from Database. To help DB2 free system resources held for
each connection, you should explicitly close the connection to the database
before exiting the client application.
The CHECKERR macro/function is an error checking utility which is external to
the program. The location of this error checking utility depends upon the
programming language used:
C For C programs that call DB2 APIs, the sqlInfoPrint function
in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C
embedded SQL programs, the sqlInfoPrint function in
utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.
Java Any SQL error is thrown as an SQLException and handled in
the catch block of the application.
COBOL CHECKERR is an external program named checkerr.cbl.
See “Using GET ERROR MESSAGE in Example Programs” on page 118 for the
source code for this error checking utility.
Chapter 7. Stored Procedures 213
Example OUT Client Application: Java
import java.sql.*; // JDBC classes 1
import java.math.BigDecimal; // BigDecimal support for packed decimal type
class Spclient
{
static String sql = "";
static String procName = "";
static String inLanguage = "";
static CallableStatement callStmt;
static int outErrorCode = 0;
static String outErrorLabel = "";
static double outMedian = 0;
static
{
try
{
System.out.println();
System.out.println("Java Stored Procedure Sample");
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
}
catch (Exception e)
{
System.out.println("\nError loading DB2 Driver...\n");
e.printStackTrace();
}
}
public static void main(String argv[])
{
Connection con = null;
// URL is jdbc:db2:dbname
String url = "jdbc:db2:sample";
try
{
// connect to sample database
// connect with default id/password
con = DriverManager.getConnection(url); 2
// turn off autocommit
con.setAutoCommit(false); 3
outLanguage(con);
outParameter(con);
inParameters(con);
inoutParam(con, outMedian);
resultSet(con);
twoResultSets(con);
allDataTypes(con);
// rollback any changes to the database
con.rollback(); 8
con.close();
214 Application Development Guide
}
catch (Exception e)
{
try { con.close(); } catch (Exception x) { }
e.printStackTrace ();
}
} // end main
public static void outParameter(Connection con)
throws SQLException
{
// prepare the CALL statement for OUT_PARAM
procName = "OUT_PARAM";
sql = "CALL " + procName + "(?, ?, ?)";
callStmt = con.prepareCall(sql);
// register the output parameter 4
callStmt.registerOutParameter (1, Types.DOUBLE);
callStmt.registerOutParameter (2, Types.INTEGER);
callStmt.registerOutParameter (3, Types.CHAR);
// call the stored procedure 5
System.out.println ("\nCall stored procedure named " + procName);
callStmt.execute();
// retrieve output parameters 6
outMedian = callStmt.getDouble(1);
outErrorCode = callStmt.getInt(2);
outErrorLabel = callStmt.getString(3);
if (outErrorCode == 0) { 7
System.out.println(procName + " completed successfully");
System.out.println ("Median salary returned from OUT_PARAM = "
+ outMedian);
}
else { // stored procedure failed
System.out.println(procName + " failed with SQLCODE "
+ outErrorCode);
System.out.println(procName + " failed at " + outErrorLabel);
}
}
}
Chapter 7. Stored Procedures 215
Example OUT Client Application: C
#include <stdio.h> 1
#include <stdlib.h>
#include <sql.h>
#include <sqlda.h>
#include <sqlca.h>
#include <string.h>
#include "utilemb.h"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
/* Declare host variable for stored procedure name */
char procname[254];
/* Declare host variables for stored procedure error handling */
sqlint32 out_sqlcode; 4
char out_buffer[33];
EXEC SQL END DECLARE SECTION;
int main(int argc, char *argv[]) {
EXEC SQL CONNECT TO sample; 2
EMB_SQL_CHECK("CONNECT TO SAMPLE");
outparameter();
EXEC SQL ROLLBACK;
EMB_SQL_CHECK("ROLLBACK");
printf("\nStored procedure rolled back.\n\n");
/* Disconnect from Remote Database */
EXEC SQL CONNECT RESET; 8
EMB_SQL_CHECK("CONNECT RESET");
return 0;
}
int outparameter() {
/********************************************************\
* Call OUT_PARAM stored procedure *
\********************************************************/
EXEC SQL BEGIN DECLARE SECTION;
/* Declare host variables for passing data to OUT_PARAM */
double out_median;
EXEC SQL END DECLARE SECTION;
strcpy(procname, "OUT_PARAM");
printf("\nCALL stored procedure named %s\n", procname);
/* OUT_PARAM is PS GENERAL, so do not pass a null indicator */
EXEC SQL CALL :procname (:out_median, :out_sqlcode, :out_buffer); 5 6
EMB_SQL_CHECK("CALL OUT_PARAM");
/* Check that the stored procedure executed successfully */
if (out_sqlcode == 0) 7
{
216 Application Development Guide
printf("Stored procedure returned successfully.\n");
/***********************************************************\
* Display the median salary returned as an output parameter *
\***********************************************************/
printf("Median salary returned from OUT_PARAM = %8.2f\n", out_median);
}
else
{ /* print the error message, roll back the transaction */
printf("Stored procedure returned SQLCODE %d\n", out_sqlcode);
printf("from procedure section labelled \"%s\".\n", out_buffer);
}
return 0;
}
OUT Stored Procedure Description
1. Declare Signature. The procedure returns three parameters: a DOUBLE for
the median value; an INTEGER for the SQLCODE, and a CHAR for any
error message. You must specify the equivalent data types as arguments in
the stored procedure function definition using the DB2 type mappings
specified in the programming chapter for each language.
2. Declare a CURSOR Ordered by Salary. To work with multiple rows of
data, C stored procedures issue a DECLARE CURSOR statement and JDBC
stored procedures create a ResultSet object. The ORDER BY SALARY
clause enables the stored procedure to retrieve salaries in an ascending
order.
3. Determine Total Number of Employees. The stored procedure uses a
simple SELECT statement with the COUNT function to retrieve the
number of employees in the EMPLOYEE table.
4. FETCH Median Salary. The stored procedure issues successive FETCH
statements until it assigns the median salary to a variable.
5. Assign the Median Salary to the Output Variable. To return the value of
the median salary to the client application, assign the value to the
argument in the stored procedure function or method declaration that
corresponds to the OUT parameter.
6. Return to the Client Application. Only PARAMETER STYLE DB2DARI
stored procedures return values to the client. For more information on
DB2DARI stored procedures, see “Appendix C. DB2DARI and
DB2GENERAL Stored Procedures and UDFs” on page 751.
Chapter 7. Stored Procedures 217
Example OUT Parameter Stored Procedure: Java
import java.sql.*; // JDBC classes
import COM.ibm.db2.jdbc.app.*; // DB2 JDBC classes
import java.math.BigDecimal; // Packed Decimal class
public class Spserver
{
public static void outParameter (double[] medianSalary,
int[] errorCode, String[] errorLabel) throws SQLException 1
{ try
{
int numRecords;
int counter = 0;
errorCode[0] = 0; // SQLCODE = 0 unless SQLException occurs
// Get caller's connection to the database
Connection con = DriverManager.getConnection("jdbc:default:connection");
errorLabel[0] = "GET CONNECTION";
String query = "SELECT COUNT(*) FROM staff";
errorLabel[0] = "PREPARE COUNT STATEMENT";
PreparedStatement stmt = con.prepareStatement(query);
errorLabel[0] = "GET COUNT RESULT SET";
ResultSet rs = stmt.executeQuery();
// move to first row of result set
rs.next();
// set value for the output parameter
errorLabel[0] = "GET NUMBER OF RECORDS";
numRecords = rs.getInt(1); 3
// clean up first result set
rs.close();
stmt.close();
// get salary result set
query = "SELECT CAST(salary AS DOUBLE) FROM staff "
+ "ORDER BY salary";
errorLabel[0] = "PREPARE SALARY STATEMENT";
PreparedStatement stmt2 = con.prepareStatement(query);
errorLabel[0] = "GET SALARY RESULT SET";
ResultSet rs2 = stmt2.executeQuery(); 2
while (counter < (numRecords / 2 + 1))
{
errorLabel[0] = "MOVE TO NEXT ROW";
rs2.next(); 4
counter++;
}
errorLabel[0] = "GET MEDIAN SALARY";
medianSalary[0] = rs2.getDouble(1); 5
// clean up resources
rs2.close();
218 Application Development Guide
stmt2.close();
con.close(); 6
}
catch (SQLException sqle)
{
errorCode[0] = sqle.getErrorCode();
}
}
}
Chapter 7. Stored Procedures 219
Example OUT Parameter Stored Procedure: 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>
/* Declare function prototypes for this stored procedure library */
SQL_API_RC SQL_API_FN out_param (double *, sqlint32 *, char *); 1
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
/* Declare host variables for basic error-handling */
sqlint32 out_sqlcode;
char buffer[33];
/* Declare host variables used by multiple stored procedures */
sqlint16 numRecords;
double medianSalary;
EXEC SQL END DECLARE SECTION;
SQL_API_RC SQL_API_FN out_param (double *outMedianSalary,
sqlint32 *out_sqlerror, char buffer[33])
{
EXEC SQL INCLUDE SQLCA;
EXEC SQL WHENEVER SQLERROR GOTO return_error;
int counter = 0;
*out_sqlerror = 0;
strcpy(buffer, "DECLARE c1");
EXEC SQL DECLARE c1 CURSOR FOR 2
SELECT CAST(salary AS DOUBLE) FROM staff
ORDER BY salary;
strcpy(buffer, "SELECT");
EXEC SQL SELECT COUNT(*) INTO :numRecords FROM staff; 3
strcpy(buffer, "OPEN");
EXEC SQL OPEN c1;
strcpy(buffer, "FETCH");
while (counter < (numRecords / 2 + 1)) {
EXEC SQL FETCH c1 INTO :medianSalary; 4
/* Set value of OUT parameter to host variable */
*outMedianSalary = medianSalary; 5
counter = counter + 1;
}
220 Application Development Guide
strcpy(buffer, "CLOSE c1");
EXEC SQL CLOSE c1;
return (0); 6
/* Copy SQLCODE to OUT parameter if SQL error occurs */
return_error:
{
*out_sqlerror = SQLCODE;
EXEC SQL WHENEVER SQLERROR CONTINUE;
return (0);
}
} /* end out_param function */
Chapter 7. Stored Procedures 221
Code Page Considerations
The code page considerations depend on the server.
When a client program (using, for example, code page A) calls a remote
stored procedure that accesses a database using a different code page (for
example, code page Z), the following events occur:
1. Input character string parameters (whether defined as host variables or in
an SQLDA in the client application) are converted from the application
code page (A) to the one associated with the database (Z). Conversion
does not occur for data defined in the SQLDA as FOR BIT DATA.
2. Once the input parameters are converted, the database manager does not
perform any more code page conversions.
Therefore, you must run the stored procedure using the same code page
as the database, in this example, code page Z. It is a good practice to
prep, compile, and bind the server procedure using the same code page as
the database.
3. When the stored procedure finishes, the database manager converts the
output character string parameters (whether defined as host variables or in
an SQLDA in the client application) and the SQLCA character fields from
the database code page (Z) back to the application code page (A).
Conversion does not occur for data defined in the SQLDA as FOR BIT
DATA.
Note: If the parameter of the stored procedure is defined as FOR BIT DATA
at the server, conversion does not occur for a CALL statement to DB2
Universal Database for OS/390 or DB2 Universal Database for AS/400,
regardless of whether it is explicitly specified in the SQLDA. (Refer to
the section on the SQLDA in the SQL Reference for details.)
For more information on this topic, see “Conversion Between Different Code
Pages” on page 504.
C++ Consideration
When writing a stored procedure in C++, you may want to consider declaring
the procedure name using extern “C”, as in the following example:
extern “C” SQL_API_RC SQL_API_FN proc_name( short *parm1, char *parm2)
The extern "C" prevents type decoration (or mangling) of the function name
by the C++ compiler. Without this declaration, you have to include all the
type decorations for the function name when you call the stored procedure.
Graphic Host Variable Considerations
Any stored procedure written in C or C++, that receives or returns graphic
data through its parameter input or output should generally be precompiled
with the WCHARTYPE NOCONVERT option. This is because graphic data
222 Application Development Guide
passed through these parameters is considered to be in DBCS format, rather
than the wchar_t process code format. Using NOCONVERT means that
graphic data manipulated in SQL statements in the stored procedure will also
be in DBCS format, matching the format of the parameter data.
With WCHARTYPE NOCONVERT, no character code conversion occurs
between the graphic host variable and the database manager. The data in a
graphic host variable is sent to, and received from, the database manager as
unaltered DBCS characters. Note that if you do not use WCHARTYPE
NOCONVERT, it is still possible for you to manipulate graphic data in
wchar_t format in a stored procedure; however, you must perform the input
and output conversions manually.
CONVERT can be used in FENCED stored procedures, and it will affect the
graphic data in SQL statements within the stored procedure, but not through
the stored procedure’s interface. NOT FENCED stored procedures must be
built using the NOCONVERT option.
In summary, graphic data passed to or returned from a stored procedure
through its input or output parameters is in DBCS format, regardless of how
it was precompiled with the WCHARTYPE option.
For important information on handling graphic data in C applications, see
“Handling Graphic Host Variables in C and C++” on page 609. For
information on EUC code sets and application guidelines, see “Japanese and
Traditional Chinese EUC and UCS-2 Code Set Considerations” on page 511,
and more specifically to “Considerations for Stored Procedures” on page 515.
Multisite Update Consideration
Stored procedures that applications call with CONNECT TYPE 2 cannot issue
a COMMIT or ROLLBACK, either dynamically or statically.
NOT FENCED Stored Procedures
Your stored procedure can run as either a FENCED or a NOT FENCED stored
procedure, depending on whether you register the stored procedure as
FENCED or NOT FENCED in the CREATE PROCEDURE statement.
A NOT FENCED stored procedure runs in the same address space as the
database manager (the DB2 Agent’s address space). Running your stored
procedure as NOT FENCED results in increased performance when compared
with running it as FENCED because FENCED stored procedures, by default,
run in a special DB2 process. The address space of this process is distinct from
the DB2 System Controller.
Chapter 7. Stored Procedures 223
Notes:
1. While you can expect performance improvements from running NOT
FENCED stored procedures, user code can accidentally or maliciously
damage the database control structures. You should only use NOT
FENCED stored procedures when you need to maximize the performance
benefits. Test all your stored procedures thoroughly prior to running them
as NOT FENCED.
2. If a severe error does occur while you are running a NOT FENCED stored
procedure, the database manager determines whether the error occurred in
the stored procedure code or the database code, and attempts an
appropriate recovery.
For debugging purposes, consider using local FENCED stored procedures. A
local FENCED procedure is a PARAMETER STYLE DB2DARI procedure. To
call a local FENCED procedure, issue CALL <library-name>!<entry-point>,
where library-name represents the name of the shared library, and entry-point
represents the entry point of the shared library for the stored procedure. If the
name of the shared library and the entry point are the same, you can issue
CALL <entry-point>.
NOT FENCED and regular FENCED stored procedures complicate your
debugging efforts by giving your debugger access to additional address space.
Local FENCED stored procedures run in the application’s address space and
allow your debugger to access both the application code and the stored
procedure code. To enable local FENCED stored procedures for debugging,
perform the following steps:
1. Register the stored procedure as a FENCED stored procedure.
2. Set the DB2_STPROC_ALLOW_LOCAL_FENCED registry variable to true. For
information on registry variables, refer to the Administration Guide:
Implementation.
3. Run the client application on the same machine as the DB2 server.
Note: While debugging a local FENCED stored procedure, be careful not to
introduce statements that violate the restrictions listed in “Restrictions”
on page 209. DB2 treats calls to local FENCED stored procedures as
calls to a subroutine of the client application. Therefore, local FENCED
stored procedures can contain statements that violate restrictions on
normal stored procedures, like performing CONNECT statements in the
procedure body.
When you write a NOT FENCED stored procedure, keep in mind that it may
run in a threaded environment, depending on the operating system. Thus, the
stored procedure must either be completely re-entrant, or manage its static
variables so that access to these variables is serialized.
224 Application Development Guide
Note: You should not use static data in stored procedures, because DB2
cannot guarantee that the static data in a stored procedure is or is not
reinitialized on subsequent invocations.
NOT FENCED stored procedures must be precompiled with the
WCHARTYPE NOCONVERT option. See “The WCHARTYPE Precompiler
Option in C and C++” on page 611 for more information.
DB2 does not support the use of any of the following features in NOT
FENCED stored procedures:
v 16-bit
v Multi-threading
v Nested calls: calling or being called by another stored procedure
v Result sets: returning result sets to a client application or caller
v REXX
The following DB2 APIs and any DB2 CLI API are not supported in a NOT
FENCED stored procedure:
v BIND
v EXPORT
v IMPORT
v PRECOMPILE PROGRAM
v ROLLFORWARD DATABASE
Returning Result Sets from Stored Procedures
You can code stored procedures to return one or more result sets to DB2 CLI,
ODBC, JDBC, or SQLJ client applications. Aspects of this support include:
v Only DB2 CLI, ODBC, JDBC, and SQLJ clients can accept result sets.
v DB2 clients that use embedded SQL can accept multiple result sets if the
stored procedure resides on a server that is accessible from a DataJoiner
Version 2 server. Stored procedures on host and AS/400 platforms can
return multiple result sets to DB2 Connect clients. Stored procedures on
DB2 Universal Database servers can return multiple result sets to host and
AS/400 clients. Consult the product documentation for DataJoiner or the
host or AS/400 platform for more information.
v The client application program can describe the result sets returned.
v Result sets must be processed in serial fashion by the application. A cursor
is automatically opened on the first result set and a special call
(SQLMoreResults for DB2 CLI, getMoreResults for JDBC, getNextResultSet
for SQLJ) is provided to both close the cursor on one result set and to open
it on the next.
v The stored procedure indicates that a result set is to be returned by
declaring a cursor on that result set, opening a cursor on that result set, and
Chapter 7. Stored Procedures 225
leaving the cursor open when exiting the procedure. If more than one
cursor is left open, the result sets are returned in the order in which their
cursors were opened
v Only unread or unfetched rows are passed back in the result set.
v Stored procedures which return result sets must be run in FENCED mode.
v A COMMIT or ROLLBACK will close all cursors except WITH HOLD
cursors.
v The RESULT_SETS column in the DB2CLI.PROCEDURES table indicates
whether or not a stored procedure returns result sets. When you declare the
stored procedure with the CREATE PROCEDURE statement, the DYNAMIC
RESULT SETS clause sets this value to indicate the number of result sets
returned by the stored procedure.
For additional details on handling result sets:
v in DB2 CLI, refer to the CLI Guide and Reference.
v in Java, refer to the DB2 Java Enablement web page at
http://www.ibm.com/software/data/db2/java/ for links to the JDBC and
SQLJ specifications.
Example: Returning a Result Set from a Stored Procedure
This sample stored procedure shows how to return a result set to the client
application in the following supported languages:
C spserver.sqc
Java Spserver.java
This sample stored procedure accepts one IN parameter and returns one OUT
parameter and one result set. The stored procedure uses the IN parameter to
create a result set containing the values of the NAME, JOB, and SALARY
columns for the STAFF table for rows where SALARY is greater than the IN
parameter.
1 Register the stored procedure using the DYNAMIC RESULT SETS
clause of the CREATE PROCEDURE statement. For example, to
register the stored procedure written in embedded SQL for C, issue
the following statement:
CREATE PROCEDURE RESULT_SET_CLIENT
(IN salValue DOUBLE, OUT sqlCode INTEGER)
DYNAMIC RESULT SETS 1
LANGUAGE C
PARAMETER STYLE GENERAL
NO DBINFO
FENCED
READS SQL DATA
PROGRAM TYPE SUB
EXTERNAL NAME 'spserver!one_result_set_to_client'
226 Application Development Guide
2 For embedded SQL in C stored procedures, use the DECLARE
CURSOR and OPEN CURSOR statements to create an open cursor.
For CLI stored procedures, use the SQLPrepare and SQLBindParameter
APIs to create a result set. For Java stored procedures written with
JDBC, use the prepareStatement and executeQuery methods to create
a result set.
3 Close the connection to the database without closing the cursor or
result set. This step does not apply to embedded SQL in C stored
procedures.
4 Java stored procedures: for each result set that a PARAMETER STYLE
JAVA stored procedure returns, you must include a corresponding
ResultSet[] argument in the stored procedure method signature.
Chapter 7. Stored Procedures 227
C Example: SPSERVER.SQC (one_result_set_to_client)
SQL_API_RC SQL_API_FN one_result_set_to_client
(double *insalary, sqlint32 *out_sqlerror)
{
EXEC SQL INCLUDE SQLCA;
EXEC SQL WHENEVER SQLERROR GOTO return_error;
l_insalary = *insalary;
*out_sqlerror = 0;
EXEC SQL DECLARE c3 CURSOR FOR 2
SELECT name, job, CAST(salary AS INTEGER)
FROM staff
WHERE salary > :l_insalary
ORDER BY salary;
EXEC SQL OPEN c3; 2
/* Leave cursor open to return result set */
return (0); 3
/* Copy SQLCODE to OUT parameter if SQL error occurs */
return_error:
{
*out_sqlerror = SQLCODE;
EXEC SQL WHENEVER SQLERROR CONTINUE;
return (0);
}
} /* end one_result_set_to_client function */
228 Application Development Guide
Java Example: Spserver.java (resultSetToClient)
public static void resultSetToClient
(double inSalaryThreshold, // double input
int[] errorCode, // SQLCODE output
ResultSet[] rs) // ResultSet output 4
throws SQLException
{
errorCode[0] = 0; // SQLCODE = 0 unless SQLException occurs
try {
// Get caller's connection to the database
Connection con =
DriverManager.getConnection("jdbc:default:connection");
// get salary result set using a parameter marker
String query = "SELECT name, job, CAST(salary AS DOUBLE) " +
"FROM staff " +
"WHERE salary > ? " +
"ORDER BY salary";
// prepare the SQL statement
PreparedStatement stmt = con.prepareStatement(query);
// set the value of the parameter marker (?)
stmt.setDouble(1, inSalaryThreshold);
// get the result set that will be returned to the client
rs[0] = stmt.executeQuery(); 2
// to return a result set to the client, do not close ResultSet
con.close(); 3
}
catch (SQLException sqle)
{
errorCode[0] = sqle.getErrorCode();
}
}
Chapter 7. Stored Procedures 229
Example: Accepting a Result Set from a Stored Procedure: This sample
client application shows how to accept a result set from a stored procedure in
the following supported languages:
C (using CLI) spclient.c
Java Spclient.java
This sample client application calls the RESULT_SET_CLIENT stored procedure
and accepts one result set. The client application then displays the contents of
the result set.
1 Call the stored procedure with arguments that correspond to the
parameters you declared in the CREATE PROCEDURE statement.
2 JDBC applications use the getNextResultSet method to accept the first
result set from the stored procedure.
3 Fetch the rows from the result set. The sample CLI client uses a while
loop to fetch and display all rows from the result set. The sample
JDBC client calls a class method called fetchAll that fetches and
displays all rows from a result set.
230 Application Development Guide
CLI Example: SPCLIENT.C (one_result_set_to_client):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlcli1.h>
#include <sqlca.h>
#include "utilcli.h" /* Header file for CLI sample code */
SQLCHAR stmt[50];
SQLINTEGER out_sqlcode;
char out_buffer[33];
SQLINTEGER indicator;
struct sqlca sqlca;
SQLRETURN rc,rc1 ;
char procname[254];
SQLHANDLE henv; /* environment handle */
SQLHANDLE hdbc; /* connection handle */
SQLHANDLE hstmt1; /* statement handle */
SQLHANDLE hstmt2; /* statement handle */
SQLRETURN sqlrc = SQL_SUCCESS;
double out_median;
int oneresultset1(SQLHANDLE);
int main(int argc, char *argv[])
{
SQLHANDLE hstmt; /* statement handle */
SQLHANDLE hstmt_oneresult; /* statement handle */
char dbAlias[SQL_MAX_DSN_LENGTH + 1] ;
char user[MAX_UID_LENGTH + 1] ;
char pswd[MAX_PWD_LENGTH + 1] ;
/* Declare variables for passing data to INOUT_PARAM */
double inout_median;
/* checks the command line arguments */
rc = CmdLineArgsCheck1( argc, argv, dbAlias, user, pswd );
if ( rc != 0 ) return( 1 ) ;
/* allocate an environment handle */
printf("\n Allocate an environment handle.\n");
sqlrc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv ) ;
if ( sqlrc != SQL_SUCCESS )
{ printf( "\n--ERROR while allocating the environment handle.\n" ) ;
printf( " sqlrc = %d\n", sqlrc);
printf( " line = %d\n", __LINE__);
printf( " file = %s\n", __FILE__);
return( 1 ) ;
}
/* allocate a database connection handle */
printf(" Allocate a database connection handle.\n");
sqlrc = SQLAllocHandle( SQL_HANDLE_DBC, henv, &hdbc ) ;
Chapter 7. Stored Procedures 231
HANDLE_CHECK( SQL_HANDLE_ENV, henv, sqlrc, &henv, &hdbc ) ;
/* connect to the database */
printf( " Connecting to the database %s ...\n", dbAlias ) ;
sqlrc = SQLConnect( hdbc,
(SQLCHAR *)dbAlias, SQL_NTS,
(SQLCHAR *)user, SQL_NTS,
(SQLCHAR *)pswd, SQL_NTS
) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, sqlrc, &henv, &hdbc ) ;
printf( " Connected to the database %s.\n", dbAlias ) ;
/* set AUTOCOMMIT off */
sqlrc = SQLSetConnectAttr( hdbc,
SQL_ATTR_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF, SQL_NTS) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, sqlrc, &henv, &hdbc ) ;
/* allocate one or more statement handles */
printf(" Allocate a statement handle.\n");
sqlrc = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, sqlrc, &henv, &hdbc ) ;
sqlrc = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt_oneresult ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, sqlrc, &henv, &hdbc ) ;
/********************************************************\
* Call oneresultsettocaller stored procedure *
\********************************************************/
rc = oneresultset1(hstmt_oneresult);
rc = SQLFreeHandle( SQL_HANDLE_STMT, hstmt_oneresult ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, rc, &henv, &hdbc ) ;
/* ROLLBACK, free resources, and exit */
rc = SQLEndTran( SQL_HANDLE_DBC, hdbc, SQL_COMMIT ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, rc, &henv, &hdbc ) ;
printf("\nStored procedure rolled back.\n\n");
/* Disconnect from Remote Database */
rc = SQLFreeHandle( SQL_HANDLE_STMT, hstmt ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, rc, &henv, &hdbc ) ;
printf( "\n>Disconnecting .....\n" ) ;
rc = SQLDisconnect( hdbc ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, rc, &henv, &hdbc ) ;
rc = SQLFreeHandle( SQL_HANDLE_DBC, hdbc ) ;
HANDLE_CHECK( SQL_HANDLE_DBC, hdbc, rc, &henv, &hdbc ) ;
rc = SQLFreeHandle( SQL_HANDLE_ENV, henv ) ;
if ( rc != SQL_SUCCESS ) return( SQL_ERROR ) ;
232 Application Development Guide
return( SQL_SUCCESS ) ;
}
int oneresultset1(hstmt)
SQLHANDLE hstmt; /* statement handle */
{
/********************************************************\
* Call one_result_set_to_client stored procedure *
\********************************************************/
double insalary = 20000;
SQLINTEGER salary_int;
SQLSMALLINT num_cols;
char name[40];
char job[10];
strcpy(procname, "RESULT_SET_CALLER"); 1
printf("\nCALL stored procedure: %s\n", procname);
strcpy((char*)stmt,"CALL RESULT_SET_CALLER ( ?,? )");
rc = SQLPrepare(hstmt, stmt, SQL_NTS);
STMT_HANDLE_CHECK( hstmt, rc);
/* Bind the parameter to application variables () */
rc = SQLBindParameter(hstmt, 1,
SQL_PARAM_INPUT, SQL_C_DOUBLE,
SQL_DOUBLE,0,
0, &insalary,
0, NULL);
rc = SQLBindParameter(hstmt, 2,
SQL_PARAM_OUTPUT, SQL_C_LONG,
SQL_INTEGER,0,
0, &out_sqlcode,
0, NULL);
STMT_HANDLE_CHECK( hstmt, rc);
rc = SQLExecute(hstmt);
rc1 = SQLGetSQLCA(henv, hdbc, hstmt, &sqlca);
STMT_HANDLE_CHECK( hstmt, rc);
rc = SQLNumResultCols( hstmt, &num_cols ) ;
STMT_HANDLE_CHECK( hstmt, rc);
printf("Result set returned %d columns\n", num_cols);
/* bind columns to variables */
rc = SQLBindCol( hstmt, 1, SQL_C_CHAR, name, 40, &indicator);
STMT_HANDLE_CHECK( hstmt, rc);
rc = SQLBindCol( hstmt, 2, SQL_C_CHAR, job, 10, &indicator);
STMT_HANDLE_CHECK( hstmt, rc);
rc = SQLBindCol( hstmt, 3, SQL_C_LONG, &salary_int, 0, &indicator);
STMT_HANDLE_CHECK( hstmt, rc);
/* fetch result set returned from stored procedure */
rc = SQLFetch( hstmt ); 2
Chapter 7. Stored Procedures 233
rc1 = SQLGetSQLCA(henv, hdbc, hstmt, &sqlca);
STMT_HANDLE_CHECK( hstmt, rc);
printf("\n--------Name---------, --JOB--, ---Salary-- \n");
while (rc == SQL_SUCCESS && rc != SQL_NO_DATA_FOUND ) 3
{
printf("%20s,%10s, %d\n",name,job,salary_int);
rc = SQLFetch( hstmt );
}
STMT_HANDLE_CHECK( hstmt, rc);
/* Check that the stored procedure executed successfully */
if (rc == SQL_SUCCESS) {
printf("Stored procedure returned successfully.\n");
}
else {
printf("Stored procedure returned SQLCODE %d\n", out_sqlcode);
}
rc = SQLCloseCursor(hstmt);
return(rc);
}
234 Application Development Guide
Java Example: Spclient.java (resultSetToClient):
// prepare the CALL statement for RESULT_SET_CLIENT
procName = "RESULT_SET_CLIENT";
sql = "CALL " + procName + "(?, ?)"; 1
callStmt = con.prepareCall(sql);
// set input parameter to median value passed back by OUT_PARAM
callStmt.setDouble (1, outMedian);
// register the output parameter
callStmt.registerOutParameter (2, Types.INTEGER);
// call the stored procedure
System.out.println ("\nCall stored procedure named " + procName);
callStmt.execute();
// retrieve output parameter
outErrorCode = callStmt.getInt(2);
if (outErrorCode == 0) {
System.out.println(procName + " completed successfully");
ResultSet rs = callStmt.getResultSet(); 2
while (rs.next()) {
fetchAll(rs); 3
}
// close ResultSet
rs.close();
}
else { // stored procedure failed
System.out.println(procName + " failed with SQLCODE "
+ outErrorCode);
}
Chapter 7. Stored Procedures 235
Resolving Problems
If a stored procedure application fails to execute properly, ensure that:
v The stored procedure is built using the correct calling sequence, compile
options, and so on.
v The application executes locally with both client application and stored
procedure on the same workstation.
v The stored procedure is stored in the proper location in accordance with the
instructions in the Application Building Guide.
For example, in an OS/2 environment, the dynamic link library for a
FENCED stored procedure is located in the instance_name\function
directory on the database server.
v The application, except if it is written in DB2 CLI and JDBC, is bound to
the database.
v The stored procedure accurately returns any SQLCA error information to
the client application.
v Stored procedure function names are case-sensitive, and must match exactly
on client and server.
v If you register the stored procedure with a CREATE PROCEDURE
statement, stored procedure function names must not match their library
name.
For example, the database manager will execute the stored procedure
myfunc contained in the Windows 32-bit operating system library
myfunc.dll as a DB2DARI function, disregarding the values specified in its
associated CREATE PROCEDURE statement.
Note: For more information on debugging Java stored procedures, see
“Debugging Stored Procedures in Java” on page 651.
You can use the debugger supplied with your compiler to debug a local
FENCED stored procedure as you would any other application. Consult your
compiler documentation for information on using the supplied debugger.
For example, to use the debugger supplied with Visual Studio™ on Windows
NT, perform the following steps:
Step 1. Set the DB2_STPROC_ALLOW_LOCAL_FENCED registry variable to true.
Step 2. Compile the source file for the stored procedure DLL with the -Zi
and -Od flags, and then link the DLL using the -DEBUG option.
Step 3. Copy the resulting DLL to the instance_name \function directory of
the server.
Step 4. Invoke the client application on the server with the Visual Studio
debugger. For the client application outcli.exe, enter the following
command:
236 Application Development Guide
msdev spclient.exe
Step 5. When the Visual Studio debugger window opens, select Project —>
Settings.
Step 6. Click the Debug tab.
Step 7. Click the Category arrow and select the Additional DLLs.
Step 8. Click the New button to create a new module.
Step 9. Click the Browse button to open the Browse window.
Step 10. Select the module spserver.dll and click OK to close the Settings
window.
Step 11. Open the source file for the stored procedure and set a breakpoint.
Step 12. Click the Go button. The Visual Studio debugger stops when the
stored procedure is invoked.
Step 13. At this point, you can debug the stored procedure using the Visual
Studio debugger.
Refer to the Visual Studio product documentation for further information on
using the Visual Studio debugger.
Chapter 7. Stored Procedures 237
238 Application Development Guide
Chapter 8. Writing SQL Procedures
Comparison of SQL Procedures and External Restrictions on Nested SQL Procedures 249
Procedures . . . . . . . . . . . . 239 Returning Result Sets From SQL Procedures 249
Valid SQL Procedure Body Statements . . . 240 Returning Result Sets to Caller or Client 250
Issuing CREATE PROCEDURE Statements 242 Returning Result Sets to the Client . . 250
Handling Conditions in SQL Procedures . . 243 Returning Result Sets to the Caller . . 251
Declaring Condition Handlers . . . . 243 Receiving Result Sets as a Caller . . . . 251
SIGNAL and RESIGNAL Statements . . 245 Debugging SQL Procedures . . . . . . 252
SQLCODE and SQLSTATE Variables in Displaying Error Messages for SQL
SQL Procedures . . . . . . . . . 246 Procedures . . . . . . . . . . . 252
Using Dynamic SQL in SQL Procedures . . 246 Debugging SQL Procedures Using
Nested SQL Procedures . . . . . . . . 248 Intermediate Files . . . . . . . . . 255
Passing Parameters Between Nested SQL Examples of SQL Procedures . . . . . . 255
Procedures . . . . . . . . . . . 248
Returning Result Sets From Nested SQL
Procedures . . . . . . . . . . . 249
An SQL procedure is a stored procedure in which the procedural logic is
contained in a CREATE PROCEDURE statement. The part of the CREATE
PROCEDURE statement that contains the code is called the procedure body.
To create an SQL procedure, simply issue the CREATE PROCEDURE
statement like any other DDL statement. You can also use the IBM DB2 Stored
Procedure Builder to help you define the stored procedure to DB2, specify the
source statements for the SQL procedure, and prepare the procedure for
execution. For more information on the IBM DB2 Stored Procedure Builder,
see “Chapter 9. IBM DB2 Stored Procedure Builder” on page 261.
This chapter discusses how to write a CREATE PROCEDURE statement that
includes a procedure body. For more information on the syntax of the
CREATE PROCEDURE statement and the procedure body, refer to the SQL
Reference. For more information on using the IBM DB2 Stored Procedure
Builder to create SQL procedures, see “Chapter 9. IBM DB2 Stored Procedure
Builder” on page 261.
Comparison of SQL Procedures and External Procedures
Like external stored procedure definitions, SQL procedure definitions provide
the following information:
v The procedure name.
v Parameter attributes.
v The language in which the procedure is written. For an SQL procedure, the
language is SQL.
© Copyright IBM Corp. 1993, 2000 239
v Other information about the procedure, such as the specific name of the
procedure and the number of result sets returned by the procedure.
Unlike a CREATE PROCEDURE statement for an external stored procedure,
the CREATE PROCEDURE statement for an SQL procedure does not specify
the EXTERNAL clause. Instead, an SQL procedure has a procedure body,
which contains the source statements for the stored procedure.
The following example shows a CREATE PROCEDURE statement for a simple
stored procedure. The procedure name, the list of parameters that are passed
to or from the procedure, and the LANGUAGE parameter are common to all
stored procedures. However, the LANGUAGE value of SQL and the
BEGIN...END block, which forms the procedure body, are particular to an
SQL procedure.
CREATE PROCEDURE UPDATE_SALARY_1 1
(IN EMPLOYEE_NUMBER CHAR(6), 2
IN RATE INTEGER) 2
LANGUAGE SQL 3
BEGIN
UPDATE EMPLOYEE 4
SET SALARY = SALARY * (1.0 * RATE / 100.0 )
WHERE EMPNO = EMPLOYEE_NUMBER;
END
Notes for the previous example:
1 The stored procedure name is UPDATE_SALARY_1.
2 The two parameters have data types of CHAR(6) and INTEGER. Both are
input parameters.
3 LANGUAGE SQL indicates that this is an SQL procedure, so a procedure
body follows the other parameters.
4 The procedure body consists of a single SQL UPDATE statement, which
updates rows in the employee table.
Within the SQL procedure body, you cannot use OUT parameters as a value
in any expression. You can only assign values to OUT parameters using the
assignment statement, or as the target variable in the INTO clause of SELECT,
VALUES and FETCH statements. You cannot use IN parameters as the target
of assignment or INTO clauses.
Valid SQL Procedure Body Statements
A procedure body consists of a single SQL procedure statement. The types of
statements that you can use in a procedure body include:
Assignment statement
Assigns a value to an output parameter or to an SQL variable, which is a
240 Application Development Guide
variable that is defined and used only within a procedure body. You
cannot assign values to IN parameters.
CASE statement
Selects an execution path based on the evaluation of one or more
conditions. This statement is similar to the CASE expression described in
the SQL Reference.
FOR statement
Executes a statement or group of statements for each row of a table.
GET DIAGNOSTICS statement
The GET DIAGNOSTICS statement returns information about the
previous SQL statement.
GOTO statement
Transfers program control to a user-defined label within an SQL routine.
IF statement
Selects an execution path based on the evaluation of a condition.
ITERATE statement
Passes the flow of control to a labelled block or loop.
LEAVE statement
Transfers program control out of a loop or block of code.
LOOP statement
Executes a statement or group of statements multiple times.
REPEAT statement
Executes a statement or group of statements until a search condition is
true.
RESIGNAL statement
The RESIGNAL statement is used within a condition handler to resignal
an error or warning condition. It causes an error or warning to be
returned with the specified SQLSTATE, along with optional message text.
RETURN statement
Returns control from the SQL procedure to the caller. You can also return
an integer value to the caller.
SIGNAL statement
The SIGNAL statement is used to signal an error or warning condition. It
causes an error or warning to be returned with the specified SQLSTATE,
along with optional message text.
SQL statement
The SQL procedure body can contain any SQL statement listed in
“Appendix A. Supported SQL Statements” on page 723.
Chapter 8. Writing SQL Procedures 241
WHILE statement
Repeats the execution of a statement or group of statements while a
specified condition is true.
Compound statement
Can contain one or more of any of the other types of statements in this
list, as well as SQL variable declarations, condition handlers, or cursor
declarations.
For a complete list of the SQL statements allowed within an SQL procedure
body, see “Appendix A. Supported SQL Statements” on page 723. For detailed
descriptions and syntax of each of these statements, refer to the SQL Reference.
Issuing CREATE PROCEDURE Statements
To issue a CREATE PROCEDURE statement as a DB2 Command Line
Processor (DB2 CLP) script, you must use an alternate terminating character
for SQL statements in the script. The semicolon (';') character, the default for
DB2 CLP scripts, terminates SQL statements within the SQL procedure body.
To use an alternate terminating character in DB2 CLP scripts, select a
character that is not used in standard SQL statements. In the following
example, the at sign ('@') is used as the terminating character for a DB2 CLP
script named script.db2:
CREATE PROCEDURE UPDATE_SALARY_IF
(IN employee_number CHAR(6), IN rating SMALLINT)
LANGUAGE SQL
BEGIN
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE EXIT HANDLER FOR not_found
SIGNAL SQLSTATE '20000' SET MESSAGE_TEXT = 'Employee not found';
IF (rating = 1)
THEN UPDATE employee
SET salary = salary * 1.10, bonus = 1000
WHERE empno = employee_number;
ELSEIF (rating = 2)
THEN UPDATE employee
SET salary = salary * 1.05, bonus = 500
WHERE empno = employee_number;
ELSE UPDATE employee
SET salary = salary * 1.03, bonus = 0
WHERE empno = employee_number;
END IF;
END
@
To process the DB2 CLP script from the command line, use the following
syntax:
db2 -tdterm-char -vf script-name
242 Application Development Guide
where term-char represents the terminating character, and script-name
represents the name of the DB2 CLP script to process. To process the
preceding script, for example, issue the following command from the CLP:
db2 -td@ -vf script.db2
Handling Conditions in SQL Procedures
Condition handlers determine the behavior of your SQL procedure when a
condition occurs. You can declare one or more condition handlers in your SQL
procedure for general DB2 conditions, defined conditions for specific
SQLSTATE values, or specific SQLSTATE values. For more information on
general conditions and on defining your own conditions, see “Declaring
Condition Handlers”.
If a statement in your SQL procedure issues an SQLWARNING or NOT
FOUND condition, and you have declared a handler for the respective
condition, DB2 passes control to the corresponding handler. If you have not
declared a handler for that particular condition, DB2 sets the variables
SQLSTATE and SQLCODE with the corresponding values for the condition
and passes control to the next statement in the procedure body.
If a statement in your SQL procedure raises an SQLEXCEPTION condition,
and you declared a handler for the specific SQLSTATE or the
SQLEXCEPTION condition, DB2 passes control to that handler. If DB2
successfully executes the handler, the values of SQLSTATE and SQLCODE
return ‘00000’ and 0 respectively.
If a statement in your SQL procedure raises an SQLEXCEPTION condition,
and you have not declared a handler for the specific SQLSTATE or the
SQLEXCEPTION condition, DB2 terminates the SQL procedure and returns to
the client.
Declaring Condition Handlers
The general form of a handler declaration is:
DECLARE handler-type HANDLER FOR condition SQL-procedure-statement
When DB2 raises a condition that matches condition, DB2 passes control to the
condition handler. The condition handler performs the action indicated by
handler-type, and then executes SQL-procedure-statement.
handler-type
CONTINUE
Specifies that after SQL-procedure-statement completes, execution
continues with the statement after the statement that caused the
error.
Chapter 8. Writing SQL Procedures 243
EXIT Specifies that after SQL-procedure-statement completes, execution
continues at the end of the compound statement that contains the
handler.
UNDO
Specifies that before SQL-procedure-statement executes, DB2 rolls
back any SQL operations that have occurred in the compound
statement that contains the handler. After SQL-procedure-statement
completes, execution continues at the end of the compound
statement that contains the handler.
Note: You can only declare UNDO handlers in ATOMIC
compound statements.
condition
DB2 provides three general conditions:
NOT FOUND
Identifies any condition that results in an SQLCODE of +100 or an
SQLSTATE of ‘02000’.
SQLEXCEPTION
Identifies any condition that results in a negative SQLCODE.
SQLWARNING
Identifies any condition that results in a warning condition
(SQLWARN0 is ‘W’), or that results in a positive SQL return code
other than +100.
You can also use the DECLARE statement to define your own condition
for a specific SQLSTATE. For more information on defining your own
condition, refer to the SQL Reference.
SQL-procedure-statement
You can use a single SQL procedure statement to define the behavior of
the condition handler. DB2 accepts a compound statement delimited by a
BEGIN...END block as a single SQL procedure statement. If you use a
compound statement to define the behavior of a condition handler, and
you want the handler to retain the value of either the SQLSTATE or
SQLCODE variables, you must assign the value of the variable to a local
variable or parameter in the first statement of the compound block. If the
first statement of a compound block does not assign the value of
SQLSTATE or SQLCODE to a local variable or parameter, SQLSTATE and
SQLCODE cannot retain the value that caused DB2 to invoke the
condition handler.
Note: You cannot define another condition handler within the condition
handler.
244 Application Development Guide
The following examples demonstrate simple condition handlers:
Example: CONTINUE handler: This handler assigns the value of 1 to the local
variable at_end when DB2 raises a NOT FOUND condition. DB2 then passes
control to the statement following the one that raised the NOT FOUND
condition.
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE CONTINUE HANDLER FOR not_found SET at_end=1;
Example: EXIT handler: The procedure declares NO_TABLE as the condition
name for SQLSTATE 42704 (name is an undefined name). The condition
handler for NO_TABLE places the string Table does not exist into output
parameter OUT_BUFFER. The handler then causes the SQL procedure to exit
the compound statement in which the handler is declared.
DECLARE NO_TABLE CONDITION FOR SQLSTATE '42704';
DECLARE EXIT HANDLER FOR NO_TABLE
BEGIN
SET OUT_BUFFER='Table does not exist';
END
Example: UNDO handler: The procedure declares an UNDO condition handler
for SQLSTATE 42704 without first defining a name for the SQLSTATE. The
handler causes the SQL procedure to roll back the current unit of work, place
the string Table does not exist into output parameter OUT_BUFFER, and
exit the compound statement in which the handler is declared.
DECLARE UNDO HANDLER FOR SQLSTATE '42704'
BEGIN
SET OUT_BUFFER='Table does not exist';
END;
Note: You can only declare UNDO handlers in ATOMIC compound
statements.
SIGNAL and RESIGNAL Statements
You can use the SIGNAL and RESIGNAL statements to explicitly raise a
specific SQLSTATE. Use the SET MESSAGE_TEXT clause of the SIGNAL and
RESIGNAL statements to define the text that DB2 displays for a
custom-defined SQLSTATE.
In the following example, the SQL procedure body declares a condition
handler for the custom SQLSTATE 72822. When the procedure executes the
SIGNAL statement that raises SQLSTATE 72822, DB2 invokes the condition
handler. The condition handler tests the value of the SQL variable var with an
IF statement. If var is OK, the handler redefines the SQLSTATE value as 72623
and assigns a string literal to the text associated with SQLSTATE 72623. If var
is not OK, the handler redefines the SQLSTATE value as 72319 and assigns the
value of var to the text associated with that SQLSTATE.
Chapter 8. Writing SQL Procedures 245
DECLARE EXIT CONDITION HANDLER FOR SQLSTATE '72822'
BEGIN
IF ( var = 'OK' )
RESIGNAL '72623' SET MESSAGE_TEXT = 'Got SQLSTATE 72822';
ELSE
RESIGNAL '72319' SET MESSAGE_TEXT = var;
END;
SIGNAL SQLSTATE '72822';
For more information on the SIGNAL and RESIGNAL statements, refer to the
SQL Reference.
SQLCODE and SQLSTATE Variables in SQL Procedures
To help debug your SQL procedures, you might find it useful to insert the
value of the SQLCODE and SQLSTATE into a table at various points in the
SQL procedure, or to return the SQLCODE and SQLSTATE values in a
diagnostic string as an OUT parameter. To use the SQLCODE and SQLSTATE
values, you must declare the following SQL variables in the SQL procedure
body:
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE SQLSTATE CHAR(5) DEFAULT ‘00000’;
You can also use CONTINUE condition handlers to assign the value of the
SQLSTATE and SQLCODE variables to local variables in your SQL procedure
body. You can then use these local variables to control your procedural logic,
or pass the value back as an output parameter. In the following example, the
SQL procedure returns control to the statement following each SQL statement
with the SQLCODE set in a local variable called RETCODE.
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE retcode INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET retcode = SQLCODE;
DECLARE CONTINUE HANDLER FOR SQLWARNING SET retcode = SQLCODE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET retcode = SQLCODE;
Note: When you access the SQLCODE or SQLSTATE variables in an SQL
procedure, DB2 sets the value of SQLCODE to 0 and SQLSTATE to
‘00000’ for the subsequent statement.
Using Dynamic SQL in SQL Procedures
SQL procedures, like external stored procedures, can issue dynamic SQL
statements. If your dynamic SQL statement does not include parameter
markers and you plan to execute it only once, use the EXECUTE IMMEDIATE
statement.
246 Application Development Guide
If your dynamic SQL statement contains parameter markers, you must use the
PREPARE and EXECUTE statements. If you you plan to execute a dynamic
SQL statement multiple times, it might be more efficient to issue a single
PREPARE statement and to issue the EXECUTE statement multiple times
rather than issuing the EXECUTE IMMEDIATE statement each time. To use
the PREPARE and EXECUTE statements to issue dynamic SQL in your SQL
procedure, you must include the following statements in the SQL procedure
body:
Step 1. Declare a variable of type VARCHAR that is large enough to hold
your dynamic SQL statement using a DECLARE statement.
Step 2. Assign a statement string to the variable using a SET statement. You
cannot include variables directly in the statement string. Instead, you
must use the question mark ('?') symbol as a parameter marker for
any variables used in the statement.
Step 3. Create a prepared statement from the statement string using a
PREPARE statement.
Step 4. Execute the prepared statement using an EXECUTE statement. If the
statement string includes a parameter marker, use a USING clause to
replace it with the value of a variable.
Note: Statement names defined in PREPARE statements for SQL procedures
are treated as scoped variables. Once the SQL procedure exits the scope
in which you define the statement name, DB2 can no longer access the
statement name. Inside any compound statement, you cannot issue two
PREPARE statements that use the same statement name.
Example: Dynamic SQL statements: The following example shows an SQL
procedure that includes dynamic SQL statements.
The procedure receives a department number (deptNumber) as an input
parameter. In the procedure, three statement strings are built, prepared, and
executed. The first statement string executes a DROP statement to ensure that
the table to be created does not already exist. This table is named
DEPT_deptno_T, where deptno is the value of input parameter deptNumber. A
CONTINUE HANDLER ensures that the SQL procedure will continue if it
detects SQLSTATE 42704 (“undefined object name”), which DB2 returns from
the DROP statement if the table does not exist. The second statement string
issuees a CREATE statement to create DEPT_deptno_T. The third statement
string inserts rows for employees in department deptno into DEPT_deptno_T.
The third statement string contains a parameter marker that represents
deptNumber. When the prepared statement is executed, parameter deptNumber
is substituted for the parameter marker.
CREATE PROCEDURE create_dept_table
(IN deptNumber VARCHAR(3), OUT table_name VARCHAR(30))
LANGUAGE SQL
Chapter 8. Writing SQL Procedures 247
BEGIN
DECLARE stmt VARCHAR(1000);
-- continue if sqlstate 42704 ('undefined object name')
DECLARE CONTINUE HANDLER FOR SQLSTATE '42704'
SET stmt = '';
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SET table_name = 'PROCEDURE_FAILED';
SET table_name = 'DEPT_'||deptNumber||'_T';
SET stmt = 'DROP TABLE '||table_name;
PREPARE s1 FROM stmt;
EXECUTE s1;
SET stmt = 'CREATE TABLE '||table_name||
'( empno CHAR(6) NOT NULL, '||
'firstnme VARCHAR(12) NOT NULL, '||
'midinit CHAR(1) NOT NULL, '||
'lastname VARCHAR(15) NOT NULL, '||
'salary DECIMAL(9,2))';
PREPARE s2 FROM STMT;
EXECUTE s2;
SET stmt = 'INSERT INTO '||table_name || ' ' ||
'SELECT empno, firstnme, midinit, lastname, salary '||
'FROM employee '||
'WHERE workdept = ?';
PREPARE s3 FROM stmt;
EXECUTE s3 USING deptNumber;
END
Nested SQL Procedures
Your SQL procedures can contain CALL statements to call other SQL
procedures. This feature, called nested stored procedures, enables you to reuse
existing SQL procedures and design more complex applications.
Passing Parameters Between Nested SQL Procedures
To call a target SQL procedure from within a caller SQL procedure, simply
include a CALL statement with the appropriate number and types of
parameters in your caller. If the target returns OUT parameters, the caller can
use the returned values in its own statements.
For example, you can create an SQL procedure that calls a target SQL
procedure with the name “SALES_TARGET” and that accepts a single OUT
parameter of type INTEGER with the following SQL:
CREATE PROCEDURE NEST_SALES(OUT budget DECIMAL(11,2))
LANGUAGE SQL
BEGIN
DECLARE total INTEGER DEFAULT 0;
SET total = 6;
CALL SALES_TARGET(total);
SET budget = total * 10000;
END
248 Application Development Guide
Returning Result Sets From Nested SQL Procedures
If a target SQL procedure returns result sets, either the caller or the client
application receives the result sets, depending on the DECLARE CURSOR
statements issued by the target SQL procedure. For each DECLARE CURSOR
statement in the target that includes the WITH RETURN TO CLIENT clause,
the caller does not receive the result set. For WITH RETURN TO CLIENT
cursors, the result set is returned directly to the client application.
For more information on returning result sets from nested SQL procedures,
see “Returning Result Sets to Caller or Client” on page 250.
Restrictions on Nested SQL Procedures
Keep the following restrictions in mind when designing your application
architecture:
LANGUAGE
SQL procedures can only call stored procedures written in SQL or C.
You cannot call other host language stored procedures from within an
SQL procedure.
16 levels of nesting
You may only include a maximum of 16 levels of nested calls to SQL
procedures. A scenario where SQL procedure A calls SQL procedure B,
and SQL procedure B calls SQL procedure C, is an example of three
levels of nested calls.
Recursion
You can create an SQL procedure that calls itself recursively. Recursive
SQL procedures must comply with the previously described restriction
on the maximum levels of nesting.
Security
An SQL procedure cannot call a target SQL procedure that is
cataloged with a higher SQL data access level. For example, an SQL
procedure created with the CONTAINS SQL clause can call SQL
procedures created with either the CONTAINS SQL clause or the NO
SQL clause, and cannot call SQL procedures created with either the
READS SQL DATA clause or the MODIFIES SQL DATA clause.
An SQL procedure created with the NO SQL clause cannot issue a
CALL statement.
Returning Result Sets From SQL Procedures
Returning result sets from SQL procedures is similar to returning result sets
from external stored procedures. Client applications must use the CLI, JDBC,
or SQLJ application programming interfaces to accept result sets from an SQL
procedure. SQL procedures that call other SQL procedures also can accept
Chapter 8. Writing SQL Procedures 249
result sets from those procedures. To return a result set from an SQL
procedure, write your SQL procedure as follows:
1. Declare the number of result sets that the SQL procedure returns using the
DYNAMIC RESULT SETS clause of the CREATE PROCEDURE statement.
2. Declare a cursor using the DECLARE CURSOR statement.
3. Open the cursor using the OPEN CURSOR statement.
4. Exit from the SQL procedure without closing the cursor.
For example, you can write an SQL procedure that returns a single result set,
based on the value of the INOUT parameter threshold, as follows:
CREATE PROCEDURE RESULT_SET (INOUT threshold SMALLINT)
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
DECLARE cur1 CURSOR WITH RETURN TO CALLER FOR
SELECT name, job, years
FROM staff
WHERE years < threshold;
OPEN cur1;
END
Returning Result Sets to Caller or Client
If your application returns result sets from nested SQL procedures, you must
use the WITH RETURN clause of the DECLARE CURSOR statement to ensure
that DB2 returns the result sets to the appropriate location. If a target SQL
procedure returns