IBM Informix Developer's Handbook - IBM Redbooks
IBM Informix Developer's Handbook - IBM Redbooks
IBM Informix Developer's Handbook - IBM Redbooks
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>IBM</strong> <strong>Informix</strong><br />
Developer’s <strong>Handbook</strong><br />
Learn application development with<br />
supported APIs, drivers, and interfaces<br />
Understand <strong>Informix</strong> supported<br />
programming environments<br />
Follow practical examples to<br />
develop an <strong>Informix</strong> application<br />
ibm.com/redbooks<br />
Front cover<br />
Whei-Jen Chen<br />
Krishna Doddi<br />
Manoj Ghogale<br />
David Jay<br />
Javier Sagrera
International Technical Support Organization<br />
<strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
October 2010<br />
SG24-7884-00
Note: Before using this information and the product it supports, read the information in<br />
“Notices” on page ix.<br />
First Edition (October 2010)<br />
This edition applies to <strong>IBM</strong> <strong>Informix</strong> Version 11.5.<br />
© Copyright International Business Machines Corporation 2010. All rights reserved.<br />
Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosure restricted by GSA ADP<br />
Schedule Contract with <strong>IBM</strong> Corp.
Contents<br />
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix<br />
Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x<br />
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi<br />
The team who wrote this book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi<br />
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii<br />
Now you can become a published author, too! . . . . . . . . . . . . . . . . . . . . . . . . xiii<br />
Comments welcome. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv<br />
Stay connected to <strong>IBM</strong> <strong>Redbooks</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1<br />
1.1 Server options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2<br />
1.1.1 <strong>Informix</strong> servers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2<br />
1.2 <strong>Informix</strong> tools for developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />
1.2.1 <strong>Informix</strong> Connect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />
1.2.2 <strong>Informix</strong> Client Software Development Kit. . . . . . . . . . . . . . . . . . . . . . 9<br />
1.2.3 4GL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />
1.2.4 Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />
1.2.5 <strong>Informix</strong> DataBlade Developers Kit . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />
1.2.6 <strong>Informix</strong> Spatial DataBlade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />
1.2.7 PHP on <strong>Informix</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
1.3 <strong>Informix</strong> overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
1.3.1 Architecture overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
1.3.2 <strong>Informix</strong> developer environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />
1.3.3 <strong>Informix</strong> capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment . . . . . . . . . 25<br />
2.1 Server setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
2.1.1 Planning for the installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
2.1.2 Installing <strong>Informix</strong> Server. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
2.1.3 Configuring <strong>Informix</strong> Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />
2.2 Client setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />
2.2.1 <strong>Informix</strong> Client options. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />
2.2.2 Installing and setting up Client SDK . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />
2.2.3 Setting up <strong>IBM</strong> Data Server drivers. . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />
2.2.4 Setting up <strong>Informix</strong> JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />
Chapter 3. Working with the ODBC driver . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />
3.1 ODBC and <strong>Informix</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. iii
3.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />
3.2.1 <strong>IBM</strong> <strong>Informix</strong> ODBC Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />
3.2.2 <strong>IBM</strong> Data Server Driver for ODBC and CLI. . . . . . . . . . . . . . . . . . . . 70<br />
3.2.3 Verifying connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74<br />
3.3 Developing an ODBC application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />
3.3.1 Connecting to the database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />
3.3.2 Type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />
3.3.3 Performing database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />
3.3.4 Handling special data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95<br />
3.3.5 Error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />
3.3.6 Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117<br />
Chapter 4. Working with ESQL/C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />
4.1 <strong>Informix</strong> ESQL/C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />
4.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />
4.3 Windows system configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />
4.4 Developing an ESQL/C application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />
4.4.1 Creating an ESQL/C application . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />
4.4.2 Performing database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />
4.4.3 Data types mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />
4.4.4 Handling special data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />
4.4.5 Exception handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />
4.4.6 Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />
Chapter 5. Working with the JDBC drivers . . . . . . . . . . . . . . . . . . . . . . . . 153<br />
5.1 JDBC drivers for an <strong>Informix</strong> database . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />
5.1.1 <strong>IBM</strong> <strong>Informix</strong> JDBC Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />
5.1.2 <strong>IBM</strong> Data Server Driver for JDBC and SQLJ . . . . . . . . . . . . . . . . . 156<br />
5.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />
5.2.1 Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />
5.2.2 Verify connectivity with <strong>Informix</strong> JDBC Driver . . . . . . . . . . . . . . . . . 158<br />
5.2.3 Verify connectivity with the Data Server Driver . . . . . . . . . . . . . . . . 160<br />
5.3 JDBC type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />
5.4 Performing database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />
5.4.1 Connection to the database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />
5.4.2 Manipulating data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />
5.5 <strong>Informix</strong> additional features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />
5.5.1 Batch inserts or updates and using ResultSet metadata . . . . . . . . 175<br />
5.5.2 BIGSERIAL data type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177<br />
5.5.3 <strong>Informix</strong> smart large objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178<br />
5.5.4 Secure Socket Layer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182<br />
5.6 Typical errors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185<br />
5.6.1 Class not found errors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186<br />
iv <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.6.2 Connectivity errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186<br />
5.6.3 Syntax errors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />
5.7 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />
5.7.1 <strong>IBM</strong> <strong>Informix</strong> JDBC Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />
5.7.2 <strong>IBM</strong> Data Server Driver for JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . 189<br />
6.1 Hibernate for Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190<br />
6.1.1 Overview of Hibernate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190<br />
6.1.2 Hibernate concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191<br />
6.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />
6.2.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />
6.2.2 Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />
6.3 Using Hibernate with an <strong>Informix</strong> database. . . . . . . . . . . . . . . . . . . . . . . 198<br />
6.3.1 Components of a Hibernate application . . . . . . . . . . . . . . . . . . . . . 198<br />
6.3.2 Working with a Hibernate object . . . . . . . . . . . . . . . . . . . . . . . . . . . 203<br />
6.3.3 Using annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider. . . . . . . . . . . . . 215<br />
7.1 <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216<br />
7.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216<br />
7.2.1 Installation and setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216<br />
7.2.2 Verifying connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217<br />
7.3 Developing an OLE DB application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219<br />
7.3.1 Supported interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220<br />
7.3.2 Connecting to database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221<br />
7.3.3 Type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222<br />
7.3.4 Cursors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224<br />
7.3.5 Typical database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225<br />
7.4 Visual Basic, ADO.NET, and SQL Server . . . . . . . . . . . . . . . . . . . . . . . . 237<br />
7.4.1 OLE DB with Visual Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />
7.4.2 ADO.NET and the OLEDB bridge . . . . . . . . . . . . . . . . . . . . . . . . . . 240<br />
7.4.3 SQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242<br />
7.5 Troubleshooting and tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />
7.5.1 Typical errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />
7.5.2 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248<br />
Chapter 8. Working with .NET data providers. . . . . . . . . . . . . . . . . . . . . . 253<br />
8.1 <strong>Informix</strong> and .NET data providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254<br />
8.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254<br />
8.2.1 <strong>IBM</strong> <strong>Informix</strong> .Net Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254<br />
8.2.2 <strong>IBM</strong> Data Server Provider for .NET. . . . . . . . . . . . . . . . . . . . . . . . . 255<br />
8.2.3 Verifying connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256<br />
8.3 Developing a .NET application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259<br />
Contents v
8.3.1 Connecting to the database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259<br />
8.3.2 Data type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262<br />
8.3.3 Performing database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />
8.3.4 Handling <strong>Informix</strong> specific data types . . . . . . . . . . . . . . . . . . . . . . . 276<br />
8.3.5 Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290<br />
8.3.6 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291<br />
8.4 Visual Studio Add-In for Visual Studio. . . . . . . . . . . . . . . . . . . . . . . . . . . 293<br />
Chapter 9. Working with PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295<br />
9.1 <strong>Informix</strong> and PHP extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296<br />
9.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297<br />
9.2.1 Installing OAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297<br />
9.2.2 Verifying the PDO_INFORMIX setup . . . . . . . . . . . . . . . . . . . . . . . 299<br />
9.2.3 Verifying the PDO setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299<br />
9.2.4 Verifying connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300<br />
9.3 Developing a PHP application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301<br />
9.3.1 Connecting to a database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302<br />
9.3.2 Performing database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . 304<br />
9.3.3 Handling complex data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310<br />
9.3.4 Working with PHP extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319<br />
9.3.5 Exception handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324<br />
9.3.6 Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328<br />
Chapter 10. User-defined routines. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329<br />
10.1 An overview of UDRs and database extensions . . . . . . . . . . . . . . . . . . 330<br />
10.1.1 Considerations for UDRs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331<br />
10.1.2 About UDRs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332<br />
10.1.3 Considerations for extending data types. . . . . . . . . . . . . . . . . . . . 334<br />
10.2 Developing UDRs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336<br />
10.2.1 UDR examples in SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336<br />
10.2.2 UDRs in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340<br />
10.2.3 UDRs in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348<br />
10.3 DataBlades and bladelets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352<br />
10.3.1 Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353<br />
10.3.2 <strong>IBM</strong> <strong>Informix</strong> provided DataBlades . . . . . . . . . . . . . . . . . . . . . . . . 357<br />
10.3.3 Developing a bladelet routine . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359<br />
Chapter 11. Working with Ruby on Rails. . . . . . . . . . . . . . . . . . . . . . . . . . 361<br />
11.1 A brief overview of Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362<br />
11.1.1 Architecture of Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363<br />
11.1.2 Ruby Driver and Rails Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . 363<br />
11.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364<br />
11.2.1 Ruby <strong>Informix</strong> driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364<br />
11.2.2 Data Server Ruby driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367<br />
vi <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
11.2.3 Rails adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369<br />
11.3 Database operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373<br />
11.4 Using the Rails Adapter with Ruby <strong>Informix</strong> . . . . . . . . . . . . . . . . . . . . . 384<br />
11.4.1 Creating database objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384<br />
11.4.2 Creating the Rails application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385<br />
11.4.3 Modifying the database configuration file . . . . . . . . . . . . . . . . . . . 386<br />
11.4.4 Creating the Rails model and controllers . . . . . . . . . . . . . . . . . . . 387<br />
11.4.5 Starting the Rails web server . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389<br />
11.4.6 Demonstrating website application . . . . . . . . . . . . . . . . . . . . . . . . 389<br />
11.5 Using the Rails Adapter with <strong>IBM</strong>_DB. . . . . . . . . . . . . . . . . . . . . . . . . . 391<br />
11.5.1 Creating the Rails application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392<br />
11.5.2 Modifying the database configuration file . . . . . . . . . . . . . . . . . . . 392<br />
11.5.3 Creating model, control, and view. . . . . . . . . . . . . . . . . . . . . . . . . 393<br />
11.5.4 Migrating the model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394<br />
11.5.5 Starting the Rails web server . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396<br />
11.5.6 Checking the application from website . . . . . . . . . . . . . . . . . . . . . 396<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . 399<br />
12.1 Basic concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400<br />
12.1.1 <strong>IBM</strong> <strong>Informix</strong> 4GL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400<br />
12.1.2 Service-oriented architecture and Web Services . . . . . . . . . . . . . 400<br />
12.1.3 Web Services development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400<br />
12.1.4 <strong>Informix</strong> 4GL and Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . 401<br />
12.1.5 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401<br />
12.2 Setup and configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402<br />
12.2.1 Prerequisites and supported platforms . . . . . . . . . . . . . . . . . . . . . 402<br />
12.2.2 Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403<br />
12.3 <strong>Informix</strong> 4GL Web Services tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404<br />
12.3.1 The w4glc Web Services compiler . . . . . . . . . . . . . . . . . . . . . . . . 404<br />
12.3.2 The w4gl utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406<br />
12.3.3 Web Services Description Language Parser (wsdl_parser) . . . . . 407<br />
12.3.4 I4GL Web Services process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409<br />
12.4 Developing a web service with I4GL . . . . . . . . . . . . . . . . . . . . . . . . . . . 409<br />
12.4.1 Example I4GL function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410<br />
12.4.2 Host and application details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411<br />
12.4.3 Definition of the web service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412<br />
12.4.4 Generate the configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . 416<br />
12.4.5 Deployment of the web service . . . . . . . . . . . . . . . . . . . . . . . . . . . 420<br />
12.4.6 Packaging of the web service . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421<br />
12.4.7 Starting the Axis2 application server. . . . . . . . . . . . . . . . . . . . . . . 422<br />
12.4.8 Consuming the I4GL web service . . . . . . . . . . . . . . . . . . . . . . . . . 422<br />
12.5 Consuming a web service with I4GL . . . . . . . . . . . . . . . . . . . . . . . . . . . 425<br />
12.5.1 Web service to consume. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425<br />
Contents vii
12.5.2 Compiling the wrapper code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427<br />
12.5.3 Using the web service from an I4GL application. . . . . . . . . . . . . . 428<br />
12.6 Troubleshooting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429<br />
12.6.1 Typical problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429<br />
12.6.2 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434<br />
Chapter 13. Application development considerations. . . . . . . . . . . . . . . 437<br />
13.1 Concurrency and locking. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438<br />
13.1.1 Types of locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438<br />
13.1.2 Lock duration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440<br />
13.1.3 Lock granularity. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440<br />
13.2 Locking issues and performance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445<br />
13.2.1 Deadlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446<br />
13.3 Isolation levels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447<br />
13.4 Configuration options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449<br />
13.4.1 Server identification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449<br />
13.4.2 Storage space identifiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450<br />
13.4.3 Limiters and limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452<br />
13.4.4 Java configuration parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 457<br />
13.5 Working with your database administrator . . . . . . . . . . . . . . . . . . . . . . 458<br />
13.5.1 Parameters for negotiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461<br />
13.5.2 Monitoring isolation levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462<br />
13.5.3 Monitoring locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463<br />
13.5.4 Monitoring user threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464<br />
Appendix A. Parameters in the onconfig file . . . . . . . . . . . . . . . . . . . . . . 467<br />
Appendix B. Accommodating distributed transactions . . . . . . . . . . . . . 469<br />
B.1 Distributed transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470<br />
B.2 TP/XA Transaction Manager XA Interface Library . . . . . . . . . . . . . . . . . 470<br />
B.3 XA_TOOL ESQL/C sample. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471<br />
Related publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479<br />
<strong>IBM</strong> <strong>Redbooks</strong> publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479<br />
Other publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479<br />
Online resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480<br />
How to get <strong>IBM</strong> <strong>Redbooks</strong> publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480<br />
Help from <strong>IBM</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480<br />
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481<br />
viii <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Notices<br />
This information was developed for products and services offered in the U.S.A.<br />
<strong>IBM</strong> may not offer the products, services, or features discussed in this document in other countries. Consult<br />
your local <strong>IBM</strong> representative for information on the products and services currently available in your area.<br />
Any reference to an <strong>IBM</strong> product, program, or service is not intended to state or imply that only that <strong>IBM</strong><br />
product, program, or service may be used. Any functionally equivalent product, program, or service that<br />
does not infringe any <strong>IBM</strong> intellectual property right may be used instead. However, it is the user's<br />
responsibility to evaluate and verify the operation of any non-<strong>IBM</strong> product, program, or service.<br />
<strong>IBM</strong> may have patents or pending patent applications covering subject matter described in this document.<br />
The furnishing of this document does not give you any license to these patents. You can send license<br />
inquiries, in writing, to:<br />
<strong>IBM</strong> Director of Licensing, <strong>IBM</strong> Corporation, North Castle Drive, Armonk, NY 10504-1785 U.S.A.<br />
The following paragraph does not apply to the United Kingdom or any other country where such<br />
provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION<br />
PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR<br />
IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,<br />
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer<br />
of express or implied warranties in certain transactions, therefore, this statement may not apply to you.<br />
This information could include technical inaccuracies or typographical errors. Changes are periodically made<br />
to the information herein; these changes will be incorporated in new editions of the publication. <strong>IBM</strong> may<br />
make improvements and/or changes in the product(s) and/or the program(s) described in this publication at<br />
any time without notice.<br />
Any references in this information to non-<strong>IBM</strong> websites are provided for convenience only and do not in any<br />
manner serve as an endorsement of those websites. The materials at those websites are not part of the<br />
materials for this <strong>IBM</strong> product and use of those websites is at your own risk.<br />
<strong>IBM</strong> may use or distribute any of the information you supply in any way it believes appropriate without<br />
incurring any obligation to you.<br />
Information concerning non-<strong>IBM</strong> products was obtained from the suppliers of those products, their published<br />
announcements or other publicly available sources. <strong>IBM</strong> has not tested those products and cannot confirm<br />
the accuracy of performance, compatibility or any other claims related to non-<strong>IBM</strong> products. Questions on<br />
the capabilities of non-<strong>IBM</strong> products should be addressed to the suppliers of those products.<br />
This information contains examples of data and reports used in daily business operations. To illustrate them<br />
as completely as possible, the examples include the names of individuals, companies, brands, and products.<br />
All of these names are fictitious and any similarity to the names and addresses used by an actual business<br />
enterprise is entirely coincidental.<br />
COPYRIGHT LICENSE:<br />
This information contains sample application programs in source language, which illustrate programming<br />
techniques on various operating platforms. You may copy, modify, and distribute these sample programs in<br />
any form without payment to <strong>IBM</strong>, for the purposes of developing, using, marketing or distributing application<br />
programs conforming to the application programming interface for the operating platform for which the<br />
sample programs are written. These examples have not been thoroughly tested under all conditions. <strong>IBM</strong>,<br />
therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. ix
Trademarks<br />
<strong>IBM</strong>, the <strong>IBM</strong> logo, and ibm.com are trademarks or registered trademarks of International Business<br />
Machines Corporation in the United States, other countries, or both. These and other <strong>IBM</strong> trademarked<br />
terms are marked on their first occurrence in this information with the appropriate symbol (® or ),<br />
indicating US registered or common law trademarks owned by <strong>IBM</strong> at the time this information was<br />
published. Such trademarks may also be registered or common law trademarks in other countries. A current<br />
list of <strong>IBM</strong> trademarks is available on the Web at http://www.ibm.com/legal/copytrade.shtml<br />
The following terms are trademarks of the International Business Machines Corporation in the United States,<br />
other countries, or both:<br />
AIX®<br />
DataBlade®<br />
DB2 Universal Database<br />
DB2®<br />
developerWorks®<br />
x <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Distributed Relational Database<br />
Architecture<br />
DRDA®<br />
<strong>IBM</strong>®<br />
<strong>Informix</strong>®<br />
MQSeries®<br />
The following terms are trademarks of other companies:<br />
Rational®<br />
<strong>Redbooks</strong>®<br />
Redpaper<br />
<strong>Redbooks</strong> (logo) ®<br />
UC2<br />
WebSphere®<br />
Java, and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other<br />
countries, or both.<br />
Microsoft, Windows, and the Windows logo are trademarks of Microsoft Corporation in the United States,<br />
other countries, or both.<br />
UNIX is a registered trademark of The Open Group in the United States and other countries.<br />
Linux is a trademark of Linus Torvalds in the United States, other countries, or both.<br />
Other company, product, or service names may be trademarks or service marks of others.
Preface<br />
<strong>IBM</strong>® <strong>Informix</strong>® is a low-administration, easy-to-use, and embeddable database<br />
that is ideal for application development. It supports a wide range of development<br />
platforms, such as Java, .NET, PHP, and web services, enabling developers to<br />
build database applications in the language of their choice. <strong>Informix</strong> is designed<br />
to handle RDBMS data and XML without modification and can be extended<br />
easily to handle new data sets.<br />
This <strong>IBM</strong> <strong>Redbooks</strong>® publication provides fundamentals of <strong>Informix</strong> application<br />
development. It covers the <strong>Informix</strong> Client installation and configuration for<br />
application development environments. It discusses the skills and techniques for<br />
building <strong>Informix</strong> applications with Java, ESQL/C, OLE DB, .NET, PHP, Ruby on<br />
Rails, DataBlade®, and Hibernate.<br />
The book uses code examples to demonstrate how to develop an <strong>Informix</strong><br />
application with various drivers, APIs, and interfaces. It also provides application<br />
development troubleshooting and considerations for performance.<br />
This book is intended for developers who use <strong>IBM</strong> <strong>Informix</strong> for application<br />
development. Although some of the topics that we discuss are highly technical,<br />
the information in the book might also be helpful for managers or database<br />
administrators who are looking to better understand their <strong>Informix</strong> development<br />
environment.<br />
The team who wrote this book<br />
This book was produced by a team of specialists from around the world working<br />
at the International Technical Support Organization, Rochester Center.<br />
Whei-Jen Chen is a Project Leader at the International Technical Support<br />
Organization, San Jose Center. She has extensive experience in application<br />
development, database design and modeling, and DB2® system administration.<br />
Whei-Jen is an <strong>IBM</strong> Certified Solutions Expert in Database Administration and<br />
Application Development, and an <strong>IBM</strong> Certified IT Specialist<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. xi
Krishna Doddi (also known as Prasad) has been with <strong>IBM</strong><br />
<strong>Informix</strong> since 1996. He worked in <strong>Informix</strong> Advanced Support<br />
for Client Products. The main products he supported were<br />
<strong>Informix</strong> Client Software Development Kit (Client SDK), mostly<br />
on the Microsoft Windows® platform. After the <strong>IBM</strong> acquisition<br />
in 2001, he moved to DB2 Advanced Support, again in the<br />
Client Products division. After five years in DB2, he moved to<br />
<strong>Informix</strong> QA for <strong>Informix</strong> Client products, including JCC<br />
Common Client and PureQuery. He is currently the contact for <strong>Informix</strong><br />
Integration projects.<br />
xii <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Manoj Ghogale is a Tech Lead in the <strong>Informix</strong> team at ISL<br />
India. Manoj has 9 years of industry experience and has been<br />
with <strong>IBM</strong> for 5 years. He has worked on numerous automation<br />
projects and new features of <strong>Informix</strong> Server. He holds a<br />
Bachelor's degree in Engineering from National Institute of<br />
Engineering, Mysore, India.<br />
David Jay is a Staff Software Engineer in the <strong>IBM</strong> North<br />
Americas Technical Support Team, providing advanced<br />
technical support, defect discovery, and support training for<br />
<strong>Informix</strong> products and SolidDB. David joined the <strong>Informix</strong><br />
Support team in September 1996, and has served in various<br />
software and support roles since the 1980’s. He has a<br />
Bachelor of Science from Pennsylvania State University, and<br />
enjoys public speaking assignments whenever he gets them<br />
through Toastmasters and <strong>IBM</strong>.<br />
Javier Sagrera is a software engineer on the Common Client<br />
Technologies (CCT) group. He joined the <strong>Informix</strong> team in 2000<br />
and has over 15 years experience in application development<br />
for <strong>Informix</strong> database servers and <strong>Informix</strong> clients. Currently<br />
based on the <strong>IBM</strong> UK Bedfont Lab in London, he has extensive<br />
knowledge on all the Microsoft® technologies and is<br />
considered as a subject matter expert worldwide on all the<br />
<strong>Informix</strong> development tools.
Acknowledgements<br />
Thanks to the following people for their contributions to this project:<br />
Jacques Roy<br />
Ted Wasserman<br />
Rakeshkumar Naik<br />
Jonathan Leffler<br />
Robert Uleman<br />
Richard Snoke<br />
Guy Bowerman<br />
<strong>IBM</strong> Software Group<br />
Greg Holmes<br />
Alberto Bortolan<br />
Adam Hattrell<br />
<strong>IBM</strong> Bedfont Laboratory<br />
Emma Jacobs<br />
International Technical Support Organization, Rochester Center<br />
Now you can become a published author, too!<br />
Here’s an opportunity to spotlight your skills, grow your career, and become a<br />
published author—all at the same time! Join an ITSO residency project and help<br />
write a book in your area of expertise, while honing your experience using<br />
leading-edge technologies. Your efforts will help to increase product acceptance<br />
and customer satisfaction, as you expand your network of technical contacts and<br />
relationships. Residencies run from two to six weeks, and you can participate<br />
either in person or as a remote resident working from your home base.<br />
Find out more about the residency program, browse the residency index, and<br />
apply online at:<br />
ibm.com/redbooks/residencies.html<br />
Preface xiii
Comments welcome<br />
Your comments are important to us!<br />
We want our books to be as helpful as possible. Send us your comments about<br />
this book or other <strong>IBM</strong> <strong>Redbooks</strong> publications in one of the following ways:<br />
► Use the online Contact us review <strong>Redbooks</strong> form found at:<br />
ibm.com/redbooks<br />
► Send your comments in an email to:<br />
redbooks@us.ibm.com<br />
► Mail your comments to:<br />
<strong>IBM</strong> Corporation, International Technical Support Organization<br />
Dept. HYTD Mail Station P099<br />
2455 South Road<br />
Poughkeepsie, NY 12601-5400<br />
Stay connected to <strong>IBM</strong> <strong>Redbooks</strong><br />
► Find us on Facebook:<br />
http://www.facebook.com/<strong>IBM</strong><strong>Redbooks</strong><br />
► Follow us on Twitter:<br />
http://twitter.com/ibmredbooks<br />
► Look for us on LinkedIn:<br />
http://www.linkedin.com/groups?home=&gid=2130806<br />
► Explore new <strong>Redbooks</strong> publications, residencies, and workshops with the<br />
<strong>IBM</strong> <strong>Redbooks</strong> weekly newsletter:<br />
https://www.redbooks.ibm.com/<strong>Redbooks</strong>.nsf/subscribe?OpenForm<br />
► Stay current on recent <strong>Redbooks</strong> publications with RSS Feeds:<br />
http://www.redbooks.ibm.com/rss.html<br />
xiv <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
1<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong><br />
We live in a business world that requires a variety of software components to fully<br />
handle the scope of business needs. When considered as a group, the products<br />
have to work together, and they must scale well to cover the demands of<br />
business to assure continued growth. Frequently, a significant criteria list might<br />
be needed to specify all the facets that are encountered in daily, monthly, and<br />
yearly periods of business operations. Developers must be sensitive to how the<br />
database engine functions, what server-side capabilities can be brought into<br />
play, and what development tasks need to occur on the client or application side.<br />
In this chapter, we provide an overview of <strong>Informix</strong> products and capabilities. Our<br />
approach is somewhat reference oriented. In addition to the overview of <strong>Informix</strong><br />
products, we also provide an awareness of the architecture of <strong>IBM</strong> <strong>Informix</strong> from<br />
both the server-side and the client-side, to better acquaint the developer with<br />
perspectives that you might need to know.<br />
We also discuss the capabilities of the products in the <strong>Informix</strong> suite. The <strong>IBM</strong><br />
<strong>Informix</strong> products that we describe demonstrate how to manage business<br />
performance, handle all types of business applications, minimize administration<br />
through various self-automated and self-monitored options and, at all times,<br />
optimize the setup to help minimize costs for maintenance and resource<br />
allocations.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 1
1.1 Server options<br />
There are a number of options for <strong>IBM</strong> <strong>Informix</strong> servers that are designed to meet<br />
the needs of business enterprises that range in size from a small group with a<br />
few employees up to several thousand people. The database needs to meet a<br />
variety of requirements of a diverse group. This section includes a brief<br />
discussion of each of the server possibilities and how they are generally used.<br />
With the wealth of different server types available, unless otherwise noted, each<br />
comes with the opportunity to use the <strong>IBM</strong> <strong>Informix</strong> Client Software Development<br />
Kit (Client SDK) for development of applications. Prior to renaming the brand<br />
editions in May 2010, <strong>IBM</strong> <strong>Informix</strong> had several versions. You can expect that<br />
each version is backward and forward compatible, beginning with 7.2x and<br />
moving forward through 7.3x, 9.2x, 9.30, 9.40, and 10.00. The Cheetah series<br />
began with <strong>Informix</strong> Dynamic Server 11.10 and quickly advanced into the 11.50<br />
family. At this writing, the <strong>IBM</strong> <strong>Informix</strong> Ultimate series is now available, which<br />
begins with 11.50.xC7 and following.<br />
It is helpful to be aware of version numbering conventions:<br />
► The letters UC or UD at the end of a version number indicate 32-bit UNIX®.<br />
► The letters TC indicate a 32-bit Windows version.<br />
► The letters FC indicate a 64-bit version (UNIX or Windows).<br />
1.1.1 <strong>Informix</strong> servers<br />
These naming conventions become important in application and memory<br />
addressability. A 32-bit application works with a 64-bit server, but the application<br />
can access only 32-bit memory.<br />
Several options are available for <strong>IBM</strong> <strong>Informix</strong> servers. Each option is designed to<br />
address specific needs in business. This section discusses these different<br />
options.<br />
No-charge editions<br />
The following no-charge editions are available as separate offerings subject to<br />
the <strong>IBM</strong> International License Agreement for Non-Warranted Programs (ILAN):<br />
► <strong>Informix</strong> Developer Edition<br />
► <strong>Informix</strong> Innovator-C Edition<br />
2 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>Informix</strong> Developer Edition<br />
The <strong>Informix</strong> Developer Edition provides all the features of <strong>Informix</strong> at no cost,<br />
when used solely for application development and testing. It can be used for<br />
application development and prototyping with no time limits. Support is available<br />
by way of the <strong>Informix</strong> user community through Internet forums.<br />
<strong>Informix</strong> Developer Edition offers the following application development choices:<br />
► C<br />
► C++<br />
► .NET<br />
► Java<br />
► Ruby on Rails<br />
► Perl<br />
► Python<br />
► PHP<br />
► 4GL<br />
<strong>Informix</strong> Developer Edition supports the following operating systems:<br />
► AIX®<br />
► HP<br />
► UNIX<br />
► Linux®<br />
► Macintosh<br />
► Sun Solaris<br />
► Windows<br />
This <strong>Informix</strong> edition is limited to 1 CPU, 1 GB memory, and 8 GB storage.<br />
<strong>Informix</strong> Developer Edition includes the following bundle:<br />
► <strong>Informix</strong> Client Software Development Kit<br />
► <strong>Informix</strong> DataBlade Developers Kit<br />
► <strong>Informix</strong> Spatial Datablade<br />
<strong>Informix</strong> Innovator-C Edition<br />
The Innovator-C Edition provides a robust and powerful environment that is<br />
designed to support small production workloads. It features the most widely used<br />
data processing functionality, including limited Enterprise Replication and high<br />
availability clustering.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 3
This edition offers the following application development choices:<br />
► C<br />
► C++<br />
► .NET<br />
► Java<br />
► Ruby on Rails<br />
► Perl<br />
► 4GL<br />
Innovator-C Edition supports the following operating systems:<br />
► AIX<br />
► HP<br />
► UNIX<br />
► Linux<br />
► Macintosh<br />
► Sun Solaris<br />
► Windows<br />
This edition is limited to one socket with no more than four cores and with a total<br />
of 2 GB of RAM that are operating from the same installation.<br />
Note: Innovator-C Edition is an offering to be used for development, test, and<br />
user production workloads without a license fee. This edition can be used only<br />
by user organizations. It cannot be redistributed without signing a<br />
redistribution contract. Support is community-based though an optional<br />
for-charge service and support package is available.<br />
Licensed versions<br />
In a business enterprise that demands constant uptime and consistent delivery<br />
requirements, it helps to have a license and professional, timely support to cover<br />
every need. There are several licensing options available.<br />
In this section, we discuss the <strong>Informix</strong> Server versions that are licensed<br />
subscriptions. Table 1-1 describes the terms and abbreviations for these editions.<br />
The criteria might be subject to change over time. Consult with a sales<br />
representative for the most accurate definitions.<br />
4 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Table 1-1 <strong>Informix</strong> licensed versions<br />
Licensing term Defined<br />
Processor Value Unit<br />
(PVU)<br />
(also known as processor-based<br />
pricing)<br />
Authorized User<br />
Single Install<br />
Calculated using the number of processor cores in the physical server<br />
multiplied by the corresponding value units based on processor<br />
architecture. An unlimited user or connection license and is usually<br />
the optimal choice when the user or session load cannot be controlled<br />
or counted.<br />
A single named user or specific individual accessing one installation<br />
of <strong>Informix</strong> on each physical or virtual server. That authorized user can<br />
establish multiple connections to an <strong>Informix</strong> instance on the server.<br />
Each connection is for the exclusive use of that one authorized user<br />
from a single client device.<br />
Concurrent Session A single logical connection from a client device to an <strong>Informix</strong> instance<br />
on each physical or logical server. Each connection, whether active or<br />
not, requires a license, regardless of whether it comes from one client<br />
device with multiple users or from a single user establishing multiple<br />
connections. The number of concurrent sessions is counted from the<br />
client device, rather than at the server level, regardless of whether the<br />
connection is directly to the <strong>Informix</strong> instance or indirectly or whether<br />
it is through application servers, persistent connectivity layers,<br />
connection multiplexers or concentrators, or any other technology<br />
inserted between the actual user and the <strong>Informix</strong> instance.<br />
Limited Use Socket<br />
(LU Socket)<br />
Available only on <strong>Informix</strong> Growth Edition, this license allows for<br />
licensing on a physical socket potentially containing multiple cores. A<br />
LU Socket license is required for each active processor socket. This<br />
licensing metric can be used only on a physical server with no more<br />
than four physical sockets. You can purchase licenses for up to four<br />
physical sockets and use up to 16 cores.<br />
Install An “Install” is an installed copy of an <strong>Informix</strong> product on a physical<br />
server (or partition thereof) or in a virtual machine image. For<br />
example, if a physical server is segmented into partitions, whether<br />
logical (also know as LPARs) or physical, each partition containing<br />
<strong>Informix</strong> is considered a separate <strong>IBM</strong> <strong>Informix</strong> “Install” for licensing<br />
purposes and restrictions. The concept of an “Install” applies to the<br />
licensing limits specified for all <strong>Informix</strong> Editions.<br />
<strong>Informix</strong> Choice Edition<br />
<strong>Informix</strong> Choice Edition is ideal for small to medium size business. This edition is<br />
available for both Microsoft Windows and Apple Macintosh operating systems.<br />
This edition is limited to a total of eight cores over a maximum of two sockets and<br />
8 GB of RAM operating. The <strong>Informix</strong> Choice Edition includes limited Enterprise<br />
Replication (ER) clustering with 2-root nodes to send or receive data updates<br />
within the cluster. This edition also provides limited high availability (HA) cluster<br />
functionality. Along with the primary server, you can have one secondary node,<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 5
either an HDR secondary or RSS secondary. The HA cluster secondary node<br />
can be used for SQL operations. This edition does not support use of the Shared<br />
Disk secondary (SD secondary) node type.<br />
<strong>Informix</strong> Growth Edition<br />
The <strong>Informix</strong> Growth Edition is available on all platforms (Linux, UNIX, Windows,<br />
and Apple Macintosh). The Growth Edition is ideal for mid-sized companies or<br />
departmental servers in an enterprise deployment. It can be deployed on up to<br />
16 cores over a maximum of four sockets and 16 GB of RAM operating from the<br />
same Install. License options include Authorized User Single Install, Concurrent<br />
Session, PVU, and LU Socket metrics. LU Socket enables licensing by physical<br />
processor socket and is limited to physical servers with no more than four<br />
physical processor sockets.<br />
<strong>Informix</strong> Growth Edition gives you additional database functionality, including<br />
unlimited ER cluster nodes of any type for sending or receiving data updates<br />
within the cluster. From a licensing perspective, because ER nodes are<br />
stand-alone, each ER node must be fully licensed. <strong>Informix</strong> Growth edition<br />
supports up to two HA cluster secondary nodes of any type. As long as the<br />
secondary node or nodes are functioning only as a backup secondary, they can<br />
be deployed without charge. However, if you use a secondary node for SQL<br />
operations (read or write), the secondary node must be fully licensed.<br />
<strong>Informix</strong> Ultimate Edition<br />
The <strong>IBM</strong> <strong>Informix</strong> Ultimate Edition includes nearly all the <strong>Informix</strong> features and<br />
functionality with unlimited scalability required for the highest OLTP and<br />
warehousing performance.<br />
<strong>Informix</strong> Ultimate Edition supports the following operating systems:<br />
► AIX<br />
► HP<br />
► UNIX<br />
► Linux<br />
► Macintosh<br />
► Sun Solaris<br />
► Windows<br />
This edition can be licensed by PVU, Concurrent Session, or Authorized User<br />
Single Install metrics.<br />
With this edition, full HA cluster and ER functionality is available, including<br />
unlimited ER nodes and all HA cluster secondary instance types. From a<br />
licensing perspective, because ER nodes are stand-alone, each ER node must<br />
be fully licensed, but HA secondary nodes can be deployed without charge if they<br />
6 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
are functioning only as a backup secondary. If you use any secondary node for<br />
SQL operations (read or write), the secondary node must be fully licensed.<br />
This edition offers the Geodetic and Excalibur DataBlades add-on.<br />
The Storage Optimization Feature was released with <strong>Informix</strong> version11.5 xC4<br />
and following. The Storage Optimization Feature provides data compression to<br />
dramatically reduce data storage and backup/recovery costs and administration.<br />
The reduced data footprint also provides a significant increase in performance for<br />
data retrieval.<br />
Extended Parallel Server<br />
The <strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server is a high-end database that is<br />
typically used for scalable data warehousing with fast data loading and<br />
comprehensive data management. It is designed for a broad range of enterprises<br />
that require complex, query-intensive analytical applications.<br />
<strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server uses the following examples:<br />
► Reliable data manipulation<br />
► Ad hoc queries from data warehouses<br />
► A combined data warehouse and data mart<br />
► Rapid and concurrent data loading and query execution<br />
<strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server provides full parallel query processing,<br />
while being able to use hardware resources to deliver mainframe-caliber<br />
scalability, manageability, and performance with minimal OS and administrative<br />
overhead. For more information, see Database Strategies: Using <strong>Informix</strong> XPS<br />
and DB2 Universal Database, SG24-6437.<br />
<strong>Informix</strong> OnLine<br />
<strong>IBM</strong> <strong>Informix</strong> OnLine is an easy to use, embeddable relational database server<br />
for low-to-medium workloads.<br />
It has less features and functionality than the <strong>Informix</strong> Ultimate Server, but it<br />
scales well while providing online transaction processing support and the<br />
assurance of data integrity. <strong>Informix</strong> OnLine has rich multimedia data<br />
management capabilities, supporting the storage of a wide range of media such<br />
as documents, images, and audio by way of text and byte columns. Also, it is not<br />
designed to handle extended data types or replication.<br />
This server supports a wide variety of application development tools, along with a<br />
large number of other third-party tools, through support for the ODBC and JDBC<br />
industry standards for client connectivity.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 7
There are three edition options for this server:<br />
► <strong>Informix</strong> OnLine Extended Edition 5.20<br />
This edition option is a full-featured, easy-to-use SQL database with low<br />
administrative overhead. It contains two popular <strong>Informix</strong> products, <strong>Informix</strong><br />
OnLine and <strong>Informix</strong> STAR, and provides distributed computing with a proven<br />
database server. It allows the Rapid Application Development tools to use<br />
pipes to communicate with OnLine Extended Edition 5.20 servers running on<br />
the same system, instead of having to use a TCP/IP loopback connection with<br />
<strong>Informix</strong> Star or <strong>Informix</strong> Net. It supports greater than 2 GB chunk offsets, to<br />
use the entire capacity of the disk drive without partitioning it into smaller<br />
logical devices and employing logical volume managers.<br />
The operating systems this edition supports include AIX, HP UNIX, and Sun<br />
Solaris.<br />
► <strong>Informix</strong> OnLine Extended Edition for Linux<br />
This edition is same as the <strong>Informix</strong> OnLine Extended Edition 5.20 but is but<br />
only available for Linux.<br />
► <strong>Informix</strong> OnLine Personal Edition<br />
This is the single-user product version, available only on Linux. It provides the<br />
same functionality as <strong>IBM</strong> <strong>Informix</strong> OnLine Extended Edition, at an<br />
economical cost. It enables you to quickly become familiar with the<br />
ease-of-use and multimedia capability of this proven relational database<br />
management system.<br />
Standard Engine<br />
<strong>IBM</strong> <strong>Informix</strong> Standard Engine is an embeddable database server that runs on<br />
UNIX, Linux and Windows. It provides an ideal solution for developing small to<br />
medium-sized applications that need the power of SQL without database<br />
administration requirements (low-maintenance, high-reliability). For limited scale<br />
databases, it delivers excellent performance, adheres to data consistency<br />
standards, and still provides client/server capabilities. It can be seamlessly<br />
integrated with <strong>Informix</strong> application development tools and third-party<br />
development tools compliant with the ODBC and JDBC standards.<br />
This edition supports the following operating systems:<br />
► AIX<br />
► HP UNIX<br />
► Linux<br />
► Sun Solaris<br />
► Windows<br />
8 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
1.2 <strong>Informix</strong> tools for developers<br />
There is a significant list of tools available from <strong>Informix</strong> for developers. We focus<br />
on the available application programming interface (API) options and language<br />
possibilities. There are also other utilities and tools that are provided with the<br />
engine that promote further ease of use.<br />
The 4GL developer’s edition includes the ACE Report Writer, which can be used<br />
to generate forms and reports quickly. The High Performance Loader, onload,<br />
onunload, and External table are bulk load and unload utilities that provide fast<br />
flat file movements. Other tools include I-Spy for auditing, MaxConnect, and the<br />
OpenAdmin Tool (OAT) for remote server administration and SQL.<br />
For more information about these or other tools, consult with a sales<br />
representative or visit the <strong>Informix</strong> support website at:<br />
http://www-947.ibm.com/support/entry/portal/Overview/Software/Information_Manag<br />
ement/<strong>Informix</strong>_Product_Family<br />
1.2.1 <strong>Informix</strong> Connect<br />
<strong>Informix</strong> Connect is the run time version of Client SDK that comes with the server<br />
engine. You use Client SDK and related tools to develop your application. When<br />
it is ready for use in production, <strong>Informix</strong> Connect is the tool used to deploy the<br />
application. It is supplied with the server engine software and provides the<br />
connectivity and runtime libraries which permit interaction between the engine<br />
and the application. While some <strong>Informix</strong> Connect development can be done<br />
without Client SDK, the great majority should be handled through the Client SDK<br />
API.<br />
1.2.2 <strong>Informix</strong> Client Software Development Kit<br />
Client SDK is a package of several APIs that are optimized for developing<br />
applications for <strong>IBM</strong> <strong>Informix</strong> servers. Client SDK allows developers to write<br />
applications in the language they prefer and to build applications that can access<br />
multiple <strong>IBM</strong> <strong>Informix</strong> databases. In this section, we discuss the API packages<br />
that are included in Client SDK.<br />
Open Database Connectivity<br />
Open Database Connectivity (ODBC) is a specification for a database API. It is<br />
based on the Call Level Interface specifications from X/Open and the<br />
International Standards Organization and International Electromechanical<br />
Commission (ISO/IEC). ODBC supports SQL statements with a library of C<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 9
functions. An application calls these functions to implement ODBC functionality.<br />
ODBC applications can perform the following operations:<br />
► Connect to and disconnect from data sources.<br />
► Retrieve information about data sources.<br />
► Retrieve information about the <strong>IBM</strong> <strong>Informix</strong> ODBC Driver.<br />
► Set and retrieve <strong>IBM</strong> <strong>Informix</strong> ODBC Driver options.<br />
► Prepare and send SQL statements.<br />
► Retrieve SQL results and process the results dynamically.<br />
► Retrieve information about SQL results and process the information<br />
dynamically.<br />
ODBC lets you allocate storage before or after the SQL results are available.This<br />
feature lets you determine the results and the action to take without the<br />
limitations that predefined data structures impose. ODBC does not require a<br />
preprocessor to compile an application program. ODBC supports Secure<br />
Sockets Layer (SSL) connections. For information about using the SSL protocol,<br />
see <strong>IBM</strong> <strong>Informix</strong> Version 11.5 Security Guide, SC23-7754.<br />
<strong>Informix</strong> ODBC supports the following additional features and capabilities:<br />
► Microsoft Transaction Server (MTS) environment. For more information about<br />
MTS, see the MTS sections in <strong>IBM</strong> <strong>Informix</strong> ODBC Driver Programmers<br />
Manual, SC23-9423.<br />
► ODBC can handle extended data types such as:<br />
– Collection (LIST, MULTISET, and SET)<br />
– Distinct<br />
– Opaque (fixed, unnamed)<br />
– Row (named, unnamed)<br />
– Smart large objects (BLOB and CLOB)<br />
– Client functions that support extended data types<br />
► Long identifiers<br />
► Global Language Support (GLS) data types (NCHAR,NVARCHAR).<br />
► Support for Unicode and XA.<br />
► IPv6 internet protocol.<br />
10 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
ODBC with the <strong>IBM</strong> <strong>Informix</strong> ODBC Driver can include the following<br />
components:<br />
► Driver manager<br />
An application can link to a driver manager that links to the driver specified by<br />
the data source. The driver manager also checks parameters and transitions.<br />
You can purchase the ODBC Driver Manager from a third-party vendor for<br />
most UNIX platforms. On Microsoft Windows platforms, the ODBC Driver<br />
Manager is a part of the Operating System.<br />
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver<br />
This driver provides an interface to the <strong>Informix</strong> database server. Applications<br />
can use the driver in the following configurations:<br />
– To link to the ODBC driver manager<br />
– To link to the Driver Manager Replacement and the driver<br />
– To link to the driver directly<br />
► Data sources<br />
The driver provides access to the following data sources:<br />
– Database management systems (DBMS) and database servers<br />
– Databases<br />
– Operating systems and network software required for accessing the<br />
database<br />
JDBC and SQLJ<br />
JDBC is an application programming interface (API) that Java applications use to<br />
access relational databases. <strong>IBM</strong> <strong>Informix</strong> database systems can be supported<br />
by one of two APIs for client applications and applets written in Java:<br />
► <strong>IBM</strong> Data Server Driver<br />
You can use the <strong>IBM</strong> Data Server Driver (also known as the <strong>IBM</strong> common<br />
client driver) with either DB2 or <strong>Informix</strong>. This JDBC driver lets you write Java<br />
applications to access a local <strong>Informix</strong> Server, DB2 data, or any remote<br />
relational data on a server that supports DRDA®. Using this API, you can<br />
access these database systems using JDBC, SQLJ or pureQuery. SQLJ<br />
provides support for embedded static SQL in Java applications.<br />
Note: The <strong>IBM</strong> Data Server Driver is introduced with Client SDK beginning<br />
with Client SDK 3.50.XC7. At this writing, the Client SDK distributed with<br />
<strong>Informix</strong> database server is one (1) version less than the server version. As<br />
an example, Client SDK 3.50.XC6 is the version distributed with Server<br />
edition 11.50.XC7.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 11
The <strong>IBM</strong> Data Server Driver for JDBC and SQLJ is a single driver that<br />
includes JDBC Type 2 and JDBC Type 4 behavior. For connections to <strong>IBM</strong><br />
<strong>Informix</strong> databases, only Type 4 behavior is supported. <strong>IBM</strong> Data Server<br />
Driver for JDBC and SQLJ Type 4 driver behavior is also referred to as <strong>IBM</strong><br />
Data Server Driver for JDBC and SQLJ type 4 connectivity. For more<br />
information about these APIs, see the Java discussions at:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=/com.i<br />
bm.db2.luw.apdv.java.doc/doc/rjvjdb2o.html<br />
► <strong>IBM</strong> <strong>Informix</strong> JDBC Driver<br />
<strong>IBM</strong> <strong>Informix</strong> JDBC Driver is a native-protocol, pure- Java driver (JDBC type<br />
4). Thus, when you use <strong>IBM</strong> <strong>Informix</strong> JDBC Driver in a Java program that<br />
uses the JDBC API to connect to an <strong>IBM</strong> <strong>Informix</strong> database, your session<br />
connects directly to the database or database server, without a middle tier.<br />
When deciding which JDBC interface to use, it is important to understand the<br />
differences between these two APIs. If you need to work with extended data<br />
types and features that are unique to <strong>IBM</strong> <strong>Informix</strong> databases, use <strong>IBM</strong> <strong>Informix</strong><br />
JDBC Driver.<br />
To support DataSource objects, connection pooling, and distributed transactions,<br />
<strong>IBM</strong> <strong>Informix</strong> JDBC Driver provides classes that implement interfaces and<br />
classes described in the JDBC 3.0 API from Sun Microsystems.<br />
<strong>Informix</strong> classes implementing Java interfaces<br />
Table 1-2 lists the Java interfaces and classes and the <strong>Informix</strong> classes that<br />
implement them.<br />
Table 1-2 Java interfaces and classes<br />
JDBC interface class <strong>Informix</strong> class<br />
java.io.Serializable com.informix.jdbcx.IfxCoreDataSource<br />
java.sql.Connection com.informix.jdbc.IfmxConnection<br />
javax.sql.ConnectionEventListener com.informix.jdbcx.IfxConnectionEvent-<br />
Listener<br />
javax.sql.ConnectionPoolDataSource com.informix.jdbcx.IfxConnectionPoolData-<br />
Source<br />
javax.sql.DataSource com.informix.jdbcx.IfxDataSource<br />
javax.sql.PooledConnection com.informix.jdbcx.IfxPooledConnection<br />
javax.sql.XADataSource com.informix.jdbcx.IfxXADataSource<br />
java.sql.ParameterMetaData com.informix.jdbc.IfxParameterMetaData<br />
12 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>IBM</strong> <strong>Informix</strong> JDBC Driver, Version 3.0, and later implements the updateXXX()<br />
methods defined in the ResultSet interface by the JDBC 3.0 specification. These<br />
methods, such as updateClob, are further defined in the J2SDK 1.4.x API (and<br />
later versions) and require that the ResultSet object can be updated. The<br />
updateXXX methods allow rows to be updated using Java variables and objects<br />
and extend to include additional JDBC types. These methods update JDBC<br />
types implemented with locators, not the data that is designated by the locators.<br />
<strong>Informix</strong> classes Java specification<br />
To support the <strong>Informix</strong> implementation of SQL statements and data types, <strong>IBM</strong><br />
<strong>Informix</strong> JDBC Driver provides classes that extend the JDBC 3.0 API. For<br />
information about the Java classes and the <strong>Informix</strong> classes that application<br />
programs can use to extend them, see <strong>IBM</strong> <strong>Informix</strong> JDBC Driver Programmer's<br />
Guide, v3.50, SC23-9421.<br />
Because <strong>IBM</strong> <strong>Informix</strong> has extended functionality, extra data types, and smart<br />
large objects, several <strong>Informix</strong> classes provide support for functionality that is not<br />
present in the JDBC 3.0 specification. Table 1-3 lists these classes.<br />
Table 1-3 <strong>Informix</strong> classes beyond the Java Specification<br />
JDBC interface or class <strong>Informix</strong> class Provides support for<br />
java.lang.object UDTManager Deploying opaque data types in the<br />
database server<br />
java.lang.object UDTMetaData Deploying opaque data types in the<br />
database server<br />
java.lang.object UDRManager Deploying user-defined routines in<br />
the database server<br />
java.lang.object UDRMetaData Deploying user-defined routines in<br />
the database server<br />
In releases prior to JDK Version 1.4, the UDTManager and UDRManager helper<br />
classes included in ifxtools.jar were not accessible from a packaged class. As<br />
of <strong>IBM</strong> <strong>Informix</strong> JDBC Driver 2.21.JC3, all these classes are in the udtudrmgr<br />
package. For backwards compatibility, unpackaged versions of these classes are<br />
also included. To access a packaged class, use the following import statements<br />
in your program:<br />
► Import udtudrmgr.UDTManager;<br />
► Import udtudrmgr.UDRManager;<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 13
OLEDB<br />
Microsoft OLE DB is a specification for a set of data access interfaces that are<br />
designed to enable a variety of data stores to work together seamlessly. OLE DB<br />
includes the following components:<br />
► Data providers<br />
► Data consumers<br />
► Service components<br />
Each data provider makes data available to consumers in a tabular form through<br />
virtual tables. Data consumers use the OLE DB interfaces to access data. You<br />
can use the <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider to enable client applications, such as<br />
ActiveX Data Object (ADO) applications and web pages, to access data on an<br />
<strong>Informix</strong> server.<br />
You can find detailed information about the characteristics of the <strong>IBM</strong> <strong>Informix</strong><br />
OLE DB Provider in <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider Programmer's Guide,<br />
Version 3.50, SC23-9424. The <strong>IBM</strong> OLEDB provider works with any <strong>IBM</strong> <strong>Informix</strong><br />
Server version greater than or equal to 7.3 (7.3, 8.x, 9.x,10.x or 11.x). For<br />
information about OLE DB architecture and programming, go to the Microsoft<br />
website and search for “Introduction to OLE DB” topic:<br />
http://www.microsoft.com<br />
ESQL/C<br />
<strong>Informix</strong> ESQL/C is an API that enables you to embed SQL statements directly<br />
into a C program by means of the <strong>Informix</strong> ESQL/C preprocessor, esql. The<br />
preprocessor converts each SQL statement and <strong>IBM</strong> <strong>Informix</strong>-specific code to<br />
C-language source code and then invokes the C compiler to compile it.<br />
<strong>Informix</strong> ESQL/C includes the following components:<br />
► The <strong>Informix</strong> ESQL/C libraries of C functions, which provide access to the<br />
database server, and all the <strong>Informix</strong> data types<br />
► The <strong>Informix</strong> ESQL/C header files, which define the data structures,<br />
constants, and macros useful to an <strong>Informix</strong> ESQL/C program<br />
► The esql command, which processes the <strong>Informix</strong> ESQL/C source code to<br />
create a C source file that it passes to the C compiler<br />
► The finderr utility on the UNIX system and the <strong>Informix</strong> Error Messages<br />
Windows-based utility that provides information about <strong>IBM</strong> <strong>Informix</strong> specific<br />
error messages<br />
► GLS locale and code set conversion files for locale specific information<br />
14 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
1.2.3 4GL<br />
1.2.4 Ruby on Rails<br />
<strong>IBM</strong> <strong>Informix</strong> 4GL is a fourth-generation application development and production<br />
environment that provides power and flexibility without the need for<br />
third-generation languages such as C. The following package options are<br />
available for all UNIX and Linux operating systems:<br />
► <strong>Informix</strong> 4GL Rapid Development System and <strong>Informix</strong> 4GL Interactive<br />
Debugger provide a pseudo-compiled development environment for<br />
applications.<br />
► <strong>Informix</strong> 4GL C Compiler provides the components needed to develop and<br />
compile a high-performance application for a production environment.<br />
<strong>IBM</strong> <strong>Informix</strong> supports database access for client applications written in the<br />
popular and dynamic open source programming language called Ruby. To<br />
accomplish this, the developer must use a Ruby Driver and a Rails Adapter for<br />
the standard Ruby framework Rails. <strong>IBM</strong> <strong>Informix</strong> offers the following methods for<br />
using Ruby and Ruby on Rails:<br />
► The Ruby driver and Rails Adapter for <strong>IBM</strong> Data Servers is supported on<br />
<strong>Informix</strong>, Version 11.10 or later, using the DRDA protocol. This option requires<br />
the <strong>IBM</strong> Data Server Driver for ODBC and the call level interface (CLI) which<br />
are available as part of the <strong>Informix</strong> Client SDK.<br />
► Ruby/<strong>Informix</strong> and Rails <strong>Informix</strong>_adapter are specific for <strong>Informix</strong> database<br />
server and work with <strong>Informix</strong> database connections. Support all versions of<br />
the <strong>IBM</strong> <strong>Informix</strong> database servers and require the Client SDK libraries for the<br />
communication with the database server.<br />
You can download the both packages from the Rubyforge website at:<br />
http://rubyforge.org/projects/ruby-informix/<br />
1.2.5 <strong>Informix</strong> DataBlade Developers Kit<br />
The <strong>IBM</strong> <strong>Informix</strong> DataBlade Developers Kit (DBDK) is an aid for developing<br />
DataBlade modules. The kit runs on Microsoft Windows and generates much of<br />
the code you need for a DataBlade. DataBlades can be developed without a<br />
DBDK on operating systems other than Windows; however, it is noteworthy that<br />
the procedures for setting up the development environment on a UNIX system<br />
are complex without help from DBDK.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 15
The DataBlade Developers Kit provides several graphical user interfaces for<br />
creating and working with <strong>Informix</strong> DataBlade modules.:<br />
► BladeSmith: A tool for organizing a DataBlade module development project.<br />
You can use BladeSmith to create a project and define the objects, such as<br />
data types and routines, that belong to the DataBlade module. BladeSmith<br />
generates source files, header files, make files, functional test files, SQL<br />
scripts, messages files, and packaging files.<br />
► DBDK Visual C++ Add-In and IfxQuery: Tools for debugging a DataBlade<br />
module using Microsoft Visual C++ on Windows. The add-in automates many<br />
of the debugging tasks and calls the IfxQuery tool to run unit tests for<br />
DataBlade module routines.<br />
► BladePack: A tool for creating a DataBlade module package. BladePack can<br />
create a simple directory tree containing files to be installed or an installation<br />
that includes an interactive user interface.<br />
► BladeManager: A command line tool on UNIX (or Windows) that comes with<br />
the <strong>Informix</strong> Server. It is a utility that is needed for registering and<br />
unregistering DataBlade modules in <strong>Informix</strong> databases.<br />
1.2.6 <strong>Informix</strong> Spatial DataBlade<br />
Many of the <strong>IBM</strong> <strong>Informix</strong> database servers support DataBlades. Of these<br />
DataBlades, a couple of blades are fairly popular because of their usefulness and<br />
flexibility. We introduce one of these blades here to open your interest into an<br />
area that you might not have considered. Location based data is one of several<br />
features and benefits that can be found in the <strong>IBM</strong> <strong>Informix</strong> Spatial DataBlade.<br />
The Spatial DataBlade can transform both traditional and location-based data<br />
into essential information through the following functions:<br />
► Expands <strong>IBM</strong> <strong>Informix</strong> Server to provide SQL-based spatial data types and<br />
functions that can be used directly through standard SQL queries or with<br />
client-side Geographic Information Systems (GIS) software.<br />
► Delivers innovative spatial technology through a convenient no-charge<br />
download.<br />
► Generates vital business intelligence for a competitive edge.<br />
► Maximizes spatial data capabilities to enable critical business decisions.<br />
► Works in an enterprise replication environment which includes spatial data<br />
types.<br />
► Enables organizations to manage complex geospatial information alongside<br />
traditional data, without sacrificing the efficiency of the relational database<br />
model.<br />
16 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
1.2.7 PHP on <strong>Informix</strong><br />
► Includes R-tree indexing.<br />
R-tree is built into the database kernel and works directly with extended data<br />
types to enable proper geospatial data management. Unlike standard indices,<br />
the R-tree does not divide space into a full coverage of non- overlapping,<br />
adjacent cells. Instead, it uses data partitioning, where each object is<br />
automatically represented by a bounding box that is entirely determined by its<br />
own shape. These bounding boxes might overlap and do not need to cover<br />
the entire space. As a result there is no need to know the spatial extent of the<br />
data in advance.<br />
PHP, the powerful and popular server-side scripting language for creating web<br />
content, has become an important platform for <strong>Informix</strong> development. The<br />
<strong>Informix</strong> OpenAdmin Tool (OAT), which provides the ability to administer multiple<br />
<strong>Informix</strong> instances from a single location, is written entirely in the PHP language.<br />
<strong>Informix</strong> supports database access for client applications written in the PHP<br />
programming language by using a PDO (PHP Data Object) extension that<br />
functions as a database extraction layer. The primary PHP driver available for<br />
<strong>Informix</strong> is called PDO_<strong>IBM</strong>, and is supported on <strong>Informix</strong> Version 11.10, and<br />
following. The other available PHP driver is PDO_INFORMIX. It is the older of<br />
these two, and is the driver used for the <strong>Informix</strong> OAT. You can find the drivers for<br />
PHP on the PECL for PHP website at:<br />
http://pecl.php.net/package/PDO_<strong>IBM</strong><br />
1.3 <strong>Informix</strong> overview<br />
This section provides a description of the database architecture from a developer<br />
perspective. There are entire manuals to describe server side functions and<br />
administration. In this section, we limit our overview to things that the developer<br />
should consider. For more information about the details of the <strong>IBM</strong> <strong>Informix</strong><br />
Server engine, consult the <strong>IBM</strong> <strong>Informix</strong> Administrators Guide, which is available<br />
at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
adref.doc/adref.htm<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 17
1.3.1 Architecture overview<br />
An understanding of the <strong>Informix</strong> database server architecture is beneficial for<br />
application development.<br />
Client/server architecture<br />
<strong>IBM</strong> <strong>Informix</strong> servers are based on a multi-threaded client/server architecture.<br />
Regardless of the edition that you use, the server can support a large number of<br />
client connections while users are accessing data. A quick overview provides<br />
insights into what aspects influence development and what aspects might need<br />
DBA attention.<br />
The <strong>IBM</strong> <strong>Informix</strong> Server edition environments consist of several parts that are<br />
usually not directly visible to the developer. These parts include shared memory,<br />
disk storage, and virtual processors (VPs). The virtual processors are divided<br />
into VP classes to manage specific tasks, such as SQL query processing (CPU),<br />
physical and logical log processes (PIO and LIO), network connection processes<br />
(NET), engine administration (ADM), miscellaneous (MSC), and disk I/O (AIO<br />
and KAIO) processes. On UNIX and Apple Macintosh systems, VPs are visible<br />
as virtual processes. On Windows environments, the VPs are seen as<br />
threads.The program execution process is visible as oninit. Virtual processes<br />
communicate by way of shared memory structures known as mutexes (mutually<br />
exclusive). Because shared memory is also used to move database data in<br />
buffers, it helps to know that some memory buffers are reserved for engine<br />
processing and some buffers allocated (and managed dynamically) for data<br />
processing.<br />
Network protocols and other connection types<br />
Users can connect to <strong>IBM</strong> <strong>Informix</strong> Server using a network connection, shared<br />
memory, pipes, DRDA, or multiplexed connections. Shared memory and pipe<br />
connections are only available on the same system as the server instance.<br />
Network connections are the most popular, because they can connect different<br />
systems and will use either a TCP/IP or a socket connection, depending on the<br />
operating system. Regardless of the method that you choose, it is important to<br />
minimize the overhead costs associated with opening and closing connections.<br />
Keep sessions open only as long as it is prudent, and re-use connections when<br />
possible.<br />
Authentication and user connections<br />
By default, authentication is determined for users connecting to a database by<br />
the existence of a user name in the operating system environment. Other<br />
authentication methods include lightweight directory access protocol (LDAP), or<br />
single sign-on (SSO) through Kerberos.<br />
18 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
If you need a secured authentication method, <strong>IBM</strong> <strong>Informix</strong> Server also has two<br />
secured connection methods by way of the encrypted Communication Support<br />
Module (CSM) and single password CSM. You can use one or the other, but not<br />
both at the same time. Neither the encrypted CSM not the single password CSM<br />
method work over multiplexed connections. Enterprise Replication (ER) and high<br />
availability (HA) replication support encryption but not with CSM. Secure Sockets<br />
Layer (SSL) communications are an alternative to <strong>Informix</strong>-specific encryption<br />
CSMs. SSL encrypts data end-to-end and secures TCP/IP and Distributed<br />
Relational Database Architecture (DRDA) connections between two points<br />
over a network. For setup information, see the developerWorks® article Protect<br />
your data with Secure Sockets Layer support in <strong>Informix</strong> Dynamic Server, Part 1:<br />
Setting up SSL support in IDS, which is available at:<br />
http://www.ibm.com/developerworks/data/library/techarticle/dm-0912securesockets1<br />
/index.html<br />
Programming and user process considerations<br />
Any SQL or stored procedure call is parsed and optimized when the engine first<br />
receives the statement. Parsing and optimization methods require a little extra<br />
running time. If you can minimize this time, you can speed up query execution.<br />
The following factors can speed up execution time:<br />
► If you cannot optimize the query syntax in advance, have the DBA set the<br />
OPTCOMPIND parameter in the onconfig file to 2 to use distribution data<br />
(determined by Update statistics usage).<br />
► If you can optimize all of your query syntax expressions in advance, you might<br />
want your DBA to consider using OPTCOMPIND 1 or 2.<br />
► If the same statement is executed multiple times, it is placed in a procedure or<br />
dictionary cache. Cache statements run faster.<br />
► If the statement is a prepared statement, the statement can be shared by<br />
multiple users and does not need reparsing or optimizing.<br />
► When a statement execution thread is ready to run, it is scheduled to run on a<br />
CPU VP. On a system with multiple processors where each CPU VP maps to<br />
a physical processor, multiple statements can run concurrently on multiple<br />
CPU VPs. Take advantage of multiple CPUs whenever possible by means of<br />
concurrent queries, parallel subqueries, and the degree of parallelism.<br />
► When a statement runs on the CPU VP, it is given a brief block of time to run.<br />
At the end of the time unit, it is placed into a VP wait cycle, and the next<br />
statement thread gets a time block on the VP queue. This use of a wait cycle<br />
gives all executing processes an opportunity to make progress, regardless of<br />
the number of processes that are running.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 19
Physical and logical logging<br />
As each of the various CPU processes are running, the database engine keeps<br />
track of a number of operations. When data is retrieved from disk, the data image<br />
is placed in a physical log buffer. Change process information is placed in the<br />
logical log. When image data is affected by SQL processing, the physical image<br />
is modified periodically and written back to disk.<br />
During this process the physical log image and logical log information are also<br />
written into log records. Depending on the commit protocols and isolation levels,<br />
this information is available to the DBA through recovery operations in the event<br />
that the information is needed. The DBA can also use automatic recovery<br />
features that are built in to the engine can if the engine abruptly goes offline due<br />
to a power failure or crash.<br />
Commit protocols<br />
If transactions are made against a database that uses unbuffered logging, the<br />
records in the logical-log buffer are guaranteed to be written to disk during<br />
commit processing. When control returns to the application after the COMMIT<br />
statement (and before the PREPARE statement for distributed transactions), the<br />
logical-log records are on the disk. The database server flushes the records as<br />
soon as any transaction in the buffer is committed (that is, a commit record is<br />
written to the logical-log buffer).<br />
If transactions are made against a database that uses buffered logging, the<br />
records are held (buffered) in the logical-log buffer for as long as possible. They<br />
are not flushed from the logical-log buffer in shared memory to the logical log on<br />
disk until one of the following situations occurs:<br />
► The buffer is full.<br />
► A commit on a database with unbuffered logging flushes the buffer.<br />
► A checkpoint occurs.<br />
► The connection is closed.<br />
If you use buffered logging and a failure occurs, you cannot expect the database<br />
server to recover the transactions that were in the logical-log buffer when the<br />
failure occurred. Thus, you might lose some committed transactions. In return for<br />
this risk of using buffered logging, performance during alterations improves<br />
slightly.<br />
20 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Consider using buffered logging only if the following situations:<br />
► If the database is updated frequently (speed of updating is important).<br />
► If the application that is performing the transaction can continue as it is and if<br />
you decide that the price (in time and effort) of returning the database to a<br />
consistent state by either removing the effects or reapplying the transaction is<br />
too high or you can re-create the updates in the event of failure.<br />
If a transaction failure does occur, you can simply choose to leave the<br />
database in its inconsistent state if the transaction does not significantly affect<br />
database data.<br />
As you consider whether to use a buffered or unbuffered logging method,<br />
remember that no automatic process or utility can perform a rollback of a<br />
committed transaction or can commit part of a transaction that has been rolled<br />
back. Without detailed knowledge of the application, messages are not enough<br />
to determine what has happened. Based on your knowledge of your application<br />
and your system, you might need to help the DBA determine when to roll back or<br />
to follow though on interrupted transactions. For more information about this<br />
topic, see the Guide to SQL: Tutorial section on interrupted modifications, which<br />
is available at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
sqlt.doc/ids_sqt_277.htm<br />
1.3.2 <strong>Informix</strong> developer environment<br />
The environment references that you need have some variations, depending on<br />
whether you use the <strong>IBM</strong> common client API or the native Client SDK and which<br />
operating system you use. The environment settings must include identifying the<br />
following references to the components:<br />
► Setting the INFORMIXDIR environment variable to the installation directory of<br />
the software installation location.<br />
► Setting up <strong>Informix</strong> Server information (INFORMIXSERVER).<br />
► Adding the $INFORMIXDIR/bin to the PATH environment variable.<br />
On Windows, you have to set only the <strong>Informix</strong> database server information with<br />
the setnet32.exe utility. On UNIX-type platforms, you might want to set the<br />
environment variables so that they are set in your user profile at login or use a<br />
setup script. You also need to reference the sqlhosts file to identify <strong>Informix</strong><br />
Server information. In the chapters that follow, we explain how to install the<br />
server and provide specific details for the relevant API environment details.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 21
Before you select an API or library, be aware that some operating systems are<br />
not available for some of the tools and that server functionality might not be<br />
accessible with some of the APIs. Consider the following information when<br />
making an API selection:<br />
► Client SDK<br />
– Optimized for <strong>IBM</strong> <strong>Informix</strong> database servers.<br />
– Works with PHP.<br />
– Directly interfaces with <strong>Informix</strong> Server; no middle tier required.<br />
– Supports all the <strong>Informix</strong> data types, extended data types, and smart<br />
BLOBs.<br />
► <strong>IBM</strong> Data Server Client<br />
– Optimized for compatibility and easy development with <strong>Informix</strong> and DB2<br />
<strong>IBM</strong> database servers.<br />
– Works with PHP and Ruby.<br />
– No available interface for Apple Mac systems.<br />
– Includes support for C and Fortran development.<br />
– No DataBlade API.<br />
1.3.3 <strong>Informix</strong> capabilities<br />
<strong>IBM</strong> <strong>Informix</strong> database servers provide a broad spectrum of features that allow<br />
resilience in response to fast changing systems and applications in a modern<br />
business environment. If you are aware of what the product can already do<br />
before you begin to develop, it aids and enables you to support applications and<br />
easily grow to meet new demands.<br />
This section provides a brief overview of the capabilities of <strong>Informix</strong>.<br />
Resilient<br />
High availability and fast recovery are standard aspects of an on-demand<br />
environment. <strong>Informix</strong> database server engines have a variety of ways to<br />
configure the instance, so there is little chance of the engine being unavailable at<br />
critical points in time.<br />
Reliable<br />
In addition to availability, there is a need for continuity. <strong>Informix</strong> has options to<br />
protect a server environment, such as backup servers, full independent copies of<br />
the processing environment for failover, and workload balancing at alternate<br />
locations around the world.<br />
22 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Available<br />
There is an <strong>Informix</strong> solution to fit any situation that requires availability:<br />
► Continuous availability feature<br />
► High availability replication with allowance for multiple secondary servers<br />
Secure<br />
<strong>Informix</strong> supports open, industry-standard security mechanisms such as roles,<br />
password-based authentication, and RDBMS schema authorizations. These<br />
open standards ensure flexibility and security with easy validation and<br />
verification. Column-level encryption and Pluggable Authentication Modules<br />
(PAM) are also available. The Advanced Access Control Feature offers cell-,<br />
column-, and row-level label-based access control (LBAC). Thus, access to data<br />
can be controlled down to the individual cell level.<br />
Adaptable<br />
<strong>Informix</strong> is adaptable from the server side and from the client side. The engine<br />
component can be stripped down, embedded, and run with little user<br />
intervention. Using the developer tools that we discuss in this book, a developer<br />
can provide customized deployment by way of client applications and server-side<br />
processes, such as stored procedures, multiple triggers on tables and views,<br />
user-defined functions, and DataBlades<br />
Fast<br />
<strong>Informix</strong> is known for fast OLTP performance. Application performance is helped<br />
by capabilities such as committed isolation level and non-blocking checkpoints,<br />
which provide maximum concurrency. Direct I/O calls to file systems can result in<br />
performance similar to raw device I/O. SQL performance can be improved<br />
through techniques or configuration options that redirect or focus the engine’s<br />
optimizer decisions by way of optimizer directives in the SQL or by way of<br />
automated update statistics collection that inform the method used for running<br />
the query. When the DBA and the developer both focus on performance, it<br />
enables things to run more smoothly and reduces infrastructure costs.<br />
Flexible<br />
There are a number of APIs that are available, both as specific programming<br />
language supplements and as interfaces that extend the architecture of the<br />
server instance. In <strong>Informix</strong> 11 and following, the Web Feature Service API<br />
allows developers to use location-based services or location-enabled IT services.<br />
It is implemented in the Open GeoSpatial Consortium Web Feature Service<br />
(OGC WFS) API. This API also interacts with location-based data provided by<br />
the <strong>IBM</strong> <strong>Informix</strong> Spatial and Geodetic DataBlade modules. There is a significant<br />
amount of extensibility when using DataBlade technology.<br />
Chapter 1. Introduction to <strong>IBM</strong> <strong>Informix</strong> 23
Hidden, behind-the- scenes tasking<br />
Business systems and applications need to work with minimum invasions into the<br />
operations of a business environment. From the server side, applications can run<br />
in an automated mode and perform self-maintenance. From an application<br />
perspective, database administrative tasks can be controlled and run from inside<br />
an application. The SQL Administration API allows you to do tasks such as space<br />
management, monitoring and manipulating memory, running scheduled tasks,<br />
and monitoring user sessions without developer intervention after they are setup<br />
and running.<br />
Customizable install footprint<br />
The installation of <strong>Informix</strong> can be automated, and the installation footprint can<br />
be customized using the Deployment Wizard so that you can limit the installation<br />
to only the data server functionality that you need and can reduce the size and<br />
costs of a solution for your software deployment.<br />
Affordable, reduced complexity<br />
When <strong>IBM</strong> or the developer adds features, the first impression is that the<br />
application will be more complex. With the features described up to this point, it is<br />
a surprise to discover that administration can actually be reduced by means of an<br />
application or one of the APIs and GUIs that we explain in this book.<br />
24 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 2. Setting up an <strong>Informix</strong><br />
development environment<br />
2<br />
This chapter describes how to set up an environment for <strong>Informix</strong> application<br />
development, including the installation and configuration of <strong>Informix</strong> Server and<br />
<strong>Informix</strong> Client. We focus our discussion on <strong>Informix</strong> Client products selection,<br />
installation, and configuration. Reading this chapter can help you understand<br />
how to complete the following tasks:<br />
► Decide which <strong>Informix</strong> Client product is suitable for your application.<br />
► Install and configure all <strong>Informix</strong> Client products that can connect to <strong>Informix</strong>.<br />
► Know any special consideration for each <strong>Informix</strong> Client in terms of<br />
connectivity.<br />
Note: In this chapter, we use the terms <strong>Informix</strong> database server, <strong>Informix</strong><br />
Server, and <strong>Informix</strong> interchangeably. In addition, we also use the terms<br />
Client products, Client, and <strong>Informix</strong> Client interchangeably.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 25
2.1 Server setup<br />
In this section, we describe the various methods of installing <strong>Informix</strong> Server. We<br />
discuss how you can plan for the installation depending upon your specific<br />
requirement. We focus the discussion on the configuration that is required at the<br />
database server side to enable <strong>Informix</strong> Client connectivity.<br />
2.1.1 Planning for the installation<br />
In general, an application developer is not involved in the <strong>Informix</strong> Server product<br />
planning and installation. However, you need to communicate the application<br />
requirements to the DBA, who plans, installs, and configures the <strong>Informix</strong><br />
database server for you. For example, the DBA needs to know if you need a<br />
smart blob space or a reduced footprint of the server or DataBlades. The<br />
<strong>Informix</strong> installer provides options to install only a small footprint of the server<br />
where you can add or remove the features as needed. For more information<br />
about DataBlades, see 10.3, “DataBlades and bladelets” on page 352.<br />
2.1.2 Installing <strong>Informix</strong> Server<br />
<strong>IBM</strong> provides the <strong>Informix</strong> Server editions, described in 1.1, “Server options” on<br />
page 2, in .zip, .tar, or .dmg format, depending on the platform on which<br />
<strong>Informix</strong> Server is installed. You can install <strong>Informix</strong> Server using several<br />
methods. The common installation methods include using the console, a GUI, or<br />
silent mode.<br />
Because we focus on <strong>Informix</strong> Client products for application development in this<br />
book, we describe only the silent mode of <strong>Informix</strong> Server installation on a UNIX<br />
platform. For other methods and platforms, refer to the individual installation PDF<br />
file that comes with the product.<br />
To perform a silent installation on UNIX, use the following steps:<br />
1. Extract the compressed .tar file into any directory using the following<br />
command:<br />
tar -xvpf IIF.11.50.tar<br />
26 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
2. Create the installation .ini file that specifies the installation options. You can<br />
use the sample installation .ini file, server.ini, as a template. The<br />
server.ini file is located in the directory that is used to extract the server<br />
media package. Edit the following information in the server.ini file, and save<br />
the file with a new name, for example myserver.ini:<br />
– Change -P installLocation="/opt/<strong>IBM</strong>/informix" to the location of<br />
directory where you want to install the product.<br />
– Change -G licenseAccepted= option to true to accept the software<br />
license. Otherwise, the installation process stops:<br />
3. Start the silent installation with the following command:<br />
./ids_install -silent -options myserver.ini<br />
Performing these steps installs <strong>Informix</strong> Server in your specified directory along<br />
with a demonstration instance by the name demo_on.<br />
For more information about <strong>Informix</strong> installation and creating new instances, refer<br />
to the installation guide PDF that comes with the media file.<br />
2.1.3 Configuring <strong>Informix</strong> Server<br />
The connectivity information allows a client application to connect to any <strong>IBM</strong><br />
<strong>Informix</strong> database server in the network. The connectivity data for a particular<br />
database server includes the following connectivity data and information is<br />
required:<br />
► The host name of the computer or node on which the database server runs<br />
► The type of connection that an application can use to connect to the server<br />
► The service name or port number used by the database server<br />
► The database instance (also called the database server name)<br />
Even if the client application and the database server are on the same computer<br />
or node, you might need to specify connectivity information for your client<br />
application.<br />
Connectivity on UNIX<br />
On UNIX, the sqlhosts file contains the connectivity information. By default, this<br />
file resides in the $INFORMIXDIR/etc directory. You can use the<br />
INFORMIXSQLHOSTS environment variable to specify an alternative location for<br />
the connectivity information. Edit this file according to the protocol that you are<br />
using or the port number to which your client application will connect.<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 27
A typical sqlhosts file contains the following fields:<br />
Server Protocol Hostname Service/Port Options<br />
Here is an example:<br />
demo_on onipcshm on_hostname on_servername k=0<br />
demo_se seipcpip se_hostname sqlexec<br />
Each line in the sqlhosts file defines the following information about an <strong>Informix</strong><br />
database server:<br />
► Server<br />
Specifies the name of the <strong>IBM</strong> <strong>Informix</strong> database server. This value normally<br />
correspond to the value in the INFORMIXSERVER environment variable.<br />
► Protocol<br />
Specifies the protocol that is used for the communication with the database<br />
server. It must be the same protocol that the database server uses.<br />
► Hostname<br />
Specifies the system where the <strong>Informix</strong> database server is running.<br />
► Service/Port<br />
Specifies the TCP port or service name, which must be defined in the<br />
/etc/services file, that is used by the <strong>Informix</strong> database server to accept<br />
incoming connections.<br />
► Options<br />
Specifies additional options for the communication, such as buffer size or<br />
connection redirection rules.<br />
APIs such as ESQL/C or ODBC rely on the sqlhosts file to obtain the<br />
connection parameters for the database server.<br />
Example 2-1 shows how a ESQL/C application connects to an <strong>Informix</strong> database<br />
server.<br />
Example 2-1 UNIX connection example<br />
informix@kodiak:/work$ cat $INFORMIXDIR/etc/sqlhosts<br />
#server protocol hostname service/port<br />
demo_on onsoctcp kodiak demo_on_tcp<br />
informix@kodiak:/work$ grep demo_on_tcp /etc/services<br />
demo_on_tcp 9089/tcp # Service for demo_on IDS server<br />
informix@kodiak:/work$ cat connect.ec<br />
#include <br />
int main()<br />
28 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
{<br />
EXEC SQL CONNECT TO 'stores_demo@demo_on';<br />
printf("Connected\n");<br />
}<br />
informix@kodiak:/work$ esql connect.ec -o connect<br />
informix@kodiak:/work$ ./connect<br />
Connected<br />
informix@kodiak:/work$<br />
The definition for the demo_on <strong>Informix</strong> Server specifies the communication<br />
protocol that is used as onsoctcp. The system where the database server is<br />
running is kodiak, and the TCP port that the server uses is demo_on_tcp. The<br />
service name is defined as port 9089 in the /etc/services configuration file.<br />
The ESQL/C example connects to the server and opens the stores_demo<br />
database using the following instruction:<br />
EXEC SQL CONNECT TO 'stores_demo@demo_on';<br />
For more information about the sqlhosts file, refer to the <strong>IBM</strong> <strong>Informix</strong> Dynamic<br />
Server (IDS) Information Center topic:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
admin.doc/ids_admin_0158.htm<br />
Connectivity on a Windows system<br />
You can set the connectivity on a Windows system using the setnet32.exe utility<br />
that resides in the $INFORMIXDIR/bin directory. This utility is bundled with <strong>IBM</strong><br />
<strong>Informix</strong> Client Software Development Kit (Client SDK). Use this utility to create<br />
or change the server name, protocol, service name, and host name. You also can<br />
use the setnet32.exe utility to set any environment variable that <strong>Informix</strong><br />
products use.<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 29
Figure 2-1 shows the Server Information tab of the setnet32.exe utility on a<br />
Windows system. Here, you can specify the server name, host IP address,<br />
network protocol, and service name.<br />
Figure 2-1 The Setnet32 Server Information tab<br />
Note: If you specify the name of the service (for example sqlexec) rather than<br />
the port number, make sure the service name that you use is defined in the<br />
Windows system services file that is located in the<br />
C:\WINDOWS\system32\drivers\etc directory.<br />
Enabling DRDA support<br />
Since version 11.x, <strong>Informix</strong> supports the DRDA protocol. If you plan to use the<br />
Data Server driver packages to connect to an <strong>Informix</strong> database server, you must<br />
enable DRDA support as described in this section.<br />
30 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Windows systems<br />
Use the setnet32 utility to configure an <strong>Informix</strong> instance to use DRDA protocol:<br />
1. Start the setnet32 utility from any command window.<br />
2. Go to Server Information tab (Figure 2-2).<br />
a. Select the required instance from the <strong>IBM</strong> <strong>Informix</strong> Server field. Our<br />
example is demo_my.<br />
b. Change the name of the <strong>Informix</strong> server, and specify a new name for the<br />
DRDA alias, for example demo_my_drda.<br />
c. The HostName field is pre-filled. If it is not, enter your host name or IP<br />
address.<br />
d. Change the Protocolname field from olsoctcp to drsoctcp to make it<br />
DRDA compliant.<br />
e. Change the Service Name field to specify the service name or port<br />
number that the DRDA alias will use.<br />
Figure 2-2 Configuring DRDA support<br />
3. Click Apply, and then click OK.<br />
After adding all the connection details for the DRDA alias, the configuration file<br />
for the <strong>Informix</strong> database server must be updated to link the new server definition<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 31
with the existing <strong>Informix</strong> instance. In our example, we use demo_my for the<br />
<strong>Informix</strong> database server and demo_my_drda for the DRDA instance.<br />
You can use the DBSERVERALIASES parameter in the onconfig file to specify<br />
an alias for a database server, as shown in Example 2-2.<br />
Example 2-2 Using the DBSERVERALIASES parameter<br />
...<br />
DBSERVERNAME demo_my # Name of default Dynamic Server<br />
# DBSERVERNAME or DBSERVERALIASES that uses a<br />
# DBSERVERALIASES - The list of up to 32 alternative UDRs,<br />
DBSERVERALIASES demo_my_drda # List of alternate dbserver names<br />
# DBSERVERNAME or DBSERVERALIASES that uses ...<br />
...<br />
The database configuration file is located in etc directory of your <strong>Informix</strong><br />
database server. You can use the ONCONFIG environment variable to specify<br />
the name of this file. The complete path using environment variables for the<br />
database configuration file is:<br />
► On a UNIX platform, $INFORMIXDIR\etc\$ONCONFIG<br />
► On a Windows platform, %INFORMIXDIR%\etc\%ONCONFIG%<br />
You must restart the <strong>Informix</strong> database engine for the changes to take effect.<br />
After you complete these steps, the instance is now ready to accept connections<br />
as a DRDA compliant server.<br />
32 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
On a Windows system, you can enable the DRDA support for the demonstration<br />
instance during the installation if you use the Custom installation option.<br />
Figure 2-3 shows the installation panel where you enable the DRDA support.<br />
Figure 2-3 Enable DRDA support for the default instance<br />
Linux and UNIX systems<br />
On Linux and UNIX systems, you can enable the DRDA support by editing the<br />
following <strong>Informix</strong> configuration files:<br />
► Edit the sqlhosts file specified in the $INFORMIXSQLHOSTS environment<br />
variable, and add the following line:<br />
drsoctcp <br />
where:<br />
– is the name for the DRDA alias<br />
– drsoctcp is the protocol used by the new alias<br />
– the name or IP address of the system that is running <strong>Informix</strong><br />
Server<br />
– is the name of the TCP service or port number for<br />
the DRDA instance<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 33
2.2 Client setup<br />
► Edit the <strong>Informix</strong> Server configuration file located in the etc directory of your<br />
<strong>Informix</strong> Server directory to include the new database definition as an alias for<br />
the existing <strong>Informix</strong> Server.<br />
You can use the environment variables to access this file, for example<br />
$INFORMIXDIR/etc/$ONCONFIG. This file is the file that contains a list of<br />
configuration parameters for <strong>Informix</strong> Server. Example 2-3 shows the<br />
configuration file that we used in our database server.<br />
Example 2-3 Linux onconfig file sample<br />
informix@irk:/usr3/11.50$ grep DBSERV $INFORMIXDIR/etc/$ONCONFIG<br />
# DBSERVERNAME - The name of the default database server<br />
# DBSERVERALIASES - The list of up to 32 alternative UDRs,<br />
# DBSERVERNAME or DBSERVERALIASES that uses a<br />
DBSERVERNAME demo_my<br />
DBSERVERALIASES demo_my_drda<br />
You must restart <strong>Informix</strong> Server for these changes to take effect.<br />
You can find additional information about the sqlhosts file at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.admin.doc/i<br />
ds_admin_0158.htm<br />
<strong>Informix</strong> is a low-administration, easy-to-use, and embeddable database that<br />
supports various application development languages and technologies, such as<br />
Java, .NET, PHP, and Web Services. <strong>Informix</strong> can handle XML data easily and<br />
can be extended to handle new data sets using DataBlades.<br />
In this section, we describe how to install and configure <strong>Informix</strong> Client products<br />
to support the application development that you need.<br />
2.2.1 <strong>Informix</strong> Client options<br />
<strong>IBM</strong> offers the following <strong>Informix</strong> Client products that you can use to develop and<br />
use an <strong>IBM</strong> <strong>Informix</strong> database server:<br />
► <strong>IBM</strong> <strong>Informix</strong> Client Software Development Kit (Client SDK) includes several<br />
APIs that are designed for developing with an <strong>Informix</strong> database.<br />
► <strong>IBM</strong> <strong>Informix</strong> Connect is a runtime version of Client SDK.<br />
34 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
► <strong>IBM</strong> <strong>Informix</strong> JDBC Driver is Java driver that is optimized for <strong>Informix</strong><br />
databases.<br />
► <strong>IBM</strong> Data Server Client includes the API tools that are needed for<br />
development with <strong>IBM</strong> databases such as DB2 and <strong>Informix</strong>, plus functionality<br />
for database administration and client/server configuration.<br />
► <strong>IBM</strong> Data Server Runtime Client is the runtime version of the <strong>IBM</strong> Data Server<br />
Client.<br />
► <strong>IBM</strong> Data Server Driver Package is a lightweight version of the <strong>IBM</strong> Data<br />
Server Runtime that includes only the interfaces and drivers.<br />
► <strong>IBM</strong> Data Server Driver for JDBC and SQLJ is a Java driver for <strong>IBM</strong> Data<br />
Servers.<br />
Client SDK and <strong>Informix</strong> Connect have distinct functions:<br />
► Client SDK contains a group of application-programming interfaces (APIs)<br />
that developers can use to write applications for <strong>Informix</strong>, plus other client<br />
components. Client SDK must be installed on computers that application<br />
programmers will use to write applications.<br />
► <strong>Informix</strong> Connect contains runtime libraries of the Client SDK APIs, plus other<br />
client components. Install <strong>Informix</strong> Connect on computers that users use to<br />
connect to <strong>Informix</strong> database servers.<br />
The same rule applies to <strong>IBM</strong> Data Server products. Do not use packages that<br />
are designed for development, such as <strong>IBM</strong> Data Server Client, to deploy an<br />
application. Some components, such as <strong>Informix</strong> JDBC Driver, do not have a<br />
specific runtime version, so you can use the same product in both cases.<br />
In addition to these products, you can use open source drivers, such as PHP or<br />
Ruby, to develop with an <strong>IBM</strong> <strong>Informix</strong> database.<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 35
Figure 2-4 illustrates the options that are available to develop with an <strong>IBM</strong><br />
<strong>Informix</strong> database.<br />
Figure 2-4 Available <strong>Informix</strong> Client products for application development<br />
2.2.2 Installing and setting up Client SDK<br />
<strong>IBM</strong> <strong>Informix</strong> Client Software Development Kit (Client SDK) is a collection of<br />
components for developing and running client applications that connect to the<br />
<strong>Informix</strong> database server. Client SDK bundles with the following components and<br />
utilities:<br />
► ESQL/C with XA support<br />
► <strong>IBM</strong> <strong>Informix</strong> Object Interface for C++<br />
► <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider (Windows only)<br />
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver with MTS support<br />
► <strong>IBM</strong> <strong>Informix</strong> .NET Provider (Windows only)<br />
► <strong>IBM</strong> <strong>Informix</strong> GLS<br />
► The Global Security Kit (GSKit)<br />
► Password Communication Support Modules (CSM)<br />
► Documentation Viewer<br />
36 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
► The finderr utility on UNIX systems and the <strong>Informix</strong> Error Messages utility<br />
on Windows systems<br />
► The iLogin utility (Windows only)<br />
Starting from version 3.50.xC5, the <strong>IBM</strong> Data Server Driver Package is also<br />
bundled with the Windows system version of Client SDK and <strong>Informix</strong> Connect.<br />
Client SDK provides access to all the <strong>Informix</strong> database servers. Table 2-1 lists<br />
the <strong>Informix</strong> database servers that supports Client SDK.<br />
Table 2-1 Supported database servers<br />
Database server Versions<br />
<strong>IBM</strong> <strong>Informix</strong> 7.x, 9.x,10.x and 11.50<br />
<strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server 8.50 and higher<br />
<strong>IBM</strong> <strong>Informix</strong> Standard Engine 7.25<br />
<strong>IBM</strong> <strong>Informix</strong> Online 5.20 and higher<br />
Some components, such as <strong>Informix</strong> OLE DB Provider, do not allow a connection<br />
to an <strong>IBM</strong> <strong>Informix</strong> Standard Engine server. Refer to the component release<br />
notes for more information:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.relnotes.do<br />
c/relnotes_ClientSDK350xc7.html<br />
Completing the preliminary tasks<br />
Before you install Client SDK, perform the following preliminary tasks:<br />
1. Determine the following locations:<br />
– Media location: This directory is the directory where all the media files<br />
reside.<br />
– Installation location: This directory is the location where the installer<br />
places all the binaries and the configuration files. This directory is<br />
represented by the environment variable $INFORMIXDIR. If<br />
$INFORMIXDIR is set, the defined location is the default installation<br />
location.<br />
– Java location: The script that is used during the installation process<br />
requires the use of a Java virtual machine (JVM). A JVM is bundled<br />
together with Client SDK. However, if required, it is possible to specify a<br />
different JVM for the installation process. The minimum version is Sun<br />
JRE 1.4.2.<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 37
2. Prepare the environment.<br />
You need to check if you have the informix user and the informix group on<br />
your computer. If you do not, create them. For additional information, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.igul.do<br />
c/ids_in_005x.htm<br />
Installing the Client SDK<br />
<strong>Informix</strong> provides various methods for installing Client SDK. In this section, we<br />
demonstrate using the GUI to install in both UNIX and Windows environments.<br />
Installing Client SDK on UNIX using the GUI<br />
To install Client SDK in GUI mode:<br />
1. Start the installation GUI with the following command from the root user:<br />
./installclientsdk -gui<br />
Remember to set the DISPLAY environment variable to the window that you<br />
want to project the installation. Figure 2-5 shows the Welcome panel.<br />
Click Next.<br />
38 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Figure 2-5 Installation of Client SDK on UNIX Welcome panel<br />
2. On the license agreement panel, select I accept the terms and the license<br />
agreement and click Next.
3. Specify the directory where you want to install <strong>Informix</strong> Client (Figure 2-6).<br />
Enter the full path name. Click Next.<br />
Figure 2-6 Specify the installation directory<br />
4. Select Typical or Custom (Figure 2-7). A Custom installation allows you to<br />
select the features to install. Click Next.<br />
Figure 2-7 Select the type of installation<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 39
5. If you selected a Custom installation, you next need to select the features that<br />
you want to install and click Next (Figure 2-8). If you want to install whatever<br />
the installer offers, select a Typical installation.<br />
Figure 2-8 Custom options to choose<br />
40 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
6. The installer presents the summary of available space and the components<br />
that you have chosen for installation, as shown in Figure 2-9. If needed, you<br />
can go back and change the installation selections. Click Next to start the<br />
installation.<br />
Figure 2-9 Installation summary<br />
7. Figure 2-10 shows that the installation is complete without any errors.<br />
Figure 2-10 Installation complete without any errors<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 41
At this stage, the Client SDK installation is complete. If the <strong>Informix</strong> database<br />
server is located on a different system, you might need to set up the connection<br />
information and modify the sqlhosts file, using the details of your <strong>Informix</strong><br />
Server.<br />
Installing Client SDK on a Windows system<br />
Installation on a Windows system for the Client SDK is similar to UNIX the<br />
installation except that the Windows system installation has the option to install<br />
<strong>IBM</strong> Data Server Driver Package. If you select the option to install these drivers,<br />
refer to “Installing the <strong>IBM</strong> Data Server Driver package” on page 44 for<br />
Installation details.<br />
Configuration utility<br />
On Windows systems, the Client SDK comes bundled with the setnet32.exe<br />
utility for configuring the Client SDK. The Client SDK has default values set and<br />
is ready to use unless you want to change the protocol, server information, or any<br />
environment variable under which the client application runs.<br />
The setnet32.exe utility sets environment variables and network parameters that<br />
<strong>IBM</strong> <strong>Informix</strong> products use at run time. The environment variables and<br />
connectivity parameters are stored in the Windows system registry and are valid<br />
for every <strong>Informix</strong> Client product that you install, except <strong>Informix</strong> JDBC Driver.<br />
The setnet32.exe utility has the following tabs:<br />
► The Environment tab allows you to set environment variables.<br />
► The Server Information tab allows you to set database server network<br />
information.<br />
► The Host Information tab allows you to set your host computer and login<br />
information.<br />
► The About Setnet32 tab provides information about the setnet32.exe utility.<br />
For more information about these tabs, refer to the Client SDK installation PDF<br />
file that comes with Client SDK.<br />
Special consideration for 32- and 64-bit products<br />
Beginning with version 3.50.FC4, you can install both 32-bit and 64-bit Client<br />
SDK on the same Windows system.<br />
Before installing the version 3.50.FC4 product, you must uninstall the existing<br />
version 3.50.FC3 or earlier Client SDK from your system.<br />
42 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
To uninstall your existing products, use one of the following methods:<br />
► Use the Add/Remove program.<br />
► Run the setup.exe program from the installed product media. When the<br />
Maintenance Menu displays, select Remove.<br />
You must install the new 32-bit and 64-bit products in separate directories.<br />
The 32-bit Client SDK installs in the following directory by default:<br />
C:\Program Files (x86)\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK<br />
The 64-bit Client SDK installs in the following directory by default:<br />
C:\Program Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK<br />
The 64-bit product installation adds a suffix to the Program group folder and the<br />
Add/Remove entry. This suffix provides an easy identification between the 32-bit<br />
(no suffix) and 64-bit (suffix) product installations. For more information, refer to<br />
the release notes.<br />
2.2.3 Setting up <strong>IBM</strong> Data Server drivers<br />
Several types of <strong>IBM</strong> Data Server Clients and Drivers are available.<br />
The following <strong>IBM</strong> Data Server Clients are available:<br />
► <strong>IBM</strong> Data Server Client, contains all the APIs and tools that are needed for<br />
development against DB2 and <strong>Informix</strong> databases, including tools for remote<br />
administration.<br />
► <strong>IBM</strong> Data Server Runtime Client, contains all the APIs but only the minimum<br />
number of utilities for setup and configure the development environment.<br />
► <strong>IBM</strong> Data Server Driver Package, contains only the APIs and drivers without<br />
any additional tools.<br />
The client products contain the APIs and drivers plus additional tools that are<br />
designed for remote database administration and configuration of the client<br />
environment.<br />
The following <strong>IBM</strong> Data Server Drivers are available:<br />
► <strong>IBM</strong> Data Server Driver for JDBC and SQLJ<br />
► <strong>IBM</strong> Data Server Driver for ODBC and CLI<br />
► <strong>IBM</strong> Data Server Driver for .NET<br />
► <strong>IBM</strong> Data Server open source drivers such as PHP or Ruby<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 43
You can download some of these components and install them as separate<br />
products.<br />
Each <strong>IBM</strong> Data Server Client and driver provides a particular type of support:<br />
► For Java applications only, use <strong>IBM</strong> Data Server Driver for JDBC and SQLJ.<br />
► For applications using ODBC or CLI only, use <strong>IBM</strong> Data Server Driver for<br />
ODBC and CLI<br />
► For applications using ODBC, CLI, .NET, OLE DB, PHP, Ruby, JDBC, or<br />
SQLJ, use <strong>IBM</strong> Data Server Driver Package.<br />
► <strong>IBM</strong> Data Server Runtime Client and <strong>IBM</strong> Data Server Client includes all of<br />
above drivers and some functions of DB2.<br />
In this section, we discuss the installation and configuration of the following<br />
drivers for UNIX and Windows platforms:<br />
► <strong>IBM</strong> Data Server Driver for JDBC and SQLJ<br />
► <strong>IBM</strong> Data Server Driver for ODBC and CLI<br />
► <strong>IBM</strong> Data Server Driver Package<br />
For the installation and configuration of <strong>IBM</strong> Data Server Runtime Client and <strong>IBM</strong><br />
Data Server Client, refer to:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=/com.ibm.s<br />
wg.im.dbclient.install.doc/doc/c0022615.html<br />
Installing the <strong>IBM</strong> Data Server Driver package<br />
The <strong>IBM</strong> Data Server Driver package is a lightweight solution and the<br />
recommended package for user code deployment. It provides robust runtime<br />
support for applications using ODBC, CLI, .NET, OLE DB, PHP, Ruby, JDBC, or<br />
SQLJ without the need of installing Data Server Runtime Client or Data Server<br />
Client. Here we discuss the installation of the drivers presented under the <strong>IBM</strong><br />
Data Server Driver Package.<br />
Installing the driver package on UNIX and Linux<br />
You can download the <strong>IBM</strong> Data Server Driver Package and individual drivers<br />
such as <strong>IBM</strong> Data Server Driver for JDBC and SQLJ from the <strong>IBM</strong> Support and<br />
downloads website at:<br />
http://www.ibm.com/support/docview.wss?rs=4020&uid=swg21385217<br />
Extract <strong>IBM</strong> Data Server Drivers Package to an empty directory after you<br />
download it.<br />
If you want to install the complete <strong>IBM</strong> Data Server Driver Package, run the<br />
installIDSDriver command. This driver package includes database drivers for<br />
44 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Java, ODBC/CLI, PHP, and Ruby on Rails, each of which is stored in its own<br />
subdirectory. The Java and ODBC/CLI drivers are compressed.<br />
If you want to install individual drivers that are contained in the driver package,<br />
follow these steps (first two steps are common for all drivers):<br />
1. Extract the Data Server Driver Package archive onto the target system.<br />
2. For the Java and ODBC/CLI drivers, extract the driver file into the chosen<br />
installation directory.<br />
3. Install the driver that you need:<br />
– Java<br />
This driver provides support for client applications written in Java using<br />
JDBC. The path and file name of the Java driver are<br />
Path: jdbc_sqlj_driver/platform<br />
File name: db2_db2driver_for_jdbc_sqlj.zip<br />
For additional information, refer to “Installing <strong>IBM</strong> Data Server Driver for<br />
JDBC and SQLJ” on page 51.<br />
– ODBC and CLI<br />
The open source drivers included in the <strong>IBM</strong> Data Server Driver Package,<br />
such as PHP or Ruby driver, require the use of the Data Server CLI driver<br />
for connection to the database engine. If you plan to use any of these<br />
drivers, you need to install the ODBC driver.<br />
Path: odbc_cli_driver/platform<br />
File name: ibm_data_server_driver_for_odbc_cli.tar.Z<br />
For additional information, refer to “Installing <strong>IBM</strong> Data Server Driver for<br />
ODBC and CLI” on page 52.<br />
– PHP<br />
The <strong>IBM</strong>_DB2 and PDO_<strong>IBM</strong> are the PHP extensions for <strong>IBM</strong> Data<br />
Servers including <strong>Informix</strong>. Installing the <strong>IBM</strong>_DB2 or PDO_<strong>IBM</strong><br />
extensions enables any application in the PHP environment to interact with<br />
<strong>Informix</strong> database servers. The extensions included with the <strong>IBM</strong> Data<br />
Server Driver Package might be of a lower version as compared to the one<br />
available on the PHP repository.<br />
Download the latest extension from PHP repository page because it might<br />
contain important fixes and new features:<br />
For <strong>IBM</strong>_DB2<br />
http://pecl.php.net/package/ibm_db2<br />
For PDO_<strong>IBM</strong><br />
http://pecl.php.net/package/pdo_ibm<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 45
46 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
The PHP driver path and file names in the <strong>IBM</strong> Data Server Driver<br />
Packages are as follows:<br />
Path<br />
php_driver/platform/php32 or php_driver/platform/php64<br />
Files<br />
ibm_db2_n.n.n.so and pdo_ibm_n.n.n.so<br />
where n represents the version of the extension.<br />
Consider the following information before you install the PHP driver:<br />
Prerequisite: The PHP drivers require the ODBC/CLI driver that is also<br />
included in the <strong>IBM</strong> Data Server Driver Package to be installed.<br />
Installation instructions: The following online information explains how<br />
to install PECL extensions.<br />
Through the source code:<br />
http://www.php.net/manual/en/install.pecl.phpize.php<br />
Through the PECL command:<br />
http://www.php.net/manual/en/install.pecl.pear.php<br />
Installing the <strong>IBM</strong>_DB2 extension:<br />
http://www.php.net/manual/en/ibm-db2.installation.php<br />
Installing the PDO_<strong>IBM</strong> extension:<br />
http://www.php.net/pdo_ibm<br />
– Ruby on Rails<br />
The <strong>IBM</strong>_DB adapter and driver as a gem enables any application in the<br />
Ruby environment, including Rails, to interact with <strong>IBM</strong> data servers. The<br />
path, file name, and installation information for Ruby on Rails Driver is as<br />
follows:<br />
Path<br />
ruby_driver/platform<br />
File<br />
ibm_db-0.10.0.gem
Consider the following information before you install the Ruby on Rails<br />
Driver:<br />
Prerequisite: The Ruby on Rails Driver requires the ODBC/CLI driver<br />
that is also included in the <strong>IBM</strong> Data Server Driver Package to be<br />
installed.<br />
To install the Ruby on Rails Driver, from the location of the gem file, run<br />
the following command:<br />
gem install ibm_db-0.10.0.gem<br />
You are now ready to use any of these drivers on UNIX or Linux.<br />
Installing the driver package on Windows<br />
You can obtain the Data Server Driver Package for windows from the same site<br />
as the package for UNIX:<br />
http://www.ibm.com/support/fixcentral/<br />
The file for Windows is an .exe file, which is the installation file for the driver<br />
package.<br />
To install the driver package:<br />
1. Start the installation by executing the .exe file. The welcome window opens,<br />
as shown in Figure 2-11. Click Next.<br />
Figure 2-11 Welcome panel for Data Server Driver Package<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 47
2. On the Software License Agreement panel, accept the license agreement,<br />
and click Next.<br />
3. Specify the installation directory in the Custom Setup panel (Figure 2-12). You<br />
can change the installation directory to a location other then the default. Click<br />
Next.<br />
Figure 2-12 Change the installation directory<br />
48 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
4. The <strong>IBM</strong> data server driver copy name is used to identify a location where<br />
<strong>IBM</strong> Data Server Driver Package is installed on the computer. Enter the copy<br />
name for the location that you have chosen, the default is IBDBCL1<br />
(Figure 2-13). Click Next.<br />
Figure 2-13 Set the <strong>IBM</strong> data server driver copy name<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 49
5. Verify the installation selections on the summary panel (Figure 2-14). If<br />
everything is correct, begin the installation by clicking Install.<br />
Figure 2-14 Summary of the installation<br />
50 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
6. When the installation completes, the complete panel opens as shown in<br />
Figure 2-15. Click Finish. The installation of Data Server Driver for <strong>Informix</strong> is<br />
complete.<br />
Figure 2-15 Installation complete<br />
Note: The Windows installer of the <strong>IBM</strong> Data Server Driver Package does not<br />
provide any option to install individual components. All the drivers and<br />
interfaces that are included in the package are installed by default.<br />
Installing <strong>IBM</strong> Data Server Driver for JDBC and SQLJ<br />
To install <strong>IBM</strong> Data Server Driver for JDBC and SQLJ:<br />
1. Obtain Java SDK.<br />
Before you install the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ, you must<br />
have an SDK for Java installed on your computer:<br />
– For JDBC 3.0 functions, you need Java SDK 1.4.2 or later.<br />
– For JDBC 4.0 functions, you need Java SDK 6 or later.<br />
2. Download the .zip file for the latest version of the <strong>IBM</strong> Data Server Driver for<br />
JDBC and SQLJ at:<br />
http://www.ibm.com/software/data/support/data-server-clients/download.html<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 51
3. Extract the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ compressed file to the<br />
installation location. The compressed file contains the following files:<br />
– db2jcc.jar<br />
– db2jcc4.jar<br />
– sqlj.zip<br />
– sqlj4.zip<br />
4. Modify the CLASSPATH environment variable to include the appropriate files,<br />
You can set the CLASSPATH using the following command:<br />
java -classpath \.jar<br />
– For JDBC:<br />
Include the db2jcc.jar file in the CLASSPATH if you plan to use the<br />
version of the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ that includes<br />
only JDBC 3.0 and earlier functions.<br />
Include the db2jcc4.jar file in the CLASSPATH if you plan to use the<br />
version of the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ that includes<br />
JDBC 4.0 and earlier functions.<br />
– For SQLJ<br />
The steps are similar to the JDBC except that the files to be included in<br />
CLASSPATH are sqlj.zip and sqlj4.zip.<br />
For more information regarding configuration and customizing of the <strong>IBM</strong> Data<br />
Server Driver for JDBC and SQLJ, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.jccids.doc/<br />
ids_jcc_0008.htm<br />
Installing <strong>IBM</strong> Data Server Driver for ODBC and CLI<br />
The <strong>IBM</strong> Data Server Driver for ODBC and CLI solution is designed mainly for<br />
independent software vendor (ISV) deployments.To install the <strong>IBM</strong> Data Server<br />
Driver for ODBC and CLI:<br />
1. Depending on your operating system, download the compressed file that is<br />
available at:<br />
https://www14.software.ibm.com/webapp/iwm/web/preLogin.do?land=en_US&source<br />
=swg-informixfpd<br />
2. Extract the file to an installation directory of your choice.<br />
52 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Important: Include either the db2jcc.jar or the db2jcc4.jar files in the<br />
CLASSPATH. Do not include both files.
3. On Mac OS X, set the DYLD_LIBRARY_PATH environment variable to the<br />
clidriver/lib directory path.<br />
For specific information about the setup and configuration of the ODBC, refer to<br />
3.2, “Setup and configuration” on page 58.<br />
2.2.4 Setting up <strong>Informix</strong> JDBC<br />
<strong>Informix</strong> JDBC is a platform independent, industry-standard driver that provides<br />
enhanced support for distributed transactions. It consists of a set of interfaces<br />
and classes written in the Java programming language which can run on AIX,<br />
HP-UX, Linux, Solaris, Windows, and all other platforms that support Java. It also<br />
supports extensibility with a user-defined type (UDT) routine manager that<br />
simplifies the creation and use of UDTs.<br />
JCC support is available through the <strong>IBM</strong> Data Server Driver package that has a<br />
driver to run Java application using JCC.<br />
This section describes how to install <strong>Informix</strong> JDBC Driver, which is bundled with<br />
the <strong>Informix</strong> database server. There is a separate JDBC directory with a .jar file<br />
inside when you extract the <strong>Informix</strong> database server from the installation media.<br />
<strong>Informix</strong> JDBC Driver, Version 3.50, strives to be compliant with the Sun<br />
Microsystems JDBC 3.0 specification. Nearly all the features that are required for<br />
the Sun Microsystems JDBC 3.0 specification have the specified behavior. For<br />
the Sun Microsystems JDBC 3.0 driver optional features, if the feature is<br />
supported by <strong>IBM</strong> <strong>Informix</strong> Version 11.50, then it is supported by <strong>Informix</strong> JDBC<br />
Driver, Version 3.50.<br />
<strong>Informix</strong> JDBC Driver is a native-protocol, pure-Java driver (Type 4). Thus, when<br />
you use <strong>Informix</strong> JDBC Driver in a Java program that uses the JDBC API to<br />
connect to an <strong>Informix</strong> database, your session connects directly to the database<br />
or database server without a middle tier<br />
Installing the JDBC driver<br />
The JDBC driver installation procedure is same for both Windows and UNIX<br />
platforms.<br />
To install the JDBC driver in console mode, use the following command:<br />
java -cp dir/setup.jar run -console<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 53
For a silent installation on both Windows and UNIX, run the following command<br />
from a command prompt:<br />
java -cp /setup.jar run -silent -P<br />
product.installLocation=<br />
where:<br />
► is the location of the setup.jar file<br />
► is the directory where you want to install the JDBC driver<br />
The installation is complete when the command finishes executing. If you want to<br />
log information during the installation, specify the -log parameter.<br />
To install <strong>Informix</strong> JDBC Driver in GUI mode on Windows:<br />
1. Start the GUI mode installation with the following command:<br />
java -cp dir/setup.jar run<br />
The Welcome panel opens (Figure 2-16). Click Next.<br />
54 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Figure 2-16 Starting panel for JDBC informix installation<br />
2. Accept the license agreement, and click Next.
3. Specify the installation directory or use the default location as shown in<br />
Figure 2-17, and click Next.<br />
Figure 2-17 Specify the installation directory<br />
4. Review the summary information that displays (Figure 2-18), and click Next<br />
complete the installation.<br />
Figure 2-18 Summary window<br />
Chapter 2. Setting up an <strong>Informix</strong> development environment 55
Configuring the JDBC driver<br />
To use JDBC driver in an application, you must set the CLASSPATH environment<br />
variable to point to the driver files. The CLASSPATH environment variable tells<br />
the JVM and other applications where to find the Java class libraries that are<br />
used in a Java program-log parameter.<br />
UNIX<br />
Use one of the following methods to set the CLASSPATH environment variable<br />
on a UNIX system:<br />
► Add the full path name of the ifxjdbc.jar file to CLASSPATH:<br />
56 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
setenv CLASSPATH /jdbcdriv/lib/ifxjdbc.jar:$CLASSPATH<br />
► Extract the ifxjdbc.jar file, and add its directory to CLASSPATH:<br />
cd /jdbcdriv/lib<br />
jar xvf ifxjdbc.jar<br />
setenv CLASSPATH /jdbcdriv/lib:$CLASSPATH<br />
Windows system<br />
Use one of the following methods to set the CLASSPATH environment variable<br />
on a Windows system:<br />
► Add the full path name of the ifxjdbc.jar file to CLASSPATH:<br />
set CLASSPATH=c:\jdbcdriv\lib\ifxjdbc.jar;%CLASSPATH%<br />
► Extract the ifxjdbc.jar file, and add its directory to CLASSPATH:<br />
cd c:\jdbcdriv\lib<br />
jar xvf ifxjdbc.jar<br />
set CLASSPATH=c:\jdbcdriv\lib;%CLASSPATH%<br />
You are now ready to develop a Java application using JDBC.
Chapter 3. Working with the ODBC<br />
driver<br />
This chapter discusses the configuration and development of applications using<br />
Open Database Connectivity (ODBC) interfaces to access an <strong>Informix</strong> database<br />
server.<br />
This chapter includes the following topics:<br />
► ODBC and <strong>Informix</strong><br />
► Setup and configuration<br />
► Developing an ODBC application<br />
3<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 57
3.1 ODBC and <strong>Informix</strong><br />
ODBC is a software API method based on the X/Open Call Level Interface to<br />
access data from a database management system. ODBC allows developers to<br />
write database applications without having to know a proprietary interface for a<br />
specific database. In an ODBC environment, the ODBC driver is the component<br />
that directly communicates with the database server. It receives calls from the<br />
application or the ODBC Driver Manager and converts those calls into messages<br />
that the database server can understand.<br />
The following ODBC drivers for <strong>Informix</strong> are available:<br />
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver<br />
► <strong>IBM</strong> Data Server Driver for ODBC and CLI (CLI Driver)<br />
3.2 Setup and configuration<br />
In this section, we discuss the setup and configuration parameters of the ODBC<br />
driver.<br />
3.2.1 <strong>IBM</strong> <strong>Informix</strong> ODBC Driver<br />
The <strong>Informix</strong> ODBC Driver is installed by default as part of <strong>Informix</strong> Client<br />
Software Development Kit (Client SDK). It is based on the Microsoft Open<br />
Database Connectivity (ODBC) Version 3.0 standard.<br />
Table 3-1 lists the <strong>Informix</strong> database server that support <strong>Informix</strong> ODBC.<br />
Table 3-1 Supported databases<br />
Database server Versions<br />
<strong>IBM</strong> <strong>Informix</strong> 10.0,11.10,11.50<br />
<strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server 8.50 and higher<br />
<strong>IBM</strong> <strong>Informix</strong> Standard Engine 7.25<br />
<strong>IBM</strong> <strong>Informix</strong> Online 5.20 and higher<br />
Windows system configuration<br />
On a Windows system, the ODBC drivers is registered automatically within the<br />
system during the <strong>Informix</strong> installation process.<br />
58 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The following directory is the default installation directory:<br />
C:\Program Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK<br />
The INFORMIXDIR environment variable points to the directory where the<br />
product was installed.<br />
Note: If you install a 32-bit version of Client SDK on a Windows x64, the<br />
default directory is C:\Program Files (x86)\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK.<br />
The ODBC driver requires that additional libraries are loaded. These libraries are<br />
located in the %INFORMIXDIR%\bin directory. Make sure that this directory is part<br />
of your PATH variable.<br />
The name of the ODBC driver depends on the version:<br />
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver: For the Windows 32-bit driver, see Figure 3-1.<br />
Figure 3-1 Windows x86 ODBC driver administrator<br />
Chapter 3. Working with the ODBC driver 59
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver (64-bit): for the Windows 64-bit driver. See<br />
Figure 3-2.<br />
60 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Figure 3-2 Windows x64 ODBC driver administrator<br />
To create an <strong>Informix</strong> ODBC Data Source (DSN), open the ODBC Administrator,<br />
choose a DSN type, and select the <strong>Informix</strong> ODBC driver, the DSN-configuration<br />
parameters are common to both drivers (32-bit and 64-bit).<br />
Note: The default ODBC Data Source Administrator on a Windows x64<br />
system is the 64-bit version. If you want to create a 32-bit ODBC DSN you<br />
must use the 32-bit version<br />
C:\WINDOWS\SysWOW64\odbcad32.exe
Figure 3-3 shows the <strong>Informix</strong> ODBC Connection tab.<br />
Figure 3-3 Connection parameters<br />
Table 3-2 lists the required DSN parameters. If you have already defined an<br />
<strong>Informix</strong> server using the setnet32.exe utility, which is a component of Client<br />
SDK, most of these values are updated automatically when selecting an <strong>Informix</strong><br />
server from the drop-down box.<br />
Table 3-2 Required DSN values<br />
Parameter Description<br />
Data source name Name of the DSN<br />
Server name Name of the <strong>Informix</strong> database server<br />
Host name Name or IP address of the computer where the database server<br />
runs<br />
Service Name or value of the tcp service used by the database server<br />
Protocol Communication Protocol used by the database server<br />
Database Name of the database to which the DSN connects by default<br />
Chapter 3. Working with the ODBC driver 61
You can set optional configuration parameters in the Environment and Advanced<br />
tabs (Figure 3-4).<br />
Figure 3-4 Environment parameters<br />
Table 3-3 describe the parameters for the Environment tab.<br />
Table 3-3 Environment parameters<br />
Parameter Description<br />
Client Locale Locale for the client machine<br />
Database Locale Locale used when the database was created<br />
Use Server Database Locale Using this option the Database Locale is automatically<br />
retrieved from the database<br />
Translation Library Code set conversion library<br />
Translation Option Options for a third-party conversion library<br />
Cursor Behavior Close or preserve a cursor when a transaction is<br />
resolved<br />
VMB Character How to report the length for Varying Multi byte<br />
characters<br />
Fetch Buffer Size Size of the communication data package<br />
Isolation Level Default Isolation Level for the connection<br />
62 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Note: The code set value for the Client Locale parameter is determined by the<br />
code set used in the application, which in most cases is the same as the<br />
operating system. The default code set on a Windows system is CP1252.<br />
The default code set for an <strong>Informix</strong> database is en_us.8859-1 (ISO 8859-1).<br />
Some versions of the <strong>IBM</strong> <strong>Informix</strong> database server (for example, Version 9.40<br />
and Version 10.00) allow a connection from a client with a mismatched<br />
DB_LOCALE value. This option is no longer available. A client application<br />
must set the Database Locale parameter (DB_LOCALE) to the same value as<br />
the locale of the database (locale used at creation time).<br />
The typical syntax for an <strong>Informix</strong> locale is language_territory@codeset.<br />
Figure 3-5 shows the Advanced tab configuration parameters.<br />
Figure 3-5 Advanced parameters<br />
Table 3-4 describes the parameters for the Advanced tab.<br />
Table 3-4 Advanced parameters<br />
Parameter Description<br />
Auto Commit Optimization Optimize client-server communication deferring<br />
the commit work after all the cursors are close.<br />
Open-Fetch-Close Optimization Optimize client-server communication<br />
automatically closing the cursor after all the<br />
rows have been retrieved.<br />
Chapter 3. Working with the ODBC driver 63
Parameter Description<br />
Insert Cursors Buffers all the rows send by an insert cursor<br />
and uses only one package to send the data to<br />
the server.<br />
Scrollable Cursors Enable scrollable cursors.<br />
Report KeySet Cursors Report support for KeySet-driver cursors.<br />
Report Standard ODBC Types Only Report standard ODBC types Only. Extended<br />
types are mapped to standard ODBC types.<br />
Describe Decimal Floating Point as<br />
SQL_REAL / SQL_DOUBLE<br />
64 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Decimal columns without a scale are reported<br />
as SQL_REAL or SQL_DOUBLE.<br />
Do Not Use Lvarchar Do not report Lvarchar columns when using<br />
SQL_VARCHAR.<br />
Report Char columns as Wide Char<br />
columns<br />
length in Chars for<br />
SQLGetDiagRecW<br />
Char columns described by SQLDescribeCol<br />
are reported as Wide char columns<br />
(SQLWCHAR)<br />
Returns the number of characters rather than<br />
the number of bytes.
For more information about each one of these parameters, place the cursor over<br />
a parameter, and press F1 (Figure 3-6).<br />
Figure 3-6 ODBC DSN Help<br />
UNIX configuration<br />
On UNIX platforms, the default installation directory for Client SDK is the<br />
/opt/<strong>IBM</strong>/informix directory.<br />
The INFORMIXDIR environment variable should point to the directory where the<br />
product was installed.<br />
On UNIX platforms, an ODBC driver manager is not normally supplied as part of<br />
the operating system. Client SDK does not include an ODBC driver manager<br />
library, however, the Client SDK does have a Driver Manager Replacement<br />
(DMR) library which provides most of the features of an ODBC driver manager.<br />
If the application does not use an ODBC driver manager, such as unixODBC or<br />
Data Direct Driver Manager, the application must be linked directly to the <strong>Informix</strong><br />
ODBC libraries. The ODBC libraries are located in the $INFORMIXDIR/lib/cli<br />
directory.<br />
Chapter 3. Working with the ODBC driver 65
Table 3-5 shows the ODBC libraries that are included with Client SDK.<br />
Table 3-5 UNIX ODBC libraries<br />
Library Description<br />
libifcli.a or libcli.a Static version for single (nonthreaded)<br />
libifcli.so or iclis09b.so Shared version for single (nonthreaded)<br />
libthcli.a Static version for multithreaded library<br />
libthcli.so or iclit09b.so Shared version for multithreaded library<br />
libifdrm.so or idmrs09a.so Shared library for DMR (thread safe)<br />
The shared-library path environment variable specifies the library search path.<br />
This variable should contains at least $INFORMIX/lib, $INFORMIXDIR/lib/esql,<br />
and $INFORMIXDIR/lib/cli for the ODBC driver to work.<br />
There are three configuration files for the ODBC driver:<br />
► sqlhosts<br />
► odbc.ini<br />
► odbcinst.ini<br />
Note: The information in the sqlhosts file is also used by all the other Client<br />
SDK components and by the <strong>Informix</strong> database server. Be aware that any<br />
changes in this file can have an impact in other clients and servers.<br />
On a Windows system, instead of using a text file, this information is stored in<br />
the registry through the setnet32.exe utility.<br />
The sqlhosts file<br />
This text file contains most of the information that is required to connect to an<br />
<strong>IBM</strong> <strong>Informix</strong> database server.<br />
The default location for this file is the $INFORMIXDIR/etc/sqlhosts directory. You<br />
can use the INFORMIXSQLHOSTS environment variable to point to a different<br />
location.<br />
Example 3-1 shows a simple sqlhosts file.<br />
Example 3-1 The sqlhosts file<br />
#server protocol hostname service/port<br />
on_demo onsoctcp kodiak 9088<br />
66 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
For more information about the sqlhosts file, refer to the Client/Server<br />
Communications manual at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.admin.doc/i<br />
ds_admin_0158.htm<br />
The odbcinst.ini file<br />
This file contains a list of installed ODBC drivers on the UNIX system and<br />
specific attributes for each driver, such as the location of the shared library.<br />
The default location of this file is under the home directory as<br />
$HOME/.odbcinst.ini. A sample odbcinst.ini file is located in the<br />
$INFORMIXDIR/etc directory.<br />
Example 3-2 shows a simple odbcinst.ini file.<br />
Example 3-2 The odbcinst.ini file<br />
;---------------------------------------------------------------------------<br />
[ODBC Drivers]<br />
<strong>IBM</strong> INFORMIX ODBC DRIVER=Installed<br />
[<strong>IBM</strong> INFORMIX ODBC DRIVER]<br />
Driver=/extra/informix/lib/cli/iclit09b.so<br />
Setup=/extra/informix/lib/cli/iclit09b.so<br />
APILevel=1<br />
ConnectFunctions=YYY<br />
DriverODBCVer=03.51<br />
FileUsage=0<br />
SQLLevel=1<br />
smProcessPerConnect=Y<br />
The odbinst.ini file has two sections:<br />
► ODBC Drivers: Lists the ODBC drivers installed in the system.<br />
► Driver Properties: Lists the properties for a specific ODBC driver.<br />
Table 3-6 lists the parameters in the Driver Properties section.<br />
Table 3-6 Parameters in the Driver Properties section<br />
Keyword Description<br />
Driver Location of the ODBC library<br />
Setup Location of the Setup library<br />
APILevel ODBC interface conformance level that the driver supports<br />
ConnectFunctions Support for SQLConnect, SQLDriverConnect, and<br />
SQLBrowserConnect<br />
Chapter 3. Working with the ODBC driver 67
Keyword Description<br />
DriverODBCVer Supported version of the ODBC driver<br />
FileUsage How handle File-system DSN<br />
SQLLevel Type of SQL-92 grammar supported<br />
The odbc.ini file<br />
The odbc.ini file is the data source configuration information. The default<br />
location of this file is under the home directory as $HOME/.odbc.ini.<br />
Alternatively, you can use the ODBCINI environment variable to point the<br />
odbc.ini to a different location. A sample odbc.ini file is located in the<br />
$INFORMIXDIR/etc directory.<br />
Example 3-3 shows a simple odbc.ini file.<br />
Example 3-3 The odbc.ini file<br />
[ODBC Data Sources]<br />
Infdrv1=<strong>IBM</strong> INFORMIX ODBC DRIVER<br />
;<br />
[Infdrv1]<br />
Driver=/extra/informix/lib/cli/iclis09b.so<br />
Description=<strong>IBM</strong> INFORMIX ODBC DRIVER<br />
Database=stores_demo<br />
LogonID=odbc<br />
pwd=odbc<br />
Servername=demo_on<br />
CursorBehavior=0<br />
CLIENT_LOCALE=en_us.8859-1<br />
DB_LOCALE=en_us.8859-1<br />
TRANSLATIONDLL=/extra/informix/lib/esql/igo4a304.so<br />
;<br />
[ODBC]<br />
;UNICODE=UCS-4<br />
;<br />
; Trace file Section<br />
;<br />
Trace=0<br />
TraceFile=/tmp/odbctrace.out<br />
InstallDir=/extra/informix<br />
TRACEDLL=idmrs09a.so<br />
68 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
This file contains three sections:<br />
► ODBC Data Sources: DSN name and description of the driver used<br />
► Data Source Specification: Configuration parameters for this particular DSN<br />
► ODBC: Global options such as Unicode mode or Tracing<br />
Table 3-7 lists the available parameters for the odbc.ini configuration file.<br />
Table 3-7 Parameters for the odbc.ini file<br />
Keyword Description<br />
Driver Location of the ODBC shared library<br />
Description DSN description<br />
Database Database name<br />
LogonID User Id<br />
pwd Password<br />
Server Database server<br />
CLIENT_LOCALE Locale used by the client application<br />
DB_LOCALE Locale of the database<br />
TRANSLATIONDLL Location of the code set conversion library<br />
CURSORBEHAVIOR Close or preserve a cursor when a<br />
transaction is resolved<br />
DefaultUDTFetchType Default type for a UDT (SQL_C_BINARY<br />
or SQL_C_CHAR)<br />
ENABLESCROLLABLECURSORS Enable Scrollable cursors<br />
ENABLEINSERTCURSORS Optimize insert cursors process<br />
OPTIMIZEAUTOCOMMIT Defer the commit message after all cursor<br />
are closed<br />
NEEDODBCTYPESONLY Extended types are mapped to standard<br />
ODBC types<br />
OPTOFC Close cursors after all the rows have been<br />
fetched<br />
REPORTKEYSETCURSORS Report support for keyset-driven cursors<br />
FETCHBUFFERSIZE Set the size of the fetch buffer<br />
Chapter 3. Working with the ODBC driver 69
Keyword Description<br />
DESCRIBEDECIMALFLOATPOINT Describe float types as SQL_REAL or<br />
SQL_DOUBLE<br />
USESERVERDBLOCALE Use the server database locale<br />
DONOTUSELVARCHAR Don’t report Lvarchar columns when using<br />
SQL_VARCHAR<br />
REPORTCHARCOLASWIDECHARCOL Char columns described by<br />
SQLDescribeCol are reported as Wide<br />
char columns (SQLWCHAR)<br />
ISOLATIONLEVEL Default isolation level<br />
UNICODE Type of Unicode (UCS-2, UCS-4)<br />
TRACE Trace enable or disable<br />
TRACEFILE Location of the trace file<br />
TRACEDLL Trace library name (idmrs09a.so)<br />
3.2.2 <strong>IBM</strong> Data Server Driver for ODBC and CLI<br />
The <strong>IBM</strong> Data Server Driver for ODBC and CLI (CLI Driver) is installed as part of<br />
the <strong>IBM</strong> Data Server Driver Package or as a separate product. The <strong>IBM</strong> Data<br />
Server Package is bundle with Client SDK. It allows you to connect to <strong>IBM</strong><br />
<strong>Informix</strong> and <strong>IBM</strong> DB2 databases with the same set of libraries.<br />
The communication protocol used is DRDA instead of the native <strong>Informix</strong> SQLI.<br />
This driver is supported against <strong>Informix</strong> Version 11.10 and Version 11.50.<br />
Windows system configuration<br />
The <strong>IBM</strong> Data Server Driver installs in the following directory by default:<br />
C:\Program Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER DRIVER<br />
The ODBC driver is registered in the Windows system during the installation of<br />
the <strong>IBM</strong> Data Server Driver package. The name of the ODBC driver is <strong>IBM</strong> DB2<br />
ODBC Driver.<br />
70 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Figure 3-7 shows the Drivers tab of the ODBC Data Source Administrator, which<br />
lists both ODBC drivers:<br />
► <strong>IBM</strong> DB2 ODBC DRIVER<br />
► <strong>IBM</strong> INFORMIX ODBC DRIVER<br />
Figure 3-7 ODBC Data Source Administrator<br />
To create an ODBC Data Source (DSN) using the <strong>IBM</strong> Data Server Driver, open<br />
the ODBC Administrator, choose a DSN type, and select the <strong>IBM</strong> DB2 ODBC<br />
Driver.<br />
Figure 3-8 shows the Add dialog box.<br />
Figure 3-8 DSN Add dialog box<br />
Chapter 3. Working with the ODBC driver 71
If there is no database alias defined, you must add one with the connection<br />
details of your <strong>IBM</strong> <strong>Informix</strong> database server (Figure 3-9).<br />
Figure 3-9 DSN Settings dialog box<br />
Figure 3-10 shows the Advanced Settings tab.<br />
Figure 3-10 DSN Advanced Settings dialog box<br />
72 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Table 3-8 lists the minimum required parameters to connect to an <strong>Informix</strong> server.<br />
Table 3-8 CLI parameters<br />
CLI parameter Description<br />
Hostname Name of the system where the <strong>Informix</strong> server is running<br />
Port TCP Port used by the DRDA <strong>Informix</strong> alias<br />
Protocol Transport protocol (TCP/IP)<br />
Database Database server<br />
The configuration settings are stored as a File-DSN (a text file that contains all<br />
the information that is required to connect to the database) in the<br />
%USERPROFILE%\db2cli.ini file. Example 3-4 shows a db2cli.ini file.<br />
Example 3-4 The db2cli.ini file<br />
[test]<br />
Database=stores_demo<br />
Protocol=TCPIP<br />
Port=9089<br />
Hostname=kodiak<br />
For a complete description of all the CLI parameters, refer to the DB2 Information<br />
Center at:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=/com.ibm.d<br />
b2.luw.apdv.cli.doc/doc/r0007964.html<br />
UNIX configuration<br />
On UNIX platforms, the default installation directory for <strong>IBM</strong> Data Server Client<br />
package is /opt/<strong>IBM</strong>/db2/V9.7. The library for the ODBC driver is libdb2.a,<br />
which is located in the lib directory of your Data Server installation.<br />
Similar to the installation on a Windows system, the ODBC configuration is<br />
stored in the db2cli.ini file, which by default is included in the cfg directory of<br />
your Data Server installation or is defined by the DB2CLIINI environment variable<br />
Chapter 3. Working with the ODBC driver 73
Example 3-5 shows a simple db2cli.ini file.<br />
Example 3-5 The db2cli.ini file<br />
[test]<br />
Database=stores_demo<br />
Protocol=TCPIP<br />
Port=9089<br />
Hostname=kodiak<br />
For more information about the db2cli.ini file, refer to:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=/com.ibm.d<br />
b2.luw.apdv.cli.doc/doc/c0007882.html<br />
3.2.3 Verifying connectivity<br />
On a Windows system, you can use the ODBC Data Source Administrator to<br />
verify the connection with the <strong>Informix</strong> database server.<br />
When using the <strong>IBM</strong> <strong>Informix</strong> ODBC driver, you can verify the connection using<br />
the Apply & Test Connection option, as shown in Figure 3-11.<br />
Figure 3-11 <strong>Informix</strong> ODBC connection test<br />
74 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
With the <strong>IBM</strong> Data Server Driver for ODBC, you can perform the connection test<br />
using the Connect option, as shown in Figure 3-12.<br />
Figure 3-12 <strong>IBM</strong> Data Server Driver connection test<br />
Client SDK on UNIX does not include any tool to test an ODBC DSN. You can<br />
use the C samples in $INFORMIXDIR\demo\cli to check whether the ODBC DSN<br />
is working.<br />
3.3 Developing an ODBC application<br />
In this section, we describe how to connect to a database server, type mapping<br />
between standard ODBC and <strong>IBM</strong> <strong>Informix</strong>, and how to perform basic operations<br />
with the ODBC driver.<br />
3.3.1 Connecting to the database<br />
Typically, an ODBC application performs the following steps:<br />
1. Connects to the database.<br />
To connect to the database, the application must pass the connection details<br />
to the ODBC driver. These details can be included directly in the connection<br />
string or stored as a Data Source Name (DSN).<br />
2. Processes SQL statements.<br />
Chapter 3. Working with the ODBC driver 75
The application sends SQL request to the ODBC driver to perform database<br />
operations (insert, select, delete, update, and so on).<br />
3. Resolves any open transaction.<br />
If the application is using a transaction, it resolves (commits or rolls back) any<br />
open transactions.<br />
4. Terminates the connection, and frees the allocated resources.<br />
These steps are done using the ODBC API functions. Figure 3-13 shows a<br />
typical sequence of calls. The labels inside the gray boxes correspond with the<br />
ODBC API functions that are used.<br />
Figure 3-13 Typical ODBC calls used by applications<br />
Example 3-6 shows a simple C program that connects to an ODBC DSN.<br />
76 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 3-6 A test_connect.c sample<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
if (argc != 2)<br />
{<br />
fprintf (stdout, "Please specify the name of a DSN!\n");<br />
return(1);<br />
}<br />
else<br />
{<br />
if (strstr (argv[1],"DRIVER")==NULL)<br />
sprintf((char *) connStrIn, "DSN=%s;", (char *)argv[1]);<br />
else<br />
sprintf((char *) connStrIn, "%s;", (char *)argv[1]);<br />
}<br />
rc = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Environment Handle Allocation failed!\n");<br />
return (1);<br />
}<br />
rc = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "SQLSetEnvAttr failed!\n");<br />
return (1);<br />
}<br />
rc = SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
Chapter 3. Working with the ODBC driver 77
}<br />
78 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
}<br />
fprintf (stdout, "Connection Handle Allocation failed!\n");<br />
return (1);<br />
rc = SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, 1000,<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Connectedion failed!\n");<br />
return (1);<br />
}<br />
fprintf (stdout, "Connected\n");<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
You can compile this code on a Windows system using the following command:<br />
cl /DWIN32 -I%INFORMIXDIR%\incl\cli odbc32.lib test_connect.c<br />
Example 3-7 shows how to compile and run the sample.<br />
Example 3-7 The test_connec.c sample output<br />
c:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -I%INFORMIXDIR%\incl\cli<br />
odbc32.lib /nologo test_connect.c<br />
test_connect.c<br />
D:\work>test_connect<br />
Please specify the name of a DSN!<br />
D:\workt>test_connect dummy_dsn<br />
Connection failed!<br />
D:\work>test_connect test<br />
Connected<br />
D:\work>
3.3.2 Type mapping<br />
You can use the sample C program to perform a DSN-less connection.<br />
Example 3-8 shows the sample C program with a DSN-less connection string<br />
using the <strong>Informix</strong> ODBC driver and the Data Server driver for ODBC.<br />
Example 3-8 A DSN-less connection<br />
C:\work>test_connect "DRIVER={<strong>IBM</strong> INFORMIX ODBC<br />
DRIVER};SERVER=demo_on;DATABASE=stores_demo;HOST=kodiak;PROTOCOL=onsoctcp;SERVI<br />
CE=9088;UID=informix;PWD=password;";<br />
Connected<br />
C:\work>test_connect "driver={<strong>IBM</strong> DB2 ODBC<br />
DRIVER};Database=stores_demo;hostname=kodiak;port=9089;protocol=TCPIP;<br />
uid=informix; pwd=password"<br />
Connected<br />
C:\work><br />
The data types that are used on the database differ from the data types that are<br />
used by your application. This section shows the data type mapping that is<br />
needed when working with specific <strong>Informix</strong> data types with the <strong>Informix</strong> ODBC<br />
driver.<br />
When a query is executed by the application, the data returned by the <strong>Informix</strong><br />
server might be in a different format than what the application uses. The ODBC<br />
driver converts the data that is passed between the application and the database<br />
server. This process is invisible to the application. The only requirement for the<br />
application is to specify the correct data types when calling the ODBC driver<br />
functions.<br />
The application sets ValueType and ParameterType when calling the bind<br />
functions.<br />
Example 3-9 shows the definition for one of the bind functions,<br />
SQLBindParameter(). The ODBC driver converts the data type specified in<br />
ValueType to the data type specified in ParameterType.<br />
Example 3-9 SQLBindParameter definition<br />
SQLBindParameter(<br />
SQLHSTMT StatementHandle, /* hstmt */<br />
SQLUSMALLINT ParameterNumber, /* ipar */<br />
SQLSMALLINT InputOutputType, /* fParamType */<br />
SQLSMALLINT ValueType, /* fCType */<br />
SQLSMALLINT ParameterType, /* fSqlType */<br />
SQLUINTEGER ColumnSize, /* cbColDef */<br />
Chapter 3. Working with the ODBC driver 79
SQLSMALLINT DecimalDigits, /* ibScale */<br />
SQLPOINTER ParameterValuePtr, /* rgbValue */<br />
SQLINTEGER BufferLength, /* cbValueMax */<br />
SQLINTEGER *StrLen_or_IndPtr); /* pcbValue */<br />
Example 3-10shows an SQLBindParameter() call where the application is binding<br />
an SQL_C_BINARY parameter with an SQL_INFX_UDT_FIXED value.<br />
Example 3-10 Using an SQLBindParameter() call<br />
SQLBindParameter (<br />
hstmt,<br />
1,<br />
SQL_PARAM_INPUT,<br />
SQL_C_BINARY,<br />
SQL_INFX_UDT_FIXED,<br />
loptr_size,<br />
0,<br />
loptr_buffer,<br />
loptr_size,<br />
&loptr_valsize);<br />
Table 3-9 shows the <strong>Informix</strong>-specific data type mapping that is used with the<br />
<strong>Informix</strong> ODBC driver.<br />
Table 3-9 Mapping for specific <strong>Informix</strong> data types<br />
<strong>Informix</strong> SQL <strong>Informix</strong> ODBC driver<br />
BIGINT SQL_INFX_BIGINT<br />
BIGSERIAL SQL_INFX_BIGINT<br />
BLOB SQL_IFMX_UDT_BLOB<br />
BOOLEAN SQL_BIT<br />
BYTE SQL_LONGVARBINARY<br />
CLOB SQL_IFMX_UDT_CLOB<br />
DATETIME SQL_TIMESTAMP<br />
DISTINCT Any<br />
IDSSECURITYLABEL Built-in DISTINCT OF VARCHAR(128)<br />
INT8 SQL_BIGINT<br />
INTERVAL DAY SQL_INTERVAL_DAY<br />
80 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>Informix</strong> SQL <strong>Informix</strong> ODBC driver<br />
INTERVAL DAY TO HOUR SQL_INTERVAL_DAY_TO_HOUR<br />
INTERVAL DAY TO MINUTE SQL_INTERVAL_DAY_TO_MINUTE<br />
INTERVAL DAY TO SECOND SQL_INTERVAL_DAY_TO_SECOND<br />
INTERVAL HOUR SQL_INTERVAL_HOUR<br />
INTERVAL HOUR TO MINUTE SQL_INTERVAL_HOUR_TO_MINUTE<br />
INTERVAL HOUR TO SECOND SQL_INTERVAL_HOUR_TO_SECOND<br />
INTERVAL MINUTE SQL_INTERVAL_MINUTE<br />
INTERVAL MINUTE TO SECOND SQL_INTERVAL_MINUTE _TO_SECOND<br />
INTERVAL MONTH SQL_INTERVAL_MONTH<br />
INTERVAL SECOND SQL_INTERVAL_SECOND<br />
INTERVAL YEAR SQL_INTERVAL_YEAR<br />
INTERVAL YEAR TO MONTH SQL_INTERVAL_YEAR_TO_MONTH<br />
LIST, MULTISET, SET Any<br />
LVARCHAR SQL_VARCHAR<br />
MONEY SQL_DECIMAL<br />
NCHAR SQL_CHAR<br />
NVARCHAR SQL_VARCHAR<br />
OPAQUE (fixed) SQL_INFX_UDT_FIXED<br />
OPAQUE (varying) SQL_INFX_UDT_VARYING<br />
ROW Any<br />
SERIAL SQL_INTEGER<br />
SERIAL8 SQL_BIGINT<br />
TEXT SQL_LONGVARCHAR<br />
For a complete list of Data Type mapping, refer to the <strong>IBM</strong> <strong>Informix</strong> ODBC Driver<br />
Programmer’s Manual, which is available at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.odbc.doc/si<br />
i04979050.htm#sii04979050<br />
Chapter 3. Working with the ODBC driver 81
If your application requires only Standard ODBC data types, you can enable the<br />
Report Standard ODBC Types option in your DSN or connection string. When<br />
this option is enabled, the ODBC driver handles smart large objects (BLOB and<br />
CLOB) as though they were simple large objects (byte and text). The driver<br />
generates the smart-large-object calls (ifx_lo_open, ifx_lo_write, and so on)<br />
automatically.<br />
This option changes the mapping for user define types (including MULTISET,<br />
SET, ROW, and LIST) to SQL_C_CHAR.<br />
These options are also available as the following ODBC attributes:<br />
► SQL_INFX_ATTR_ODBC_TYPES_ONLY<br />
► SQL_INFX_ATTR_LO_AUTOMATIC<br />
► SQL_INFX_ATTR_DEFAULT_UDT_FETCH_TYPE<br />
3.3.3 Performing database operations<br />
In this section, we show samples of how to use the ODBC driver to perform basic<br />
operations with an <strong>Informix</strong> database. Most of these tasks are common to any<br />
ODBC application, so we do not explain them in detail.<br />
This section includes the following topics:<br />
► Simple SQL statements<br />
► Fetching data<br />
► Using parameters<br />
► Calling SQL routines<br />
► Local transactions<br />
► Distributed transactions<br />
Simple SQL statements<br />
You can run SQL statements directly using the SQLExecDirect() function. This<br />
function is commonly used when there is no need to run the SQL statement more<br />
than once. The ODBC driver prepares the statement, executes it, and frees the<br />
resources in one operation.<br />
Example 3-11 shows a sample of using the SQLExecDirect() function to set the<br />
LOCK WAIT time out.<br />
Example 3-11 SQLExecDirect() sample, Simple_sql.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
82 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
int sqllen;<br />
SQLCHAR *sqlstmt;<br />
if (argc != 3)<br />
{<br />
fprintf (stdout, "Please specify the name of a DSN and the SQL Statement to<br />
run!\n");<br />
return(1);<br />
}<br />
else<br />
{<br />
if (strstr (argv[1],"DRIVER")==NULL)<br />
sprintf((char *) connStrIn, "DSN=%s;", (char *)argv[1]);<br />
else<br />
sprintf((char *) connStrIn, "%s;", (char *)argv[1]);<br />
}<br />
sqllen = strlen((char *)argv[2]);<br />
sqlstmt = (SQLCHAR *) malloc (sqllen + sizeof(char));<br />
strcpy((char *)sqlstmt, (char *)argv[2]);<br />
rc = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Environment Handle Allocation failed!\n");<br />
return (1);<br />
}<br />
rc = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "SQLSetEnvAttr failed!\n");<br />
return (1);<br />
}<br />
Chapter 3. Working with the ODBC driver 83
84 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
rc = SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Connection Handle Allocation failed!\n");<br />
return (1);<br />
}<br />
rc = SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, 1000,<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Connection failed!\n");<br />
return (1);<br />
}<br />
}<br />
fprintf (stdout, "Connected\n");<br />
rc = SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
if (rc != SQL_SUCCESS)<br />
{<br />
fprintf (stdout, "Statement Handle Allocation failed!\n");<br />
return (1);<br />
}<br />
rc = SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)<br />
{<br />
fprintf (stdout, "SQLExecDirectW() failed!\n");<br />
return (1);<br />
}<br />
fprintf (stdout, "Executed SQL Statement:\n%s\n",sqlstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-12 shows the result of running the sample program with different SQL<br />
statements. In this example, we input the SQL statement directly from the<br />
command line. In a real application, the SQL is normally constructed based on<br />
customer input.<br />
In this simple example, we also did not provide error information if the execution<br />
fails. You can obtain error information using the SQLGetDiagRec() function, which<br />
we discuss in 3.3.5, “Error handling” on page 112.
Example 3-12 Output of SQLDirectExec sample<br />
c:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -I%INFORMIXDIR%\incl\cli<br />
odbc32.lib /nologo simple_sql.c<br />
simplesql.c<br />
c:\work>simples_sql.exe test "set lock mode to wait 10"<br />
Connected<br />
Executed SQL Statement:<br />
set lock mode to wait 10<br />
c:\work>simple_sql.exe test "create temp table temp1(c1 int)"<br />
Connected<br />
Executed SQL Statement:<br />
create temp table temp1(c1 int)<br />
c:\work>simple_sql.exe test "invalid_SQL"<br />
Connected<br />
SQLExecDirectW() failed!<br />
c:\work><br />
Fetching data<br />
Example 3-13 demonstrates how to run a SELECT statement to retrieve data<br />
from the server. To make the code clear, we have removed all the error handling.<br />
Example 3-13 Select.c sample<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
SQLCHAR sqlstmt[100];<br />
SQLCHAR code[2+1];<br />
Chapter 3. Working with the ODBC driver 85
}<br />
SQLCHAR sname[15+1];<br />
SQLLEN lcode=0;<br />
SQLLEN lsname=0;<br />
86 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
sprintf((char *) connStrIn, "DSN=demo_on");<br />
sprintf((char *) sqlstmt, "SELECT code, sname FROM state where code
Using parameters<br />
In most of the cases, an application runs the same SQL statement several times,<br />
so it makes sense if the SQL statement is prepared and then used with different<br />
values.<br />
Example 3-15 demonstrates how to run a simple SQL SELECT statement using<br />
an input parameter. The SQL statement is prepared and then executed with one<br />
parameter.<br />
Example 3-15 Example of a parametrized query, Select_param.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
SQLCHAR sqlstmt[100];<br />
SQLCHAR inputcode[2+1];<br />
SQLCHAR code[2+1];<br />
SQLCHAR sname[15+1];<br />
SQLLEN lcode = 0;<br />
SQLLEN lsname = 0;<br />
SQLSMALLINT datatype, decimaldigits, nullable;<br />
SQLUINTEGER paramsize;<br />
sprintf((char *) connStrIn, "DSN=demo_on");<br />
sprintf((char *) sqlstmt, "SELECT code, sname FROM state where code < ?");<br />
sprintf((char *) inputcode, "CA");<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
Chapter 3. Working with the ODBC driver 87
fprintf (stdout, "Connected\n");<br />
88 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
SQLPrepare (hstmt, sqlstmt, strlen(sqlstmt));<br />
SQLDescribeParam(hstmt, 1, &datatype, ¶msize, &decimaldigits, &nullable);<br />
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, datatype, paramsize,<br />
decimaldigits, inputcode, 0, NULL);<br />
}<br />
SQLExecute (hstmt);<br />
fprintf (stdout, "Executed SQL Statement:\n%s\nUsing: '%s'\n",sqlstmt,inputcode);<br />
SQLBindCol (hstmt, 1, SQL_C_CHAR, &code, sizeof(code), &lcode);<br />
SQLBindCol (hstmt, 2, SQL_C_CHAR, &sname, sizeof(sname), &lsname);<br />
while ((rc = SQLFetch(hstmt))!=SQL_NO_DATA_FOUND)<br />
fprintf (stdout, "Fetched: %s, %s\n",code, sname);<br />
SQLCloseCursor(hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-16 shows the SQL statement that the program prepares and the<br />
parameter that is used for the placeholder.<br />
Example 3-16 Output for Select_param.c<br />
C:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -ID:\infx\csdk350tc7\incl\cli<br />
odbc32.lib /nologo select_param.c<br />
select_param.c<br />
C:\work>select_param<br />
Connected<br />
Executed SQL Statement:<br />
SELECT code, sname FROM state where code < ?<br />
Using: 'CA'<br />
Fetched: AK, Alaska<br />
Fetched: AL, Alabama<br />
Fetched: AR, Arkansas<br />
Fetched: AZ, Arizona<br />
C:\work>
Calling SQL routines<br />
In this section, we demonstrate how to call an SQL routine from an ODBC<br />
application. The following example is a simple stored procedure that returns the<br />
user name and session ID:<br />
create procedure get_sid(user char(20)) returning char(100);<br />
return 'user= '||trim(user)|| ' session= '||dbinfo('sessionid');<br />
end procedure;<br />
We use the following ODBC standard syntax for calling a database routine:<br />
{? = call client_routine(?, ?,...)}<br />
The first placeholder (?) is used only when the first parameter of the routine is an<br />
output parameter. If the first parameter is not an output parameter, you can run<br />
the routine (stored procedure or SQL function) using the following syntax:<br />
{call client_routine(?, ?, ?, ?)}<br />
Example 3-17 illustrates how to call the get_sid() SQL function.<br />
Example 3-17 SPL function sample, Function.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
SQLCHAR sqlstmt[100];<br />
SQLCHAR errorn[20+1];<br />
SQLCHAR result[100+1];<br />
SQLLEN lresult = 0;<br />
sprintf((char *) connStrIn, "DSN=demo_on");<br />
sprintf((char *) sqlstmt, "{? = call get_sid(?)}");<br />
sprintf((char *) errorn, "informix");<br />
Chapter 3. Working with the ODBC driver 89
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
fprintf (stdout, "Connected\n");<br />
90 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
SQLBindParameter(hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_CHAR,sizeof(result),0,<br />
result, sizeof(result), &lresult);<br />
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(errorn),0,<br />
errorn, 0, NULL);<br />
}<br />
SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
SQLFetch(hstmt);<br />
fprintf (stdout, "Executed SQL Statement:\n%s\nUsing: '%s'\n",sqlstmt,errorn);<br />
fprintf (stdout, "Value returned: %s\n",result);<br />
SQLCloseCursor(hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-18 shows the input parameter passed to the get_sid() procedure<br />
and the return values.<br />
Example 3-18 Output of Function.c<br />
C:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -ID:\infx\csdk350tc7\incl\cli<br />
odbc32.lib /nologo function.c<br />
function.c<br />
C:\work>function<br />
Connected<br />
Executed SQL Statement:<br />
{? = call get_sid(?)}<br />
Using: 'informix'<br />
Value returned: user: informix session: 213<br />
C:\work><br />
Local transactions<br />
The default transaction mode for the <strong>Informix</strong> ODBC driver is auto-commit. The<br />
transaction is committed automatically if the SQL statement runs successfully.
You can switch to manual-commit by setting the SQL_AUTOCOMMIT_OFF<br />
attribute using the SQLSetConnectAttr() function. When an application sets<br />
SQL_AUTOCOMMIT_OFF, the next SQL statement automatically starts a<br />
transaction that remains open until the application calls SQLEndTran().<br />
In manual mode, all the statements executed by the application are committed or<br />
rolled back when the application calls SQLEndTran().<br />
Having auto-commit set might be more convenient when running simple SQL<br />
statements because there are less tasks to care about in the application code.<br />
However, auto-commit gives less control to the developer than using the<br />
manual-commit mode. When using auto-commit, there is no option to roll back a<br />
particular change in the database or to insert a multiple rows as a batch<br />
operation, which might improve the performance of the application.<br />
Example 3-19 illustrates how to run a local transaction in manual mode.<br />
Example 3-19 Transaction.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
SQLCHAR sqlstmt[100];<br />
SQLINTEGER cnum = 101;<br />
SQLSMALLINT datatype, decimaldigits, nullable;<br />
SQLUINTEGER paramsize;<br />
sprintf((char *) connStrIn, "DSN=demo_on");<br />
sprintf((char *) sqlstmt, "INSERT INTO orders(order_num,order_date,customer_num)<br />
VALUES (0,current,?)");<br />
Chapter 3. Working with the ODBC driver 91
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
fprintf (stdout, "Connected\n");<br />
92 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
SQLSetConnectAttr (hdbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, (SQLINTEGER)<br />
NULL);<br />
SQLPrepare (hstmt, sqlstmt, strlen(sqlstmt));<br />
SQLDescribeParam(hstmt, 1, &datatype, ¶msize, &decimaldigits, &nullable);<br />
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SHORT, datatype, paramsize,<br />
decimaldigits, &cnum, 0, NULL);<br />
}<br />
while (cnumcl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -ID:\infx\csdk350tc7\incl\cli<br />
odbc32.lib /nologo transact.c<br />
transact.c<br />
C:\work>del pp*<br />
C:\work>transact.exe<br />
Connected<br />
Executed SQL Statement:<br />
INSERT INTO orders(order_num,order_date,customer_num) VALUES (0,current,?)<br />
Using: '101'<br />
Executed SQL Statement:
INSERT INTO orders(order_num,order_date,customer_num) VALUES (0,current,?)<br />
Using: '102'<br />
Executed SQL Statement:<br />
INSERT INTO orders(order_num,order_date,customer_num) VALUES (0,current,?)<br />
Using: '103'<br />
Executed SQL Statement:<br />
INSERT INTO orders(order_num,order_date,customer_num) VALUES (0,current,?)<br />
Using: '104'<br />
Transaction Rolled back<br />
C:\work><br />
Distributed transactions<br />
On a Windows system, you can run a distributed transaction using the Microsoft<br />
Distributed Transaction Coordinator (MSDTC) service.<br />
Example 3-21 demonstrates how to run a distributed transaction. The example<br />
opens two connections to two different database servers and enlists both<br />
connections into the same distributed transaction.<br />
Example 3-21 Transaction_dtc.cpp<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc, hdbc2;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt, hstmt2;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[100],connStrIn2[100];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
SQLCHAR sqlstmt[100];<br />
SQLINTEGER cnum = 101;<br />
SQLSMALLINT datatype, decimaldigits, nullable;<br />
SQLUINTEGER paramsize;<br />
Chapter 3. Working with the ODBC driver 93
94 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
ITransactionDispenser *pTransactionDispenser = NULL;<br />
ITransaction * pITransaction;<br />
sprintf((char *) connStrIn , "DSN=server_1");<br />
sprintf((char *) connStrIn2, "DSN=server_2");<br />
sprintf((char *) sqlstmt, "INSERT INTO orders(order_num,order_date,customer_num)<br />
VALUES (0,current,101)");<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc2);<br />
// Get DTC<br />
DtcGetTransactionManager(NULL, NULL,IID_ITransactionDispenser, 0, 0, NULL, (void**)<br />
&pTransactionDispenser);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
fprintf (stdout, "Connected to %s\n",connStrIn);<br />
SQLDriverConnect (hdbc2, NULL, connStrIn2, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
fprintf (stdout, "Connected to %s\n",connStrIn2);<br />
//Start the transaction on DTC<br />
pTransactionDispenser->BeginTransaction<br />
(NULL,ISOLATIONLEVEL_READCOMMITTED,0,NULL,&pITransaction);<br />
//Enlist the connections in distributed transaction<br />
SQLSetConnectAttr(hdbc, SQL_ATTR_ENLIST_IN_DTC,<br />
(SQLPOINTER)pITransaction,SQL_IS_INTEGER);<br />
SQLSetConnectAttr(hdbc2, SQL_ATTR_ENLIST_IN_DTC,<br />
(SQLPOINTER)pITransaction,SQL_IS_INTEGER);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
fprintf (stdout, "Executed SQL Statement:\n%s\nUsing: '%d'\n",sqlstmt,cnum);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc2, &hstmt2 );<br />
if (SQLExecDirect (hstmt2, sqlstmt, SQL_NTS)!=SQL_SUCCESS)<br />
{<br />
fprintf(stdout, "Rolling back global transaction\n");<br />
pITransaction->Abort(NULL,FALSE,FALSE);<br />
}<br />
else<br />
{<br />
pITransaction->Commit( 0, XACTTC_SYNC_PHASEONE, 0 );<br />
pITransaction->Release();<br />
fprintf(stdout, "Transaction Committed\n");<br />
}
}<br />
SQLFreeHandle(SQL_HANDLE_STMT,hstmt2);<br />
SQLFreeHandle(SQL_HANDLE_STMT,hstmt);<br />
SQLDisconnect (hdbc2);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc2);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-22 shows how to compile the sample and the output of the program.<br />
Example 3-22 Output of Transact_dtc.cpp<br />
C:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -I%INFORMIXDIR%\incl\cli<br />
odbc32.lib xoleHlp.lib /nologo transaction_dtc.cpp<br />
transaction_dtc.cpp<br />
C:\work>transaction_dtc<br />
Connected to DSN=server_1<br />
Connected to DSN=server_2<br />
Executed SQL Statement:<br />
INSERT INTO orders(order_num,order_date,customer_num) VALUES (0,current,101)<br />
Using: '101'<br />
Transaction Committed<br />
C:\work><br />
Note: Remember that the DSN must be accessible to the MSDTC service. In<br />
Example 3-21 on page 93, test_1 and test_2 are both created as<br />
System-DSN.<br />
3.3.4 Handling special data types<br />
This section describes how to work with the <strong>Informix</strong> specific data types, such as<br />
smart large objects and complex data types.<br />
Smart large objects<br />
Smart large objects are a type of large objects supported by <strong>Informix</strong>. Smart<br />
large objects are logically stored in a table column but physically stored in a<br />
specific type of dbspaces called smart blob space (sbspace).<br />
The information stored in the table column is structure that contains information<br />
about the large object, such as the pointer to the location in the sbspace that<br />
Chapter 3. Working with the ODBC driver 95
contains the data or special attributes. <strong>Informix</strong> has two types of smart large<br />
objects.<br />
► BLOB: Stores binary data<br />
► CLOB: Stores character data<br />
A client application uses these data structures to perform random I/O operations<br />
on the large data, such as open, read, or write operations. There are two options<br />
for accessing smart large objects from an ODBC application:<br />
► Use the smart-large-object ODBC API.<br />
If the application requires random access to the large data, it must use the<br />
smart large object functions. These functions give the application a greater<br />
control over the smart-large-object data in terms of object properties,<br />
concurrency access, and logging.<br />
► Use smart-large-object automation.<br />
The ODBC driver automatically uses the ODBC API for handling large<br />
objects. The application can access smart large objects as standard ODBC<br />
data types (SQL_LONGVARBINARY and SQL_LONGVARCHAR).<br />
To enable smart-large-object automation use the “Report Standard ODBC<br />
Types Only DSN” option under the DSN Advanced tab or set the<br />
SQL_INFX_ATTR_LO_AUTOMATIC connection attribute.<br />
Data structures for smart large objects<br />
Table 3-10 describes the data structures used by the smart-large-object<br />
functions.<br />
Table 3-10 Smart large object data structures<br />
Data structure Name Description<br />
lofd file descriptor Defines the file descriptor to access<br />
smart-large-object data.<br />
loptr pointer structure Contains the security information and<br />
pointer for a smart large object. This is the<br />
data stored with the table columns.<br />
lospec specification structure Contains the storage characteristics for a<br />
smart large object.<br />
lostat status structure Contains the status information for a smart<br />
large object.<br />
96 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Client functions to access smart large objects<br />
Table 3-11 lists the SQL functions that you can use in your ODBC program to<br />
access the <strong>Informix</strong> smart large objects.<br />
Table 3-11 Smart large object client functions<br />
Function Description<br />
ifx_lo_alter(loptr, lospec) Alters the storage attributes like Logging or<br />
Last-access<br />
ifx_lo_close(lofd) Closes a smart large object<br />
ifx_lo_col_info(colname, lospec) Updates column-level storage characteristics<br />
ifx_lo_create(lospec, flags, loptr, lofd) Creates and opens a new smart large object<br />
ifx_lo_def_create_spec(lospec) Creates a smart-large-object specification<br />
structure<br />
ifx_lo_open(lofd, loptr, flags) Opens a smart large object<br />
ifx_lo_read(lofd, buf) Reads data from a smart large object<br />
ifx_lo_readwithseek(lofd, buf, offset,<br />
whence)<br />
ifx_lo_seek(), ifx_lo_seek(lofd, offset,<br />
whence, seek_pos)<br />
ifx_lo_specget_estbytes(lospec,<br />
estbytes)<br />
Reads starting at a specific location<br />
Sets the file position for the next operation<br />
Returns the estimated size<br />
ifx_lo_specget_extsz(lospec, extsz) Returns the allocation extent size<br />
ifx_lo_specget_flags(lospec, flags) Returns create-time flags<br />
ifx_lo_specget_maxbytes(lospec,<br />
maxbytes)<br />
ifx_lo_specget_sbspace(lospec,<br />
sbspace)<br />
ifx_lo_specset_estbytes(lospec,<br />
estbytes)<br />
Returns the maximum number of bytes<br />
Returns the sbspace name<br />
Sets the estimated number of bytes<br />
ifx_lo_specset_extsz(lospec, extsz) Sets the allocation extent size<br />
ifx_lo_specset_flags(lospec, flags) Sets the create-time flags<br />
ifx_lo_specset_maxbytes(lospec,<br />
maxbytes)<br />
Sets the maximum number of bytes<br />
Chapter 3. Working with the ODBC driver 97
Function Description<br />
ifx_lo_specset_sbspace(lospec,<br />
sbspace)<br />
Calling client functions from ODBC<br />
An application must use the following standard ODBC syntax to call the<br />
smart-large-object functions:<br />
{? = call function_name (?, ?,...)}<br />
The following example calls the ifx_lo_open() function:<br />
{? = call ifx_lo_open(?, ?, ?)}<br />
Creating a smart large object<br />
To create a smart large object, you must create a BLOB descriptor and then<br />
insert the BLOB into the database.<br />
Use the following steps to create a new smart large object:<br />
1. Allocate memory for the specification structure using lospec.<br />
2. Create a lospec using ifx_lo_def_create_spec().<br />
3. Initialize the specification structure using lospec.<br />
4. Allocate memory for the pointer structure using loptr.<br />
5. Create the object using ifx_lo_create().<br />
98 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Sets the sbspace name<br />
ifx_lo_stat(lofd, lostat) Initializes a smart-large-object status structure<br />
ifx_lo_stat_atime(lostat, atime) Returns the last-access time<br />
ifx_lo_stat_cspec(lostat, lospec) Returns the specification structure<br />
ifx_lo_stat_ctime(lostat, ctime) Returns the last-time change<br />
ifx_lo_stat_refcnt(lostat, refcount) Returns the number of references<br />
ifx_lo_stat_size(lostat, size) Returns the size<br />
ifx_lo_tell(lofd, seek_pos) Returns the current file position<br />
ifx_lo_truncate(lofd, offset) Truncates a smart large object at a given<br />
position<br />
ifx_lo_write(lofd, buf) Writes data<br />
ifx_lo_writewithseek(lofd, buf, offset,<br />
whence)<br />
Writes data at a specific location
6. Write the data into the object using ifx_lo_write().<br />
7. Perform an SQL operation (INSERT, UPDATE, and so on).<br />
8. Close the smart large object using ifx_lo_close().<br />
Example 3-23 shows how to insert a BLOB into the database.<br />
Example 3-23 Create_lo.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000], connStrOut[1000], sqlstmt[100];<br />
SQLSMALLINT connStrOutLen;<br />
SQLINTEGER cnum = 101;<br />
SQLSMALLINT datatype, decimaldigits, nullable;<br />
SQLUINTEGER paramsize;<br />
SQLCHAR colname[25] = "catalog.advert_descr";<br />
SQLINTEGER colname_size = SQL_NTS;<br />
SQLCHAR* file_name = (SQLCHAR *) "create_lo.c";<br />
SQLCHAR* blob_data;<br />
SQLSMALLINT blob_size;<br />
SQLINTEGER blob_wsize, mode = LO_RDWR, cbMode = 0;<br />
int fd=0;<br />
struct stat statbuf;<br />
/* BLOB file descriptor */<br />
SQLINTEGER lofd;<br />
SQLINTEGER lofd_valsize = 0;<br />
/* BLOB pointer structure */<br />
SQLCHAR* loptr_buffer;<br />
SQLSMALLINT loptr_size;<br />
SQLINTEGER loptr_valsize = 0;<br />
/* BLOB specification structure */<br />
SQLCHAR* lospec_buffer;<br />
Chapter 3. Working with the ODBC driver 99
SQLSMALLINT lospec_size;<br />
SQLINTEGER lospec_valsize = 0;<br />
sprintf((char *) connStrIn, "DSN=demo_on");<br />
sprintf((char *) sqlstmt, "INSERT INTO catalog(catalog_num,advert_descr) VALUES<br />
(0,?)");<br />
/* Read the file to insert in the BLOB */<br />
stat(file_name,&statbuf);<br />
blob_size = statbuf.st_size;<br />
blob_data = malloc (blob_size + 1);<br />
fd = _open (file_name, O_RDONLY);<br />
_read (fd, blob_data, blob_size);<br />
_close(fd);<br />
blob_data[blob_size] = '\0';<br />
blob_wsize = blob_size;<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, sizeof(connStrOut),<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
/* Obtain the size of the lospec and allocate memory */<br />
SQLGetInfo (hdbc, SQL_INFX_LO_SPEC_LENGTH, &lospec_size, sizeof(lospec_size), NULL);<br />
lospec_buffer = malloc (lospec_size);<br />
/* Call ifx_lo_def_create_spec() to create a lospec */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT_OUTPUT, SQL_C_BINARY,<br />
SQL_INFX_UDT_FIXED, (UDWORD)lospec_size, 0, lospec_buffer, lospec_size,<br />
&lospec_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_def_create_spec(?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Call ifx_lo_col_info() to initialise the lospec */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(colname),<br />
0, colname, sizeof(colname), &colname_size);<br />
lospec_valsize = lospec_size;<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT_OUTPUT, SQL_C_BINARY,<br />
SQL_INFX_UDT_FIXED, (UDWORD)lospec_size, 0, lospec_buffer, lospec_size,<br />
&lospec_valsize);<br />
rc=SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_col_info(?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Obtain the size of the smart loptr and allocate memory */<br />
SQLGetInfo (hdbc, SQL_INFX_LO_PTR_LENGTH, &loptr_size, sizeof(loptr_size), NULL);<br />
loptr_buffer = malloc (loptr_size);<br />
/* Call ifx_lo_create() to create the BLOB */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_UDT_FIXED,<br />
(UDWORD)lospec_size, 0, lospec_buffer, lospec_size, &lospec_valsize);<br />
100 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
c=SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, (UDWORD)0,<br />
0, &mode, sizeof(mode), &cbMode);<br />
loptr_valsize = loptr_size;<br />
SQLBindParameter (hstmt, 3, SQL_PARAM_INPUT_OUTPUT, SQL_C_BINARY,<br />
SQL_INFX_UDT_FIXED, (UDWORD)loptr_size, 0, loptr_buffer, loptr_size, &loptr_valsize);<br />
SQLBindParameter (hstmt, 4, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, (UDWORD)0,<br />
0, &lofd, sizeof(lofd), &lofd_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_create(?, ?, ?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Call ifx_lo_write to write the BLOB data */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&lofd, sizeof(lofd), &lofd_valsize);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,<br />
(UDWORD)blob_size, 0, blob_data, blob_size, &blob_wsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_write(?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
loptr_valsize = loptr_size;<br />
/* Call SQLExecDirect to do the INSERT */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_UDT_FIXED,<br />
(UDWORD)loptr_size, 0, loptr_buffer, loptr_size, &loptr_valsize);<br />
SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
/* Call ifx_lo_close() to close the BLOB */<br />
rc = SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,<br />
(UDWORD)0, 0, &lofd, sizeof(lofd), &lofd_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_close(?)}", SQL_NTS);<br />
}<br />
free (lospec_buffer);<br />
free (loptr_buffer);<br />
free (blob_data);<br />
SQLFreeStmt (hstmt, SQL_CLOSE);<br />
SQLFreeHandle (SQL_HANDLE_STMT, hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Accessing a smart large object<br />
Reading a smart large object from the database involves the following steps:<br />
1. Allocate memory for the smart-large-object pointer structure (loptr).<br />
2. Perform the SELECT statement.<br />
3. Bind the smart-large-object pointer structure variable with loptr retrieved<br />
from the database.<br />
Chapter 3. Working with the ODBC driver 101
4. Call ifx_lo_open() to open the smart large object with the loptr fetched<br />
from the database. This step opens the smart large object on the database.<br />
5. Obtain the size of the smart large object status structure (lostat) and allocate<br />
enough memory.<br />
6. Call ifx_lo_stat() to obtain the BLOB status structure, so that we can<br />
retrieve the estimated size of the BLOB in the next step.<br />
7. Call ifx_lo_stat_size() to retrieve the size of the BLOB data and to allocate<br />
enough memory.<br />
8. Call ifx_lo_read() to retrieve the BLOB data.<br />
9. Perform any operation with the BLOB data.<br />
10.Call ifx_lo_close() to close the BLOB.<br />
Example 3-24 shows a simple application that selects a CLOB column from the<br />
database.<br />
Example 3-24 The select_lo.c application<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBChdbc;<br />
SQLHENVhenv;<br />
SQLHSTMThstmt;<br />
/* BLOB file descriptor */<br />
SQLLENlofd;<br />
SQLLENlofd_valsize = 0;<br />
/* BLOB pointer structure */<br />
SQLCHAR*loptr_buffer;<br />
SQLSMALLINTloptr_size;<br />
SQLLENloptr_valsize = 0;<br />
/* BLOB status structure */<br />
SQLCHAR*lostat_buffer;<br />
SQLSMALLINTlostat_size;<br />
SQLLENlostat_valsize = 0;<br />
/* BLOB data */<br />
102 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
SQLCHAR*lo_data;<br />
SQLLENlo_data_valsize = 0;<br />
SQLRETURNrc = 0;<br />
SQLCHAR*dsn = "demo_on";<br />
SQLCHAR*selectStmt = (SQLCHAR *) "SELECT advert_descr FROM catalog where catalog_num<br />
= 10075";<br />
SQLINTEGERmode = LO_RDONLY;<br />
SQLLENlo_size;<br />
SQLLENcbMode = 0, cbLoSize = 0;<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLConnect (hdbc, dsn, SQL_NTS, (SQLCHAR *) "", SQL_NTS, (SQLCHAR *) "", SQL_NTS);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
/* Get the size of the loptr */<br />
SQLGetInfo (hdbc, SQL_INFX_LO_PTR_LENGTH, &loptr_size, sizeof(loptr_size), NULL);<br />
loptr_buffer = malloc (loptr_size);<br />
SQLExecDirect (hstmt, selectStmt, SQL_NTS);<br />
/* Bind the loptr with the resulset column */<br />
SQLBindCol (hstmt, 1, SQL_C_BINARY, loptr_buffer, loptr_size, &loptr_valsize);<br />
SQLFetch (hstmt);<br />
SQLCloseCursor (hstmt);<br />
/* Call ifx_lo_open() using the loptr fetched from the database */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&lofd, sizeof(lofd), &lofd_valsize);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_UDT_FIXED,<br />
(UDWORD)loptr_size, 0, loptr_buffer, loptr_size, &loptr_valsize);<br />
SQLBindParameter (hstmt, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&mode, sizeof(mode), &cbMode);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{? = call ifx_lo_open(?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Get the size of the lostat and allocate enough memory */<br />
SQLGetInfo (hdbc, SQL_INFX_LO_STAT_LENGTH, &lostat_size, sizeof(lostat_size), NULL);<br />
lostat_buffer = malloc(lostat_size);<br />
/* Call ifx_lo_stat() to get the BLOB lostat */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&lofd, sizeof(lofd), &lofd_valsize);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT_OUTPUT, SQL_C_BINARY,<br />
SQL_INFX_UDT_FIXED, (UDWORD)lostat_size, 0, lostat_buffer, lostat_size,<br />
&lostat_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_stat(?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Call ifx_lo_stat_size to get the size of the BLOB data and allocate enough memory*/<br />
Chapter 3. Working with the ODBC driver 103
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_UDT_FIXED,<br />
(UDWORD)lostat_size, 0, lostat_buffer, lostat_size, &lostat_valsize);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_BIGINT, (UDWORD)0, 0,<br />
&lo_size, sizeof(lo_size), &cbLoSize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_stat_size(?, ?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
lo_data = malloc (lo_size + 1);<br />
/* Call ifx_lo_read() to get the BLOB data */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&lofd, sizeof(lofd), &lofd_valsize);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_CHAR, lo_size, 0,<br />
lo_data, lo_size, &lo_data_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_read(?, ?)}", SQL_NTS);<br />
lo_data[lo_size] = '\0';<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* Call ifx_lo_close() to close the BLOB */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, (UDWORD)0, 0,<br />
&lofd, sizeof(lofd), &lofd_valsize);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{call ifx_lo_close(?)}",SQL_NTS);<br />
}<br />
fprintf(stdout,"%s",lo_data);<br />
free (loptr_buffer);<br />
free (lostat_buffer);<br />
free (lo_data);<br />
SQLFreeStmt (hstmt, SQL_CLOSE);<br />
SQLFreeHandle (SQL_HANDLE_STMT, hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Smart large object automation<br />
Developing an application to handle smart large objects using the smart large<br />
object automation method does not require any special attention. Use<br />
SQL_LONGBINARY and SQL_LONGVARCHAR as data type.<br />
Example 3-25 Illustrates how to insert a smart large object with the<br />
SQL_INFX_ATTR_LO_AUTOMATIC enabled.<br />
Example 3-25 Lo_auto.c<br />
#include <br />
#include <br />
#include <br />
104 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLCHAR* dsn="demo_on";<br />
SQLCHAR sqlstmt[128];<br />
SQLCHAR str[128];<br />
SQLLEN str_len = SQL_NTS;<br />
SQLSMALLINT BufferLength = 128;<br />
SQLINTEGER int_val;<br />
SQLRETURN rc = 0;<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLConnect (hdbc, dsn, SQL_NTS, (SQLCHAR *) "", SQL_NTS, (SQLCHAR *) "", SQL_NTS);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
/* Set SQL_INFX_ATTR_ODBC_TYPES_ONLY connection attribute to FALSE*/<br />
SQLSetConnectAttr(hdbc, SQL_INFX_ATTR_ODBC_TYPES_ONLY, (SQLPOINTER) SQL_FALSE,<br />
SQL_IS_UINTEGER);<br />
/* Set SQL_INFX_ATTR_LO_AUTOMATIC connection attribute to TRUE*/<br />
SQLSetConnectAttr(hdbc, SQL_INFX_ATTR_LO_AUTOMATIC, (SQLPOINTER) SQL_TRUE,<br />
SQL_IS_UINTEGER);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
/* Preare the SQL statement */<br />
sprintf(sqlstmt, "INSERT INTO catalog(catalog_num,advert_descr) VALUES (0,?)");<br />
SQLPrepare (hstmt, sqlstmt, SQL_NTS);<br />
/* Bind the input parameter and Execute the SQL */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR,<br />
BufferLength, 0, &str, 0, &str_len);<br />
SQLExecute (hstmt);<br />
SQLFreeStmt (hstmt, SQL_CLOSE);<br />
SQLFreeHandle (SQL_HANDLE_STMT, hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
Chapter 3. Working with the ODBC driver 105
}<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Complex data types<br />
Rows and Collections are composite values that consist of one or more<br />
elements. You can use the execute SQL statements to access an entire row or<br />
collection. However, you cannot access individual elements within a row or<br />
collection.<br />
To access composite values from the ODBC driver you must use the Client<br />
functions for Rows and Collections.<br />
Client functions for Rows and Collections<br />
The ODBC functions for handling Rows and Collections start with the prefix<br />
ifx_rc_. Table 3-12 lists the available functions.<br />
Table 3-12 Row and Collection Client functions<br />
Function Description<br />
ifx_rc_count() Returns the number of elements or fields that are in a row or<br />
collection<br />
ifx_rc_create() Creates a buffer for a row or collection<br />
ifx_rc_delete() Deletes an element from a collection<br />
ifx_rc_describe() Returns information about the complex data type or and individual<br />
element<br />
ifx_rc_fetch() Returns the value of an element that is in a row or collection<br />
ifx_rc_free() Frees a row or collection handle<br />
ifx_rc_insert() Inserts a new element into a collection<br />
ifx_rc_isnull() Evaluate if a complex type is NULL or not<br />
ifx_rc_setnull() Set a complex type to NULL<br />
ifx_rc_typespec() Returns the type specification for a row or collection<br />
ifx_rc_update() Updates the value for an element that is in a row or collection<br />
106 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Creating a complex data type<br />
Example 3-26 shows how to perform an INSERT operation using the <strong>Informix</strong><br />
ODBC functions ifx_rc_xxx. We use a simple table customer_rc defined as<br />
follows:<br />
CREATE ROW TYPE name_t (<br />
fname VARCHAR(15),<br />
lname VARCHAR(15)<br />
);<br />
CREATE TABLE customer_rc (<br />
customer_num SERIAL,<br />
customer_name name_t,<br />
contact_dates LIST(DATETIME YEAR TO DAY NOT NULL)<br />
);<br />
Example 3-26 Create_rc.c<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBChdbc;<br />
SQLHENVhenv;<br />
SQLHSTMThstmt;<br />
/* Row variables */<br />
HINFX_RChrow;<br />
HINFX_RChlist;<br />
SQLCHAR*dsn = "demo_on";<br />
SQLRETURNrc = 0;<br />
SQLINTEGERi, in;<br />
SQLLENdata_size = SQL_NTS;<br />
SQLSMALLINTposition = SQL_INFX_RC_ABSOLUTE;<br />
SQLSMALLINTjump;<br />
SQLCHARrow_data[2][15] = {"Ludwig", "Pauli"};<br />
SQLLENrow_data_size = SQL_NTS;<br />
SQLCHARlist_data[2][25] = {"2008-08-16","2008-08-16"};<br />
SQLLENlist_data_size = SQL_NTS;<br />
Chapter 3. Working with the ODBC driver 107
SQLCHAR*insertStmt = (SQLCHAR *) "INSERT INTO customer_rc VALUES (0, ?, ?)";<br />
SQLLENcbHrow = 0, cbHlist = 0, cbPosition = 0, cbJump = 0;<br />
/* Connection */<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLConnect (hdbc, dsn, SQL_NTS, (SQLCHAR *) "", SQL_NTS, (SQLCHAR *) "", SQL_NTS);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
/* Call ifx_rc_create() to allocate a row handle */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_BINARY, SQL_INFX_RC_ROW,<br />
sizeof(HINFX_RC), 0, &hrow, sizeof(HINFX_RC), &cbHrow);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, (SQLCHAR *)<br />
"ROW(fname VARCHAR(15), lname VARCHAR(15))", 0, &data_size);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{? = call ifx_rc_create(?)}", SQL_NTS);<br />
/* Call ifx_rc_create() to allocate a list handle */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_BINARY, SQL_INFX_RC_LIST,<br />
sizeof(HINFX_RC), 0, &hlist, sizeof(HINFX_RC), &cbHlist);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, (SQLCHAR *)<br />
"LIST (DATETIME YEAR TO DAY NOT NULL)", 0, &data_size);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{? = call ifx_rc_create(?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
/* call ifx_rc_update() to insert elements into the row */<br />
for (i=0; i
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_RC_COLLECTION,<br />
sizeof(HINFX_RC), 0, hrow, sizeof(HINFX_RC), &cbHrow);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_RC_COLLECTION,<br />
sizeof(HINFX_RC), 0, hlist, sizeof(HINFX_RC), &cbHlist);<br />
SQLExecDirect (hstmt, insertStmt, SQL_NTS);<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_RC_ROW,<br />
sizeof(HINFX_RC), 0, hrow, sizeof(HINFX_RC), &cbHrow);<br />
SQLExecDirect(hstmt, (SQLCHAR *)"{call ifx_rc_free(?)}", SQL_NTS);<br />
/* Free the list handle */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_INFX_RC_LIST,<br />
sizeof(HINFX_RC), 0, hlist, sizeof(HINFX_RC), &cbHlist);<br />
SQLExecDirect(hstmt, (SQLCHAR *)"{call ifx_rc_free(?)}", SQL_NTS);<br />
}<br />
SQLFreeStmt (hstmt, SQL_CLOSE);<br />
SQLFreeHandle (SQL_HANDLE_STMT, hstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-27 shows the information inserted into the customer_rc table after the<br />
execution of the example.<br />
Example 3-27 The customer_rc table<br />
> select * from customer_rc<br />
> ;<br />
customer_num 1<br />
customer_name ROW('Ludwig','Pauli')<br />
contact_dates LIST{'2008-08-16','2008-08-16'}<br />
1 row(s) retrieved.<br />
><br />
Chapter 3. Working with the ODBC driver 109
Accessing a complex data type<br />
Example 3-28 demonstrates how to select a Row type with the ODBC driver.<br />
Example 3-28 The select_row.c application<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLHSTMT hstmt, hstmt_row;<br />
HINFX_RC hrow;<br />
SQLCHAR *dsn="demo_on";<br />
SQLRETURN rc = 0;<br />
SQLINTEGER i;<br />
SQLSMALLINT position = SQL_INFX_RC_ABSOLUTE, jump = 0;<br />
SQLLEN cbHrow = 0, cbPosition = 0, cbJump = 0, cbRCData = 0;<br />
SQLLEN data_size = SQL_NTS, cbrow = 0;<br />
SQLCHAR* sqlstmt = (SQLCHAR *) "SELECT customer_name FROM customer_rc WHERE<br />
customer_num = 1";<br />
SQLCHAR row_data[200];<br />
/* Connection */<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLConnect (hdbc, dsn, SQL_NTS, (SQLCHAR *) "", SQL_NTS, (SQLCHAR *) "", SQL_NTS);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt);<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt_row);<br />
/* Call ifx_rc_create() to allocate a row handle */<br />
SQLBindParameter (hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_BINARY, SQL_INFX_RC_ROW,<br />
sizeof(HINFX_RC), 0, &hrow, sizeof(HINFX_RC), &cbHrow);<br />
SQLBindParameter (hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, (SQLCHAR *)<br />
"row", 0, &data_size);<br />
SQLExecDirect (hstmt, (SQLCHAR *) "{? = call ifx_rc_create(?)}", SQL_NTS);<br />
SQLFreeStmt (hstmt, SQL_RESET_PARAMS);<br />
110 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
* Bind the row handle with the resulset column */<br />
SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
SQLBindCol (hstmt, 1, SQL_C_BINARY, (SQLPOINTER) hrow, sizeof(HINFX_RC), &cbHrow);<br />
SQLFetch (hstmt);<br />
/* Get the row data */<br />
fprintf(stdout, "Row data:\n");<br />
for (i=0; icl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -ID:\infx\csdk350tc7\incl\cli<br />
odbc32.lib /nologo select_row.c<br />
select_row.c<br />
C:\work>select_row<br />
Row data:<br />
Chapter 3. Working with the ODBC driver 111
C:\work><br />
3.3.5 Error handling<br />
Ludwig<br />
Pauli<br />
In this section, we show how to obtain additional information when an ODBC<br />
function fails.<br />
Error handling sample<br />
When an ODBC function fails, it stores information in the diagnostic record. This<br />
record contains information such as error messages, warning, and status<br />
information about the success or failure of the ODBC call.<br />
An ODBC application can retrieve the diagnostic record or individual fields using<br />
the SQLGetDiagRec() and SQLGetDiagField() ODBC functions.<br />
Example 3-30 shows a typical usage of SQLGetDiagRec().<br />
Example 3-30 A sample connect_error.c application<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
SQLHENV henv;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
UCHAR SqlState[200] = "", ErrorMsg[200] = "";<br />
SQLINTEGER IsamError = 0;<br />
SDWORD NativeError = 0L;<br />
SWORD ErrorMsgp = 0;<br />
SQLSMALLINT recnum = 1;<br />
if (argc != 2)<br />
112 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
{<br />
fprintf (stdout, "Please specify the name of a DSN!\n");<br />
return(1);<br />
}<br />
else<br />
{<br />
if (strstr (argv[1],"DRIVER")==NULL)<br />
sprintf((char *) connStrIn, "DSN=%s;", (char *)argv[1]);<br />
else<br />
sprintf((char *) connStrIn, "%s;", (char *)argv[1]);<br />
}<br />
rc = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
rc = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
rc = SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
rc = SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, 1000,<br />
&connStrOutLen, SQL_DRIVER_NOPROMPT);<br />
if (rc != SQL_SUCCESS)<br />
{<br />
SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, recnum, SqlState, &NativeError, ErrorMsg,<br />
199, &ErrorMsgp);<br />
fprintf(stdout, "SqlState = %s\n Native Error = %d\n Error Message = %s\n",<br />
SqlState, NativeError, ErrorMsg);<br />
fprintf (stdout, "Connection failed!\n");<br />
return (1);<br />
}<br />
}<br />
fprintf (stdout, "Connection Successful\n");<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Example 3-31 shows the output of Example 3-30 on page 112. In addition to the<br />
ODBC SQLSTATE error, it returns the <strong>Informix</strong> native error and the description<br />
for the error.<br />
Example 3-31 Output of the connect_error.c application<br />
C:\work>cl /DWIN32 /D_CRT_SECURE_NO_DEPRECATE -I%INFORMIXDIR%\incl\cli<br />
odbc32.lib /nologo connect_error.c<br />
connect_error.c<br />
C:\work>connect_error wrongDNS<br />
SqlState = IM002<br />
Native Error = 0<br />
Error Message = [Microsoft][ODBC Driver Manager] Data source name not found<br />
and no default driver specified<br />
Connection failed!<br />
Chapter 3. Working with the ODBC driver 113
C:\work>connect_error demo_on;UID=wronguser<br />
SqlState = 28000<br />
Native Error = -951<br />
Error Message = [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]Incorrect password<br />
or user wronguser@localhost is not known on the database server.<br />
Connection failed!<br />
C:\work><br />
Because this function is called quite often, it make sense to have a function to<br />
display the error message. Example 3-32 illustrates a typical error handling<br />
function.<br />
Example 3-32 A sample simple_select_werr.c application<br />
#include <br />
#include <br />
#include <br />
#ifdef WIN32<br />
#include <br />
#include <br />
#include <br />
#endif<br />
#include "infxcli.h"<br />
void CheckDiag (SQLSMALLINT handle_type, SQLHANDLE handle, char *text)<br />
{<br />
RETCODErc = SQL_SUCCESS;<br />
UCHARSqlState[200] = "", ErrorMsg[200] = "";<br />
SQLINTEGERIsamError = 0;<br />
SDWORDNativeError = 0L;<br />
SWORDErrorMsgp = 0;<br />
SQLSMALLINTrecnum = 1;<br />
fprintf (stdout,"Error in %s \n",text);<br />
while (rc != SQL_NO_DATA_FOUND) {<br />
rc = SQLGetDiagRec(handle_type, handle, recnum, SqlState, &NativeError,<br />
ErrorMsg, 199, &ErrorMsgp);<br />
if (rc != SQL_NO_DATA_FOUND) {<br />
SQLGetDiagField(handle_type, handle, recnum, SQL_DIAG_ISAM_ERROR,<br />
&IsamError, SQL_IS_INTEGER, NULL);<br />
fprintf (stdout," SqlState = %s\n Native Error = %d\n Error Message = %s\n<br />
ISAM Error = %d\n", SqlState, NativeError, ErrorMsg, IsamError);<br />
}<br />
recnum++;<br />
}<br />
}<br />
int main (long argc, char* argv[])<br />
{<br />
SQLHDBC hdbc;<br />
114 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
SQLHENV henv;<br />
SQLHSTMT hstmt;<br />
SQLRETURN rc = 0;<br />
SQLCHAR connStrIn[1000];<br />
SQLCHAR connStrOut[1000];<br />
SQLSMALLINT connStrOutLen;<br />
int sqllen;<br />
SQLCHAR *sqlstmt;<br />
if (argc != 3)<br />
{<br />
fprintf (stdout, "Please specify the name of a DSN and the SQL Statement to<br />
run!\n");<br />
return(1);<br />
}<br />
else<br />
{<br />
if (strstr (argv[1],"DRIVER")==NULL)<br />
sprintf((char *) connStrIn, "DSN=%s;", (char *)argv[1]);<br />
else<br />
sprintf((char *) connStrIn, "%s;", (char *)argv[1]);<br />
}<br />
sqllen = strlen((char *)argv[2]);<br />
sqlstmt = (SQLCHAR *) malloc (sqllen + sizeof(char));<br />
strcpy((char *)sqlstmt, (char *)argv[2]);<br />
SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);<br />
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);<br />
SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc);<br />
SQLDriverConnect (hdbc, NULL, connStrIn, SQL_NTS, connStrOut, 1000, &connStrOutLen,<br />
SQL_DRIVER_NOPROMPT);<br />
fprintf (stdout, "Connected\n");<br />
SQLAllocHandle (SQL_HANDLE_STMT, hdbc, &hstmt );<br />
}<br />
rc = SQLExecDirect (hstmt, sqlstmt, SQL_NTS);<br />
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)<br />
{<br />
CheckDiag(SQL_HANDLE_STMT, hstmt,"SQLExecDirect()");<br />
fprintf (stdout, "SQLExecDirectW() failed!\n");<br />
return (1);<br />
}<br />
fprintf (stdout, "Executed SQL Statement:\n%s\n",sqlstmt);<br />
SQLDisconnect (hdbc);<br />
SQLFreeHandle (SQL_HANDLE_DBC, hdbc);<br />
SQLFreeHandle (SQL_HANDLE_ENV, henv);<br />
return (rc);<br />
Chapter 3. Working with the ODBC driver 115
We create a function called CheckDiag() to retrieve the diagnostic information:<br />
void CheckDiag (SQLSMALLINT handle_type, SQLHANDLE handle, char *text)<br />
Example 3-33 shows the output of the previous sample.<br />
Example 3-33 Output of the simple_select_werr.c application<br />
C:\work>simple_sql_werror demo_on "wrong sql"<br />
Connected<br />
Error in SQLExecDirect()<br />
SqlState = 42000<br />
Native Error = -201<br />
Error Message = [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]A syntax error has<br />
occurred.<br />
ISAM Error = 0<br />
SQLExecDirectW() failed!<br />
C:\work>simple_sql_werror demo_on "DELETE from wrongtable"<br />
Connected<br />
Error in SQLExecDirect()<br />
SqlState = 42S02<br />
Native Error = -206<br />
Error Message = [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]The specified table<br />
(wrongtable) is not in the database.<br />
ISAM Error = -111<br />
SQLExecDirectW() failed!<br />
C:\work><br />
Note: If an error is generated by the database server, the error message<br />
contains the string [<strong>Informix</strong>] before the description of the error. For<br />
example:<br />
ODBC SQLSTATE errors with <strong>Informix</strong> ODBC Driver<br />
The error that is returned by the ODBC function is based on the X/Open standard<br />
SQLSTATE errors. For the SQLSTATE codes, refer to the <strong>Informix</strong> ODBC Driver<br />
Guide at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.odbc.doc/si<br />
i091033222.htm#sii091033222<br />
116 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Error in SQLExecDirect()<br />
SqlState = 42S02<br />
Native Error = -206<br />
Error Message = [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]The specified<br />
table (test) is not in the database.<br />
ISAM Error = -111<br />
SQLExecDirectW() failed!
3.3.6 Troubleshooting<br />
In this section, we discuss typical problems when using the <strong>Informix</strong> ODBC<br />
drivers and available traces.<br />
Environment<br />
Make sure the setup of <strong>Informix</strong> Client is correct. If your application fails to load<br />
the ODBC driver, verify that the <strong>Informix</strong> Client or Data Server Client libraries are<br />
accessible to the application.<br />
► The environment variable PATH should contain the bin directory of your Client<br />
package.<br />
► The shared library PATH variable on a UNIX system should contain the<br />
directory where the shared libraries are located.<br />
► Make sure that the INFORMIXDIR variable is set correctly (as an environment<br />
variable or stored in the registry using the setnet32.exe utility).<br />
► Any 32-bit applications require 32-bit drivers to work. Make sure that the<br />
drivers that you have installed are of the same type as your application.<br />
If your application fails to connect, make sure the connection details are valid.<br />
Client SDK on Windows contains a simple connection tool called iLogin, which<br />
you can use to check whether you have connection with the database server. If<br />
iLogin fails to connect, all the other drivers will fail also.<br />
► Check that the values stored in the registry with the setnet32.exe utility are<br />
valid.<br />
► If you use 32-bit and 64-bit drivers on the same system, remember that on a<br />
Windows system, there are two registry hives to store the connectivity<br />
information. Make sure that both are correct.<br />
Tracing<br />
When developing an application using the ODBC driver, most problems are<br />
caused by passing incorrect parameters to the ODBC functions.<br />
ODBC Trace<br />
You can generate a trace file of all the calls to the ODBC driver using the ODBC<br />
Trace facility. On a Windows system, you can enable ODBC Trace using the<br />
ODBC Data Source Administrator.<br />
Chapter 3. Working with the ODBC driver 117
Figure 3-14 shows the Tracing tab.<br />
Figure 3-14 Enabling trace<br />
Note: Remember to select Machine-Wide tracing for all user identities if<br />
your application runs as a service or with a different user.<br />
Example 3-34 shows some of the entries in an ODBC trace file.<br />
Example 3-34 ODBC trace<br />
...<br />
rccreate 99c-db8 EXIT SQLBindParameter with return code 0<br />
(SQL_SUCCESS)<br />
HSTMT 01D51DC0<br />
UWORD 2<br />
SWORD 1 <br />
SWORD 1 <br />
SWORD 1 <br />
SQLULEN 25<br />
SWORD 0<br />
PTR 0x002CFEC8<br />
SQLLEN 0<br />
SQLLEN * 0x002CFF50 (-3)<br />
rccreate 99c-db8 ENTER SQLBindParameter<br />
HSTMT 01D51DC0<br />
UWORD 3<br />
SWORD 1 <br />
SWORD 5 <br />
118 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
...<br />
SWORD 5 <br />
SQLULEN 0<br />
SWORD 0<br />
PTR 0x002CFF68<br />
SQLLEN 0<br />
SQLLEN * 0x002CFF54<br />
On a UNIX system, ODBC trace is active if the Trace parameter in the [ODBC]<br />
section of the odbc.ini configuration file is set to 1.<br />
Example 3-35 shows the Trace settings for the odbc.ini file.<br />
Example 3-35 Trace in the odbc.ini file<br />
;<br />
; Trace file Section<br />
;<br />
Trace=1<br />
TraceFile=/tmp/odbctrace.out<br />
InstallDir=/opt/<strong>IBM</strong>/informix<br />
TRACEDLL=idmrs09a.so<br />
SQLIDEBUG<br />
All the <strong>Informix</strong> clients use the SQLI protocol to communicate with the database<br />
server.<br />
In addition to the ODBC trace, you can generate an SQLIDEBUG trace that<br />
contains all the message between the client application and the database server.<br />
You can enable SQLIDEBUG trace at the <strong>Informix</strong> Client side by defining the<br />
environment variable as follows:<br />
SQLIDEBUG=2:path_to_trace_files<br />
Alternatively, you can enable trace at the server side by running the following<br />
command:<br />
onmode -p 1 sqli_dbg<br />
Note: On a Windows system, when using the server side tracing, the<br />
SQLIDEBUG files are created in the C:\temp\sqli directory. This directory<br />
must exist before you can enable the trace.<br />
On a UNIX system, the trace files are created in the /tmp/sqli directory.<br />
Chapter 3. Working with the ODBC driver 119
Example 3-36 demonstrates how to set SQLIDEBUG trace on the client.<br />
Example 3-36 SQLIDEBUG sample<br />
C:\work>set SQLIDEBUG=2:sqli_trace<br />
C:\work>simple_sql_werror demo_on "wrong_sql"<br />
Connected<br />
Error in SQLExecDirect()<br />
SqlState = 42000<br />
Native Error = -201<br />
Error Message = [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]A syntax error has<br />
occurred.<br />
ISAM Error = 0<br />
SQLExecDirectW() failed!<br />
C:\work>dir sqli_trace_2940_1016_1f428b8<br />
Volume in drive C is W2003<br />
Volume Serial Number is 50DA-70D7<br />
Directory of C:\work<br />
22/06/2010 18:39 348 sqli_trace_2940_1016_1f428b8<br />
1 File(s) 348 bytes<br />
0 Dir(s) 76,628,099,072 bytes free<br />
C:\work><br />
The SQLIDEBUG trace files contains the SQLI packages between the server and<br />
the client. To obtain an readable output, you must run the sqliprt tool.<br />
Example 3-37 demonstrates how to run sqliprt.<br />
Example 3-37 The sqliprt output<br />
C:\work>sqliprt -o trace.txt sqli_trace_2940_1016_1f428b8<br />
C:\work>type trace.txt<br />
SQLIDBG Version 1<br />
C->S (4) Time: 2010-06-22 18:39:05.43700<br />
SQ_INTERNALVER<br />
Internal Version Number: 316<br />
C->S (14) Time: 2010-06-22 18:39:05.43700<br />
SQ_PROTOCOLS<br />
SQ_EOT<br />
S->C (14) Time: 2010-06-22 18:39:05.43700<br />
SQ_PROTOCOLS<br />
120 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
SQ_EOT<br />
C->S (90) Time: 2010-06-22 18:39:05.43700<br />
SQ_INFO<br />
INFO_ENV<br />
Name Length = 12<br />
Value Length = 34<br />
"DBTEMP"="C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp"<br />
"SUBQCACHESZ"="10"<br />
"OPTOFC"="0"<br />
INFO_DONE<br />
SQ_EOT<br />
S->C (2) Time: 2010-06-22 18:39:05.43700<br />
SQ_EOT<br />
C->S (4) Time: 2010-06-22 18:39:05.43700<br />
SQ_BEGIN<br />
SQ_EOT<br />
S->C (10) Time: 2010-06-22 18:39:05.43700<br />
SQ_XACTSTAT<br />
SQ_EOT<br />
C->S (4) Time: 2010-06-22 18:39:05.43700<br />
SQ_CMMTWORK<br />
SQ_EOT<br />
S->C (10) Time: 2010-06-22 18:39:05.43700<br />
SQ_XACTSTAT<br />
SQ_EOT<br />
C->S (22) Time: 2010-06-22 18:39:05.43700<br />
SQ_PREPARE<br />
# values: 0<br />
CMD.....: "wrong_sql" [9]<br />
SQ_NDESCRIBE<br />
SQ_WANTDONE<br />
SQ_EOT<br />
S->C (12) Time: 2010-06-22 18:39:05.43700<br />
SQ_ERR<br />
SQL error..........: -201<br />
ISAM/RSAM error....: 0<br />
Offset in statement: 1<br />
Error message......: "" [0]<br />
SQ_EOT<br />
C:\work><br />
Note: On a UNIX system, the equivalent of the sqliprt tool is sqliprint.<br />
Chapter 3. Working with the ODBC driver 121
DRDADEBUG<br />
All <strong>IBM</strong> Data Server drivers use the DRDA protocol to communicate with the<br />
database server.<br />
Similar to SQLIDEBUG, you can enable DRDADEBUG trace at the server side<br />
by running the following onmode command:<br />
onmode -p 1 drda_dbg<br />
The DRDA trace files are created in the /tmp/drda directory for a UNIX system<br />
and the C:\temp\drda directory for a Windows system. You need to use the<br />
drdaprint tool to convert the trace files to a readable format.<br />
Example 3-38 shows the use of drdaprint.<br />
Example 3-38 Using the drdaprint tool<br />
C:\temp\drda>dir<br />
Volume in drive C is W2003<br />
Volume Serial Number is 50DA-70D7<br />
Directory of C:\temp\drda<br />
22/06/2010 18:50 .<br />
22/06/2010 18:50 ..<br />
22/06/2010 18:50 2,269 drda.47<br />
1 File(s) 2,269 bytes<br />
2 Dir(s) 76,628,135,936 bytes free<br />
C:\temp\drda>drdaprint<br />
Usage: drdaprint [-f] [-o outfile] inpfile<br />
-f: format hex dump<br />
C:\temp\drda>drdaprint -o trace.txt drda.47<br />
C:\temp\drda><br />
Example 3-39 shows a section of the DRDA trace file.<br />
Example 3-39 DRDATrace file<br />
C:\temp\drda>type trace.txt<br />
DRDADBG Version 1<br />
1 data IDS DRDA Communication Manager<br />
function sqljcIntReceive()<br />
bytes 270<br />
time 2010-06-22 18:50:13.31200<br />
RECEIVE BUFFER(AS):<br />
122 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
EXCSAT RQSDSS (ASCII) (EBCDIC)<br />
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0123456789ABCDEF<br />
0000 00C3D041000100BD 10410080115EA289 ...A.....A...^.. .C}..........;si<br />
0010 949793856DA29893 6DA685999996994B ....m...m......K mple_sql_werror.<br />
0020 85A7F0F6F1F4F0C4 F9F4F0F0F0000000 ................ ex06140D94000...<br />
0030 0000000000000000 0000000000000000 ................ ................<br />
0040 0000000000000000 000000000060F0F0 .............`.. .............-00<br />
0050 F0F1C1C4D4C9D5C9 E2E3D9C1E3D6D940 ...............@ 01ADMINISTRATOR<br />
0060 4040404040404040 4040404040404040 @@@@@@@@@@@@@@@@<br />
0070 E2E3D6D9C5E26DC4 F0C4C2F240404040 ......m.....@@@@ STORES_D0DB2<br />
0080 4040404040404040 40F0001814041403 @@@@@@@@@....... 0......<br />
0090 000A2407000A1474 0005240F00081440 ..$....t..$....@ ...............<br />
00A0 0009000B1147D8C4 C2F261D5E3000A11 .....G....a..... ......QDB2/NT...<br />
00B0 6DC4E4C2C9E3D600 0C115AE2D8D3F0F9 m.........Z..... _DUBITO...]SQL09<br />
00C0 F0F7F0 ... 070<br />
Chapter 3. Working with the ODBC driver 123
124 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 4. Working with ESQL/C<br />
This chapter discusses ESQL/C, an SQL application programming interface<br />
(API) that enables you to embed Structured Query Language (SQL) statements<br />
directly into a C program. ESQL/C is bundled with the <strong>Informix</strong> Client Software<br />
Development Kit (Client SDK). The ESQL/C preprocessor, esql, converts each<br />
SQL statement and all <strong>IBM</strong> <strong>Informix</strong>-specific code to C-language source code<br />
and invokes the C compiler to compile it.<br />
The advantage of using ESQL/C is that it supports all the data types as well as<br />
extended data types of <strong>Informix</strong> and is optimized for an <strong>IBM</strong> <strong>Informix</strong> database. If<br />
you are using only <strong>Informix</strong> as your database server, then using ESQL/C might<br />
be your best choice.<br />
In this chapter, we discuss the basic elements of an <strong>Informix</strong> ESQL/C application<br />
and show how to perform database operations within an ESQL/C program. We<br />
end the chapter by looking at various methods in handling exceptions and<br />
troubleshooting.<br />
This chapter includes the following topics:<br />
► <strong>Informix</strong> ESQL/C<br />
► Setup and configuration<br />
► Windows system configuration<br />
► Developing an ESQL/C application<br />
4<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 125
4.1 <strong>Informix</strong> ESQL/C<br />
<strong>Informix</strong> ESQL/C includes the following software components:<br />
► The <strong>Informix</strong> ESQL/C libraries of C functions, which provide access to the<br />
database server<br />
► The <strong>Informix</strong> ESQL/C header files, which provide definitions for the data<br />
structures, constants, and macros that are useful to the <strong>Informix</strong> ESQL/C<br />
program<br />
► The esql command, which processes the <strong>Informix</strong> ESQL/C source code to<br />
create a C source file that it then passes to the C compiler<br />
► The finderr utility on the UNIX system and the <strong>Informix</strong> Error Messages<br />
Windows-based <strong>Informix</strong> error messages utility that enable you to obtain<br />
information about <strong>IBM</strong> <strong>Informix</strong>-specific error messages<br />
On Windows platforms, <strong>Informix</strong> provides the following additional utilities:<br />
► The setnet32.exe utility, which is a Windows-based utility that enables you to<br />
set configuration information<br />
► The iLogin utility, which is a demonstration program that displays a dialog box<br />
with fields for the connection parameters and for testing a connection to the<br />
database server (uses the stores7 database)<br />
Figure 4-1 gives an overview of the relationship between ESQL/C and the native<br />
C and illustrates the process of the transfer of control.<br />
Figure 4-1 Relationship between <strong>Informix</strong> ESQL/C and C<br />
When you have created an <strong>Informix</strong> ESQL/C program file, you run the esql<br />
command on that file. By default, the <strong>Informix</strong> ESQL/C preprocessor runs first<br />
and translates the embedded SQL statements in the program into <strong>Informix</strong><br />
ESQL/C function calls that communicate with the database server. The <strong>Informix</strong><br />
ESQL/C preprocessor produces a C source file and calls the C compiler. The C<br />
compiler then compiles your source file and links any other C source file, object<br />
file, or library file the same way as any other C program.<br />
126 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
If the esql command does not encounter errors in one of these steps, it<br />
generates an executable file. You can run the compiled <strong>Informix</strong> ESQL/C<br />
program as you would run any C program. When the program runs, it calls the<br />
<strong>Informix</strong> ESQL/C library procedures. The library procedures set up<br />
communications with the database server to carry out the SQL operations.<br />
4.2 Setup and configuration<br />
<strong>Informix</strong> ESQL/C is installed, by default, as a part of Client SDK. The release<br />
notes for Client SDK contain the information about supported versions of the <strong>IBM</strong><br />
<strong>Informix</strong> database server. Make sure that you install the version of Client SDK<br />
that is supported by the database with which you are working.<br />
4.3 Windows system configuration<br />
On a Windows system, the default Client SDK installation directory is C:\Program<br />
Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK. Make sure that the INFORMIXDIR<br />
environment variable is pointing to the directory where the product was installed.<br />
The PATH environment variable needs to contain the following directories:<br />
► The path to the bin directory that is under the installation directory needs to<br />
be defined (that is, the PATH variable needs the $INFORMIXDIR/bin directory<br />
defined).<br />
► The path where native C compiler is located. <strong>Informix</strong> Client products are<br />
certified with the Microsoft Visual C++ 2005 SP1.<br />
UNIX configuration<br />
On UNIX platforms, the default installation directory for Client SDK is<br />
/opt/<strong>IBM</strong>/informix. The INFORMIXDIR environment variable needs to point to<br />
the directory where the product was installed. Add the following directories to the<br />
PATH environment variable:<br />
► The bin directory under the installation directory, $INFORMIXDIR/bin<br />
► The path to the native C compiler<br />
The sqlhosts file contains the information that is required to connect to an <strong>IBM</strong><br />
<strong>Informix</strong> database server. You must set this file to include an entry for your<br />
<strong>Informix</strong> database server name, ESQL/C required protocol, host name, and port<br />
number. By default, the sqlhosts file is under the $INFORMIXDIR/etc/ directory.<br />
You can use the INFORMIXSQLHOSTS environment variable to point to a<br />
different location.<br />
Chapter 4. Working with ESQL/C 127
Example 4-1 shows a simple sqlhosts file.<br />
Example 4-1 The sqlhosts file<br />
#server protocol hostname service/port<br />
demo_on onsoctcp kodiak 9088<br />
For more information about the sqlhosts file, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.admin.doc/i<br />
ds_admin_0158.htm<br />
4.4 Developing an ESQL/C application<br />
In this section, we describe how to connect to a database, how to perform basic<br />
database operations such as delete and insert, and how to update using<br />
ESQL/C. We also describe the handling of extended data types by ESQL/C.<br />
We use the sample instance and database that <strong>Informix</strong> Server provides to<br />
demonstrate ESQL/C application development. The sample instance, demo_on, is<br />
created when the <strong>Informix</strong> server is installed. On a Windows operating system,<br />
the sample instance is ol_svr_custom. You can create a sample database,<br />
stores_demo, under the demo_on instance and populate that sample database<br />
with tables and data by running the dbaccessdemo utility that resides in the<br />
$INFORMIXDIR/bin/ directory.<br />
4.4.1 Creating an ESQL/C application<br />
To build an <strong>Informix</strong> ESQL/C application:<br />
1. Develop a C program with the embedded <strong>Informix</strong> SQL statements, and name<br />
the program with a .ec or .ecp extension. The SQL statements are qualified<br />
with an EXEC SQL keyword or the dollar sign ($) symbol as shown in the<br />
following example:<br />
EXEC SQL include sqlca;<br />
EXEC SQL SELECT fname into :c1 FROM customer WHERE customer_num=:i1<br />
$SET ISOLATION DIRTY READ;<br />
2. Preprocess the <strong>Informix</strong> ESQL/C source file with the esql command. The<br />
esql command also invokes the C compiler to compile the program into object<br />
code.<br />
As necessary, correct errors that are reported by the preprocessor and the<br />
compiler and then repeat this step.<br />
128 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
3. Link the object code into one or more executable files using the esql<br />
command. The executable files have a .exe extension.<br />
4. Run the application.<br />
You run the compiled <strong>Informix</strong> ESQL/C program as you would any C program.<br />
When the program runs, it calls the <strong>Informix</strong> ESQL/C library procedures. The<br />
library procedures set up communications with the database server to carry<br />
out the SQL operations.<br />
Example 4-2 shows a simple program, customer.ec, that connects to the<br />
database and retrieves data. All the SQL-related statements are embedded in<br />
the C program with the EXEC SQL keyword.<br />
Example 4-2 Simple ESQL/C program<br />
#include <br />
EXEC SQL include sqlca;<br />
EXEC SQL include sqltypes;<br />
int main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
int i1=101;<br />
char c1[50];<br />
char c2[50];<br />
EXEC SQL END DECLARE SECTION;<br />
int i2;<br />
EXEC SQL connect to 'stores_demo';<br />
EXEC SQL select fname,lname into :c1, :c2 from customer where<br />
customer_num=:i1<br />
;<br />
if (SQLCODE == 100)<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data not found\n");<br />
}<br />
else<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data found \n");<br />
printf("Last Name \t%s\n",c1);<br />
printf("First Name \t%s\n",c2);<br />
}<br />
}<br />
Chapter 4. Working with ESQL/C 129
Compiling ESQL/C programs<br />
The esql command translates <strong>Informix</strong> ESQL/C code to C code and then calls<br />
the C compiler to compile and link the C code.<br />
The C compiler takes the following actions:<br />
1. Compiles the C language statements to object code.<br />
2. Links to <strong>Informix</strong> ESQL/C libraries and to any other files or libraries that you<br />
specify.<br />
3. Creates an executable file.<br />
The general syntax for an esql command to compile is as follows:<br />
esql <br />
By default, the esql command creates an executable called a.out in the current<br />
directory. You can explicitly specify the name of the executable file with the -o<br />
option. For example, the following command compiles the customer.ec<br />
executable file shown in Example 4-2 on page 129 and produces the executable<br />
file customer.exe:<br />
esql -o customer.ec customer.exe<br />
If you run the esql command on a Windows operating system, the name of the<br />
target file defaults to the name of the first <strong>Informix</strong> ESQL/C source file on the<br />
esql command line. The extension is changed to either .exe or .dll, depending<br />
on the type of target that is generated.<br />
You can use a compiler other than the native C compiler by setting the<br />
INFORMIXC environment variable. Table 4-1 lists the native C compilers on<br />
various platforms.<br />
Table 4-1 Native C compiler<br />
Platforms Native compiler<br />
Solaris CC<br />
HP aC++<br />
Windows VC++ / VS2008/ VS2005<br />
AIX xlc<br />
Open source gcc/g++<br />
If you want to pass C compiler options that have the same names as <strong>Informix</strong><br />
ESQL/C processor options, precede them with the -cc option. For example, the<br />
130 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
following esql command passes the -od and -g options to the C compiler but<br />
uses the -db2 option itself:<br />
esql -cc -od -g demo1.ec -db2<br />
Shared libraries<br />
<strong>IBM</strong> <strong>Informix</strong> products use the <strong>Informix</strong> general libraries for interactions between<br />
the client SQL API products (<strong>IBM</strong> <strong>Informix</strong> ESQL/C and <strong>IBM</strong> <strong>Informix</strong><br />
ESQL/COBOL) and the database server. You can choose between the following<br />
types of <strong>Informix</strong> general libraries to link with your <strong>Informix</strong> ESQL/C application:<br />
► Static <strong>Informix</strong> general libraries<br />
To link a static library, the linker copies the library functions to the executable<br />
file of your <strong>Informix</strong> ESQL/C program. The static <strong>Informix</strong> general libraries<br />
allow an <strong>Informix</strong> ESQL/C program on computers that do not support shared<br />
memory to access the <strong>Informix</strong> general library functions.<br />
To link static libraries use the -static option, for example:<br />
esql -static file.ec -o file.exe<br />
► Shared <strong>Informix</strong> general libraries<br />
To link a shared library, the linker copies information about the location of the<br />
library to the executable file of your <strong>Informix</strong> ESQL/C program. The shared<br />
<strong>Informix</strong> libraries allow several applications to share a single copy of these<br />
libraries, which the operating system loads just once into shared memory.<br />
► Thread-safe versions of static and shared <strong>Informix</strong> general libraries<br />
The thread-safe versions of <strong>Informix</strong> general libraries allow an <strong>Informix</strong><br />
ESQL/C application that has several threads to call these library functions<br />
simultaneously. The thread-safe versions of <strong>Informix</strong> libraries are available as<br />
both static libraries and shared libraries.<br />
There are some platform specific considerations when you link shared <strong>Informix</strong><br />
general libraries to an ESQL/C module. The environment variable that specifies<br />
the search path at run time is different depending on the platform, as listed in<br />
Table 4-2.<br />
Table 4-2 Environment variable for shared libraries<br />
Platform Environment variable<br />
AIX LIBPATH<br />
Solaris LD_LIBRARY_PATH<br />
HP-UX SHLIB_PATH<br />
Mac OS X DYLD_LIBRARY_PATH<br />
Chapter 4. Working with ESQL/C 131
Platform Environment variable<br />
Linux LD_LIBRARY_PATH<br />
Windows LIB<br />
Set the $INFORMIXDIR/lib directory and any of its subdirectories to specify the<br />
shared-library path. For example, on Linux, set the LD_LIBRARY_PATH<br />
environment variable as follows:<br />
► Bourne shell<br />
LD_LIBRARY_PATH=$INFORMIXDIR/lib:$LD_LIBRARY_PATH<br />
export LD_LIBRARY_PATH<br />
► C shell<br />
setenv LD_LIBRARY_PATH $INFORMIXDIR/lib:$LD_LIBRARY_PATH<br />
On a Windows system, use the following command:<br />
set LIB = %INFORMIXDIR%\lib\;%LIB%<br />
To link shared <strong>Informix</strong> general libraries with an <strong>Informix</strong> ESQL/C module, you do<br />
not need to specify a command-line option. <strong>Informix</strong> ESQL/C links shared<br />
libraries by default. The following command compiles the file.ec source file with<br />
shared <strong>Informix</strong> libraries:<br />
esql myfile.ec -o myfile.exe<br />
Choosing between shared and static library versions<br />
Shared libraries are most useful in multiuser environments where only one copy<br />
of the library is required for all applications. Shared libraries bring the following<br />
benefits to your <strong>Informix</strong> ESQL/C application:<br />
► Shared libraries reduce the size of executable files because these library<br />
functions are linked dynamically on an as-needed basis.<br />
► At run time, a single copy of a shared library can be linked to several<br />
programs, which results in less memory use.<br />
► The effects of shared libraries in an <strong>Informix</strong> ESQL/C executable are<br />
transparent to the user.<br />
Although shared libraries save both disk and memory space, when an <strong>Informix</strong><br />
ESQL/C application uses them, it must perform the following overhead tasks:<br />
► Dynamically load the shared library into memory for the first time<br />
► Perform link-editing operations<br />
► Execute library position-independent code<br />
132 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
These overhead tasks can incur runtime penalties and are not necessary when<br />
you use static libraries.<br />
4.4.2 Performing database operations<br />
In this section, we discuss how to perform basic database operations in an<br />
ESQL/C application:<br />
► Database connections<br />
► Simple SQL statements (SELECT, INSERT, UPDATE, and DELETE)<br />
► Static and dynamic SQL<br />
► Calling SQL routines (stored procedures)<br />
► Using transactions<br />
In the program, we try to make use of most commonly used data types. For<br />
information about other data types, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
esqlc.doc/sii-03-sourceforchaptitle.htm<br />
Database connections<br />
When an <strong>Informix</strong> ESQL/C application executes, it has no connections to any<br />
database server. For SQL statements to run, however, such a connection must<br />
exist. To establish a connection to a database server, the <strong>Informix</strong> ESQL/C<br />
program must take the following actions:<br />
1. Use an SQL statement to establish a connection to the database server.<br />
2. Specify, in the SQL statement, the name of the database server to which to<br />
connect.<br />
The client application connects to the default database server when the<br />
application does not explicitly specify a database server for the connection. You<br />
must set the INFORMIXSERVER environment variable even if the application<br />
does not establish a connection to the default database server.<br />
If user name and password are not explicitly specified using the InetLogin<br />
structure or the USER clause, the default user ID is used to attempt the<br />
connection. The default user ID is the login name of the user who is running the<br />
application.<br />
Chapter 4. Working with ESQL/C 133
Example 4-3 shows a statement that connects to the stores_demo database<br />
under the demo_on instance.<br />
Example 4-3 Database connection statement<br />
EXEC SQL connect to 'stores_demo@demo_on';<br />
EXEC SQL connect to stores_demo user :username using :password;<br />
For more information about database connections in ESQL/C, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.esqlc.doc/s<br />
ii12164286.htm#sii12164286<br />
Simple SQL statements<br />
There are several way to execute an SQL statement from an ESQL/C application.<br />
The application must choose an appropriate method based on the nature of the<br />
SQL statement.<br />
► SQL statements that do not return a result set of data such as INSERT,<br />
DELETE, UPDATE, or Data Definition Language (DDL) can be executed<br />
using the EXEC SQL command (same as the $ prefix) or the EXEC SQL<br />
execute immediate statement. Both methods produce the same C code.<br />
EXEC SQL execute immediate :cmdstring;<br />
$CREATE TABLE my_customer (fname char(20));<br />
If the SQL statement is going to be executed more than one time during the<br />
life of the application, it can be prepared with the EXEC SQL PREPARE<br />
command and then executed when required using the EXEC SQL EXECUTE<br />
command:<br />
EXEC SQL prepare d_id from :stmt_buf;<br />
EXEC SQL execute d_id;<br />
► SQL statements that return one row can be executed using the EXEC SQL<br />
EXECUTE INTO command. These types of statement are normally referred<br />
as singleton statements.<br />
EXEC SQL SELECT specs into :mspecs FROM my_customer WHERE customer_num=2;<br />
► SQL statements that return more than one row are executed using a select<br />
cursor:<br />
EXEC SQL declare cursor1 cursor for SELECT fname into :var1 from customer;<br />
EXEC SQL open cursor1;<br />
EXEC SQL fetch cursor1;<br />
134 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 4-4 demonstrates how to execute an SQL statement using some of<br />
these previously described methods.<br />
Example 4-4 Simple SQL statements (INSERT, UPDATE, and DELETE)<br />
#include <br />
#include <br />
EXEC SQL include sqlca;<br />
EXEC SQL include sqltypes;<br />
int main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
char cmdstring[4096];<br />
string *fname[5] = { "Ludwig","Carole", "Philip", "Anthony", "Raymond" };<br />
string *lname[5] = { "Pauli", "Sadler", "Currie", "Higgins", "Vector" };<br />
string *company[5] = {"All Sports Supplies","Sports Spot", "Phil's Sports", "Play<br />
Ball!", "Los Altos Sports" };<br />
char pref='t';<br />
char *specs[5] = { "This is just any string ", "This is just another string ",<br />
"This is the third string", "This is one more string", "This is the last string"};<br />
lvarchar mspecs[250];<br />
char *order_date[5] = {"08/01/77","08/02/77","08/03/77","08/04/77","08/05/77"};<br />
float ship_charge[5] = {10000.59,590000.32,345577.12,987098.32,876893.22};<br />
char *ship_duration[5] = {"10:10:10","11:11:11","22:22:22","33:33:33","44:44:44"};<br />
EXEC SQL END DECLARE SECTION;<br />
$WHENEVER ERROR STOP;<br />
int i;<br />
EXEC SQL connect to 'stores_demo';<br />
$ CREATE TABLE my_customer (<br />
customer_num bigserial,<br />
fname char(15),<br />
lname char(15),<br />
company char(30),<br />
preferred boolean,<br />
specs lvarchar<br />
);<br />
$ CREATE TABLE my_orders (<br />
order_num bigserial,<br />
order_date date ,<br />
customer_num bigint,<br />
ship_charge money(10,2),<br />
ship_duration INTERVAL hour to second<br />
);<br />
for (i = 0; i < 5 ; i++)<br />
{<br />
$INSERT INTO my_customer VALUES (<br />
0,<br />
Chapter 4. Working with ESQL/C 135
:fname[i],<br />
:lname[i],<br />
:company[i],<br />
:pref,<br />
:specs[i]<br />
);<br />
}<br />
for (i = 0; i < 5 ; i++)<br />
{<br />
sprintf(cmdstring, "INSERT INTO my_orders VALUES (10000001,'%s',%d,%f,'%s' );",<br />
order_date[i], i,ship_charge[i], ship_duration[i]);<br />
EXEC SQL execute immediate :cmdstring;<br />
}<br />
sprintf(cmdstring, "UPDATE my_customer SET specs='This is a new spec' WHERE customer_num<br />
= 2");<br />
EXEC SQL execute immediate :cmdstring;<br />
if (SQLCODE != 0)<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
sprintf(cmdstring, "DELETE FROM my_customer where customer_num=3");<br />
EXEC SQL execute immediate :cmdstring;<br />
if (SQLCODE != 0)<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
EXEC SQL SELECT specs into :mspecs FROM my_customer WHERE customer_num=2;<br />
if (SQLCODE != 0)<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
else<br />
printf("The spec for the customer is :%s\n", mspecs);<br />
}<br />
For additional information regarding the execution of SQL statements, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.e<br />
sqlc.doc/esqlc298.htm<br />
Static and dynamic SQL<br />
The SQL statements shown in Example 4-4 on page 135 are all static SQL<br />
statements. With a static SQL statement, all the information that is needed is<br />
known at compile time. However, in some applications the programmer does not<br />
know the contents or possibly even the types of SQL statements that the<br />
program needs to execute. For example, a program might prompt the user to<br />
enter a SELECT statement, so that the programmer has no idea what columns<br />
are accessed when the program runs.<br />
Such applications require dynamic SQL statements. Dynamic SQL statements<br />
allow an <strong>IBM</strong> <strong>Informix</strong> ESQL/C program to build an SQL statement at run time, so<br />
that the contents of the statement can be determined by user input.<br />
136 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 4-5 shows how to use dynamic SQL in a program.<br />
Example 4-5 Dynamic SQL statements<br />
#include <br />
EXEC SQL include sqlca;<br />
EXEC SQL include sqltypes;<br />
int main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
int i1;<br />
char cmdstring[2048];<br />
char c1[50];<br />
char c2[50];<br />
EXEC SQL END DECLARE SECTION;<br />
int i2;<br />
EXEC SQL connect to 'stores_demo';<br />
printf("Enter the customer number [101-128] to see the names:");<br />
scanf("%d", &i1);<br />
sprintf (cmdstring, "SELECT fname,lname from customer where customer_num = ?");<br />
EXEC SQL prepare ex_id from :cmdstring;<br />
EXEC SQL execute ex_id into :c1,:c2 using :i1;<br />
;<br />
if (SQLCODE == 100)<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data not found\n");<br />
}<br />
else<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data found \n");<br />
printf("Last Name \t%s\n",c1);<br />
printf("First Name \t%s\n",c2);<br />
}<br />
}<br />
OUTPUT:<br />
Enter the customer number [101-128] to see the names:102<br />
SQLCODE=0<br />
Data found<br />
Last Name Carole<br />
First Name Sadle<br />
Chapter 4. Working with ESQL/C 137
The SELECT statement shown in Example 4-5 on page 137 is called a singleton<br />
SELECT statement because it returns only one row. For SELECT statements<br />
that return multiple rows, you must use a SELECT cursor statement.<br />
Example 4-6 shows how to use cursor for SELECT statements that return<br />
multiple rows.<br />
Example 4-6 Using cursor for SELECT statements that return multiple rows<br />
#include <br />
EXEC SQL include sqlca;<br />
EXEC SQL include sqltypes;<br />
int main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
int i;<br />
char cmdstring[2048];<br />
char c1[50];<br />
char c2[50];<br />
EXEC SQL END DECLARE SECTION;<br />
EXEC SQL connect to 'stores_demo';<br />
printf("Enter customer number [103 - 128] :");<br />
scanf("%d", &i);<br />
sprintf (cmdstring, "SELECT fname,lname from customer where customer_num < ?;");<br />
EXEC SQL prepare ex_id from :cmdstring;<br />
EXEC SQL declare ex_cursor cursor for ex_id;<br />
EXEC SQL open ex_cursor using :i;<br />
/* Print out what DESCRIBE returns*/<br />
for (;;)<br />
{<br />
EXEC SQL fetch ex_cursor into :c1,:c2;<br />
if (strncmp(SQLSTATE, "00", 2) != 0)<br />
break;<br />
if (SQLCODE == 100)<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data not found\n");<br />
}<br />
else<br />
{<br />
printf("%s\t%s\n",c1,c2);<br />
}<br />
}<br />
OUTPUT:<br />
Enter customer number [103 - 128] : 108<br />
Ludwig Pauli<br />
Carole Sadler<br />
Philip Currie<br />
138 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Anthony Higgins<br />
Raymond Vector<br />
George Watson<br />
Charles Ream<br />
For more details about dynamic SQL and the cursors, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.esqlc.doc/s<br />
ii-sc3dysq-35492.htm#sii-sc3dysq-35492<br />
Calling SQL routines<br />
In this section, we demonstrate how to call an SQL routine from an ESQL/C<br />
application. You can accomplish a wide range of objectives with SQL routines,<br />
including improving database performance, simplifying writing applications, and<br />
limiting or monitoring access to data. Because an SQL routine is stored in an<br />
executable format, you can use it for repeated tasks to improve performance.<br />
When you invoke an SPL routine, rather than straight SQL code, you can bypass<br />
repeated parsing, validity checking, and query optimization.<br />
Example 4-7 shows a simple program that contains an SPL routine that receives<br />
arguments price and percent tax and that returns price after adding the tax.<br />
Example 4-7 Calling SQL routines<br />
#include <br />
$ INCLUDE sqlca.h;<br />
$ INCLUDE sqltypes.h;<br />
int main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
char cmdstring[2048];<br />
float n1,n2;<br />
int i=2;<br />
EXEC SQL END DECLARE SECTION;<br />
EXEC SQL whenever sqlerror stop;<br />
EXEC SQL CONNECT TO 'stores_demo';<br />
sprintf(cmdstring, "CREATE FUNCTION inc_price(n1 money(8,2), n2 int) RETURNING<br />
money(8,2) RETURN(n1 + (n1 *n2)/ 100); END FUNCTION;");<br />
EXEC SQL execute immediate :cmdstring;<br />
sprintf(cmdstring, "SELECT FIRST 5 total_price, inc_price(total_price,?) from items ;");<br />
EXEC SQL prepare ex_id from :cmdstring;<br />
EXEC SQL declare ex_cursor cursor for ex_id;<br />
EXEC SQL open ex_cursor using :i;<br />
/* Print out what DESCRIBE returns*/<br />
for (;;)<br />
{<br />
Chapter 4. Working with ESQL/C 139
EXEC SQL fetch ex_cursor into :n1,:n2;<br />
if (strncmp(SQLSTATE, "00", 2) != 0)<br />
break;<br />
if (SQLCODE == 100)<br />
{<br />
printf("SQLCODE=%d\n",SQLCODE);<br />
printf("Data not found\n");<br />
}<br />
else<br />
{<br />
printf("%f\t%f\n",n1,n2);<br />
}<br />
}<br />
}<br />
OUTPUT:<br />
250.000000 255.000000<br />
960.000000 979.200012<br />
240.000000 244.800003<br />
20.000000 20.400000<br />
840.000000 856.799988<br />
Using transactions<br />
A transaction is a collection of SQL statements that are treated as a single unit of<br />
work. All the SQL statements that you issue in an ANSI-compliant database are<br />
contained in transactions automatically. With a database that is not ANSI<br />
compliant, transaction processing is an option.<br />
In a database that is not ANSI compliant, a transaction is enclosed by a BEGIN<br />
WORK statement and a COMMIT WORK or a ROLLBACK WORK statement. In<br />
an ANSI-compliant database, the BEGIN WORK statement is unnecessary,<br />
because all statements are contained in a transaction automatically. You need to<br />
indicate only the end of a transaction with a COMMIT WORK or ROLLBACK<br />
WORK statement.<br />
Example 4-8 shows an example using transactions.<br />
Example 4-8 Using transactions<br />
#include <br />
$include "sqlca.h";<br />
$include "sqlhdr.h";<br />
$include "sqltypes.h";<br />
main()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
140 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
int n1;<br />
char c1[20];<br />
EXEC SQL END DECLARE SECTION;<br />
EXEC SQL whenever sqlerror stop;<br />
EXEC SQL CREATE DATABASE itso WITH LOG;<br />
EXEC SQL CREATE TABLE t1(num serial, name char(20));<br />
EXEC SQL BEGIN WORK;<br />
EXEC SQL INSERT INTO t1 VALUES (0,'name1');<br />
EXEC SQL INSERT INTO t1 VALUES (0,'name2');<br />
EXEC SQL INSERT INTO t1 VALUES (0,'name3');<br />
EXEC SQL ROLLBACK;<br />
EXEC SQL BEGIN WORK;<br />
EXEC SQL INSERT INTO t1 VALUES (0,'name4');<br />
EXEC SQL INSERT INTO t1 VALUES (0,'name5');<br />
EXEC SQL COMMIT;<br />
EXEC SQL declare ifx_cursor cursor for select num,name into :n1,:c1 from t1;<br />
EXEC SQL open ifx_cursor ;<br />
for (;;)<br />
{<br />
EXEC SQL fetch ifx_cursor;<br />
if (sqlca.sqlcode!=0)<br />
break;<br />
printf("%d\t%s\n",n1,c1);<br />
}<br />
EXEC SQL close ifx_cursor;<br />
EXEC SQL free ifx_cursor;<br />
}<br />
OUTPUT:<br />
4 name4<br />
5 name5<br />
4.4.3 Data types mapping<br />
This section contains information about the correspondence data types between<br />
SQL and C and how to handle data types in an <strong>IBM</strong> <strong>Informix</strong> ESQL/C program.<br />
When a query is executed by the application, the data that the <strong>Informix</strong> server<br />
returns might be in a different format than the format that the application uses.<br />
The ESQL/C converts the data passed between the application and the<br />
database server. This process is transparent to the application. The only<br />
requirement for the application is to specify the correct data types in ESQL/C.<br />
Chapter 4. Working with ESQL/C 141
Table 4-3 lists a few ESQL/C data type mapping as examples.<br />
Table 4-3 <strong>Informix</strong> data type mapping<br />
<strong>Informix</strong> SQL ESQL/C<br />
BOOLEAN boolean<br />
BYTE loc_t<br />
LVARCHAR lvarchar<br />
NCHAR(n) fixchar [n] or string [n+1]<br />
NVARCHAR(m) varchar[m+1] or string [m+1]<br />
SERIAL8 int8 or ifx_int8_t<br />
TEXT loc_t<br />
BLOB ifx_lo_t<br />
CLOB ifx_lo_t<br />
LIST(e) collection<br />
MULTISET(e) collection<br />
Opaque data type lvarchar, fixed binary, or var binary<br />
ROW(...) row<br />
SET(e) collection<br />
For a complete list of all the <strong>Informix</strong> SQL to ESQL/C data type mapping, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
esqlc.doc/sii03147680.htm<br />
4.4.4 Handling special data types<br />
This section describe how to work with <strong>Informix</strong> specific data types. We discuss<br />
the following data types:<br />
► Smart large objects (BLOB and CLOB)<br />
► Collection data types (LIST, MULTISET, and SET)<br />
► ROW data type<br />
To store large objects inside database, you can use data types such as TEXT,<br />
BYTE, BLOB, and CLOBS. <strong>Informix</strong> supports simple large objects and smart<br />
large objects. Simple large objects are the TEXT and BYTE types that exist<br />
primarily for compatibility with earlier versions of <strong>Informix</strong> applications. The smart<br />
142 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
large objects are the BLOB and CLOBs. When you write new applications that<br />
need to access large objects, use smart large objects to hold character (CLOB)<br />
and binary (BLOB) data.<br />
Table 4-4 summarizes the advantages that smart large objects present over<br />
simple large objects.<br />
Table 4-4 Advantages of smart blobs over simple blobs<br />
Large object feature Simple large objects Smart large objects<br />
Maximum size of data 2 gigabytes 4 terabytes<br />
Data accessibility No random access to data Random access to data<br />
Reading the large object The database server reads<br />
a simple large object on an<br />
all or nothing basis.<br />
Writing the large object The database server<br />
updates a simple large<br />
object on an all or nothing<br />
basis.<br />
Library functions provide<br />
access that is similar to<br />
accessing an<br />
operating-system file. You<br />
can access specified<br />
portions of the smart large<br />
object.<br />
The database server can<br />
rewrite only a portion of a<br />
smart large object.<br />
Data logging Data logging is always on. Data logging can be turned<br />
on and off.<br />
Example using CLOB and BLOBs<br />
<strong>Informix</strong> ESQL/C supports the SQL data types CLOB and BLOB with the<br />
ifx_lo_t data type. Because of the potentially huge size of smart large object<br />
data, the <strong>Informix</strong> ESQL/C program does not store the data directly in a host<br />
variable. Instead, the client application accesses the data as a file-like structure.<br />
To use smart large object variables in an <strong>Informix</strong> ESQL/C program, take the<br />
following actions:<br />
1. Declare a host variable with the ifx_lo_t data type.<br />
2. Access the smart large object with a combination of the following three data<br />
structures:<br />
– The LO-specification structure, ifx_lo_create_spec_t<br />
– The LO-pointer structure, ifx_lo_t<br />
– An integer LO file descriptor<br />
Smart large objects are stored logically in a table column but stored physically in<br />
a specific type of dbspaces called smart blob space (sbspace). You must create<br />
Chapter 4. Working with ESQL/C 143
the sbspace in the server before you can run this example. For details about<br />
creating the sbspace, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.admin.doc/i<br />
ds_admin_0491.htm<br />
Example 4-9 illustrates how to work with BLOB and CLOB data types using<br />
ESQL/C functions. The filetoclob and filetoblob functions are used to enter<br />
the CLOB and BLOB data respectively. If your files are located on the client<br />
system, then use the client name as the second argument to the filetoblob<br />
function. If your files are located on the server system, then use the server name<br />
as the second argument.<br />
The output is a part of the file that you insert as a BLOB. In this example, we<br />
used a readme.txt file.<br />
Example 4-9 The ifx_lo_sample.ec file<br />
#include <br />
$include "sqlca.h";<br />
$include "sqlhdr.h";<br />
$include "sqltypes.h";<br />
main()<br />
{<br />
int error, ic1, oflags, cflags, extsz, imsize, isize, iebytes;<br />
time_t time;<br />
struct tm *date_time;<br />
char col_name[300]="test", sbspc[129];<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
fixed binary 'blob' ifx_lo_t c2;<br />
char srvr_name[256];<br />
ifx_lo_create_spec_t *cspec;<br />
ifx_lo_stat_t *stats;<br />
ifx_int8_t size, c1, estbytes, maxsize;<br />
int lofd;<br />
long atime, ctime, mtime, refcnt;<br />
EXEC SQL END DECLARE SECTION;<br />
EXEC SQL whenever sqlerror stop;<br />
EXEC SQL connect to 'stores_demo';<br />
EXEC SQL create table t2 (c1 int, c2 blob);<br />
EXEC SQL insert into t2 values (1,filetoblob ('/tmp/README.txt', 'server','t2','c2'));<br />
EXEC SQL declare ifxcursor cursor for select c1,c2 into :c1,:c2 from t2 for update;<br />
EXEC SQL open ifxcursor;<br />
for (;;)<br />
{<br />
144 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
EXEC SQL fetch ifxcursor;<br />
if (sqlca.sqlcode!=0)<br />
break;<br />
lofd = ifx_lo_open(&c2, LO_RDWR, &error);<br />
ifx_lo_read(lofd, srvr_name, 256, &error);<br />
printf("Value: %s\n",srvr_name);<br />
ifx_lo_write(lofd,col_name,5,&error);<br />
ifx_lo_close(lofd);<br />
}<br />
EXEC SQL close ifxcursor;<br />
EXEC SQL free ifxcursor;<br />
ifx_lo_close(lofd);<br />
}<br />
For more information about the ESQL/C functions to create, alter, and access<br />
BLOB and CLOB data, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
esqlc.doc/sii-03-sourceforchaptitle.htm<br />
Example using collection data types<br />
Collection data types (SET, LIST, and MULTISET) enable you to store and<br />
manipulate collections of data within a single row of a table. A collection data<br />
type has two components:<br />
► A type constructor, which determines whether the collection type is a SET,<br />
MULTISET, or LIST<br />
► An element type, which specifies the type of data that the collection can<br />
contain<br />
The elements of a collection can be of any data type. The elements of a<br />
collection are the values that the collection contains. In a collection that contains<br />
the values {'blue', 'green', 'yellow', and 'red'}, blue represents a single<br />
element in the collection. Every element in a collection must be of the same type.<br />
For example, a collection whose element type is INTEGER can contain only<br />
integer values.<br />
<strong>Informix</strong> ESQL/C uses collection variables to access collection data types. A<br />
collection variable stores the elements from a collection column as though they<br />
were rows in a table. You can use this virtual table as part of a cursor declaration<br />
to fetch the individual elements of the collection column.<br />
Chapter 4. Working with ESQL/C 145
Example 4-10 shows the ESQL/C commands that are used to retrieve an<br />
element from a collection column. The parents column is a collection of INT<br />
values. The collection is stored into the host variable hv1 and is used to open a<br />
cursor using the variable as a virtual table, table(:hv1).<br />
Example 4-10 The collection_sample<br />
EXEC SQL client collection hv1;<br />
EXEC SQL int parent_id;<br />
EXEC SQL select parents into :hv1 from grade12_parents where class_id = 1;<br />
EXEC SQL declare cur1 cursor for select id from table(:hv1);<br />
EXEC SQL open cur1;<br />
EXEC SQL fetch cur1 into :parent_id;<br />
The INT elements of the collection are retrieved from the host variable using the<br />
EXEC SQL fetch cur1 command.<br />
Example 4-11 shows a complete ESQL/C program that illustrates how to insert<br />
and select into tables that contain the collection data types.<br />
Example 4-11 Example using collection types<br />
#include <br />
static void print_collection(<br />
const char *tag,<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
parameter client collection c<br />
EXEC SQL END DECLARE SECTION;<br />
)<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
int4 value;<br />
EXEC SQL END DECLARE SECTION;<br />
mint item = 0;<br />
}<br />
146 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
EXEC SQL WHENEVER ERROR STOP;<br />
printf("COLLECTION: %s\n", tag);<br />
EXEC SQL DECLARE c_collection CURSOR FOR<br />
SELECT * FROM TABLE(:c);<br />
EXEC SQL OPEN c_collection;<br />
while (sqlca.sqlcode == 0)<br />
{<br />
EXEC SQL FETCH c_collection INTO :value;<br />
if (sqlca.sqlcode != 0) break;<br />
printf("\tItem %d, value = %d\n", ++item, value);<br />
}<br />
EXEC SQL CLOSE c_collection;<br />
EXEC SQL FREE c_collection;
mint main(int argc, char **argv)<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
client collection list (integer not null) lc1;<br />
client collection set (integer not null) sc1;<br />
client collection multiset (integer not null) mc1;<br />
char *dbase = "stores_demo";<br />
mint seq;<br />
char *stmt1 =<br />
"INSERT INTO t_collections VALUES(0, "<br />
"'LIST{-1,0,-2,3,0,0,32767,249}', 'SET{-1,0,-2,3}', "<br />
"'MULTISET{-1,0,0,-2,3,0}') ";<br />
EXEC SQL END DECLARE SECTION;<br />
if (argc > 1)<br />
dbase = argv[1];<br />
EXEC SQL WHENEVER ERROR STOP;<br />
printf("Connect to %s\n", dbase);<br />
EXEC SQL connect to :dbase;<br />
EXEC SQL CREATE TEMP TABLE t_collections<br />
(<br />
seq serial not null,<br />
l1 list (integer not null),<br />
s1 set (integer not null),<br />
m1 multiset(integer not null)<br />
);<br />
EXEC SQL EXECUTE IMMEDIATE :stmt1;<br />
EXEC SQL ALLOCATE COLLECTION :lc1;<br />
EXEC SQL ALLOCATE COLLECTION :mc1;<br />
EXEC SQL ALLOCATE COLLECTION :sc1;<br />
}<br />
EXEC SQL DECLARE c_collect CURSOR FOR<br />
SELECT seq, l1, s1, m1 FROM t_collections;<br />
EXEC SQL OPEN c_collect;<br />
EXEC SQL FETCH c_collect INTO :seq, :lc1, :sc1, :mc1;<br />
EXEC SQL CLOSE c_collect;<br />
EXEC SQL FREE c_collect;<br />
print_collection("list/integer", lc1);<br />
print_collection("set/integer", sc1);<br />
print_collection("multiset/integer", mc1);<br />
EXEC SQL DEALLOCATE COLLECTION :lc1;<br />
EXEC SQL DEALLOCATE COLLECTION :mc1;<br />
EXEC SQL DEALLOCATE COLLECTION :sc1;<br />
puts("OK");<br />
return 0;<br />
Chapter 4. Working with ESQL/C 147
Example 4-12 shows the output of Example 4-11 on page 146.<br />
Example 4-12 Collection example output<br />
Connect to stores_demo<br />
COLLECTION: list/integer<br />
Item 1, value = -1<br />
Item 2, value = 0<br />
Item 3, value = -2<br />
Item 4, value = 3<br />
Item 5, value = 0<br />
Item 6, value = 0<br />
Item 7, value = 32767<br />
Item 8, value = 249<br />
COLLECTION: set/integer<br />
Item 1, value = -1<br />
Item 2, value = 0<br />
Item 3, value = -2<br />
Item 4, value = 3<br />
COLLECTION: multiset/integer<br />
Item 1, value = -1<br />
Item 2, value = 0<br />
Item 3, value = 0<br />
Item 4, value = -2<br />
Item 5, value = 3<br />
Item 6, value = 0<br />
OK<br />
4.4.5 Exception handling<br />
The applications that you write require that the database server processes your<br />
SQL statements successfully as you intend. If a query fails and you do not know<br />
about the failure, you might display meaningless data to the user.<br />
To handle such errors, an <strong>Informix</strong> ESQL/C program must check that every SQL<br />
statement executes as you intend.<br />
This section discusses the two widely used exception handling methods in<br />
<strong>Informix</strong> applications development:<br />
► Use the SQLSTATE variable and the GET DIAGNOSTICS statement to check<br />
for runtime errors and warnings that your <strong>Informix</strong> ESQL/C program might<br />
generate.<br />
► Use the SQLCODE variable and the SQL Communications Area (sqlca) to<br />
check for runtime errors and warnings that your <strong>Informix</strong> ESQL/C program<br />
might generate.<br />
148 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
For more information about ESQL/C Exception handling, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
esqlc.doc/sii-11-40709.htm<br />
Exception handling with the SQLSTATE variable<br />
After the database server executes an SQL statement, it sets the SQLSTATE<br />
variable with a value that indicates the success or failure of the statement. From<br />
this value, your program can determine if it needs to perform further diagnostics.<br />
If the SQLSTATE variable indicates a problem, you can use the GET<br />
DIAGNOSTICS statement to obtain more information.<br />
Example 4-13 shows how to handle exceptions using the SQLSTATE variable<br />
and the GET DIAGNOSTICS statement. The GET DIAGNOSTICS statement<br />
returns information that is held in the fields of the diagnostics area. The<br />
diagnostics area is an internal structure that the database server updates after it<br />
executes an SQL statement. Each application has one diagnostics area.<br />
Example 4-13 Error handling in ESQL/C<br />
#include <br />
#include <br />
void error_chk()<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
mint excp_count;<br />
char overflow[2];<br />
mint excp_num=1;<br />
char message[255];<br />
mint mlen;<br />
char sql_state_code[6];<br />
mint i=1;<br />
EXEC SQL END DECLARE SECTION;<br />
printf("SQLSTATE: %s\n",SQLSTATE);<br />
printf("SQLCODE: %d\n", SQLCODE);<br />
printf("\n");<br />
EXEC SQL get diagnostics :excp_count = NUMBER, :overflow = MORE;<br />
EXEC SQL get diagnostics exception :i :sql_state_code = RETURNED_SQLSTATE, :message =<br />
MESSAGE_TEXT, :mlen = MESSAGE_LENGTH;<br />
printf("EXCEPTION %d: SQLSTATE=%s\n", i, sql_state_code);<br />
message[mlen-1] = '\0';<br />
printf("MESSAGE TEXT: %s\n", message);<br />
}<br />
mint main(int argc, char **argv)<br />
{<br />
EXEC SQL BEGIN DECLARE SECTION;<br />
char *dbase = "stores_demo";<br />
Chapter 4. Working with ESQL/C 149
}<br />
EXEC SQL END DECLARE SECTION;<br />
150 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
if (argc > 1)<br />
dbase = argv[1];<br />
EXEC SQL WHENEVER sqlerror CALL error_chk ;<br />
printf("Connect to %s\n", dbase);<br />
EXEC SQL connect to :dbase;<br />
EXEC SQL SELECT province FROM customer WHERE customer_num=1;<br />
return 0;<br />
OUTPUT:<br />
Connect to stores_demo<br />
SQLSTATE: IX000<br />
SQLCODE: -217<br />
4.4.6 Troubleshooting<br />
EXCEPTION 1: SQLSTATE=IX000<br />
MESSAGE TEXT: Column (province) not found in any table in the query (or SLV is<br />
undefined).<br />
In this section, we list frequent ESQL/C errors and discuss how to diagnose a<br />
problem using the trace facility. The following typical errors can occur in ESQL/C<br />
applications:<br />
► Locale mismatch<br />
The most common error you might encounter when writing ESQL/C<br />
application is a locale mismatch error:<br />
-23197 Database locale information mismatch<br />
The straight forward solution is to match all the locale-related environment<br />
variables, namely CLIENT_LOCALE, DB_LOCALE, and SERVER_LOCALE.<br />
The default values for CLIENT_LOCALE are en_us.8859-1 for UNIX and<br />
en_us.1252 for Windows systems. You can set these variables using the<br />
export (ksh) or setenv (csh) commands, depending on which shell you are<br />
using. On Windows systems, you can set the locale using the setnet32.exe<br />
utility.<br />
For more information about these locales and related material, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.<br />
ibm.glsug.doc/ids_gug_035.htm
► Compile and linking errors<br />
If there is a problem with the compile or linking, the following error message is<br />
returned:<br />
error while loading shared libraries: libifsql.so: cannot open shared<br />
object file: No such file or directory<br />
This error is due generally to a wrong setting of the shared library path. Refer<br />
to 4.4.1, “Creating an ESQL/C application” on page 128 for information about<br />
the correct settings.<br />
► Database connection errors<br />
if there is an issue with the database connection, the following error message<br />
is returned:<br />
-25596 The INFORMIXSERVER value is not listed in the sqlhosts file or the<br />
Registry.<br />
This error is due to incorrect setting of INFORMIXSERVER variable. Verify<br />
that it is set to the server instance to which you want to connect.<br />
The SQLIDEBUG trace<br />
ESQL/C uses the SQLI protocol to communicate with the <strong>Informix</strong> database<br />
server. The SQLIDEBUG trace allows you to trace all the messages between the<br />
client and server. You can use it to diagnose problems such as SQL errors,<br />
unexpected return values, or performance issues.<br />
You can enable the SQLIDEBUG trace on both the client and the server side.<br />
On the server side, you can enable SQLIDEBUG using the onmode -p 1<br />
sqli_dbg parameter.<br />
To set SQLIDEBUG on the client side, create the environment variable<br />
SQLIDEBUG with the following format:<br />
sqlidebug=2:path_trace_file<br />
where path_trace_file corresponds to the location and name of the trace file.<br />
Example 4-14 shows how to create the variable on a UNIX platform and how to<br />
decode the SQLI trace file using the sqliprint utility, which is included as part of<br />
Client SDK.<br />
Example 4-14 The SQLIDEBUG trace<br />
informix@irk:/work$ export SQLIDEBUG=2:/tmp/sqlitrace<br />
informix@irk:/work$<br />
informix@irk:/work$ sqliprint /tmp/sqlitrace_17008_0_8c819d0 | more<br />
...<br />
Chapter 4. Working with ESQL/C 151
C->S (16) Time: 2010-07-06 20:21:58.05185<br />
SQ_DBOPEN<br />
"stores7" [7]<br />
NOT EXCLUSIVE<br />
SQ_EOT<br />
S->C (28) Time: 2010-07-06 20:21:58.05487<br />
SQ_DONE<br />
Warning..: 0x15<br />
# rows...: 0<br />
rowid....: 0<br />
serial id: 0<br />
SQ_COST<br />
estimated #rows: 1<br />
estimated I/O..: 1<br />
SQ_EOT<br />
C->S (56) Time: 2010-07-06 20:21:58.05595<br />
SQ_PREPARE<br />
# values: 1<br />
CMD.....: "SELECT code, sname FROM state WHERE code = ?" [44]<br />
SQ_NDESCRIBE<br />
SQ_WANTDONE<br />
...<br />
152 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 5. Working with the JDBC<br />
drivers<br />
5<br />
In this chapter, we describe how to develop a Java application using the two<br />
available Java Database Connectivity (JDBC) drivers. This chapter includes the<br />
following topics:<br />
► JDBC drivers for an <strong>Informix</strong> database<br />
► Setup and configuration<br />
► JDBC type mapping<br />
► Performing database operations<br />
► <strong>Informix</strong> additional features<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 153
5.1 JDBC drivers for an <strong>Informix</strong> database<br />
Java Database Connectivity (JDBC) is the JavaSoft specification of a standard<br />
application programming interface (API) that allows Java programs to access<br />
database management systems.<br />
The JDBC API consists of a set of interfaces and classes written in the Java<br />
programming language. Using these standard interfaces and classes,<br />
programmers can write applications that connect to databases, send queries<br />
written in structured query language (SQL), and process the results.<br />
Because JDBC is a standard specification, one Java program that uses the<br />
JDBC API can connect to any database management system (DBMS), as long<br />
as a driver exists for that particular DBMS.<br />
For more information about the JDBC API, refer to JDBC documentation at<br />
http://java.sun.com/javase/6/docs/technotes/guides/jdbc/<br />
You can use the following JDBC drivers from a Java application to process data<br />
in an <strong>IBM</strong> <strong>Informix</strong> database:<br />
► <strong>IBM</strong> <strong>Informix</strong> JDBC Driver<br />
► <strong>IBM</strong> Data Server Driver for JDBC and SQLJ<br />
Both JDBC drivers are developed as pure-Java drivers (Type 4). Thus, when you<br />
use a Type 4 driver from a Java application, your session connects directly to the<br />
database or database server without a middle layer.<br />
5.1.1 <strong>IBM</strong> <strong>Informix</strong> JDBC Driver<br />
<strong>IBM</strong> <strong>Informix</strong> JDBC Driver is a platform-independent, industry-standard Type 4<br />
driver that provides enhanced support for distributed transactions.<br />
<strong>Informix</strong> JDBC Driver follows the JDBC 3.0 specifications, providing support for<br />
the following <strong>IBM</strong> <strong>Informix</strong> database engines:<br />
► <strong>IBM</strong> <strong>Informix</strong> 7.x, 9.4x, 10.0, 11.10 and 11.50<br />
► <strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server (XPS) 8.5x<br />
► <strong>IBM</strong> <strong>Informix</strong> Standard Engine (SE) 7.x<br />
► <strong>IBM</strong> <strong>Informix</strong> OnLine Version 5.x<br />
To use <strong>IBM</strong> <strong>Informix</strong> JDBC Driver Version 3.50.JC6, you must use a JDK 1.4.2 or<br />
later package on your platform.<br />
154 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
You can download the <strong>Informix</strong> JDBC Driver from:<br />
http://www14.software.ibm.com/webapp/download/search.jsp?go=y&rs=ifxjdbc<br />
Example 5-1 shows the contents of the <strong>Informix</strong> JDBC Driver package.<br />
Example 5-1 <strong>Informix</strong> JDBC directory<br />
demo<br />
doc<br />
javadoc<br />
release<br />
lib<br />
ifxjdbc.jar<br />
ifxjdbcx.jar<br />
ifxlang.jar<br />
ifxlsupp.jar<br />
ifxsqlj.jar<br />
ifxtools.jar<br />
license<br />
proxy<br />
This is a brief explanation of the .jar files in the driver:<br />
► ifxjdbc.jar<br />
The optimized implementations of the JDBC API interfaces, classes, and<br />
methods.<br />
► ifxjdbcx.jar<br />
The implementation of data source, connection pooling, and XA-related class<br />
files.<br />
► ifxlang.jar<br />
The localized versions of all message text supported by the driver.<br />
► ifxlsupp.jar<br />
Support functions for the ifxlang.jar package.<br />
► ifxsqlj.jar<br />
The classes for runtime support of SQLJ programs.<br />
► ifxtools.jar<br />
The ClassGenerator, lightweight directory access protocol (LDAP) loader, and<br />
other utilities.<br />
Chapter 5. Working with the JDBC drivers 155
5.1.2 <strong>IBM</strong> Data Server Driver for JDBC and SQLJ<br />
<strong>IBM</strong> Data Server Driver for JDBC and SQLJ, formerly known as <strong>IBM</strong> Driver for<br />
JDBC and SQLJ, is a JDBC driver that uses the DRDA protocol to communicate<br />
with <strong>IBM</strong> database servers. The use of a common communication protocol such<br />
as DRDA means that the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ allows you<br />
to write client applications that can use both DB2 and <strong>Informix</strong> database servers.<br />
The <strong>IBM</strong> Data Server Driver for JDBC and SQLJ is compliant with the JDBC 3.0<br />
and JDBC 4.0 specifications. This driver is included as part of the <strong>IBM</strong> Data<br />
Server Driver Package, which is bundled with <strong>Informix</strong> Client Software<br />
Development Kit (Client SDK). <strong>IBM</strong> Data Server Driver for JDBC and SQLJ is<br />
also available as a separate download at:<br />
http://www-01.ibm.com/support/docview.wss?rs=71&uid=swg24026929<br />
Example 5-2 shows a listing of the files that are included in the <strong>IBM</strong> Data Server<br />
Driver for JDBC and SQLJ installation package and highlights important files.<br />
Example 5-2 Data Server Driver for JDBC directory (snippet)<br />
db2jcc.jar<br />
db2jcc4.jar<br />
sqlj.zip<br />
sqlj4.zip<br />
jdbc4_LI_en<br />
jdbc4_LI_en.rtf<br />
jdbc_LI_en<br />
The Data Server Driver for JDBC and SQLJ includes the following JDBC drivers:<br />
► db2jcc.jar<br />
Use the db2jcc.jar file in the CLASSPATH if you plan to use the version of<br />
the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ that includes only JDBC 3.0<br />
and earlier functions.<br />
► db2jcc4.jar<br />
Use the db2jcc4.jar file in the CLASSPATH if you plan to use the version of<br />
the <strong>IBM</strong> Data Server Driver for JDBC and SQLJ that includes only JDBC 4.0<br />
and earlier functions.<br />
► sqlj.zip<br />
Provides support for SQLJ Java applications. SQLJ is used to embed SQL<br />
statements inside Java applications.<br />
► sqlj4.zip<br />
Type 4 driver for SQLJ applications.<br />
156 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.2 Setup and configuration<br />
5.2.1 Configuration<br />
In this section, we discuss the setup and configuration parameters of <strong>Informix</strong><br />
JDBC drivers. We also discuss how to verify the connection with the <strong>Informix</strong><br />
database server.<br />
The Data Server Client Driver for JDBC requires the use of a DRDA connection.<br />
Thus, the <strong>Informix</strong> database server must have an alias configured for DRDA<br />
communications. <strong>Informix</strong> JDBC Driver uses native <strong>Informix</strong> protocol or SQLI and<br />
requires no specific alias on the <strong>Informix</strong> database server when using the<br />
<strong>Informix</strong> JDBC Driver.<br />
On the database server, verify that the sqlhosts file contains the correct listeners<br />
for the <strong>Informix</strong> database server.<br />
Example 5-3 shows the sqlhosts file of the <strong>Informix</strong> server that we used in our<br />
examples.<br />
Example 5-3 Our sqlhosts file<br />
demo_on onipcshm kefka demo_on<br />
demo_on_tcp onsoctcp kefka 9088<br />
demo_on_drda drsoctcp kefka 9089<br />
For information regarding how to set up and configure a DRDA alias on the<br />
<strong>Informix</strong> server, refer to 2.1.3, “Configuring <strong>Informix</strong> Server” on page 27.<br />
On the client system, you must have one of the two JDBC drivers installed. For<br />
information about the installation and configuration, refer to 2.2.3, “Setting up<br />
<strong>IBM</strong> Data Server drivers” on page 43 and 2.2.4, “Setting up <strong>Informix</strong> JDBC” on<br />
page 53.<br />
To use any JDBC driver in an application, you must set the CLASSPATH<br />
environment variable to point to the driver files. The CLASSPATH environment<br />
variable tells the Java virtual machine (JVM) and other applications where to find<br />
the Java class libraries that are used in a Java program.<br />
Chapter 5. Working with the JDBC drivers 157
Example 5-4 shows the CLASSPATH environment variable, which contains the<br />
default location of both JDBC drivers.<br />
Example 5-4 CLASSPATH example<br />
set CLASSPATH=C:\Program Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER<br />
DRIVER\java\db2jcc.jar;C:\Program<br />
Files\<strong>IBM</strong>\<strong>Informix</strong>_JDBC_Driver\lib\ifxjdbc.jar;C:\Program<br />
Files\<strong>IBM</strong>\<strong>Informix</strong>_JDBC_Driver\lib\ifxjdbcx.jar;%CLASSPATH%;.;<br />
Products such as <strong>IBM</strong> Data Studio or <strong>IBM</strong> WebSphere® Application Server have<br />
specific location and configuration files for the JDBC drivers. Always refer to each<br />
individual documentation for setup details.<br />
You can find additional information regarding the configuration of the<br />
CLASSPATH environment variable in refer to 2.2.3, “Setting up <strong>IBM</strong> Data Server<br />
drivers” on page 43 and 2.2.4, “Setting up <strong>Informix</strong> JDBC” on page 53.<br />
5.2.2 Verify connectivity with <strong>Informix</strong> JDBC Driver<br />
In this section, we demonstrate how to verify a correct setup and configuration of<br />
the <strong>Informix</strong> JDBC Driver.<br />
To load <strong>IBM</strong> <strong>Informix</strong> JDBC Driver, the application can use the following<br />
Class.forName() Java method, passing the name of the <strong>Informix</strong> JDBC Driver as<br />
argument:<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
Example 5-5 contains a basic Java program that can be used to verify the<br />
connectivity with the <strong>Informix</strong> JDBC Driver. The connection string uses the<br />
following parameters:<br />
► The <strong>IBM</strong> <strong>Informix</strong> JDBC Driver identifier, jdbc:informix-sqli<br />
► The database server host name, kefka.lenexa.ibm.com<br />
You can also specify the IP address.<br />
► The port number of the SQLI listener of the <strong>Informix</strong> server, 9088<br />
► The database name, stores_demo<br />
► <strong>Informix</strong> instance identified by the INFORMIXSERVER variable<br />
► User and password for connecting to the database server<br />
158 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 5-5 SimpleConnection.java<br />
import java.sql.*;<br />
public class SimpleConnection {<br />
public static void main(String[] args) {<br />
String url =<br />
"jdbc:informix-sqli://kefka.lenexa.ibm.com:9088/stores_demo:INFORMIXSERVER=demo<br />
_on;user=informix;password=Ifmx4you";<br />
Connection conn = null;<br />
System.out.println("URL = \"" + url + "\"");<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
} catch (Exception e) {<br />
System.out.println("FAILED: failed to load <strong>Informix</strong> JDBC driver.");<br />
}<br />
try {<br />
conn = DriverManager.getConnection(url);<br />
} catch (SQLException e) {<br />
System.out.println("FAILED: failed to connect!");<br />
}<br />
try {<br />
System.out.println("Connected ...");<br />
DatabaseMetaData md = conn.getMetaData();<br />
System.out.println("Driver name: " + md.getDriverName());<br />
System.out.println("Driver version: " + md.getDriverVersion());<br />
System.out.println("Database product name: "<br />
+ md.getDatabaseProductName());<br />
System.out.println("Database product version: "<br />
+ md.getDatabaseProductVersion());<br />
} catch (SQLException e) {<br />
System.out.println("FAILED: failed to connect!");<br />
}<br />
try {<br />
conn.close();<br />
} catch (SQLException e) {<br />
System.out.println("FAILED: failed to close the connection!");<br />
}<br />
System.out.println("Done!");<br />
}<br />
}<br />
Chapter 5. Working with the JDBC drivers 159
Example 5-6 shows the compile line and output of the SimpleConnect.java<br />
program.<br />
Example 5-6 Running SimpleConnection.java<br />
C:\RedBook>javac SimpleConnection.java<br />
C:\RedBook>java SimpleConnection<br />
URL =<br />
"jdbc:informix-sqli://kefka:9088/stores_demo:INFORMIXSERVER=demo_on;user=i<br />
nformix;password=Ifmx4you"<br />
Connected ...<br />
Driver name: <strong>IBM</strong> <strong>Informix</strong> JDBC Driver for <strong>IBM</strong> <strong>Informix</strong> Dynamic Server<br />
Driver version: 3.50.JC6W1<br />
Database product name: <strong>Informix</strong> Dynamic Server<br />
Database product version: 11.50.FC7<br />
Done!<br />
5.2.3 Verify connectivity with the Data Server Driver<br />
You can use the same Java program (Example 5-5 on page 159) to verify the<br />
connection with the Data Server Driver for JDBC with minor changes. The<br />
following changes are required:<br />
► Load the Data Server JDBC Driver class using the Class.forName() method:<br />
Class.forName("com.ibm.db2.jcc.DB2Driver");<br />
► The name of the Data Server JDBC Driver in the connection string should be<br />
jdbc:ids.<br />
► The port number to use the Data Server Driver client is different from the<br />
<strong>Informix</strong> JDBC client. A common mistake is to specify the SQLI port in the<br />
URL that leads to an error such as the following error:<br />
com.ibm.db2.jcc.am.io: [jcc][t4][2030][11211][3.58.82] A communication<br />
error occurred during operations on the connection's underlying socket,<br />
socket input stream,or socket output stream. Error location: Reply.fill().<br />
Message: Insufficient data. ERRORCODE=-4499, SQLSTATE=08001<br />
at com.ibm.db2.jcc.am.ed.a(ed.java:319)<br />
at com.ibm.db2.jcc.t4.a.a(a.java:416)<br />
160 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 5-7 shows the changes to the SimpleConnection.java source code.<br />
You do not need to specify the INFORMIXSERVER variable when using the <strong>IBM</strong><br />
Data Server Driver for JDBC.<br />
Example 5-7 SimpleConnection.java with Data Server JDBC Driver<br />
...<br />
Class.forName("com.ibm.db2.jcc.DB2Driver");<br />
} catch (ClassNotFoundException cnfe) {<br />
System.out.println("No such class available.");<br />
return;<br />
}<br />
try {<br />
con = DriverManager<br />
.getConnection("jdbc:ids://kefka:9089/stores_demo",<br />
"informix", "Ifmx4you");<br />
System.out.println("connected");<br />
...<br />
Example 5-8 shows how to compile and run the program. Notice that the<br />
metadata information that the JDBC driver retrieves is slightly different from the<br />
metadata retrieved with <strong>IBM</strong> <strong>Informix</strong> JDBC Driver.<br />
Example 5-8 Output of SimpleConnection.java using the Data Server JDBC Driver<br />
C:\RedBook>javac SimpleConnection.java<br />
C:\RedBook>java SimpleConnection<br />
connected<br />
Driver name: <strong>IBM</strong> DB2 JDBC Universal Driver Architecture<br />
Driver version: 3.58.82<br />
Database product name: IDS/UNIX64<br />
Database product version: IFX11500<br />
connected<br />
Done<br />
5.3 JDBC type mapping<br />
Mapping is a way of specifying data type equivalents. Because there are<br />
variations between the SQL data types that are supported by each database<br />
vendor, the JDBC API defines a set of generic SQL data types that to use on the<br />
Java application. In addition to these data types, which are defined by the JDBC<br />
driver, the Java language itself has its own data types that might differ from the<br />
SQL types that the database vendor uses. When writing a Java application, the<br />
Chapter 5. Working with the JDBC drivers 161
programmer uses Java data types to manipulate the data. The application<br />
developers needs to understand the equivalent data type in JDBC and the<br />
equivalent data types on the database server.<br />
Table 5-1 lists a few of the data type mapping that is required when working with<br />
an <strong>IBM</strong> <strong>Informix</strong> database as examples.<br />
Table 5-1 Data Type Mapping from JDBC to <strong>Informix</strong> basic data types<br />
Java Type JDBC Type <strong>Informix</strong> Type<br />
long BIGINT INT8, BIGINT, BIGSERIAL<br />
byte[] BINARY, VARBINARY BYTE<br />
boolean BIT BOOLEAN<br />
java.sql.Date DATE DATE<br />
java.math.BigDecimal DECIMAL DECIMAL<br />
byte[] LONGVARBINARY BYTE or BLOB<br />
java.lang.String LONGVARCHAR TEXT or CLOB<br />
java.math.BigDecimal NUMERIC MONEY<br />
float, java.lang.Float REAL SMALLFLOAT<br />
java.sql.Time TIME DATETIME HOUR TO<br />
SECOND<br />
java.sql.Timestamp TIMESTAMP DATETIME YEAR TO<br />
FRACTION(5)<br />
For the complete list of data type mappings between the <strong>Informix</strong> SQL data types<br />
and the <strong>Informix</strong> JDBC Driver, refer to the JDBC Driver Programmer’s Guide at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.jdbc_pg.doc<br />
/sii-xc-21122.htm#sii-xc-21122<br />
For more information regarding the data types that the <strong>IBM</strong> Data Server Driver for<br />
JDBC and SQLJ use, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.jccids.doc/<br />
com.ibm.db2.luw.apdv.java.doc/doc/rjvjdata.htm<br />
162 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.4 Performing database operations<br />
In this section, we provide examples of how to use <strong>IBM</strong> <strong>Informix</strong> JDBC Driver and<br />
<strong>IBM</strong> Data Server Driver for JDBC for typical database operations, such as<br />
connection to the <strong>Informix</strong> database, and to manipulate data.<br />
5.4.1 Connection to the database<br />
A Java application can obtain a connection with an <strong>Informix</strong> database using one<br />
of the following methods:<br />
► Use the DriverManager class. This method involves loading the JDBC driver<br />
using the Class.forName() method and obtaining a connection by calling the<br />
getConnection() method:<br />
Class.forName("com.ibm.db2.jcc.DB2Driver");<br />
con = DriverManager.getConnection("jdbc:ids://...");<br />
► Use a DataSource object through the javax.sql extensions.<br />
DB2ConnectionPoolDataSource ds = new DB2ConnectionPoolDataSource();<br />
PooledConnection poolconn = ds.getPooledConnection();<br />
con = poolconn.getConnection();<br />
The DriverManager class was implemented in the original JDBC 1.0 API. This<br />
class provides direct access to all the JDBC driver features; however, it requires<br />
the application to load the JDBC driver manually and to use a hard-coded<br />
connection string with the connection details.<br />
The DataSource interface was introduced in the JDBC 2.0 API as the preferred<br />
method to obtain a JDBC connection. The application has no need to load a<br />
JDBC driver class or provide hard-coded connection details. The interface needs<br />
to know only the name of the DataSource object that it wants to use. The details<br />
for the database connection are stored within the DataSource object definition<br />
outside the application code.<br />
Connecting using the DriverManager<br />
When using the DriverManager class to connect to an <strong>IBM</strong> <strong>Informix</strong> database, a<br />
connection string must be passed to the getConnection() method with the<br />
details for the database server. These details include information such as the<br />
system where the database server is running and the port number needed for the<br />
connection.<br />
Chapter 5. Working with the JDBC drivers 163
Example 5-9 shows the syntax of the connection string used with <strong>Informix</strong> JDBC<br />
Driver and <strong>IBM</strong> Data Server Driver for JDBC.<br />
Example 5-9 DriverManager connection string syntax<br />
jdbc:[jdbc-driver]://[{ip-address|host-name}:{port-number|service-name}][/dbnam<br />
e]:INFORMIXSERVER=servername[{;user=user;password=password]<br />
Connecting to an <strong>Informix</strong> database uses the following parameters:<br />
► jdbc-driver<br />
This parameter identifies a particular JDBC driver. <strong>Informix</strong> JDBC Driver uses<br />
informix-sqli. <strong>IBM</strong> Data Server Driver for JDBC uses ids or db2.<br />
► ip-address or host-name<br />
This parameter specifies the system that is running the database server.<br />
► port-number|service-name<br />
This parameter specifies the communication port that the database server<br />
uses.<br />
► dbname<br />
This parameter names the database to open after a successful connection<br />
with the server.<br />
► INFORMIXSERVER<br />
This parameter identifies the name of the <strong>Informix</strong> database server to which<br />
to connect. This parameter is not required when using Data Server JDBC<br />
Driver.<br />
► User and password<br />
These parameters provide the authentication credentials for the connection.<br />
In addition to these standard details, which are common to all JDBC drivers, the<br />
connection string is also used to specify properties that are relevant only to a<br />
specific JDBC driver or database server. Table 5-2 lists some of the properties<br />
specific to <strong>Informix</strong> JDBC Driver.<br />
Table 5-2 <strong>Informix</strong> JDBC Driver connection string<br />
Parameter Description<br />
DB_LOCALE Locale of the database in the database server.<br />
CLIENT_LOCALE Locale used by the Client application<br />
INFORMIXCONTIME Time-out value for a connection with the server<br />
SQLIDEBUG Enable the trace of the SQLI protocol<br />
164 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Parameter Description<br />
STMT_CACHE Enables SQL statement cache on the server<br />
IFX_ISOLATION_LEVEL Specify the default Isolation Level for the session<br />
<strong>IBM</strong> Data Server Driver for JDBC supports most of the <strong>Informix</strong> JDBC Driver<br />
properties. For a complete list of the properties that are supported when<br />
connecting to an <strong>Informix</strong> database server, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
jdbc_pg.doc/sii-02conect-66368.htm<br />
Example 5-10 shows how to compile a basic Java code that connects to the<br />
<strong>Informix</strong> database. The driver name and connection string are passed as<br />
parameters for the program.<br />
Example 5-10 A connect.java file<br />
C:\work>set CLASSPATH=ifxjdbc.jar:db2jcc.jar:.<br />
C:\work>type connect.java<br />
import java.sql.*;<br />
public class connect{<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
Statement is = null;<br />
if (args.lengthjavac connect.java<br />
C:\work>java connect "com.informix.jdbc.IfxDriver"<br />
"jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;<br />
Chapter 5. Working with the JDBC drivers 165
"<br />
Connected to <strong>Informix</strong> Dynamic Server<br />
C:\work>java connect "com.ibm.db2.jcc.DB2Driver"<br />
"jdbc:ids://kodiak:9089/stores_demo:user=informix;password=password";<br />
Connected to IDS/NT64<br />
C:\work><br />
Connecting using the DataSource object extensions<br />
When using a DataSource object, the information for the database connection is<br />
stored as properties of the DataSource object. These properties are usually<br />
stored in a Java Naming and Directory Interface (JNDI) service that is managed<br />
by the Java runtime or the application server.<br />
The application requires no knowledge about the specific connection details. It<br />
makes use of the DataSource object to get a connection with the database<br />
server.<br />
In addition to the method implemented by the JDBC driver to set standard<br />
properties, both <strong>Informix</strong> JDBC Driver and Data Server Driver for JDBC provide<br />
extra methods to set and retrieve properties that are specific to the <strong>Informix</strong><br />
database. These properties have the same effect on the database session as the<br />
<strong>Informix</strong> environment variables that are used by other <strong>Informix</strong> APIs, such as<br />
ODBC or .NET.<br />
Example 5-11 shows a DataSource object that is created using the <strong>Informix</strong><br />
JDBC Driver and how to set some of the DataSource object properties.<br />
Example 5-11 DataSource object sample<br />
IfxDataSource ds = new IfxDataSource();<br />
ds.setServerName("demo_on");<br />
ds.setDatabaseName("stores_demo");<br />
ds.setPortNumber(9089);<br />
ds.setIfxIFXHOST("kodiak");<br />
ds.setIfxCLIENT_LOCALE("en_US.CP1252");<br />
ds.getConnection("informix", "password");<br />
166 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
You can use these <strong>Informix</strong> specific properties to modify the behavior of the<br />
JDBC driver or to control particular features of the database server. Table 5-3<br />
lists a portion of the DataSource object properties that are implemented in the<br />
<strong>Informix</strong> JDBC Driver.<br />
Table 5-3 DataSource object properties<br />
<strong>Informix</strong> variable Method<br />
CLIENT_LOCALE public String getIfxCLIENT_LOCALE()<br />
public void setIfxCLIENT_LOCALE()<br />
DB_LOCALE public String getIfxDB_LOCALE()<br />
public void setIfxDB_LOCALE()<br />
IFX_ISOLATION_LEVEL public String getIfxIFX_ISOLATION_LEVEL()<br />
public void setIfxIFX_ISOLATION_LEVEL (l)<br />
IFX_LOCK_MODE_WAIT public int getIfxIFX_LOCK_MODE_WAIT()<br />
public void setIfxIFX_LOCK_MODE_WAIT ()<br />
SQLIDEBUG public String getIfxSQLIDEBUG ()<br />
public void setIfxSQLIDEBUG ()<br />
STMT_CACHE public String getIfxSTMT_CACHE()<br />
public void setIfxSTMT_CACHE()<br />
USEV5SERVER public boolean isIfxUSEV5SERVER()<br />
public void setIfxUSEV5SERVER()<br />
For a complete list of all the available properties, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
jdbc_pg.doc/sii-xb-13590.htm<br />
Because <strong>IBM</strong> Data Server Driver for JDBC supports both <strong>Informix</strong> and DB2<br />
database servers, the DataSource object properties that it provides are different<br />
from those provided by <strong>Informix</strong> JDBC Driver. For a complete list, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
jccids.doc/com.ibm.db2.luw.apdv.java.doc/doc/rjvdsprp.htm<br />
Chapter 5. Working with the JDBC drivers 167
5.4.2 Manipulating data<br />
In this section, we provide basic examples of how to perform typical data<br />
manipulation tasks using a JDBC driver with an <strong>IBM</strong> <strong>Informix</strong> database.<br />
Simple SQL statements<br />
A Java application can use a Statement object to run basic SQL statements<br />
against the database server. The Statement interface provides two methods:<br />
► executeQuery() returns a ResultSet value<br />
Use this method to run SQL statements that return data (for example, a<br />
SELECT SQL statement).<br />
► executeUpdate() returns the number of rows effected by the SQL statement<br />
Use this method to perform INSERT, DELETE, and UPDATE operations or to<br />
run Data Definition Language (DDL) statements.<br />
Example 5-12 demonstrates how to perform a DELETE statement using the<br />
executeUpdate() method.<br />
Example 5-12 An executeUpdate sample<br />
C:\work>type delete.java<br />
import java.sql.*;<br />
public class delete{<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
Statement is = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn =<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/stores_demo:INFOR<br />
MIXSERVER=demo_on");<br />
168 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
is = conn.createStatement();<br />
int rc=is.executeUpdate("DELETE FROM state WHERE code='"+args[0]+"'");<br />
System.out.format("Deleted %d rows",rc);<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}
}<br />
C:\work><br />
C:\work>java delete AZ<br />
Deleted 1 rows<br />
C:\work><br />
Prepare SQL statement with parameters<br />
You can use the PreparedStatement object to run the prepared SQL statement<br />
against the database server. Use the PreparedStatement object when the<br />
application uses an SQL statement repeatedly. The application can run the SQL<br />
statement multiple times with different values, which saves the time that the<br />
application takes to process and optimize an SQL statement.<br />
Example 5-13 demonstrates how to use a PreparedStatement object. The<br />
example inserts a row in the state table using the parameters that are supplied<br />
through the command line.<br />
Example 5-13 An insert.java sample<br />
C:\work>cat insert.java<br />
import java.sql.*;<br />
import java.util.*;<br />
import java.text.*;<br />
public class insert{<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn =<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/stores_demo:INFOR<br />
MIXSERVER=demo_on");<br />
PreparedStatement pstmt=conn.prepareStatement("INSERT INTO state<br />
VALUES(?,?,?)");<br />
pstmt.setString(1, args[0]);<br />
pstmt.setString(2, args[1]);<br />
pstmt.setString(3, args[2]);<br />
int rc=pstmt.executeUpdate();<br />
System.out.format("Inserted %d rows",rc);<br />
Chapter 5. Working with the JDBC drivers 169
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}<br />
}<br />
C:\work>java insert AZ Arizona 0<br />
Inserted 1 rows<br />
C:\work><br />
Retrieve data from a database table<br />
To retrieve data from a database table or a function that returns more than one<br />
row, a JDBC application must use the ResultSet object. When the<br />
executeQuery() method from the Statement or PreparedStatement object is call,<br />
the data is returned in the form of a ResultSet object. This object allows the<br />
fetching of rows from the database.<br />
Example 5-14 demonstrates how to run an SQL SELECT statement to retrieve<br />
data using the PreparedStatement and ResultSet objects. The result of the<br />
prepare statement is stored in the ResultSet object, dbRes. After that, the<br />
dbRes.next() method is used to scroll through the ResultSet data.<br />
Example 5-14 A select.java sample<br />
C:\work>cat select.java<br />
import java.sql.*;<br />
public class select {<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
ResultSet dbRes = null;<br />
Statement is = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn =<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/stores_demo:INFOR<br />
MIXSERVER=demo_on;’);<br />
PreparedStatement pstmt=conn.prepareStatement("SELECT * FROM state where<br />
code
dbRes = pstmt.getResultSet();<br />
while (dbRes.next()) {<br />
System.out.format("%s,",dbRes.getString(1));<br />
System.out.format("%s,",dbRes.getString(2));<br />
System.out.format("%f\n",dbRes.getDouble(3));<br />
}<br />
dbRes.close();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}<br />
}<br />
C:\work>java select CA<br />
AL,Alabama ,0.040000<br />
AR,Arizona ,0.000000<br />
AZ,Arizona ,0.055000<br />
C:\work><br />
Transactions<br />
Local transaction are controlled directly with the connection object. Methods<br />
such as Connection.commit() and Connection.rollback() are used to resolved<br />
a transaction.<br />
By default, all the connections that are created by the <strong>Informix</strong> JDBC Driver<br />
Connection object are in AutoCommit mode. Thus, every SQL statement sent to<br />
the <strong>Informix</strong> database server is committed automatically. If control over the<br />
transaction is required, you can turn off the AutoCommit mode using the<br />
Connection.setAutoCommit() method.<br />
When using <strong>Informix</strong> JDBC Driver in an XA environment (XA is a standard<br />
specification for distributed transactions), the AutoCommit feature is always<br />
disabled. The transaction manager is the only component that has control over<br />
the transaction.<br />
Chapter 5. Working with the JDBC drivers 171
Example 5-15 demonstrates how to disable AutoCommit mode using the<br />
setAutoCommit(false) method.<br />
Example 5-15 A sample localtrans.java file<br />
C:\work>cat localtrans.java<br />
import java.sql.*;<br />
import java.util.*;<br />
import java.text.*;<br />
public class localtrans{<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
Statement is = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn =<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/stores_demo:INFOR<br />
MIXSERVER=demo_on");<br />
conn.setAutoCommit(false);<br />
172 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
is = conn.createStatement();<br />
int rc = is.executeUpdate("DELETE FROM state WHERE code='UK'");<br />
System.out.format("Deleted %d rows\n",rc);<br />
System.out.format("Aborting transaction\n");<br />
conn.rollback();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
e.printStackTrace();<br />
}<br />
}<br />
}<br />
C:\work>java localtrans<br />
Deleted 1 rows<br />
Aborting transaction<br />
C:\work>
Run a user-defined routine<br />
The CallableStatement object provides a way to run a user-defined routine<br />
(UDR) using a standard method that is common to all the <strong>IBM</strong> database servers.<br />
The results from the execution of a UDR is returned as a result set or as an OUT<br />
parameter. Use the following SQL syntax to call a UDR using the<br />
CallableStatement interface:<br />
"{? = call function_name (?, ?,...)}";<br />
The placeholders identify the IN, OUT, and INOUT parameters for the UDR.<br />
The application can set and retrieve the value for the routine parameters using<br />
the registerOutParameter() and getxxx() methods.<br />
Example 5-16 shows a basic UDR that is defined with two OUT parameters.<br />
Example 5-16 A state_tax SQL routine<br />
CREATE FUNCTION state_tax(OUT vtax percent,<br />
vcode CHAR(2),<br />
OUT vsname CHAR(20))<br />
RETURNS BOOLEAN;<br />
SELECT sales_tax,upper(sname)<br />
INTO vtax, vsname FROM state<br />
WHERE code=vcode;<br />
RETURN 't';<br />
END FUNCTION;<br />
We use CallableStatement to invoke this UDR from a JDBC application.<br />
Example 5-17 demonstrates how to set the IN and OUT for the UDR. The<br />
BOOLEAN value is returned directly by the SQL function and retrieved using a<br />
ResultSet object. To retrieve the OUT parameters, the example uses the<br />
getDouble() and GetString() methods.<br />
Example 5-17 A callable.java sample file<br />
C:\work>cat callable.java<br />
import java.sql.*;<br />
public class callable {<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
Chapter 5. Working with the JDBC drivers 173
conn =<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/stores_demo:INFOR<br />
MIXSERVER=demo_on;");<br />
CallableStatement cstmt = conn.prepareCall ("{? = call state_tax(?, ?,<br />
?)}");<br />
cstmt.registerOutParameter(1, Types.DOUBLE);<br />
cstmt.registerOutParameter(3, Types.CHAR);<br />
cstmt.setString(2, "CA");<br />
ResultSet dbRes = cstmt.executeQuery();<br />
// Retrieve OUT parameters from the function<br />
while (dbRes.next())<br />
System.out.format("UDR returns = %s\n", dbRes.getBoolean(1));<br />
// Retrieve OUT parameters from the function<br />
System.out.format("OUT tax %s\n",cstmt.getDouble(1));<br />
System.out.format("OUT sname %s\n",cstmt.getString(3));<br />
dbRes.close();<br />
cstmt.close();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}<br />
}<br />
C:\work>java callable<br />
UDR returns = true<br />
OUT tax 0.0825<br />
OUT sname CALIFORNIA<br />
C:\work><br />
5.5 <strong>Informix</strong> additional features<br />
This section demonstrates the use of the following additional <strong>IBM</strong> <strong>Informix</strong><br />
features:<br />
► Batch inserts or updates and using ResultSet metadata<br />
► BIGSERIAL data type<br />
► <strong>Informix</strong> smart large objects<br />
► Secure Socket Layer<br />
174 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.5.1 Batch inserts or updates and using ResultSet metadata<br />
In this section, we demonstrate the following concepts:<br />
► Batch inserts<br />
When performing multiple inserts (or updates) using a prepared statement, it<br />
is more efficient to do all the inserts in bulk. The method adds all the inserts in<br />
a batch and then runs the batch.<br />
► Using ResultSet metadata<br />
When you run a query from a Java program that generates a result set, there<br />
is additional information available that is separate from the result set itself.<br />
This additional information about the data is returned by the ResultSet<br />
metadata. For example, you might use ResultSet metadata to obtain table<br />
definition information from one database to another database. You can<br />
dynamically obtain all the details about each column that returns data.<br />
Example 5-18 shows the batch insert and how to obtain the column name and<br />
column type from the ResultSet metadata. The schema for the gentemp table is<br />
as follows:<br />
CREATE TABLE gentemp (id INT, name VARCHAR(15) NOT NULL)<br />
We use the con.prepareStatement() method to prepare the SQL INSERT<br />
statement. Then, we execute the statement using the pstmt.addBatch() method,<br />
which makes the INSERT statement a part of a batch operation. After adding 10<br />
rows to the table, we execute the batch operation using the<br />
pstmt.executeBatch() method.<br />
The metadata information for the table is retrieved using the rs.getMetaData()<br />
method.<br />
Example 5-18 Listing for the IfxBatchDemo.java file<br />
import java.sql.*;<br />
import java.io.*;<br />
public class IfxBatchDemo {<br />
public static void main(String args[]) throws SQLException, IOException,Exception {<br />
String IfxURL =<br />
"jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on";<br />
Connection con = null;<br />
int cnt = 0;<br />
System.out.println("Start");<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
con = DriverManager.getConnection(IfxURL);<br />
Chapter 5. Working with the JDBC drivers 175
con.setAutoCommit(false);<br />
Statement stmt = null;<br />
PreparedStatement pstmt = null;<br />
ResultSet rs = null;<br />
ResultSetMetaData rsmd = null;<br />
pstmt = con.prepareStatement("INSERT INTO gentemp (id,name) VALUES (?,?)");<br />
// Fill in 10 rows<br />
for (int i = 0; i < 10; i++) {<br />
pstmt.setInt(1, i + 1000);<br />
pstmt.setString(2, "String #" + i);<br />
pstmt.addBatch();<br />
}<br />
int[] rows = pstmt.executeBatch();<br />
System.out.println(" Inserted data. Rows = " + rows.length);<br />
con.commit();<br />
pstmt.close();<br />
pstmt = con.prepareStatement("SELECT * FROM gentemp");<br />
rs = pstmt.executeQuery();<br />
rsmd = rs.getMetaData();<br />
System.out.println(" " + "Col Names " + rsmd.getColumnName(1) + ","<br />
+ rsmd.getColumnName(2));<br />
System.out.println(" " + "Col Types " + rsmd.getColumnTypeName(1)<br />
+ "," + rsmd.getColumnTypeName(2));<br />
int row = 1;<br />
while (rs.next()) {<br />
System.out.println(" Row " + row + " = "<br />
+ rs.getInt(rsmd.getColumnName(1)) + ","<br />
+ rs.getString(rsmd.getColumnName(2)));<br />
row++;<br />
}<br />
pstmt.close();<br />
con.commit();<br />
con.close();<br />
}<br />
}<br />
176 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.5.2 BIGSERIAL data type<br />
<strong>IBM</strong> <strong>Informix</strong> version 11.50 implements the ANSI standard SQL data type<br />
BIGINT and the BIGSERIAL data types:<br />
► BIGINT is mapped to the JDBC standard BIGINT data type. It can be used<br />
from a Java application as any other <strong>Informix</strong> data type.<br />
► BIGSERIAL (similar to SERIAL and SERIAL8) does not have an obvious<br />
mapping to any JDBC data type. Thus, the application must use<br />
<strong>Informix</strong>-specific JDBC methods to retrieve the value of these columns.<br />
The methods to access <strong>Informix</strong> serial data types are included in the <strong>Informix</strong><br />
JDBC implementation of the JAVA java.sql.Statement interface, IfxStatement.<br />
You can use the getSerial(), getSerial8(), and getBigSerial() methods to<br />
retrieve the last value that was inserted on a serial column.<br />
Example 5-19 shows how to retrieve a BIGSERIAL value using the<br />
IfxStatement.getBigSerial() method. The example uses the following schema<br />
for the table:<br />
CREATE TABLE tempbs(id BIGSERIAL, name CHAR(10));<br />
Example 5-19 The bigserial.java sample<br />
import java.sql.*;<br />
import com.informix.jdbc.*;<br />
public class bigserial {<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
ResultSet dbRes = null;<br />
long insertedserial = 0;<br />
String<br />
url="jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;";<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn = DriverManager.getConnection(url);<br />
IfxStatement stmt= (IfxStatement) conn.createStatement();<br />
stmt.executeUpdate("INSERT INTO tempbs VALUES (0,'test');");<br />
insertedserial =stmt.getBigSerial();<br />
System.out.println("Last serial: \t"+insertedserial);<br />
stmt.executeQuery("SELECT FIRST 1 * FROM tempbs ORDER BY rowid DESC");<br />
dbRes = stmt.getResultSet();<br />
while (dbRes.next()) {<br />
Chapter 5. Working with the JDBC drivers 177
System.out.format("Last row: \t%d,",dbRes.getLong(1));<br />
System.out.format("%s\n",dbRes.getString(2));<br />
}<br />
dbRes.close();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}<br />
}<br />
Example 5-19 on page 177 inserts a row in the tempbs table and stores the last<br />
BIGSERIAL value in the insertedSerial variable. Example 5-20 shows how to<br />
compile the example and its output.<br />
Example 5-20 The output of the bigserial.java file<br />
C:\work>javac bigserial.java<br />
C:\work>java bigserial<br />
Last serial: 14<br />
Last row: 14,test<br />
C:\work><br />
5.5.3 <strong>Informix</strong> smart large objects<br />
Smart large objects are a type of large object that <strong>Informix</strong> supports. Smart large<br />
objects are stored logically in a table column but stored physically in a specific<br />
type of dbspace called smart blob space.<br />
The data that is stored in the table column is a structure that contains information<br />
about the large object, such as special attributes or pointers to the location in the<br />
smart blob space that contains the data. <strong>Informix</strong> has the following types of smart<br />
large objects:<br />
► A BLOB stores binary data.<br />
► A CLOB stores character data.<br />
178 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
You can manipulate smart large objects with the <strong>Informix</strong> JDBC Driver using the<br />
following methods:<br />
► Standard JDBC 3.0 API<br />
Using standard JDBC methods such as getString(), setAsciiStream(), or<br />
getBinaryStream() allows an application to handle smart lager objects as<br />
standard Java data types.<br />
► <strong>Informix</strong> smart-large-object extensions<br />
If an application requires random access to the large data, it must use<br />
<strong>Informix</strong> smart-large-object extensions. These extensions to the JDBC API<br />
give the application greater control over the smart-large-object data in terms<br />
of object properties, concurrency access, and logging.<br />
The IfxSmartBlob interface implements most of the smart-large-object<br />
extensions, methods such as IfxLoCreate(), IfxLoOpen(), IfxLoRead(), and<br />
IfxLoWrite() can be used to access the smart large object structures and to<br />
manipulate the data of the object.<br />
For a complete description of all the <strong>Informix</strong> smart-large-objects extensions,<br />
refer to the JDBC Driver Programmer’s Guide, which is available at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.<br />
ibm.jdbc_pg.doc/sii-04data-36421.htm<br />
Note: <strong>IBM</strong> Data Server Driver for JDBC does not support the <strong>Informix</strong> smart<br />
large object extensions. Only the standard JDBC API is available to work with<br />
smart large objects.<br />
Using the standard JDBC API<br />
Working with smart large objects using the JDBC API does not require any<br />
specific Java code. Example 5-21 demonstrates how to retrieve a CLOB column<br />
from the <strong>Informix</strong> database using the getString() method.<br />
Example 5-21 The getclob.java sample<br />
C:\work>cat getclob.java<br />
import java.sql.*;<br />
public class getclob {<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
ResultSet dbRes = null;<br />
Statement is = null;<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn = DriverManager.getConnection(<br />
"jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;");<br />
Chapter 5. Working with the JDBC drivers 179
PreparedStatement pstmt=conn.prepareStatement("SELECT * FROM catalog WHERE<br />
catalog_num=?");<br />
pstmt.setString(1, args[0]);<br />
pstmt.executeQuery();<br />
dbRes = pstmt.getResultSet();<br />
dbRes.next();<br />
System.out.println("Advert description: "+dbRes.getString("advert_descr"));<br />
dbRes.close();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
System.err.println(e);<br />
}<br />
}<br />
}<br />
C:\work>javac getclob.java<br />
C:\work>java getclob 10001<br />
Advert description: Brown leather. Specify first baseman's or infield/outfield<br />
style. Specify right- or left-handed.<br />
C:\work><br />
Using the <strong>Informix</strong> extensions<br />
The smart-large-object extensions allows a Java application to have direct<br />
control over the large object structures and data pointers that are used to<br />
describe the large object.<br />
The following interfaces provide the methods that are needed to handle smart<br />
large objects:<br />
► IfxLobDescriptor stores the internal characteristics for a smart large object.<br />
The application must create an IfxLobDescriptor object to insert a new large<br />
object in the database.<br />
► IfxLocator identifies a particular large object. An IfxLocator object can be<br />
created or retrieved from the database to perform any I/O operations with the<br />
large object.<br />
► IfxSmartBlob represents a smart large object within the <strong>Informix</strong> JDBC<br />
Driver. This object provides all the methods that are necessary to create,<br />
open, read, and write to a large object.<br />
► IfxloStat stores statistical information about a smart large object, such as<br />
the size, last access time, last modified time, and last status change.<br />
► IfxBblob and IfxCblob add extended functionality to the standard JDBC 3.0<br />
BLOB and CLOB classes.<br />
180 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 5-22 demonstrates how to read data from a smart large object using the<br />
IfxSmartBlob method. The code performs the following steps to select the CLOB<br />
column advert_descr:<br />
1. Stores the advert_descr column as an IfxCblob object using the getClob()<br />
method.<br />
2. Gets the IfxLocator from the IfxCblob object through the<br />
cblob.getLocator() method.<br />
3. Creates an IfxSmartBlob object and uses the IfxLocator to open the smart<br />
large object in smb.IfxLoOpen().<br />
4. Reads the first 200 bytes of the CLOB using the smb.IfxLoRead() method.<br />
5. Closes the large object and releases the IfxLocator.<br />
Example 5-22 The loext.java file<br />
import java.sql.*;<br />
import com.informix.jdbc.*;<br />
public class loext{<br />
public static void main( String [] args ) {<br />
Connection conn = null;<br />
byte[] buffer = new byte[200];<br />
try {<br />
Class.forName("com.informix.jdbc.IfxDriver");<br />
conn = DriverManager.getConnection(<br />
"jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;");<br />
PreparedStatement pstmt=conn.prepareStatement("SELECT * FROM catalog WHERE<br />
catalog_num=?");<br />
pstmt.setString(1, args[0]);<br />
pstmt.executeQuery();<br />
ResultSet dbRes = pstmt.getResultSet();<br />
dbRes.next();<br />
IfxCblob cblob = (IfxCblob) dbRes.getClob("advert_descr");<br />
IfxLocator loPtr = cblob.getLocator();<br />
IfxSmartBlob smb = new IfxSmartBlob(conn);<br />
int loFd = smb.IfxLoOpen(loPtr, smb.LO_RDONLY);<br />
int bytesReaded = smb.IfxLoRead(loFd, buffer, buffer.length);<br />
smb.IfxLoClose(loFd);<br />
smb.IfxLoRelease(loPtr);<br />
System.out.println("Advert description: "+new String(buffer).trim());<br />
dbRes.close();<br />
conn.close();<br />
}<br />
catch ( Exception e ) {<br />
Chapter 5. Working with the JDBC drivers 181
System.err.println(e);<br />
}<br />
}<br />
}<br />
Example 5-23 shows how to compile the loext.java sample and the output of<br />
this sample.<br />
Example 5-23 The output of the loext.java file<br />
C:\work>javac loext.java<br />
C:\work>java loext 10001<br />
Advert description: Brown leather. Specify first baseman's or infield/outfield<br />
style. Specify right- or left-handed.<br />
C:\work><br />
For more information regarding the use of smart large objects with <strong>Informix</strong><br />
JDBC Driver, refer to the JDBC Driver Programer’s Guide, which is available at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.jdbc_pg.doc<br />
/sii-04data-36421.htm#sii-04data-36421<br />
5.5.4 Secure Socket Layer<br />
In addition to the encrypted communications that are provided by the<br />
Communication Support Module (CSM), the <strong>Informix</strong> database server supports<br />
the use of Secure Socket Layer (SSL) communications for the encryption of the<br />
packages between client and server.<br />
<strong>Informix</strong> JDBC Driver supports only CSM encryptions. You must use <strong>IBM</strong> Data<br />
Server Driver for JDBC to connect to an SSL-enabled <strong>Informix</strong> database server.<br />
Before you can use SSL encryption with an <strong>Informix</strong> database, you must<br />
configure both server and client systems.<br />
182 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Preparing the <strong>Informix</strong> server for SSL<br />
To enable SSL on the <strong>Informix</strong> server, create an <strong>Informix</strong> alias using the drsocssl<br />
protocol.<br />
Example 5-24 enables SSL communications with an <strong>Informix</strong> server.<br />
Example 5-24 SSL configuration on the <strong>Informix</strong> server<br />
$gsk7capicmd -keydb -create -db kodiak.kdb -pw password -type cms -stash<br />
$gsk7capicmd -cert -create -db kodiak.kdb -pw password -label testlabel -dn<br />
"CN=bedfont.uk.ibm.com,O=ibm,C=UK" -size 1024 -default_cert yes<br />
$gsk7capicmd -cert -extract -db kodiak.kdb -format ascii -label testlabel -pw<br />
password -target testlabel.cert<br />
$ pwd<br />
/usr3/11.50/ssl<br />
$ ls -a<br />
. kodiak.crl kodiak.rdb testlabel.cert<br />
.. kodiak.kdb kodiak.sth<br />
$ onstat -c | grep ssl<br />
DBSERVERALIASES kodiak_shm,kodiak_drda,kodiak_ssl<br />
$ grep kodiak_ssl $INFORMIXSQLHOSTS<br />
kodiak_ssl drsocssl kodiak 9191<br />
$<br />
For detailed information regarding the configuration of SSL with an <strong>Informix</strong><br />
server, refer to the Configuring a Server Instance for Secure Sockets Layer<br />
Connections topic in the information center at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
sec.doc/ids_ssl_002.htm<br />
Preparing client for SSL<br />
To enable SSL encryption, both the client and server systems must use the same<br />
certificated file. You can import a certified file into the client system using the<br />
keytool utility, which is included with the Java SDK framework.<br />
Example 5-25 demonstrates how to import the testlabel.cert certificate file<br />
that was created in Example 5-24 on page 183 to enable a trusted relationship<br />
between client and server. A keystore file is created to store the keys for the SSL<br />
encrypted connection.<br />
Example 5-25 SSL Client side configuration<br />
C:\work>keytool -importcert -file testlabel.cert -keystore .keystore<br />
Enter keystore password:<br />
Re-enter new password:<br />
Owner: CN=bedfont.uk.ibm.com, O=ibm, C=UK<br />
Chapter 5. Working with the JDBC drivers 183
Issuer: CN=bedfont.uk.ibm.com, O=ibm, C=UK<br />
Serial number: -14b0e6b89751eab1<br />
Valid from: Thu Jul 15 08:10:31 PDT 2010 until: Sat Jul 16 08:10:31 PDT 2011<br />
Certificate fingerprints:<br />
MD5: 66:FA:CF:44:9F:F3:38:40:7B:9D:93:D6:D6:1C:DB:C5<br />
SHA1: CF:38:6F:CA:C9:A1:54:43:FC:64:AD:F6:DF:5F:CA:65:01:58:DE:DE<br />
Signature algorithm name: SHA1withRSA<br />
Version: 3<br />
Trust this certificate? [no]: yes<br />
Certificate was added to keystore<br />
C:\work>dir .key* /b<br />
.keystore<br />
C:\work><br />
Using a Java client<br />
A Java application can connect to the SSL <strong>Informix</strong> server by performing the<br />
following operations:<br />
► Set the javax.net.ssl.truststore system property to point to the created<br />
keystore (in our example, C:\work\.keystore).<br />
► Set the javax.net.ssl.trustStorePassword System property to the<br />
password that is used for the certificate.<br />
► Get a data source object.<br />
► Set the port number to the SSL port, 9191.<br />
► Set the data source property setSslConnection to true.<br />
Example 5-26 shows basic Java code that connects to an SSL <strong>Informix</strong> server.<br />
Example 5-26 Listing conssl.java<br />
import java.sql.*;<br />
import javax.sql.*;<br />
import java.io.*;<br />
import java.util.*;<br />
import com.ibm.db2.jcc.*;<br />
public class conssl {<br />
public static void main(String args[]) throws SQLException, IOException,<br />
Exception {<br />
System.setProperty ("javax.net.ssl.trustStore","c:/work/.keystore");<br />
System.setProperty ("javax.net.ssl.trustStorePassword","password");<br />
DB2ConnectionPoolDataSource ds = new DB2ConnectionPoolDataSource();<br />
ds.setUser("informix");<br />
184 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
}<br />
}<br />
ds.setPassword("password");<br />
ds.setServerName("kodiak");<br />
ds.setDatabaseName("stores_demo");<br />
ds.setPortNumber(9191);<br />
ds.setDriverType(4);<br />
ds.setSslConnection(true); // SSL<br />
PooledConnection poolconn = ds.getPooledConnection();<br />
Connection con = poolconn.getConnection();<br />
DatabaseMetaData md = con.getMetaData();<br />
System.out.println("Driver name: " + md.getDriverName());<br />
System.out.println("Connected to "+ md.getDatabaseProductName());<br />
System.out.println("Database product version: " +<br />
md.getDatabaseProductVersion());<br />
con.close();<br />
After a successful connection, the code produces metadata information such as<br />
the driver name and database version. Example 5-27 shows the output.<br />
Example 5-27 Output of conssl.java<br />
C:\work>javac conssl.java<br />
C:\work>java conssl<br />
Driver name: <strong>IBM</strong> DB2 JDBC Universal Driver Architecture<br />
Connected to IDS/UNIX32<br />
Database product version: IFX11500<br />
C:\work><br />
5.6 Typical errors<br />
Common issues in Java applications for <strong>Informix</strong> include errors due to an<br />
application development environment without proper configuration and SQL<br />
syntax errors. In this section, we discuss these typical errors.<br />
Chapter 5. Working with the JDBC drivers 185
5.6.1 Class not found errors<br />
The following code shows a class not found error message:<br />
C:\RedBook>java IfxSimpleConnection<br />
Exception in thread "main" java.lang.NoClassDefFoundError:<br />
IfxSimpleConnection<br />
Caused by: java.lang.ClassNotFoundException: IfxSimpleConnection<br />
...<br />
This type of error is usually from either failing to load the JDBC driver or failing to<br />
load the application class because the environment is not configured properly.<br />
To resolve this problem:<br />
► Make sure that you have the JDBC driver .jar files in your CLASSPATH.<br />
► Make sure that you have a “.” (dot) to include your current directory in the<br />
CLASSPATH.<br />
5.6.2 Connectivity errors<br />
When an application fails to connect to the server, you see an error message that<br />
is similar to the following message:<br />
Exception in thread "main" java.sql.SQLException:<br />
com.informix.asf.IfxASFException: Attempt to connect to database server<br />
(demo_on) failed.<br />
at com.informix.jdbc.IfxSqliConnect.(IfxSqliConnect.java:1319)<br />
...<br />
To resolve this problem:<br />
► Verify that the server is running and that the ports are configured correctly on<br />
the server.<br />
► Check the port number specified in the application. Remember, <strong>Informix</strong><br />
JDBC Driver connects to an SQLI port, and <strong>IBM</strong> Data Server driver connects<br />
to a DRDA port.<br />
► Verify that there is no firewall between the client and server. Use Telnet to<br />
connect from the client to the server.<br />
186 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5.6.3 Syntax errors<br />
5.7 Tracing<br />
The exception with SQLCODE -201 is a syntax error in an SQL statement that<br />
you are trying to run. Other than examining the SQL manually, it can be helpful to<br />
know the location of the SQL about which the server is complaining by obtaining<br />
the SQL statement offset as follows:<br />
try {<br />
stmt.execute( SQL );<br />
}<br />
catch(Exception e) {<br />
System.out.println ("Error Offset :"+((IfmxConnection<br />
conn).getSQLStatementOffset() );<br />
System.out.println(e.getMessage() );<br />
}<br />
Tracing the communication with the <strong>Informix</strong> database server might be required<br />
for further diagnostics. The method that you use to enable tracing depends on<br />
the <strong>Informix</strong> JDBC Driver that you are using.<br />
5.7.1 <strong>IBM</strong> <strong>Informix</strong> JDBC Driver<br />
<strong>Informix</strong> JDBC uses the <strong>Informix</strong> SQLI protocol for the communication with the<br />
database server. A trace file with all the SQLI messages can be generated by<br />
enabling the SQLITRACE feature within the JDBC driver.<br />
Depending on the method used for the connection, the SQLIDEBUG can be<br />
activated using a connection string keyword or a DataSource method:<br />
► Set trace for <strong>Informix</strong> JDBC Driver using DriverManager<br />
DriverManager.getConnection("jdbc:informix-sqli://kodiak:9088/sysmaster:INF<br />
ORMIXSERVER=demo_on;user=;password=;SQLIDEBUG=/tmp/jdbctrace");<br />
► Set trace for <strong>Informix</strong> JDBC Driver using a DataSource object<br />
IfxDataSource ifxds = new IfxDataSource();<br />
ifxds.setIfxSQLIDEBUG("c:/temp/sqli.trc");<br />
You can run the trace through the sqliprint utility to render it to text form.<br />
Chapter 5. Working with the JDBC drivers 187
5.7.2 <strong>IBM</strong> Data Server Driver for JDBC<br />
<strong>IBM</strong> Data Server Driver for JDBC uses DRDA as the network protocol and<br />
produces a DRDA trace, which is a straight text output trace that can be<br />
examined directly.<br />
You can enable DRDA trace with the JDBC using the following methods:<br />
► Set trace for <strong>IBM</strong> Data Server Driver using DriverManager<br />
DriverManager.getConnection("jdbc:ids://kodiak:9089/sysmaster:user=;passwor<br />
d=;traceFile=/jcc.trc;TraceLevel=TRACE_ALL;");<br />
► Set trace for <strong>IBM</strong> Data Server Driver using a DataSource object<br />
DB2SimpleDataSource ds = new DB2SimpleDataSource();<br />
ds.setTraceFile("/jcc.trc");<br />
ds.setTraceLevel(com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL);<br />
ds.setTraceFileAppend(false);<br />
Both SQLIDEBUG and DRDADEBUG traces can be activated at the server side<br />
rather than on the client system using the <strong>Informix</strong> onmode utility that is included<br />
with the <strong>Informix</strong> database server. You can find more information about both<br />
traces in “Tracing” on page 117.<br />
188 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate<br />
This chapter describes how to use the HIbernate Java package with an <strong>IBM</strong><br />
<strong>Informix</strong> database.<br />
It includes the following topics:<br />
► Hibernate for Java<br />
► Setup and configuration<br />
► Using Hibernate with an <strong>Informix</strong> database<br />
6<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 189
6.1 Hibernate for Java<br />
This section describes the concepts of Hibernate and the Java Persistence API<br />
(JPA) programming model.<br />
6.1.1 Overview of Hibernate<br />
Hibernate is an open source Java object-relational mapping (ORM) and<br />
persistence framework that allows you to map Plain Old Java Objects (POJO) to<br />
relational database tables using XML configuration files. Hibernate also provides<br />
a data query language, Hibernate Query Language (HQL), and retrieval facilities<br />
that help reduce development time spent on manual data handling in JDBC calls<br />
or SQL statements.<br />
Persistence data refers to any data that must be stored in the database and that<br />
must exist after the application has stopped running. Hibernate provides a way to<br />
persist the typical relational data as well as Java objects that the application<br />
uses.<br />
A relational database stores the information in a tabular way with tables and<br />
columns. A relational database ensures the integrity of the data using SQL<br />
objects such as constraints and referential integrity.<br />
An object-orientated programming (OOP) language, such as Java or C++, does<br />
not have the same data representation as relational databases. The data is<br />
represented by objects with attributes and methods. The relationship between<br />
these objects is implemented with concepts such as inheritance or polymorphism<br />
that do not exist in a relational database.<br />
ORM technologies such as Hibernate try to solve the limitations of<br />
object-orientated languages when using relational databases. ORM allows<br />
developers to focus on the business logic of the application, without the need for<br />
dealing with the data access layer. A developer needs to load only the customer<br />
object and does not need to care about how the information for that particular<br />
customer is stored in the database. ORM solutions produce more robust and<br />
portable code, which means faster development time.<br />
For more information about Hibernate, refer to:<br />
http://docs.jboss.org/hibernate/stable/core/reference/en/html/<br />
190 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
6.1.2 Hibernate concepts<br />
The Hibernate framework includes the following basic concepts:<br />
► Persistence, in Hibernate terms, refers to the concept of storing the state of a<br />
Java object in the database so that it can be restored later.<br />
► Mapping is the process to map SQL tables to Java objects. Mapping is<br />
accomplished using XML mapping files or using Java annotations (metadata<br />
that is added to the application source) in the Java code.<br />
► Object processing refers to the use of the mapped objects from the<br />
application code. How to save and load the state of an Java object using the<br />
Hibernate API or the specific Hibernate Query Language (HQL).<br />
In a simplified words, Hibernate for Java is a set of Java APIs that allows you to<br />
store and retrieve the state of Java objects into a database using a JDBC driver.<br />
Figure 6-1 illustrates the typical components of a Hibernate application.<br />
Figure 6-1 Hibernate components<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 191
A Hibernate application includes the following components (as shown in<br />
Figure 6-1):<br />
► Application represents the Java application.<br />
► Java object is the object that the application wants to persist (keep in the<br />
database).<br />
► Hibernate properties is the configuration file for the Hibernate framework,<br />
named hibernate.cfg.xml, that contains information such as the connection<br />
details for the database server or the SQL dialect to use.<br />
► XML Mapping is an XML file that contains the mappings between Java<br />
objects and database objects.<br />
► JDBC is the JDBC driver that is used for the database connection. You can<br />
use both <strong>IBM</strong> <strong>Informix</strong> JDBC Driver and <strong>IBM</strong> Data Server Driver for JDBC with<br />
Hibernate.<br />
► <strong>Informix</strong> database is the <strong>IBM</strong> <strong>Informix</strong> database server.<br />
In addition to the ORM, Hibernate also provides connection management and a<br />
transaction management services to be used with a Java application. We do not<br />
discuss these topics in this section. For information regarding the use and<br />
configuration of the connection and transaction management services, refer to:<br />
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html<br />
6.2 Setup and configuration<br />
6.2.1 Installation<br />
This section describes the installation and configuration of Hibernate to be used<br />
with an <strong>IBM</strong> <strong>Informix</strong> database.<br />
Hibernate is an open source project that you can download from:<br />
http://sourceforge.net/projects/hibernate/files<br />
The latest version of Hibernate API is V3.5.3, which fully implements the JPA 2.0.<br />
The compressed package name is<br />
hibernate-distribution-3.5.3-Final-dist.zip.<br />
192 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 6-1 shows the contents of the Hibernate compressed package.<br />
Example 6-1 Hibernate distribution content<br />
changelog.txt<br />
documentation<br />
javadocs<br />
manual<br />
hibernate-testing.jar<br />
hibernate3.jar<br />
hibernate_logo.gif<br />
lgpl.txt<br />
lib<br />
bytecode<br />
jpa<br />
hibernate-jpa-2.0-api-1.0.0.Final.jar<br />
optional<br />
c3p0<br />
ehcache<br />
infinispan<br />
jbosscache<br />
oscache<br />
proxool<br />
swarmcache<br />
required<br />
antlr-2.7.6.jar<br />
commons-collections-3.1.jar<br />
dom4j-1.6.1.jar<br />
javassist-3.9.0.GA.jar<br />
jta-1.1.jar<br />
slf4j-api-1.5.8.jar<br />
project<br />
The package contains most of the Java libraries that are required to run a<br />
Hibernate application, including the Hibernate documentation and the source<br />
code project files for the Hibernate libraries. Table 6-1 describes the Java<br />
libraries that are included.<br />
Table 6-1 Included Java libraries<br />
File Description<br />
hibernate3.jar Hibernate Core library for Relational Persistence<br />
hibernate-jpa-2.0-api-1<br />
.0.0.Final.jar<br />
Hibernate definition of the Java Persistence 2.0 API<br />
antlr-2.7.6.jar Framework for grammatical descriptions containing Java<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 193
6.2.2 Configuration<br />
File Description<br />
commons-collections-3.1<br />
.jar<br />
dom4j-1.6.1.jar XML framework for Java<br />
In addition to the libraries that are supplied by the Hibernate package, the .jar<br />
packages contain the following libraries for the JDBC driver that must be included<br />
in the CLASSPATH:<br />
► ifxjdbc.jar: <strong>IBM</strong> <strong>Informix</strong> JDBC Driver<br />
► db2jcc.jar: <strong>IBM</strong> Data Server Driver for JDBC<br />
The Hibernate package is also available as a Maven 2 artifact. Maven is an open<br />
source software project management that uses XML files based on the Project<br />
Object Model (POM) to manage all the attributes and dependencies files of a<br />
Java project. For more information about Maven, refer to the Apache Maven<br />
Project documentation at:<br />
http://maven.apache.org/what-is-maven.html<br />
In this section, we discuss the configuration settings that are required to develop<br />
an <strong>Informix</strong> application with Hibernate.<br />
194 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Types that extend and augment the Java Collections<br />
Framework<br />
javassist-3.9.0.GA.jar Java programming Assistant<br />
jta-1.1.jar The javax.transaction package<br />
slf4j-api-1.5.8.jar API for the Simple Logging Facade for Java (SLF4J)<br />
Note: The slf4j-api-1.5.8.jar package does not contain the complete set<br />
of libraries for the Simple Logging Facade for Java (SLF4J). You must use a<br />
package that provides an implementation for SLF4J in conjunction with<br />
Hibernate API.<br />
Several implementations of SLF4J are available at:<br />
http://www.slf4j.org/download.html<br />
In our examples, we use slf4j-simple-1.6.1.jar and slf4j-nop-1.6.1.jar,<br />
which provide simple-logging and discarded-logging.
CLASSPATH<br />
CLASSPATH is an environment variable that tells the Java compiler and the Java<br />
virtual machine (JVM) where to look for Java class files and Java libraries.<br />
To use Hibernate with a Java program, you must include the core library, the<br />
hibernate3.jar file, and all the libraries in the lib/required directory in the<br />
application CLASSPATH. An implementation of the SLF4J logger and the JDBC<br />
driver is also required for Hibernate to work.<br />
Example 6-2 shows a UNIX script that creates the CLASSPATH environment<br />
variable. We use slf4j-nop-1.6.1.jar with discarded-logging and the <strong>Informix</strong><br />
JDBC Driver ifxjdbc.jar file for our examples.<br />
Example 6-2 Hibernate CLASSPATH script<br />
export CLASSPATH=$CLASSPATH:.<br />
export CLASSPATH=$CLASSPATH:/work/lib/commons-collections-3.1.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/dom4j-1.6.1.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/hibernate-jpa-2.0-api-1.0.0.Final.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/hibernate3.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/javassist-3.9.0.GA.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/jta-1.1.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/slf4j-api-1.6.1.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/antlr-2.7.6.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/slf4j-nop-1.6.1.jar<br />
export CLASSPATH=$CLASSPATH:/work/lib/ifxjdbc.jar<br />
Hibernate configuration file<br />
Hibernate provides the following files to specify configuration parameters:<br />
► hibernate.properties: A standard Java properties text file<br />
► hibernate.cfg.xml: An XML formatted file<br />
Both files contain the same configuration details for the Hibernate service. If both<br />
files exist, the XML configuration file overrides the settings in the properties file.<br />
The configuration file (hibernate.propertieshibernate.cfg.xml) contains the<br />
information that Hibernate needs to connect to the database server. Details such<br />
as name of the JDBC driver class, ConnectionString, and authentication details<br />
are kept in this file.<br />
The Hibernate libraries require the configuration file (hibernate.properties or<br />
hibernate.cfg.xml) to be located in the root directory of the CLASSPATH.<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 195
Example 6-3 shows a hibernate.cfg.xml file using the <strong>Informix</strong> JDBC Driver.<br />
Example 6-3 A hibernate.cfg.xml sample<br />
<br />
<br />
<br />
<br />
<br />
<br />
com.informix.jdbc.IfxDriver<br />
<br />
<br />
jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;<br />
<br />
<br />
informix<br />
<br />
<br />
password<br />
<br />
<br />
org.hibernate.dialect.<strong>Informix</strong>Dialect<br />
<br />
false<br />
update<br />
<br />
<br />
<br />
<br />
In addition to the JDBC information, the configuration file also contains<br />
configuration details for the Hibernate service.<br />
Use the dialect property to specify the SQL dialect for the database server. A<br />
Hibernate dialect is a Java class that contains specific details regarding the SQL<br />
syntax that is needed to communicate with a particular database.<br />
When using the <strong>IBM</strong> <strong>Informix</strong> JDBC Driver with Hibernate, set the dialect to<br />
orb.hibernate.dialect.<strong>Informix</strong>Dialect. The dialect for the <strong>IBM</strong> Data Server<br />
Driver for JDBC is org.hibernate.dialect.DB2Dialect.<br />
These classes are included in the hibernate3.jar package and contain a basic,<br />
non-optimized implementation of the <strong>Informix</strong> dialect.<br />
196 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Note: You can find a patch that adds <strong>Informix</strong> SQL optimizations to the original<br />
Hibernate dialect for <strong>Informix</strong> databases at:<br />
http://www.iiug.org/opensource/files/hibernate-3.3.2_informix.tar.gz<br />
The XML configuration file allows the inclusion of XML mapping files. Use the<br />
mapping resource property to specify the XML file that contains the mapping<br />
between the SQL table and the Java object.<br />
Example 6-3 on page 196 includes the State.hbm.xml mapping file as part of the<br />
hibernate configuration, as shown in the following line:<br />
<br />
Properties, such as hibernate.show_sql or hbm2ddl.auto, control the behavior<br />
of the Hibernate service. Use the hibernate.show_sql property to dump all the<br />
SQL statements to the console, which might be useful for debugging purposes.<br />
Set the hbm2ddl.auto property to update to specify that the schema for the SQL<br />
table is updated automatically if it differs from the definition in the XML definition.<br />
For more information regarding all the supported properties for the Hibernate<br />
configuration files, refer to:<br />
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration<br />
.html<br />
XML mapping file<br />
You can use the XML mapping file to specify the relations between a Java object<br />
and an SQL database object. These mapping definitions are used to provide<br />
Hibernate with the information that is needed to persist the Java objects into the<br />
relational database. They also provide support features, such as creating the<br />
database schema and relationship between the objects.<br />
XML mapping files are not required when using Java annotations. The mapping<br />
information is added using annotations in the Java source code.<br />
Example 6-4 shows a basic XML file used to map the States table to the State<br />
Java object.<br />
Example 6-4 The State.hbm.xml file<br />
<br />
<br />
<br />
name="State"<br />
table="States"><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
The definition includes information, such as the table name and column to be<br />
used as identifier (ID), and specific attributes for the elements, such as the<br />
generator class that defines a column as an identifier that is generated<br />
automatically by the Java libraries. These details are used by the Hibernate<br />
libraries to generate SQL statements automatically, including Data Definition<br />
Language (DDL) and Data Manipulation Language (DML).<br />
For a complete list of all the attributes that are available in an XML mapping file,<br />
refer to:<br />
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping<br />
-declaration<br />
6.3 Using Hibernate with an <strong>Informix</strong> database<br />
In this section, we demonstrate how to perform basic operations using the<br />
Hibernate API with an <strong>IBM</strong> <strong>Informix</strong> database server.<br />
6.3.1 Components of a Hibernate application<br />
To develop a Hibernate application, you need to complete the following tasks:<br />
► Create the Java objects.<br />
► Create the XML Mapping files for the Java objects.<br />
► Create the configuration file for the Hibernate framework.<br />
198 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
► Create the HibernateUtil helper class that provides access to the Hibernate<br />
session.<br />
► Create the application that will use the Java persistence objects.<br />
Java object<br />
Hibernate supports the use of Plain Old Java Objects (POJO) for the definition of<br />
a persistence object. Thus, there is a special requirement when writing the Java<br />
class that represents the object.<br />
Example 6-5 shows the contents of the State.java file that is used to define the<br />
State object. The State Java object is created as any normal Java object. The<br />
object has three properties (id, code, and sname) and the usual methods to set<br />
and get those properties, such as setcode() or setsname(). The id attribute is<br />
used to uniquely identify the Java persistence object.<br />
Example 6-5 The State.java file<br />
public class State {<br />
private Long id;<br />
private String code;<br />
private String sname;<br />
}<br />
public State() {}<br />
public State(String text, String text2) {<br />
this.code = text;<br />
this.sname = text2;<br />
}<br />
public Long getId() {<br />
return id;<br />
}<br />
private void setId(Long id) {<br />
this.id = id;<br />
}<br />
public String getcode() {<br />
return code;<br />
}<br />
public void setcode(String text) {<br />
this.code = text;<br />
}<br />
public String getsname() {<br />
return sname;<br />
}<br />
public void setsname(String text) {<br />
this.sname = text;<br />
}<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 199
Example 6-6 shows the CLASSPATH variable used in the environment and how<br />
to compile the State.java source code.<br />
Example 6-6 Compile line for State.java<br />
C:\work>set classpath<br />
CLASSPATH=C:\work\lib\commons-collections-3.1.jar;C:\work\lib\dom4j-1.6.1.jar;C<br />
:\work\lib\hibernate-jpa-2.0-api-1.0.0.Final.jar;C:\work\lib\hibernate3.jar;C:\<br />
work\lib\ifxjdbc.jar;C:\work\lib\javassist-3.9.0.GA.jar;C:\work\lib\jta-1.1.jar<br />
;C:\work\lib\slf4j-api-1.6.1.jar;C:\work\lib\slf4j-nop-1.6.1.jar;C:\work\lib\an<br />
tlr-2.7.6.jar;.<br />
C:\work>javac State.java<br />
C:\work><br />
XML mapping file<br />
An XML mapping file is required to link the Java object with an <strong>Informix</strong> database<br />
object. The States table keeps all the instances of the Java State object.<br />
The common convention for the name of the XML mapping file is<br />
objectname_hbm.xml.<br />
Example 6-7 shows the contents of the State_hbm.xml file.<br />
Example 6-7 The State_hbm.xml file<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
200 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The name of the class is State. It is mapped to the <strong>Informix</strong> SQL table States.<br />
The State class contains two types of elements:<br />
► <br />
Defines the mapping from that property to the primary key column. It usually<br />
contains the generator element that is used to generate unique identifiers for<br />
the ID property.<br />
The generator class supports different methods of generating identifiers.<br />
Increment is the most basic method, and sequence allows to use an SQL<br />
sequence to obtain the identifier value. For a list of all the methods<br />
implemented in the generator interface, refer to:<br />
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#map<br />
ping-declaration-id<br />
► <br />
Defines a property for the object, including the name of the property and the<br />
name of the mapped database table column.<br />
The elements of the XML mapping file can have additional attributes to define<br />
specific characteristics of the columns it maps. You can use attributes such as<br />
type and not-null to specify the data type and the nullability of a column.<br />
Hibernate configuration file<br />
The Hibernate configuration file must reside in the root directory of the<br />
CLASSPATH. It contains configuration values for the Hibernate service and<br />
connection details for the database server.<br />
Example 6-8 shows the contents of the hibernate.cfg.xml file that we used in<br />
our sample. The connection details correspond to the <strong>IBM</strong> <strong>Informix</strong> JDBC Driver.<br />
Example 6-8 The hibernate.cfg.xml file<br />
<br />
<br />
<br />
<br />
<br />
<br />
com.informix.jdbc.IfxDriver<br />
<br />
<br />
jdbc:informix-sqli://kodiak:9088/stores_demo:INFORMIXSERVER=demo_on;<br />
<br />
<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 201
informix<br />
<br />
<br />
password<br />
<br />
<br />
org.hibernate.dialect.<strong>Informix</strong>Dialect<br />
<br />
false<br />
update<br />
<br />
<br />
<br />
Example 6-9 shows the attributes that are required for an <strong>IBM</strong> Data Server Driver<br />
for JDBC connection. The configuration file includes the State.hbm.xml XML<br />
mapping for the State object.<br />
Example 6-9 <strong>IBM</strong> Data Server Driver hibernate.cfg.xml file<br />
<br />
org.hibernate.dialect.DB2Dialect<br />
<br />
<br />
com.ibm.db2.jcc.DB2Driver<br />
<br />
<br />
jdbc:ids://kodiak:9089/stores_demo;<br />
<br />
HibernateUtil helper class<br />
The HibernateUtil class is used to interact with the Hibernate service. It<br />
performs the operations related to the Hibernate SessionFactory classes that<br />
provide a convenient way for the application to access the Hibernate session.<br />
The Java file that contains the HibernateUtil helper class is<br />
HibernateUtil.java. Example 6-10 shows a typical helper class.<br />
Example 6-10 The HiberanteUtil.java file<br />
import org.hibernate.SessionFactory;<br />
import org.hibernate.cfg.Configuration;<br />
public class HibernateUtil {<br />
private static final SessionFactory sessionFactory;<br />
202 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
static {<br />
try {<br />
// Creates the SessionFactory from hibernate.cfg.xml<br />
sessionFactory = new Configuration().configure().buildSessionFactory();<br />
}<br />
catch (Throwable ex) {<br />
System.err.println("SessionFactory creation failed." + ex);<br />
throw new ExceptionInInitializerError(ex);<br />
}<br />
}<br />
public static SessionFactory getSessionFactory() {<br />
return sessionFactory;<br />
}<br />
public static void shutdown() {<br />
// Close caches and connection pools<br />
getSessionFactory().close();<br />
}<br />
}<br />
This class creates a Hibernate session using the parameters that are specified in<br />
the Hibernate configuration file. Create the class by compiling the<br />
HibernateUtil.java Java file as follows:<br />
javac HibernateUtil.java<br />
Java application<br />
The only task that is required by a Java application to use the Hibernate<br />
persistence object is to create a Hibernate Session instance using the<br />
HibernateUtil helper class. After that, the application can create persist objects<br />
in the same manner that it does for any other Java object.<br />
6.3.2 Working with a Hibernate object<br />
Developers must follow an object-orientated methodology when writing<br />
applications using the Hibernate API. With Hibernate, the data is represented by<br />
the status and properties of an object, not by tables and rows in a relational<br />
database.<br />
Storing<br />
When a persistent object is created and saved or stored, an insert operation is<br />
performed on the database.<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 203
Example 6-11 shows basic Java code that creates a new State object using the<br />
parameters that are supplied in the command line. The example creates a<br />
Hibernate Session object using the helper class and then creates a new State<br />
object called myState. The myState.setcode() and myState.setsname() methods<br />
are used to stored the values that are passed from the command line.<br />
Example 6-11 The Create.java code<br />
import java.util.*;<br />
import org.hibernate.*;<br />
import javax.persistence.*;<br />
public class create {<br />
public static void main(String[] args) {<br />
Session Session = HibernateUtil.getSessionFactory().openSession();<br />
Transaction Transaction = Session.beginTransaction();<br />
State myState = new State();<br />
myState.setcode(args[0]);<br />
myState.setsname(args[1]);<br />
Long stateId = (Long) Session.save(myState);<br />
System.out.println("Stated added: "+myState.getcode()+", "<br />
+myState.getsname());<br />
Transaction.commit();<br />
Session.close();<br />
// Shutting down the application<br />
HibernateUtil.shutdown();<br />
}<br />
}<br />
Example 6-12 shows the compile line and the output of the create.java sample.<br />
Example 6-12 Compiling the create.java sample and the output<br />
C:\work>javac create.java<br />
C:\work>java create AZ Arizona<br />
Stated added: AZ, Arizona<br />
C:\work><br />
The hbm2ddl.auto Hibernate property is set to update in the configuration file,<br />
which means that if the table does not exist in the database, it is created<br />
automatically using the table model that is defined in the XML mapping file.<br />
204 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 6-13 shows the schema of the created States table and the new row<br />
that is added to the table.<br />
Example 6-13 States table schema<br />
D:\Infx\ids1150>dbaccess stores_demo -<br />
Database selected.<br />
> INFO COLUMNS FOR states;<br />
Column name Type Nulls<br />
id int8 no<br />
code varchar(255) yes<br />
sname varchar(255) yes<br />
> SELECT * FROM states;<br />
id 1<br />
code AZ<br />
sname Arizona<br />
1 row(s) retrieved.<br />
><br />
The Java object is made persistent, thus saving the state of the object in the<br />
database, using the Session.save() Hibernate method and committing the unit<br />
of work with Transaction.commit().<br />
Loading<br />
To retrieve a persistent object from the database server, the application must<br />
create a Hibernate session and load the state of the object into the current<br />
session.<br />
The process of loading a persistence object can be achieved using several<br />
methods:<br />
► Use Session.load() and Session.get() to retrieve the state of an object from<br />
the database using the object identifier as reference. For example, to load the<br />
State object with ID equal to 1, the following code is needed:<br />
State mystateobj = (State) Session.load(State.class,1);<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 205
The only difference between the load() and get() methods is the returned<br />
value. If the object is not found, get() returns a null and load() throws an<br />
exception.<br />
► Use an SQL or HQL query. HQL is similar to SQL but is optimized for an<br />
object-orientated environment. You can use methods, such as<br />
Session.createQuery() and Session.createCiteria(), to load the state of<br />
one or multiple objects from the database server. The following command<br />
loads all State objects into a list:<br />
List states = session.createQuery("from state").list();<br />
Example 6-14 demonstrates how to load a single object from the database using<br />
the Session.load() method.<br />
Example 6-14 The load.java sample<br />
import java.util.List;<br />
import org.hibernate.HibernateException;<br />
import org.hibernate.Session;<br />
import org.hibernate.Transaction;<br />
import org.hibernate.criterion.*;<br />
public class load {<br />
public static void main(String[] args) {<br />
}<br />
}<br />
Long stateId = null;<br />
Session Session = HibernateUtil.getSessionFactory().openSession();<br />
Transaction Transaction = Session.beginTransaction();<br />
stateId=Long.parseLong(args[0]);<br />
State mystate = (State) Session.load(State.class,stateId);<br />
System.out.println(mystate.getcode() +", " +<br />
mystate.getsname() );<br />
Transaction.commit();<br />
Session.close();<br />
206 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 6-15 shows how to compile and run the previous example. The<br />
application loads the State object identified by the id value passed through the<br />
command line.<br />
Example 6-15 Output of the load.java sample<br />
C:\work>javac load.java<br />
C:\work>java load 1<br />
AZ, Arizona<br />
C:\work>java load 2<br />
CA, California<br />
C:\work><br />
The application can use an HQL query to load all the objects, or entities, of a<br />
particular type. All the State objects are kept in the States table. Example 6-16<br />
shows how to retrieve a list of the State entities.<br />
Example 6-16 The list.java sample<br />
C:\work>cat list.java<br />
import java.util.*;<br />
import org.hibernate.*;<br />
import javax.persistence.*;<br />
public class list {<br />
public static void main(String[] args) {<br />
Session newSession = HibernateUtil.getSessionFactory().openSession();<br />
Transaction newTransaction = newSession.beginTransaction();<br />
List states = newSession.createQuery("from State order by id asc").list();<br />
for ( Iterator iter = states.iterator();<br />
iter.hasNext(); ) {<br />
State state = (State) iter.next();<br />
System.out.println(state.getId() +", "+ state.getcode()<br />
+", " + state.getsname() );<br />
}<br />
newTransaction.commit();<br />
newSession.close();<br />
HibernateUtil.shutdown();<br />
}<br />
}<br />
C:\work>javac list.java<br />
C:\work>java list<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 207
1, AZ, Arizona<br />
2, CA, California<br />
C:\work><br />
Updating<br />
Updating an persistent object is the process of changing the state of the object.<br />
No specific operation is required for this task. The application must load the<br />
object, change the object properties, and save it.<br />
Example 6-17 demonstrates how to update individual objects. It uses the<br />
parameters from the command line to change the code and sname properties of a<br />
State object.<br />
Example 6-17 The update.java sample<br />
C:\work>cat update.java<br />
import java.util.List;<br />
import org.hibernate.HibernateException;<br />
import org.hibernate.Session;<br />
import org.hibernate.Transaction;<br />
public class update {<br />
public static void main(String[] args) {<br />
Long stateId = null;<br />
Session Session = HibernateUtil.getSessionFactory().openSession();<br />
Transaction Transaction = Session.beginTransaction();<br />
stateId=Long.parseLong(args[0]);<br />
State mystate = (State) Session.load(State.class,stateId);<br />
mystate.setcode(args[1]);<br />
mystate.setsname(args[2]);<br />
Session.save(mystate);<br />
System.out.println("new values: "+ mystate.getcode()+", "+<br />
mystate.getsname() );<br />
Transaction.commit();<br />
Session.close();<br />
}<br />
}<br />
C:\work>javac update.java<br />
208 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
C:\work>java update 1 AZ ARIZONA<br />
new values: AZ, ARIZONA<br />
C:\work><br />
Criteria<br />
Criteria queries are a feature of HQL that allow the building of complex queries<br />
using an object-orientated API.<br />
Example 6-18 shows how to update a president object using an HQL criteria to<br />
retrieve the object from the database. The HQL criteria is created using the name<br />
of the class for the object that you want to select, State.class. An HQL<br />
Restriction, Restriction.eq("code", args[0]), is used to specify a filter for the<br />
criteria. This restriction specifies that the query returns the objects with a specific<br />
code value.<br />
Example 6-18 The update2.java sample<br />
import java.util.*;<br />
import org.hibernate.*;<br />
import javax.persistence.*;<br />
import org.hibernate.criterion.*;<br />
public class update2 {<br />
public static void main(String[] args) {<br />
Session Session = HibernateUtil.getSessionFactory().openSession();<br />
Transaction Transaction = Session.beginTransaction();<br />
Criteria crit = Session.createCriteria(State.class);<br />
crit.add( Restrictions.eq( "code", args[0]) );<br />
List states = crit.list();<br />
for ( Iterator iter = states.iterator(); iter.hasNext(); ) {<br />
State mystate = (State) iter.next();<br />
mystate.setsname(args[1]);<br />
Session.flush();<br />
Long msgId = (Long) Session.save(mystate);<br />
System.out.println("new values: "+ mystate.getcode()+", "+<br />
mystate.getsname() );<br />
}<br />
Transaction.commit();<br />
Session.close();<br />
HibernateUtil.shutdown();<br />
}<br />
}<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 209
Example 6-19 shows the output of the example.<br />
Example 6-19 The update2.java output<br />
C:\work>javac update2.java<br />
C:\work>java update2 AZ Arizona<br />
new values: AZ, Arizona<br />
C:\work><br />
Deleting<br />
In the Hibernate framework, deleting a persistence object means to make the<br />
object transient. A transient object is a typical Java object, but the state of the<br />
object is not stored anywhere. Thus, the object exists only during the life of the<br />
application.<br />
An object can be deleted using the Session.delete() method. Example 6-20<br />
demonstrates how to use the Session.delete() method.<br />
Example 6-20 The delete.java sample<br />
C:\work>cat delete.java<br />
import java.util.List;<br />
import org.hibernate.HibernateException;<br />
import org.hibernate.Session;<br />
import org.hibernate.Transaction;<br />
public class delete {<br />
public static void main(String[] args) {<br />
Long stateId = null;<br />
Session Session = HibernateUtil.getSessionFactory().openSession();<br />
Transaction Transaction = Session.beginTransaction();<br />
stateId=Long.parseLong(args[0]);<br />
State mystate = (State) Session.load(State.class,stateId);<br />
Session.delete(mystate);<br />
System.out.println( "Object deleted");<br />
Transaction.commit();<br />
Session.close();<br />
}<br />
}<br />
C:\work>javac delete.java<br />
210 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
C:\work>java delete 2<br />
Object deleted<br />
C:\work>java list<br />
1, AZ, Arizona<br />
C:\work><br />
One of the main features of using Hibernate is that it abstracts the JDBC layer<br />
from the application. Thus, applications written using the Hibernate API are not<br />
tied to a specific JDBC driver or relational database server.<br />
You can run all the examples that we present in this section using any of the two<br />
<strong>Informix</strong> JDBC drivers that are available, <strong>IBM</strong> <strong>Informix</strong> JDBC Driver or <strong>IBM</strong> Data<br />
Server Driver for JDBC. Refer to the “Hibernate configuration file” on page 195<br />
for information regarding how to switch between JDBC drivers.<br />
For more information about how to develop using the Hibernate API, refer to:<br />
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html<br />
6.3.3 Using annotations<br />
Java annotations is a feature added into the Java 5 Software Development Kit<br />
(SDK) that allows the inclusion of special annotations within the Java source<br />
code to express metadata relating to program objects.<br />
Hibernate supports Java annotations through the use of the Hibernate<br />
Annotations Extensions. These extensions allow you to include the definition of<br />
specific Hibernate properties, such as configuration properties or object<br />
mappings properties, directly into the Java code.<br />
With annotations, there is no need for a specific XML mapping file to link the Java<br />
objects with the database objects.<br />
Example 6-21 shows the definition of the State object using Hibernate<br />
annotations. Annotations, such as @Entity, @Table or @Column, define the<br />
mapping information that is needed to persist the Java objects into the database.<br />
Example 6-21 The state.java sample with annotations<br />
import javax.persistence.*;<br />
@Entity<br />
@Table(name="States")<br />
public class State {<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 211
private Long id;<br />
private String code;<br />
private String sname;<br />
public State() {}<br />
public State(String text, String text2) {<br />
this.code = text;<br />
this.sname = text2;<br />
}<br />
@Id<br />
@GeneratedValue(strategy=GenerationType.IDENTITY )<br />
@Column(name="id")<br />
public Long getId() {<br />
return id;<br />
}<br />
private void setId(Long id) {<br />
this.id = id;<br />
}<br />
@Column(name="code", length=2, nullable=false)<br />
public String getcode() {<br />
return code;<br />
}<br />
public void setcode(String text) {<br />
this.code = text;<br />
}<br />
@Column(name="sname", length=15, nullable=false)<br />
public String getsname() {<br />
return sname;<br />
}<br />
public void setsname(String text) {<br />
this.sname = text;<br />
}<br />
}<br />
Because all the information that is required to map the objects is specified as<br />
annotations, you do not need to include a section in the Hibernate<br />
hibernate.cfg.xml configuration file.<br />
212 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
When using annotations, the HibernateUtil helper class, HibernateUtil.java,<br />
uses the AnnotationConfiguration() interface to retrieve the Hibernate<br />
properties and the metadata definition from the object class. Example 6-22<br />
shows the HibernateUtil.java file that is used to create a SessionFactory for<br />
the State.class object.<br />
Example 6-22 The HibernateUtil.java sample for annotations<br />
import org.hibernate.SessionFactory;<br />
import org.hibernate.cfg.AnnotationConfiguration;<br />
public class HibernateUtil {<br />
private static final SessionFactory sessionFactory;<br />
static {<br />
try {<br />
// Create the SessionFactory from hibernate.cfg.xml<br />
}<br />
sessionFactory = new AnnotationConfiguration()<br />
.configure()<br />
.addAnnotatedClass(State.class)<br />
.buildSessionFactory();<br />
} catch (Throwable ex) {<br />
System.err.println("Initial SessionFactory creation failed." + ex);<br />
throw new ExceptionInInitializerError(ex);<br />
}<br />
}<br />
public static SessionFactory getSessionFactory() {<br />
return sessionFactory;<br />
}<br />
public static void shutdown() {<br />
getSessionFactory().close();<br />
}<br />
You do not need to change the code for the Java applications that use the<br />
persistence object. The process when using Hibernate annotations for<br />
manipulating the objects is the same as when using the XML mapping files.<br />
You can find a description of all the Hibernate annotation extensions at:<br />
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#en<br />
tity-hibspec<br />
Chapter 6. <strong>IBM</strong> <strong>Informix</strong> with Hibernate 213
214 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong><br />
OLE DB Provider<br />
7<br />
This chapter describes <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider. It includes the following<br />
topics:<br />
► <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider<br />
► Setup and configuration<br />
► Developing an OLE DB application<br />
► Visual Basic, ADO.NET, and SQL Server<br />
► Troubleshooting and tracing<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 215
7.1 <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider<br />
<strong>Informix</strong> OLE DB Provider is a Universal Data Access component that enables<br />
<strong>IBM</strong> <strong>Informix</strong> Server access from OLE DB consumers.<br />
Microsoft OLE DB is a specification for a set of interfaces that is designed to<br />
expose data from a variety of sources (relational and non-relational). OLE DB<br />
uses the Component Object Model (COM) to accomplish this.<br />
You can use <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider to enable client applications, such as<br />
ActiveX Data Object (ADO) applications and web pages, to access data on an<br />
<strong>IBM</strong> <strong>Informix</strong> database server.<br />
Table 7-1 shows the name and COM class ID of the <strong>IBM</strong> <strong>Informix</strong> OLE DB<br />
Provider.<br />
Table 7-1 COM class ID<br />
Name DLL CLSID<br />
ifxoledbc iifxoledbc.dll {A6D00422-FD6C-11D0-8043-00A0C90F1C59}<br />
Table 7-2 lists the <strong>Informix</strong> database servers that support <strong>IBM</strong> <strong>Informix</strong> OLE DB<br />
Provider.<br />
Table 7-2 Supported databases<br />
Database Server Versions<br />
<strong>IBM</strong> <strong>Informix</strong> 10.0,11.10,11.50<br />
<strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server 8.50 and higher<br />
<strong>IBM</strong> <strong>Informix</strong> Online 5.20 and higher<br />
7.2 Setup and configuration<br />
In this section, we discuss how to install and set up the OLE DB provider and<br />
how to perform basic connectivity tests.<br />
7.2.1 Installation and setup<br />
<strong>IBM</strong> <strong>Informix</strong> OLE DB Provider is included only in the Windows version of<br />
<strong>Informix</strong> Client Software Development Kit (Client SDK). The <strong>Informix</strong> OLE DB<br />
216 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Provider is selected by default in the Client SDK installation. During the<br />
installation process, the provider is registered automatically on the Windows<br />
registry as a command component.<br />
The default installation directory is the C:\Program<br />
Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK directory. The INFORMIXDIR environment<br />
variable should point to the directory where the product is installed. The provider<br />
shared library, ifxoledbc.dll, is located in %INFORMIXDIR%\bin directory.<br />
Because by default <strong>Informix</strong> OLE DB Provider is registered during Client SDK<br />
installation, manual registration is usually not required. However, if needed, you<br />
can register <strong>Informix</strong> OLE DB Provider manually using the Microsoft<br />
regsvr32.exe tool using the following command:<br />
regsvr32.exe %INFORMIXDIR%\bin\ifxoledbc.dll<br />
Note: On a Windows x64 (64-bit) system, there are two versions of the<br />
regsvr32.exe tool:<br />
► The 32-bit version is located in C:\WINDOWS\SysWOW64<br />
► The 64-bit version is located in C:\WINDOWS\System<br />
Use the correct version when registering <strong>Informix</strong> OLE DB Provider.<br />
Otherwise, the application will fail to load the shared library due to a<br />
mismatched version.<br />
After installation, you must run the coledbp.sql script on the database server<br />
against the sysmaster database as user informix to add the tables and functions<br />
that are required by <strong>Informix</strong> OLE DB Provider to work.<br />
The coledbp.sql script is located in the %INFORMIXDIR%\etc directory. If you want<br />
to remove the support functions and tables, use the doledbp.sql script that is<br />
located in the same directory.<br />
7.2.2 Verifying connectivity<br />
Client SDK does not contain any specific tool to test <strong>Informix</strong> OLE DB Provider.<br />
Internally, <strong>Informix</strong> OLE DB Provider uses the same <strong>Informix</strong> connection libraries<br />
as ESQL/C or ODBC. You can test the basic connection details for your database<br />
server using the iLogin utility that is included in the %INFORMIXDIR%\bin directory.<br />
Testing using Visual Basic Scripting Edition<br />
Visual Basic Scripting Edition (VBScript) is a scripting language that is included<br />
as part of the Windows operating system. It provides access to ADO objects<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 217
through COM. You can use it to test whether the <strong>Informix</strong> OLE DB Provider is<br />
configured properly.<br />
A VBScript file is a text file with a .vbs extension that is loaded automatically and<br />
executed by Visual Basic at run time.<br />
Example 7-1 shows a simple VBScript that loads <strong>Informix</strong> OLE DB Provider and<br />
selects a single row from the database.<br />
Example 7-1 The connect.vbs script<br />
' ---- Test_Ifx.vbs ----<br />
On Error Resume Next<br />
args = WScript.Arguments.Count<br />
If args < 1 then<br />
WScript.Echo "usage: connect.vbs Connection_string"<br />
WScript.Echo " e.g.: connect.vbs ""Data Source=stores_demo@demo_on;User<br />
ID=informix;Password=password;"""<br />
WScript.Quit<br />
end If<br />
set conn=createobject("ADODB.Connection")<br />
conn.provider = "Ifxoledbc"<br />
conn.connectionstring = WScript.Arguments.Item(0)<br />
conn.open<br />
If Err then<br />
WScript.Echo "Error!! "+conn.Errors(0).Description<br />
Else<br />
WScript.Echo "Connected"<br />
conn.close<br />
End If<br />
' ---- Test_Ifx.vbs ----<br />
Example 7-2 shows how run the connect.vbs script with a data source string as<br />
the parameter to test the database connectivity.<br />
Example 7-2 Output of the connect.vbs script<br />
c:\work>connect.vbs<br />
usage: connect.vbs Connection_string<br />
e.g.: connect.vbs "Data Source=stores_demo@demo_on;User<br />
ID=informix;Password=password;"<br />
c:\work>connect.vbs "Data Source=stores_demo@demo_on;User<br />
ID=informix;Password=password;"<br />
Connected<br />
218 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
c:\work>connect.vbs "Data Source=stores_demo@demo_on;User<br />
ID=informix;Password=invalid;"<br />
Error!! EIX000: (-951) Incorrect password or user informix@dubito is not known<br />
on the database server.<br />
Rowset Viewer<br />
You can also test <strong>Informix</strong> OLE DB Provider using the Rowset Viewer. The<br />
Rowset Viewer is included with the Microsoft Data Access Components (MDAC)<br />
2.8 Software Development Kit (SDK), which you can download from the following<br />
location:<br />
http://www.microsoft.com/downloads/details.aspx?FamilyID=6c050fe3-c795-4b7d-b03<br />
7-185d0506396c<br />
To test <strong>Informix</strong> OLE DB Provider using the Rowset Viewer tool, perform the<br />
following steps:<br />
1. Run the Rowset tool.<br />
2. Select File � Full Connect.<br />
3. In the Full Connect dialog box, choose Ifxoledbc in the Provider field.<br />
4. In the DataSource field, enter the database server name to which you want to<br />
connect, for example stores_demo@demo_on.<br />
5. Click OK.<br />
You also can use Rowset Viewer to run SQL statements against the database<br />
server. To execute an SQL statement, write the SQL text in the Rowset pane and<br />
click .<br />
7.3 Developing an OLE DB application<br />
This section describes the interfaces that are implemented in <strong>Informix</strong> OLE DB<br />
Provider and demonstrates how to perform basic database operations.<br />
In this section, we discuss the following topics:<br />
► Supported interfaces<br />
► Connecting to database<br />
► Type mapping<br />
► Cursors<br />
► Typical database operations<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 219
7.3.1 Supported interfaces<br />
An OLE DB application creates objects based on ADO interfaces to perform<br />
operations against a database server. Table 7-3 lists a few ADO interfaces that<br />
are implemented in <strong>Informix</strong> OLE DB Provider as examples. For a complete list,<br />
refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.oledb.doc/s<br />
ii-xa-21954.htm#sii-xa-21954<br />
Table 7-3 <strong>Informix</strong> OLE DB Provider supported interfaces<br />
Interface Description<br />
IAccessor Provides methods for accessor management<br />
IColumnsInfo Provides information about columns of a rowset or prepared<br />
command<br />
ICommand Executes commands<br />
IDBCreateCommand Obtains a new command<br />
IDBCreateSession Obtains a new session<br />
IDBDataSourceAdmin Creates, destroys, and modifies data source objects<br />
IDBProperties Gets and sets the values of properties on the data source<br />
object or enumerator and obtains information about all<br />
properties that are supported<br />
IErrorLookup Used by OLE DB error objects to determine the values of the<br />
error message, source, Help file path, and context ID based<br />
on the return code and a provider-specific error number<br />
IGetDataSource Obtains an interface pointer to the data source object<br />
IRowsetIdentity Indicates row instance identity is implemented on the rowset<br />
and enables testing for row identity<br />
ISessionProperties Returns information about the properties a session supports<br />
and the current settings of those properties<br />
ITransaction Used to commit, abort, and obtain status information about<br />
transactions<br />
220 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
7.3.2 Connecting to database<br />
In this section, we describe the connection string options with <strong>Informix</strong> OLE DB<br />
Provider.<br />
The connection details for the database server are passed to <strong>Informix</strong> OLE DB<br />
Provider using a connection string. If the Provider attribute of the Connection<br />
object is not set, you must include the provider keyword as part of the<br />
connection string to specify the name of <strong>Informix</strong> OLE DB Provider, ifxoledbc.<br />
For example:<br />
connStr="Provider=Ifxoledbc;Data Source=stores_demo@demo_on"<br />
Table 7-4 describes the specific connection string attributes for <strong>Informix</strong> OLE DB<br />
Provider.<br />
Table 7-4 Connection string attributes<br />
Keyword Description<br />
Data Source Database and <strong>Informix</strong> Server to which to connect.<br />
The syntax for the Data Source parameter is as<br />
follows:<br />
database@server<br />
If @server is not specified, the default database<br />
server is used (corresponding to the value specified<br />
by the client’s INFORMIXSERVER registry entry or<br />
environment variable).<br />
User ID The user ID used to connect to the <strong>Informix</strong> server<br />
Password The password for the user ID<br />
Persist Security Info Specifies whether the data source can keep<br />
authentication information such as the password<br />
Client_locale The client locale for the application which<br />
correspond to the locale used by the Windows OS<br />
Db_locale The database locale that was used when the<br />
database was created<br />
UNICODE Indicates whether to use <strong>IBM</strong> <strong>Informix</strong> GLS Unicode<br />
Controls and how the code set conversion to<br />
Unicode is done<br />
decasr8 If set, floating point numbers with a scale greater<br />
than 30 are returned as DBTYPE_R8<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 221
7.3.3 Type mapping<br />
Keyword Description<br />
RSASWS or<br />
REPORTSTRINGASWSTRING<br />
A typical connection string for <strong>Informix</strong> OLE DB Provider looks similar to the<br />
following string:<br />
Data Source=stores_demo@demo_on; User ID=informix; Password=password; Persist<br />
Security Info=True; CLIENT_LOCALE=en_US.CP1252; DB_LOCALE=en_US.819<br />
The connection details about the <strong>Informix</strong> server must be stored in the registry<br />
using the setnet32.exe utility included in Client SDK.<br />
<strong>Informix</strong> OLE DB provider supports all the <strong>Informix</strong> data types, both built-in and<br />
extended.<br />
Table 7-5 lists the mappings between the standard OLE DB data types and<br />
specific <strong>Informix</strong> OLE DB types.<br />
Table 7-5 <strong>Informix</strong> specific type mapping table<br />
222 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Enables you to control the data mapping for wide<br />
strings<br />
FBS or FETCHBUFFERSIZE The size in bytes of the buffer size used to send data<br />
to or from the database. Minimum value is 4096,<br />
Maximum 32767. Default is 4096.<br />
<strong>Informix</strong> SQL <strong>Informix</strong> OLE DB Provider<br />
BIGINT DBTYPE_I8<br />
BIGSERIAL DBTYPE_I8<br />
BLOB DBTYPE_BYTES<br />
BOOLEAN DBTYPE_BOOL<br />
BYTE DBTYPE_BYTES<br />
CLOB DBTYPE_STR<br />
DATETIME DBTYPE_DBDATE, DBTYPE_DBTIME,<br />
DBTYPE_DBTIMESTAMP<br />
DECIMAL DBTYPE_NUMERIC<br />
DISTINCT Same as underlying type<br />
FLOAT DBTYPE_R8
<strong>Informix</strong> SQL <strong>Informix</strong> OLE DB Provider<br />
INT8 DBTYPE_I8<br />
INTERVAL DBTYPE_STR if mapped as a string<br />
DBTYPE_Ix (8,4,2 or 1) if mapped as a numeric<br />
LIST DBTYPE_VARIANT<br />
LVARCHAR DBTYPE_STR<br />
MONEY (p
7.3.4 Cursors<br />
<strong>Informix</strong> OLE DB Provider Date and time precision<br />
DBTYPE_DBTIMESTAMP Day to fraction, day to hour, day to minute, and day to<br />
second<br />
Fraction to fraction, hour to fraction, and minute to<br />
fraction<br />
Month to fraction, month to hour, month to minute, and<br />
month to second<br />
Second to fraction<br />
Year to fraction, year to hour, year to minute, and year<br />
to second<br />
For a complete list of all the data type mapping, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.oledb.doc/u<br />
sing990839.htm#using990839<br />
An ADO application uses cursors to move among the data that returned by a<br />
recordset. ADO defines the following types of cursors:<br />
► Forward-Only (adOpenForwardOnly):<br />
Provides a copy of the records at the time the Recordset was created. This is<br />
the default cursor type of ADO.<br />
► Static (adOpenStatic):<br />
Provides a static copy of the records.<br />
► Dynamic (adOpenDynamic):<br />
Provides a real-time copy of the records, new and altered records by other<br />
users are also included.<br />
► Keyset (adOpenKeyset):<br />
Provides a updateable copy of the records at the time the Recordset was<br />
created. Only existing records are included.<br />
224 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>Informix</strong> OLE DB Provider supports all these cursor types with some limitations<br />
regarding the use of extended data types and location of the cursor (client or<br />
server side). For example:<br />
► Updates of tables with extended data types are not allowed with client-side<br />
scrollable cursors.<br />
► Server-side scrollable cursors are not supported with simple large objects<br />
(BYTE and TEXT) or collections.<br />
► ROWIDs (internal columns added by the <strong>Informix</strong> server on non fragmented<br />
tables) are required on tables bookmarks and updates.<br />
Refer to the <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider Programmer's Guide for a complete<br />
list of these caveats:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.oledb.doc/s<br />
ii-using-29878.htm#sii-using-29878<br />
7.3.5 Typical database operations<br />
In this section, we provide examples of how to use <strong>Informix</strong> OLE DB Provider to<br />
perform typical database operations against an <strong>Informix</strong> database. We cover<br />
operations such as executing SQL statements, selecting data, and handling<br />
errors from <strong>Informix</strong> OLE DB Provider.<br />
For general information about OLE DB architecture and programming, refer to:<br />
http://msdn.microsoft.com/en-us/library/ms713643%28v=VS.85%29.aspx<br />
Command<br />
You can execute SQL commands using an OLE DB provider through the<br />
ICommand interface.<br />
The application has to perform the following steps to execute an SQL statement:<br />
1. Call QueryInterface for IDBCreateCommand to check if commands are<br />
supported in the session.<br />
2. Call IDBCreateCommand::CreateCommand to create the command.<br />
3. Call ICommandText::SetCommandText to specify the SQL statement for the<br />
command.<br />
4. Call ICommand::Execute to execute the SQL statement.<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 225
Example 7-3 demonstrates how to connect to the database and execute an<br />
UPDATE statement. In this example, we use the following operations and<br />
interfaces:<br />
1. Connect to the database<br />
a. Initialize common property options (prompt, DSN, user, and password).<br />
b. Get an Interface for properties:<br />
InterfacepIDBInitialize->QueryInterface(IID_IDBProperties)<br />
pIDBProperties->SetProperties()<br />
c. Get an Interface for creating session object:<br />
pIDBInitialize->QueryInterface(IID_IDBCreateSession)<br />
d. Create a session object:<br />
pCreateSession->CreateSession()<br />
2. Create a command object:<br />
a. Get an Interface for creating command object:<br />
pSession->QueryInterface(IID_IDBCreateCommand)<br />
b. Create the command object and get ICommandText:<br />
226 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Interface:pCreateCommand->CreateCommand()<br />
3. Set the CommandText for the command object:<br />
pCommandText->SetCommandText()<br />
4. Execute the command:<br />
pCommandText->Execute()<br />
5. Clean up.<br />
Example 7-3 The command.cpp sample<br />
#define UNICODE<br />
#define _UNICODE<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
// CLSID For <strong>IBM</strong>-<strong>Informix</strong> Client Side OLE DB Provider<br />
const GUID CLSID_IFXOLEDBC= {0xa6d00422, 0xfd6c, 0x11d0,<br />
{0x80, 0x43, 0x0, 0xa0, 0xc9, 0xf, 0x1c, 0x59}};<br />
#define CHECK(hr) if (((HRESULT)(hr)) < 0) {printf( "Error"); return(hr);}
int main()<br />
{<br />
HRESULT hr = S_OK;<br />
IDBInitialize *pIDBInitialize;<br />
IDBCreateSession *pCreateSession;<br />
IDBCreateCommand *pCreateCommand;<br />
IUnknown *pSession;<br />
ICommandText *pCommandText;<br />
IDBProperties *pIDBProperties;<br />
DBPROP InitProperties[4];<br />
DBPROPSET rgInitPropSet;<br />
_bstr_t bstrDsnName = "stores_demo@demo_on";<br />
_bstr_t bstrUserName = "informix";<br />
_bstr_t bstrPassWord = "password";<br />
_bstr_t bstrCommand = (WCHAR *) L"UPDATE state SET sname = 'California'<br />
WHERE code = 'CA'";<br />
CoInitialize( NULL );<br />
// Instantiate a data source object<br />
CHECK( hr = CoCreateInstance((REFCLSID) CLSID_IFXOLEDBC, NULL,<br />
CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void **) &pIDBInitialize))<br />
// Set all Properties, Prompt, DSN, User and Password<br />
// Initialize common property options.<br />
for (ULONG i = 0; i < 4; i++ )<br />
{<br />
VariantInit(&InitProperties[i].vValue);<br />
InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;<br />
InitProperties[i].colid = DB_NULLID;<br />
InitProperties[1].vValue.vt = VT_BSTR;<br />
}<br />
InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;<br />
InitProperties[0].vValue.vt = VT_I2;<br />
InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;<br />
InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;<br />
InitProperties[1].vValue.bstrVal = bstrDsnName;<br />
InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;<br />
InitProperties[2].vValue.bstrVal = bstrUserName;<br />
InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;<br />
InitProperties[3].vValue.bstrVal = bstrPassWord;<br />
rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;<br />
rgInitPropSet.cProperties = 4;<br />
rgInitPropSet.rgProperties = InitProperties;<br />
// Get initialization properties.Interface<br />
CHECK (hr = pIDBInitialize->QueryInterface(IID_IDBProperties,<br />
(void**) &pIDBProperties))<br />
CHECK(hr = pIDBProperties->SetProperties( 1, &rgInitPropSet))<br />
CHECK( hr = pIDBProperties->Release())<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 227
Connect to the Database Server<br />
CHECK(hr = pIDBInitialize->Initialize())<br />
// Get an Interface for creating Session Object<br />
CHECK( hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession,<br />
(void **) &pCreateSession))<br />
// Create a Session Object<br />
CHECK( hr = pCreateSession->CreateSession(NULL, IID_IUnknown,<br />
(IUnknown **) &pSession))<br />
CHECK( hr = pCreateSession->Release())<br />
// Create Command Object<br />
// Get an Interface for creating Command Object<br />
CHECK( hr = pSession->QueryInterface( IID_IDBCreateCommand,<br />
(void **) &pCreateCommand))<br />
// Create Command Object and get ICommandText Interface<br />
CHECK( hr = pCreateCommand->CreateCommand(NULL, IID_ICommandText,<br />
(IUnknown **) &pCommandText ))<br />
CHECK( hr = pCreateCommand->Release())<br />
// Set the CommandText<br />
CHECK( hr = pCommandText->SetCommandText(DBGUID_DBSQL, bstrCommand))<br />
// Executing the Command<br />
CHECK( hr = pCommandText->Execute( NULL, IID_NULL, NULL, NULL, NULL))<br />
printf( "Row Updated");<br />
// Cleanup<br />
pCommandText->Release();<br />
pCommandText = NULL;<br />
pSession->Release();<br />
pSession = NULL;<br />
pIDBInitialize -> Uninitialize();<br />
pIDBInitialize -> Release();<br />
pIDBInitialize = NULL;<br />
}<br />
CoUninitialize();<br />
return(0);<br />
Example 7-4 describes how to compile and run Example 7-3 on page 226. The<br />
connection string is constructed inside the program. No command line<br />
parameters are passed.<br />
Example 7-4 Output of the command.cpp sample<br />
C:\work>cl /EHsc /nologo command.cpp<br />
command.cpp<br />
C:\work>command<br />
Row Updated<br />
C:\work><br />
228 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Rowset<br />
A rowset is a set of rows, and each row has a number of columns of data.<br />
Rowsets are the main OLE DB objects that are used to expose data from a data<br />
source.<br />
You can create a rowset object with the following methods:<br />
► Explicitly create a rowset by calling IOpenRowset::OpenRowset().<br />
► Execute an SQL statement such as SELECT that returns rows with a<br />
ICommand:Execute method.<br />
► Execute any method that returns a rowset or a schema rowset, for example,<br />
ColumnsRowset::GetColumnsRowset or IDBSchemaRowset::GetRowset.<br />
Example 7-5 demonstrates how to create a rowset object and retrieve metadata<br />
information. The operations and interfaces to initialize the session and connect to<br />
the database are always the same in a OLE DB application. This example also<br />
does the following operations:<br />
1. Connect to the database.<br />
2. Create a Command Object.<br />
3. Create a OpenRowSet Object:<br />
pCreateCommand->QueryInterface(IID_IOpenRowset)<br />
pIOpenRowset->OpenRowset()<br />
4. Obtain access to the IColumnsInfo interface from the rowset object:<br />
pRowset->QueryInterface(IID_IColumnsInfo)<br />
5. Retrieve the Column Information:<br />
pColumnsInfo->GetColumnInfo()<br />
6. Clean up.<br />
Example 7-5 The rowset.cpp sample<br />
#define UNICODE<br />
#define _UNICODE<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
// CLSID For <strong>IBM</strong>-<strong>Informix</strong> Client Side OLE DB Provider<br />
const GUID CLSID_IFXOLEDBC= {0xa6d00422, 0xfd6c, 0x11d0,<br />
{0x80, 0x43, 0x0, 0xa0, 0xc9, 0xf, 0x1c, 0x59}};<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 229
#define CHECK(hr) if (((HRESULT)(hr)) < 0) {printf( "Error"); return(hr);}<br />
int main()<br />
{<br />
HRESULT hr = S_OK;<br />
IDBInitialize *pIDBInitialize;<br />
IUnknown *pSession;<br />
ITransactionJoin *pITransactionJoin;<br />
ICommandText *pCommandText;<br />
IOpenRowset *pIOpenRowset;<br />
IColumnsInfo *pColumnsInfo;<br />
IDBProperties* pIDBProperties;<br />
DBPROP InitProperties[4];<br />
DBPROPSET rgInitPropSet;<br />
IDBCreateSession *pCreateSession = NULL;<br />
IDBCreateCommand *pCreateCommand = NULL;<br />
IRowset *pRowset = NULL;<br />
DBID TableID;<br />
DBPROPSET rgPropSets[1];<br />
DBCOLUMNINFO *pDBColumnInfo;<br />
WCHAR *pStringsBuffer;<br />
ULONG lNumCols;<br />
int i=0;<br />
_bstr_t bstrDsnName = "stores_demo@demo_on";<br />
_bstr_t bstrUserName = "informix";<br />
_bstr_t bstrPassWord = "password";<br />
_bstr_t TableName = L"customer";<br />
CoInitialize( NULL );<br />
// Instantiate a data source object<br />
CHECK( hr = CoCreateInstance((REFCLSID) CLSID_IFXOLEDBC, NULL,<br />
CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void **) &pIDBInitialize))<br />
// Set all Properties, Prompt, DSN, User and Password<br />
// Initialize common property options.<br />
for (ULONG i = 0; i < 4; i++ )<br />
{<br />
VariantInit(&InitProperties[i].vValue);<br />
InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;<br />
InitProperties[i].colid = DB_NULLID;<br />
InitProperties[1].vValue.vt = VT_BSTR;<br />
}<br />
InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;<br />
InitProperties[0].vValue.vt = VT_I2;<br />
InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;<br />
InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;<br />
InitProperties[1].vValue.bstrVal = bstrDsnName;<br />
InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;<br />
InitProperties[2].vValue.bstrVal = bstrUserName;<br />
230 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;<br />
InitProperties[3].vValue.bstrVal = bstrPassWord;<br />
rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;<br />
rgInitPropSet.cProperties = 4;<br />
rgInitPropSet.rgProperties = InitProperties;<br />
// Get initialization properties.Interface<br />
CHECK (hr = pIDBInitialize->QueryInterface(IID_IDBProperties,<br />
(void**) &pIDBProperties))<br />
CHECK(hr = pIDBProperties->SetProperties( 1, &rgInitPropSet))<br />
CHECK( hr = pIDBProperties->Release())<br />
// Connect to the Database Server<br />
CHECK(hr = pIDBInitialize->Initialize())<br />
// Get an Interface for creating Session Object<br />
CHECK( hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession,<br />
(void **) &pCreateSession))<br />
// Create a Session Object<br />
CHECK( hr = pCreateSession->CreateSession(NULL, IID_IUnknown,<br />
(IUnknown **) &pSession))<br />
CHECK( hr = pCreateSession->Release())<br />
// Create Command Object<br />
// Get an Interface for creating Command Object<br />
CHECK( hr = pSession->QueryInterface( IID_IDBCreateCommand,<br />
(void **) &pCreateCommand))<br />
// Create Command Object and get ICommandText Interface<br />
CHECK( hr = pCreateCommand->CreateCommand(NULL, IID_ICommandText,<br />
(IUnknown **) &pCommandText ))<br />
CHECK( hr = pCreateCommand->Release())<br />
// Create a OpenRowSet<br />
CHECK( hr = pCreateCommand->QueryInterface( IID_IOpenRowset,<br />
(void **)&pIOpenRowset ))<br />
TableID.eKind = DBKIND_NAME;<br />
TableID.uName.pwszName = TableName;<br />
))<br />
CHECK( hr = pIOpenRowset->OpenRowset( NULL, &TableID, NULL, IID_IRowset,<br />
0, rgPropSets, (IUnknown **)&pRowset<br />
// Obtain access to the IColumnsInfo inteface, from the Rowset object<br />
CHECK( hr = pRowset->QueryInterface(IID_IColumnsInfo, (void ** )<br />
&pColumnsInfo))<br />
// Retrieve the Column Information<br />
CHECK( hr = pColumnsInfo->GetColumnInfo( &lNumCols, &pDBColumnInfo,<br />
&pStringsBuffer))<br />
wprintf( L"\nTable : %s\nColumns :%d\nName\t\tType\tLength",<br />
TableID.uName.pwszName, lNumCols);<br />
for ( i=0; i
}<br />
232 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
wprintf( L"\n%s\t%d\t%ld", (pDBColumnInfo+i)->pwszName,<br />
(pDBColumnInfo+i)->wType, (pDBColumnInfo+i)->ulColumnSize);<br />
// Free the Column Information Interface<br />
CHECK( (hr = pColumnsInfo->Release()))<br />
// Cleanup<br />
pCommandText->Release();<br />
pCommandText = NULL;<br />
pSession->Release();<br />
pSession = NULL;<br />
pIDBInitialize -> Uninitialize();<br />
pIDBInitialize -> Release();<br />
pIDBInitialize = NULL;<br />
}<br />
CoUninitialize();<br />
return(0);<br />
Example 7-6 shows the name, type, and length columns of the customer table.<br />
Example 7-6 Output of the rowset.cpp sample<br />
C:\work>cl /EHsc /nologo rowset.cpp<br />
rowset.cpp<br />
C:\work>rowset<br />
Table : customer<br />
Columns :7<br />
Name Type Length<br />
customer_num 3 4<br />
customer_type 129 1<br />
customer_name 129 32767<br />
customer_loc 3 4<br />
contact_dates 129 32767<br />
cust_discount 131 19<br />
credit_status 129 1<br />
C:\work><br />
Large objects<br />
Large objects (simple and smart) do not require any special handling when using<br />
<strong>Informix</strong> OLE DB Provider.<br />
Smart large objects (BLOB and TEXT) support the ADO GetChunk() and<br />
AppendChunk() methods.
Example 7-7 demonstrates how to retrieve a CLOB column using the GetChunk()<br />
method from the ADO library in C++. This example fetches the first three rows of<br />
the catalog table from the sample stores_demo database and displays the data<br />
for the catalog_number and advert_descr columns.<br />
Example 7-7 The select.cpp sample<br />
#include <br />
#include <br />
#import "c:\program files\common files\system\ado\msado15.dll" rename<br />
("EOF","adoEOF") no_namespace<br />
#define CREATEiNSTANCE(sp,riid) { HRESULT _hr =sp .CreateInstance( __uuidof( riid<br />
) ); \<br />
if (FAILED(_hr)) _com_issue_error(_hr); }<br />
#define RsITEM(rs,x) rs->Fields->Item[_variant_t(x)]->Value<br />
#define UC (char *)<br />
struct InitOle {<br />
InitOle() { ::CoInitialize(NULL); }<br />
~InitOle() { ::CoUninitialize(); }<br />
} _init_InitOle_;<br />
void main(){<br />
_RecordsetPtr spRS;<br />
_ConnectionPtr spCON;<br />
_variant_t varBLOB;<br />
long lDataLength = 0;<br />
int nrows=3;<br />
try{<br />
CREATEiNSTANCE(spCON,Connection);<br />
spCON->Provider="ifxoledbc";<br />
spCON->ConnectionString = L"Data Source=stores_demo@demo_on;"<br />
L"User ID=informix; Password=password;";<br />
// Connect to the database<br />
spCON->Open( "", "", "", -1 );<br />
CREATEiNSTANCE(spRS,Recordset)<br />
spRS->PutRefActiveConnection( spCON );<br />
spRS->Open(_bstr_t("catalog"),vtMissing, adOpenForwardOnly,<br />
adLockOptimistic,adCmdTable);<br />
while( (spRS->adoEOF == false) && nrows>0 ){<br />
nrows--;<br />
printf("catalog_num = %s\n", UC _bstr_t(RsITEM(spRS,"catalog_num")));<br />
// Get the size of the large object<br />
lDataLength = spRS->Fields->Item["advert_descr"]->ActualSize;<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 233
if(lDataLength > 0) {<br />
// Call GetChunk to retrieve the Blob data<br />
VariantInit(&varBLOB);<br />
varBLOB = spRS->Fields->Item["advert_descr"]->GetChunk(lDataLength);<br />
printf("advert_descr = %s\n", UC _bstr_t(varBLOB));<br />
}<br />
// Move the cursor to the next row<br />
spRS->MoveNext();<br />
}<br />
spRS->Close();<br />
spCON->Close();<br />
}<br />
catch( _com_error &e){<br />
_bstr_t bstrSource(e.Source());<br />
_bstr_t bs = _bstr_t(" Error: ") + _bstr_t(e.Error()) +<br />
_bstr_t(" Msg: ") + _bstr_t(e.ErrorMessage()) +<br />
_bstr_t(" Description: ") + _bstr_t(e.Description());<br />
printf("Error %s\n", bs);<br />
}<br />
}<br />
#undef UC<br />
Example 7-8 shows the content of the output.<br />
Example 7-8 Output of the select.cpp sample<br />
C:\work>cl /EHsc /nologo select.cpp /DWINVER=0x0600<br />
select.cpp<br />
C:\work>select<br />
catalog_num = 10001<br />
advert_descr = Brown leather. Specify first baseman's or infield/outfield<br />
style. Specify right- or left-handed.<br />
catalog_num = 10002<br />
catalog_num = 10003<br />
C:\work><br />
Errors<br />
<strong>Informix</strong> OLE DB Provider supports the ISupportErrorInfo interface.<br />
Applications can retrieve information about an OLE DB error using this interface.<br />
Methods such as IErrorRecords->GetErrorInfo(),<br />
IErrorInfo->GetDescription() and IErrorInfo->GetSource() are fully<br />
supported by <strong>Informix</strong> OLE DB Provider.<br />
234 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 7-9 illustrates how to use these methods. This example retrieves the<br />
error information directly after the IDBInitialize->Initialize() function call.<br />
Because error handling should be done after every use of an interface function, it<br />
is a good practice to create an error handle routine to avoid code redundancy.<br />
Example 7-9 The errorinfo.cpp sample<br />
#define UNICODE<br />
#define _UNICODE<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
// CLSID For <strong>IBM</strong>-<strong>Informix</strong> Client Side OLE DB Provider<br />
const GUID CLSID_IFXOLEDBC= {0xa6d00422, 0xfd6c, 0x11d0,<br />
{0x80, 0x43, 0x0, 0xa0, 0xc9, 0xf, 0x1c, 0x59}};<br />
#define CHECK(hr) if (((HRESULT)(hr)) < 0) {printf( "Error"); return(hr);}<br />
HRESULT GetDetailedErrorInfo(<br />
HRESULThresult,<br />
IUnknown *pBadObject,<br />
GUID IID_BadIntereface);<br />
int main()<br />
{<br />
HRESULT hr = S_OK;<br />
IDBInitialize *pIDBInitialize;<br />
IUnknown *pSession;<br />
IDBProperties* pIDBProperties;<br />
DBPROP InitProperties[4];<br />
DBPROPSET rgInitPropSet;<br />
IDBCreateSession *pCreateSession = NULL;<br />
IErrorInfo *pErrorInfo = NULL;<br />
IErrorInfo *pErrorInfoRec = NULL;<br />
IErrorRecords *pErrorRecords = NULL;<br />
ISupportErrorInfo *pSupportErrorInfo = NULL;<br />
ULONG i,ulNumErrorRecs;<br />
BSTR bstrDescriptionOfError = NULL;<br />
BSTR bstrSourceOfError = NULL;<br />
_bstr_t bstrDsnName = "wrong_db@demo_on"; // WRONG DATABASE<br />
_bstr_t bstrUserName = "informix";<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 235
_bstr_t bstrPassWord = "password";<br />
CoInitialize( NULL );<br />
// Instantiate a data source object<br />
CHECK( hr = CoCreateInstance((REFCLSID) CLSID_IFXOLEDBC, NULL,<br />
CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void **) &pIDBInitialize))<br />
// Set all Properties, Prompt, DSN, User and Password<br />
// Initialize common property options.<br />
for (ULONG i = 0; i < 4; i++ )<br />
{<br />
VariantInit(&InitProperties[i].vValue);<br />
InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;<br />
InitProperties[i].colid = DB_NULLID;<br />
InitProperties[1].vValue.vt = VT_BSTR;<br />
}<br />
InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;<br />
InitProperties[0].vValue.vt = VT_I2;<br />
InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;<br />
InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;<br />
InitProperties[1].vValue.bstrVal = bstrDsnName;<br />
InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;<br />
InitProperties[2].vValue.bstrVal = bstrUserName;<br />
InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;<br />
InitProperties[3].vValue.bstrVal = bstrPassWord;<br />
rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;<br />
rgInitPropSet.cProperties = 4;<br />
rgInitPropSet.rgProperties = InitProperties;<br />
// Get initialization properties.Interface<br />
CHECK (hr = pIDBInitialize->QueryInterface(IID_IDBProperties,<br />
(void**) &pIDBProperties))<br />
CHECK(hr = pIDBProperties->SetProperties( 1, &rgInitPropSet))<br />
CHECK( hr = pIDBProperties->Release())<br />
// Connect to the Database Server<br />
hr = pIDBInitialize->Initialize();<br />
if (hr < 0) {<br />
pIDBInitialize->QueryInterface(IID_ISupportErrorInfo,<br />
(LPVOID FAR*)&pSupportErrorInfo);<br />
pSupportErrorInfo->InterfaceSupportsErrorInfo(__uuidof(pIDBInitialize));<br />
GetErrorInfo(0,&pErrorInfo);<br />
//Get the IErrorRecord interface and get the count of error recs.<br />
pErrorInfo->QueryInterface(IID_IErrorRecords,(LPVOID FAR*)&pErrorRecords);<br />
pErrorRecords->GetRecordCount(&ulNumErrorRecs);<br />
//Get the error record, (we only get the first one)<br />
pErrorRecords->GetErrorInfo(0, GetUserDefaultLCID(), &pErrorInfoRec);<br />
pErrorInfoRec->GetDescription(&bstrDescriptionOfError);<br />
236 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
pErrorInfoRec->GetSource(&bstrSourceOfError);<br />
printf("ERROR!\nResult of 0x%0x (%ld) returned\n",(long)hr,(long)hr);<br />
printf("Error Source: %S\n",bstrSourceOfError);<br />
printf("Error Description: %S\n", bstrDescriptionOfError);<br />
pErrorInfo->Release();<br />
pErrorRecords->Release();<br />
pSupportErrorInfo->Release();<br />
pErrorInfoRec->Release();<br />
SysFreeString(bstrSourceOfError);<br />
SysFreeString(bstrDescriptionOfError);<br />
} // if<br />
/* ... some useful code here .... */<br />
}<br />
pIDBInitialize -> Uninitialize();<br />
pIDBInitialize -> Release();<br />
pIDBInitialize = NULL;<br />
CoUninitialize();<br />
return(0);<br />
Example 7-10 shows the output of Example 7-9 on page 235. We use a<br />
non-existent database for the connection. An error is expected during<br />
initialization.<br />
Example 7-10 Output of the errorinfo.cpp sample<br />
C:\work>cl /EHsc /nologo errorinfo.cpp<br />
errorinfo.cpp<br />
C:\work>errorinfo<br />
ERROR!<br />
Result of 0x80004005 (-2147467259) returned<br />
Error Source: Ifxoledbc<br />
Error Description: EIX000: (-329) Database not found or no system permission.<br />
C:\work><br />
7.4 Visual Basic, ADO.NET, and SQL Server<br />
ActiveX and COM objects are a core component of any Windows operating<br />
system. For this reason, OLE DB providers are used extensively by many<br />
Windows applications. In this section, we describe how to use <strong>Informix</strong> OLE DB<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 237
Provider with some of the available Microsoft technologies and applications, such<br />
as ADO.NET or Microsoft SQL Server.<br />
7.4.1 OLE DB with Visual Basic<br />
Visual Basic (VB) is a programming language developed by Microsoft that<br />
focuses on the use of COM objects. Visual Basic is design to be easy to learn<br />
and to allow the development of database applications with far less effort than C<br />
or C++ languages. ADO and <strong>Informix</strong> OLE DB Provider are widely used with<br />
Visual Basic.<br />
In this section, we demonstrate how to use <strong>Informix</strong> OLE DB Provider to access<br />
an <strong>Informix</strong> database using a VBScript that is based on Visual Basic.<br />
Select data<br />
Example 7-11 shows how to query the state table using a recordset with a<br />
VBScript file. We open the recordset using a static server cursor (adUseServer=2<br />
and adOpenStatic=3).<br />
Example 7-11 The select.vbs script<br />
' Create the ADO objects<br />
set cx=createobject("ADODB.Connection")<br />
set cr=createobject("ADODB.Recordset")<br />
' Set the connection string<br />
cx.provider="Ifxoledbc"<br />
cx.connectionstring="Data Source=stores_demo@demo_on;"<br />
' Open the Connection<br />
cx.open<br />
set cr.activeconnection=cx<br />
cr.cursorlocation=2<br />
' Open the Recordset<br />
cr.open "SELECT * FROM state WHERE code='CA'", cx, 2, 3<br />
WScript.Echo cr.fields("sname")<br />
Example 7-12 shows the output of the this script.<br />
Example 7-12 Output of the select.vbs script<br />
c:\work>select.vbs<br />
California<br />
c:\work><br />
238 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Comparing this example with Example 7-5 on page 229, which is written on C++,<br />
shows how easy it is to use OLE DB with Visual Basic.<br />
Example 7-13 demonstrates how to scroll through a recordset.<br />
Example 7-13 The scroll.vbs script<br />
' Create the ADO objects<br />
set cx=createobject("ADODB.Connection")<br />
set cr=createobject("ADODB.Recordset")<br />
set cm=createobject("ADODB.Command")<br />
' Set the connection string<br />
cx.provider = "Ifxoledbc.2"<br />
cx.Open "Data Source=stores_demo@demo_on"<br />
' Set the Command SQL text<br />
cm.ActiveConnection = cx<br />
cm.CommandText = "SELECT * FROM state"<br />
' Open the cursor<br />
cr.CursorLocation = 3 ‘ adUseClient<br />
cr.Open cm<br />
WScript.Echo "First row: " & cr.fields("sname")<br />
' Scroll to the last element in the recordset<br />
cr.MoveLast<br />
WScript.Echo "Last row : " & cr.fields("sname")<br />
We use the MoveLast() method to position the cursor in the last record, as shown<br />
in the output in Example 7-14.<br />
Example 7-14 Output of the scroll.vbs script<br />
c:\work>scroll.vbs<br />
First row: Alaska<br />
Last row : Puerto Rico<br />
c:\work><br />
Add new data<br />
Example 7-15 demonstrate how to add a new record to a table using the<br />
AddNew() method. After adding a new record to the table, the code opens a<br />
cursor and positions it at the end of the recordset to retrieve the last inserted row.<br />
Example 7-15 The addnew.vbs script<br />
' Create the ADO objects<br />
set cx = CreateObject("ADODB.Connection")<br />
set cr = CreateObject("ADODB.Recordset")<br />
' Set the connection string<br />
cx.provider = "Ifxoledbc"<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 239
cx.connectionstring = "Data Source=stores_demo@demo_on;"<br />
cx.Open<br />
Set cr.activeconnection = cx<br />
' Open the recordset<br />
cr.Open "SELECT * FROM state", cx, 3, 2<br />
' Add a new record to the recordset<br />
cr.AddNew<br />
cr.fields("code") = "UN"<br />
cr.fields("sname") = "Unkown"<br />
cr.fields("sales_tax") = "0.0"<br />
' Update the database table<br />
cr.Update<br />
cr.Close<br />
' Retrieve the new inserterd row<br />
cr.open "SELECT * FROM state", cx, 3, 3<br />
cr.MoveLast<br />
WScript.Echo cr.fields("sname")<br />
Example 7-16 shows the value of the sname column after the update operation.<br />
Example 7-16 Output of the addnew.vbs script<br />
c:\work>addnew.vbs<br />
Unkown<br />
c:\work><br />
7.4.2 ADO.NET and the OLEDB bridge<br />
In addition to ADO, developers can also use <strong>Informix</strong> OLE DB Provider with<br />
ADO.NET. Similar to ADO but within the .NET Framework, ADO.NET is a set of<br />
.NET components that are used to retrieve, manipulate, and update data from a<br />
data source.<br />
One of the .NET data providers that is included with ADO.NET is the Microsoft<br />
OLE DB Provider for .NET. This .NET provider acts as bridge between the .NET<br />
framework and standard OLE DB providers so that developers can use .NET<br />
technology with a database server, even if there is no specific .NET data provider<br />
for that particular database.<br />
<strong>IBM</strong> has specific .NET providers to access an <strong>Informix</strong> database:<br />
► <strong>IBM</strong> <strong>Informix</strong> .NET Provider<br />
► <strong>IBM</strong> Data Server Provider for .NET<br />
However, developers can use <strong>Informix</strong> OLE DB Provider through the Microsoft<br />
OLE DB .NET Provider.<br />
240 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 7-17 demonstrates how to use <strong>Informix</strong> OLE DB Provider with a .NET<br />
application. The connection string for <strong>Informix</strong> OLE DB Provider is the same.<br />
Refer 7.3.2, “Connecting to database” on page 221 for the complete syntax.<br />
Example 7-17 The getoledb.cs sample<br />
using System;<br />
using System.Data;<br />
using System.Data.OleDb;<br />
class sample {<br />
static void Main(string[] args) {<br />
OleDbConnection con = new OleDbConnection();<br />
con.ConnectionString = "Provider=Ifxoledbc;"<br />
+" Data Source=stores_demo@demo_on; User ID=informix;"<br />
+" Password=password";<br />
DataSet ds = new DataSet();<br />
string sql = "SELECT first 3 * FROM STATE";<br />
OleDbDataAdapter da = new OleDbDataAdapter(sql,con);<br />
da.Fill(ds,"state");<br />
foreach(DataRow dr in ds.Tables[0].Rows) {<br />
Console.WriteLine(dr["code"]);<br />
Console.WriteLine(dr["sname"]);<br />
Console.WriteLine(dr["sales_tax"]);<br />
}<br />
con.Close();<br />
con.Dispose();<br />
}<br />
}<br />
Example 7-18 demonstrates how to compile and run this .NET example.<br />
Example 7-18 Output of the getoledb.cs sample<br />
c:\work>csc.exe /nologo getoledb.cs<br />
c:\work>getoledb<br />
AK<br />
Alaska<br />
0.00000<br />
HI<br />
Hawaii<br />
0.04000<br />
CA<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 241
California<br />
0.08250<br />
c:\work><br />
7.4.3 SQL Server<br />
Using <strong>Informix</strong> OLE DB Provider, you can select data from an <strong>Informix</strong> database<br />
directly from Microsoft SQL Server.<br />
SQL Server uses OLE DB to create linked servers and to retrieve data from any<br />
OLE DB data source.<br />
There are no specific steps to configure or to use <strong>Informix</strong> OLE DB Provider with<br />
SQL Server as a linked server. If <strong>Informix</strong> OLE DB Provider is installed and<br />
configured correctly, SQL Server can use it to connect to an <strong>IBM</strong> <strong>Informix</strong><br />
database server.<br />
We describe how to set up a linked server using the <strong>Informix</strong> provider in this<br />
section.<br />
242 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Figure 7-1 shows the New Linked Server dialog box where you set the<br />
connection details for <strong>Informix</strong> OLE DB Provider.<br />
Figure 7-1 New Linked Server dialog box<br />
In this dialog box, set the following fields:<br />
► Linked server: Specify the name for the SQL Server to link.<br />
► Provider: Choose <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider from the drop-down list.<br />
► Product name: Specify the name of the <strong>Informix</strong> provider, which in this<br />
example is ifxoledbc.<br />
► Data source: Specify the name of the data source as database@server.<br />
► Provider string: Specify any additional connection string parameters that the<br />
provider uses.<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 243
The user ID for the <strong>Informix</strong> database server might differ from the one that is<br />
used with SQL Server. If so, you need to set a remote user mapping. Figure 7-2<br />
shows a user mapping between the sa and informix users.<br />
Figure 7-2 Linked Server Properties: Security options<br />
You can also create linked servers using only SQL statements. Example 7-19<br />
illustrates how to create a linked server to connect to the demo_on <strong>Informix</strong><br />
instance. We use the same connection details shown in Figure 7-1 on page 243.<br />
Example 7-19 Linked Server SQL script<br />
EXEC master.dbo.sp_addlinkedserver<br />
@server = N'demo_on',<br />
@srvproduct=N'ifxoledbc',<br />
@provider=N'ifxoledbc',<br />
@datasrc=N'stores7@demo_on',<br />
@provstr=N''<br />
EXEC sp_addlinkedsrvlogin 'demo_on',false,'sa','informix','password'<br />
SELECT * FROM demo_on.stores7.informix.customer<br />
244 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The last SQL statement in the script uses the linked server to retrieve the data<br />
from the customer table:<br />
SELECT * FROM demo_on.stores7.informix.customer<br />
For more information about linked servers, refer to:<br />
http://msdn.microsoft.com/en-us/library/ms188279.aspx<br />
7.5 Troubleshooting and tracing<br />
7.5.1 Typical errors<br />
In this section, we discuss typical errors that can occur when using <strong>Informix</strong> OLE<br />
DB Provider and how to enable the tracing facility to collect diagnostic<br />
information.<br />
Most errors encountered when using <strong>Informix</strong> OLE DB Provider are due to an<br />
incorrect setup or an invalid connection string passed to the provider.<br />
<strong>Informix</strong> OLE DB Provider uses the same connectivity libraries as other<br />
components of Client SDK. It also uses the information that is stored in the<br />
registry through the setnet32.exe utility for the database server connection.<br />
It is always a good practice to test this basic connectivity using the iLogin utility<br />
that is included in the Client SDK directory.<br />
Provider not found<br />
Here, we explain the reason and the solution for a “Provider not found” error.<br />
Reason<br />
This error appears if the Windows operating system fails to locate the OLE DB<br />
provider class that is specified by the application.<br />
Solution<br />
During the installation of Client SDK, the OLE DB provider is registered<br />
automatically within the Windows registry. You can register <strong>Informix</strong> OLE DB<br />
Provider manually using the regsvr32 utility as follows:<br />
regsvr32.exe %INFORMIXDIR%\bin\ifxoledbc.dll<br />
If you are running a 64-bit version of Windows, make sure that you register the<br />
provider using the correct version of the regsvr32 binary. Otherwise, it can<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 245
happen that the 32-bit provider is registered as a 64-bit provider (or vice versa)<br />
and the Windows operating system will fail to find and load the provider.<br />
You can verify that the provider library is registered correctly by examining the<br />
Windows registry. Example 7-20 shows the registry entry for 32-bit and 64-bit<br />
<strong>Informix</strong> OLE DB Provider. Remember that the name of the <strong>Informix</strong> provider is<br />
ifxoledbc.<br />
Example 7-20 <strong>Informix</strong> OLE DB registry keys<br />
C:\work>c:\windows\syswow64\reg query<br />
"HKEY_CLASSES_ROOT\CLSID\{A6D00422-FD6C-11D0-8043-00A0C90F1C59}\InprocServer32"<br />
HKEY_CLASSES_ROOT\CLSID\{A6D00422-FD6C-11D0-8043-00A0C90F1C59}\InprocServer32<br />
(Default) REG_SZ C:\Program Files<br />
(x86)\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK\bin\ifxoledbc.dll<br />
ThreadingModel REG_SZ Both<br />
C:\work>c:\windows\system32\reg query<br />
"HKEY_CLASSES_ROOT\CLSID\{A6D00422-FD6C-11D0-8043-00A0C90F1C59}\InprocServer32"<br />
HKEY_CLASSES_ROOT\CLSID\{A6D00422-FD6C-11D0-8043-00A0C90F1C59}\InprocServer32<br />
(Default) REG_SZ C:\Program<br />
Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK\bin\ifxoledbc.dll<br />
ThreadingModel REG_SZ Both<br />
C:\work><br />
Failed to load the Ifxoledbc.dll<br />
Here, we explain the reason and the solution for a “Failed to load the<br />
Ifxoledbc.dll” error.<br />
Reason<br />
The application fails to load <strong>Informix</strong> OLE DB Provider or one of its libraries.<br />
Solution<br />
The value of INFORMIXDIR (as a environment variable or in the registry) should<br />
be the directory where Client SDK is installed. Ensure that the environment<br />
variable PATH contains the %INFORMIXDIR%\bin directory. For example:<br />
PATH=C:\Program Files\<strong>IBM</strong>\<strong>Informix</strong>\Client-SDK\bin;%PATH%<br />
When an application loads <strong>Informix</strong> OLE DB Provider, Client SDK libraries that<br />
the provider needs for communication and registry access are loaded from the<br />
directory that is used in the registration process.<br />
246 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
On 64-bit version of Windows operating system, ensure that the PATH and<br />
INFORMIXDIR variables are set correctly for all users. Some applications, such<br />
as SQL Server, are executed under credentials that are different than the logged<br />
user and that can have different settings for this variables.<br />
Failed to connect to the database<br />
Here, we explain the reason and the solution for a “Failed to connect to the<br />
database” error.<br />
Reason<br />
The most common reason for this type of error is due to invalid parameters in the<br />
connection string or invalid connectivity information.<br />
Solution<br />
If the application is getting an <strong>Informix</strong> OLE DB error, it has managed to load<br />
<strong>Informix</strong> OLE DB Provider, but it is failing during connection.<br />
For any connection error, always ensure that the basic connectivity works.<br />
Testing whether the client system has access to the database using the iLogin<br />
utility can help to narrow the problem.<br />
If iLogin fails, verify that the connection parameters for the database (server<br />
name, port number, host name, and so on) are correct. Use the setnet32.exe<br />
utility to verify that the information stored in the registry is valid.<br />
The user credentials (user ID and password) can also be set in the registry using<br />
the setnet32.exe utility. Verify that both the user ID and password are valid.<br />
The majority of the connection string parameters must be included exactly as<br />
they appear in Table 7-4 on page 221. Invalid parameters are ignored and can<br />
cause an error connection. See Table 7-7 for examples of valid parameters.<br />
Table 7-7 Valid parameters<br />
Valid Invalid<br />
Data Source DataSource, DSN<br />
User ID UserID, UID<br />
Password PWD<br />
If you are using GLS variables (Client_Locale or Db_locale), make sure that<br />
they are set correctly. If you do not use UNICODE, the code set part of<br />
Client_Locale should be the same as your operating system. Db_Locale should<br />
be your database locale.<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 247
7.5.2 Tracing<br />
Failure when handling database data<br />
Here, we explain the reason and the solution for a “Failed when handling<br />
database data” error.<br />
Reason<br />
These types of errors are caused by using the wrong data type when accessing<br />
<strong>Informix</strong> tables.<br />
Solution<br />
<strong>Informix</strong> OLE DB Provider requires additional functions and tables in the<br />
sysmaster database to handle <strong>Informix</strong> data types correctly. These objects are<br />
created when running the coledbp.sql SQL script.<br />
This script is located in the %INFORMIXDIR%\etc directory, and must be executed<br />
against the sysmaster database as the informix user.<br />
If you are upgrading from an older version of Client SDK, remove these objects<br />
by running the doledbp.sql script from the old client before using the new<br />
coledbp.sql script.<br />
Tracing is useful when diagnosing application problems such as SQL errors or<br />
unexpected behaviors and can even be used for performance analysis.<br />
A developer can set the following types of tracing when using <strong>Informix</strong> OLE DB<br />
Provider:<br />
► <strong>Informix</strong> OLE DB Trace<br />
► SQLIDEBUG<br />
<strong>Informix</strong> OLE DB Trace<br />
<strong>Informix</strong> OLE DB Trace is specific to <strong>Informix</strong> OLE DB Provider. You can set the<br />
IFXOLEDBCTRACE environment variable to point to the location of the trace file,<br />
for example:<br />
IFXOLEDBCTRACE=oledbtrace.txt<br />
The trace file that is generated contains the calls to all the OLE DB interfaces that<br />
the provider uses.<br />
Example 7-21 shows a typical <strong>Informix</strong> OLE DB trace file.<br />
Example 7-21 OLE DB trace<br />
Wed Jun 30 13:59:01 2010<br />
248 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
A44: 53C Enter Clsfact::QueryInterface<br />
Object Address: 0x032C2CB8<br />
A44: 53C Enter Clsfact::AddRef<br />
Object Address: 0x032C2CB8<br />
A44: 53C Exit Object Address: 0x032C2CB8 hr=2<br />
qi: 032C2CB8 (pif 032C2CB8)<br />
A44: 53C Enter Clsfact::Release<br />
Object Address: 0x032C2CB8<br />
A44: 53C Exit Object Address: 0x032C2CB8 hr=1<br />
A44: 53C Enter Clsfact::AddRef<br />
Object Address: 0x032C2CB8<br />
A44: 53C Exit Object Address: 0x032C2CB8 hr=2<br />
A44: 53C Enter Clsfact::Release<br />
Object Address: 0x032C2CB8<br />
A44: 53C Exit Object Address: 0x032C2CB8 hr=1<br />
A44: 53C Enter Clsfact::QueryInterface<br />
Object Address: 0x032C2CB8<br />
A44: 53C Enter Clsfact::AddRef<br />
Object Address: 0x032C2CB8<br />
A44: 53C Exit Object Address: 0x032C2CB8 hr=2<br />
...<br />
In addition to the OLE DB interfaces, the trace facility collects the return value for<br />
any method that is used during the OLE DB session, as shown in Example 7-22.<br />
Example 7-22 Entry and exit points OLE DB trace<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::AddRef<br />
A44: 53C Exit Object Address: 0x032CC370 hr=2<br />
A44: 53C Enter Datasrc::Release<br />
A44: 53C Exit Object Address: 0x032CC370 hr=1<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::XIDBProperties::SetProperties<br />
A44: 53C Exit Object Address: 0x032CC38C hr=0<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::XIDBProperties::SetProperties<br />
A44: 53C Exit Object Address: 0x032CC38C hr=0<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::XIDBInitialize::Initialize<br />
A44: 53C Exit Object Address: 0x032CC384 hr=0<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::XIDBProperties::GetProperties<br />
Chapter 7. Working with <strong>IBM</strong> <strong>Informix</strong> OLE DB Provider 249
A44: 53C Exit Object Address: 0x032CC38C hr=0<br />
A44: 53C Enter Datasrc::QueryInterface<br />
A44: 53C Enter Datasrc::XIDBCreateSession::CreateSession<br />
SQLIDEBUG<br />
SQLI is the communication protocol used by Client SDK. SQLIDEBUG is a trace<br />
facility of all the messages between an <strong>Informix</strong> client and an <strong>Informix</strong> database<br />
server. This trace is common to all the Client SDK components (ESQL/C, ODBC,<br />
OLE DB, and so on).<br />
You can find a description and how to enable SQLIDEBUG in 3.3.6,<br />
“Troubleshooting” on page 117.<br />
When diagnosing a performance problem, sometimes it is useful to collect an<br />
SQLIDEBUG trace to have an idea of where the delay occurs. Using the summary<br />
option when processing the SQLIDEBUG file with sqliprint produces a detailed<br />
description of all the messages between the client and server, as shown in<br />
Example 7-23.<br />
Example 7-23 Sqliprint summary output<br />
>>>>>>>>>>>>>>>>>> SUMMARY INFORMATION >>>>CLIENT ELAPSED CLOCK TIME (sec): 0.047000<br />
>>>>>>>>>>SERVER+NETWORK CLOCK TIME (sec): 0.000000<br />
FROM C->S<br />
Msg occured Total Avg Min Max<br />
------------------------------------------------------------------<br />
SQ_PREPARE 1 0.000000 0.000000 0.000000 0.000000<br />
SQ_ID 4<br />
SQ_CLOSE 1<br />
SQ_RELEASE 2<br />
SQ_EOT 7<br />
SQ_NDESCRIBE 1<br />
SQ_SFETCH 1 0.000000 0.000000 0.000000 0.000000<br />
SQ_WANTDONE 1<br />
SQ_EXIT 1<br />
SQ_INFO 1<br />
SQ_RET_TYPE 1<br />
SQ_INTERNALVER 1<br />
SQ_PROTOCOLS 1<br />
------------------------------------------------------------------<br />
FROM S->C<br />
Msg occured Total Avg Min Max<br />
------------------------------------------------------------------<br />
250 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
UNKNOWN 1<br />
SQ_DESCRIBE 1 0.000000 0.000000 0.000000 0.000000<br />
SQ_EOT 6<br />
SQ_TUPLE 1<br />
SQ_TUPID 1<br />
SQ_PROTOCOLS 1<br />
------------------------------------------------------------------<br />
=======================================================================<br />
COMMAND TEXT INFORMATION (first 99 are listed)<br />
a( b) CMD[##]= ' first 60 bytes of cmd text '<br />
where a = 'P'repare, or 'N'ot prepared, and b = length of command string<br />
-----------------------------------------------------------------------<br />
P( 28) CMD[ 0]='SELECT rowid, * FROM state;'<br />
-----------------------------------------------------------------------<br />
=======================================================================<br />
Fetch Array feature not used<br />
OPTOFC feture not used<br />
Number of open/reoptimzation encountered = 0<br />
Number of C->S message send = 22<br />
Number of S->C message send = 21<br />
Number of prepare statements encountered = 1<br />
Number of execute statements encountered = 0<br />
Number of singleton select encountered = 0<br />
Number of open cursor encountered = 0<br />
Number of close cursor encountered = 1<br />
Number of non-blob put = 0, averge size of each put is 0.000000<br />
Number of non-blob fetch = 1, averge size of each fetch is 25.000000<br />
Number of blob put = 0, averge size of each put is 0.000000<br />
Number of blob fetch = 0, averge size of each fetch is 0.000000<br />
>>>>>>>>>>>>>>> END SUMMARY INFORMATION
252 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 8. Working with .NET data<br />
providers<br />
8<br />
This chapter describes the .NET providers that are available for working with an<br />
<strong>Informix</strong> database server. It includes the following topics:<br />
► <strong>Informix</strong> and .NET data providers<br />
► Setup and configuration<br />
► Developing a .NET application<br />
► Visual Studio Add-In for Visual Studio<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 253
8.1 <strong>Informix</strong> and .NET data providers<br />
A .NET data provider is a .NET assembly that lets .NET applications access and<br />
manipulate data in a database by implementing several interfaces that follow the<br />
Microsoft ADO.NET architecture. Any application that can be executed by the<br />
Microsoft .NET Framework can use a .NET data provider.<br />
<strong>IBM</strong> provides the following .NET data providers to work with an <strong>IBM</strong> <strong>Informix</strong><br />
database:<br />
► <strong>IBM</strong> Infomix .NET Provider<br />
► <strong>IBM</strong> Data Server Provider for .NET<br />
Both providers rely on internal calls to the ODBC and CLI drivers. They are not<br />
100% managed-code providers.<br />
8.2 Setup and configuration<br />
This chapter describes the .NET providers that are available to connect to an<br />
<strong>IBM</strong> <strong>Informix</strong> database. We explain how to configure and test the connectivity<br />
with both Infomix .NET Provider and Data Server Provider for .NET.<br />
8.2.1 <strong>IBM</strong> <strong>Informix</strong> .Net Provider<br />
<strong>Informix</strong> .NET Provider is part of the <strong>Informix</strong> Client Software Development Kit<br />
(Client SDK). <strong>Informix</strong> .NET Provider is certified to work on both 32-bit and 64-bit<br />
editions of Windows XP, Windows Vista, Windows Server 2003, Windows Server<br />
2008, and Windows 7.<br />
The provider requires that Microsoft .NET Framework SDK 1.1 or higher is<br />
already installed on the computer. The provider is installed by default when<br />
performing a complete installation of Client SDK.<br />
<strong>Informix</strong> .NET Provider supports the following <strong>Informix</strong> database servers:<br />
► <strong>IBM</strong> <strong>Informix</strong> 10.00, 11.10, and 11.50<br />
► <strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server 8.50 and later<br />
The assembly name of the <strong>Informix</strong> .NET Provider is <strong>IBM</strong>.Data.<strong>Informix</strong>.<br />
The assembly files are located in the Client SDK directory. The provider for a<br />
.NET 1.1 application is in the %INFORMIXDIR%\netf11 directory. For a .NET 2.0<br />
and above, use the provider located in the %INFORMIXDIR\netf20 directory.<br />
254 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The installation process registers two strong-named assemblies in the global<br />
assembly cache (GAC).<br />
Table 8-1 shows <strong>Informix</strong> .NET Provider assembly versions.<br />
Table 8-1 <strong>Informix</strong> .NET Provider assembly versions<br />
.NET framework Assembly version<br />
1.1 2.81.0.0<br />
2.0 and above 3.0.0.2<br />
The assemblies are registered in the common section of the GAC (GAC_MSIL).<br />
Thus, only one version of the provider (32-bit or 64-bit) can be stored in the GAC<br />
at a time.<br />
Note: There is no 64-bit version of the .NET 1.1 Framework, so the .NET 1.1<br />
Provider is not installed on a Windows 64-bit platform.<br />
8.2.2 <strong>IBM</strong> Data Server Provider for .NET<br />
Data Server Provider for .NET is included in the <strong>IBM</strong> Data Server Package. The<br />
provider supports multiple <strong>IBM</strong> data servers, including <strong>IBM</strong> <strong>Informix</strong> (11.x) and<br />
DB2.<br />
As with <strong>Informix</strong> .NET Provider, the Data Server Provider for .NET is not 100%<br />
managed code. Thus, it requires the DB2 CLI component for communication with<br />
the database.<br />
The following Data Server providers for an <strong>Informix</strong> database are available:<br />
► <strong>IBM</strong>.Data.DB2 is the preferred .NET provider when developing new<br />
applications.<br />
► <strong>IBM</strong>.Data.<strong>Informix</strong> is the .NET provider assembly that is normally used to<br />
migrate an application that is developed using <strong>Informix</strong> .NET Provider.<br />
The assembly files are located in the netf20 subdirectory under the Data Server<br />
Client directory, which by default is C:\Program Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER<br />
DRIVER\netf20.<br />
Note: The 64-bit version of the Data Server Client Package also includes the<br />
32-bit drivers.<br />
Chapter 8. Working with .NET data providers 255
Both providers have the same assembly version, 9.0.0.2. Table 8-2 shows the<br />
Data Server Provider for .NET assembly version.<br />
Table 8-2 Data Server Provider for .NET assembly versions<br />
.NET Provider Assembly version<br />
<strong>IBM</strong>.Data.DB2 9.0.0.2<br />
<strong>IBM</strong>.Data.<strong>Informix</strong> 9.0.0.2<br />
8.2.3 Verifying connectivity<br />
<strong>Informix</strong> .NET Provider uses communication libraries internally as do the other<br />
drivers that are included in Client SDK. Thus, it uses the same connectivity<br />
information that is normally stored in the registry through the use of the<br />
setnet32.exe utility.<br />
When there is no specific tool to verify the connectivity with <strong>Informix</strong> .NET<br />
Provider, you can use a simple .NET application to test it.<br />
The Microsoft .NET Framework SDK is required to use a .NET provider that<br />
contains a .NET language compiler.<br />
Example 8-1 shows a simple C code sample that verifies the connection with the<br />
database server. This example takes the connection string as the first argument<br />
and uses it to connect to the database.<br />
Example 8-1 A connect.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class Connect<br />
{<br />
static void Main(string[] args)<br />
{<br />
IfxConnection conn;<br />
if (args.Length>0) {<br />
try {<br />
conn = new IfxConnection(args[0]);<br />
conn.Open();<br />
Console.WriteLine("Connected");<br />
Console.WriteLine(String.Format("Server Type: {0}, Server<br />
Version: {1}", conn.ServerType, conn.ServerVersion));<br />
256 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Console.WriteLine(String.Format("Database: {0}",<br />
conn.Database));<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
else<br />
Console.WriteLine("Need a connection string as argument\n e.g:<br />
\"Host=kodiak;Service=9088;Server=demo_on;Database=stores_demo;User<br />
ID=informix;password=;\"\n");<br />
}<br />
}<br />
Example 8-2 shows how to compile and run Example 8-1 on page 256.<br />
Example 8-2 Output of the connect.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.informix.dll /nologo<br />
connect.cs<br />
C:\work>connect "Server=demo_on;Database=stores_demo"<br />
Connected<br />
Server Type: <strong>Informix</strong>, Server Version: 11.50.0000 FC6<br />
Database: stores_demo<br />
C:\work><br />
If the connection information for the <strong>Informix</strong> server is stored in the registry, the<br />
only parameter that is required in the connection string is the name of the<br />
database server. The remainder of the information, such as Host or Service, is<br />
taken from the registry.<br />
At compile time, you must pass the assembly as a reference to the csc.exe<br />
compiler using the /R parameter. For example:<br />
/R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll<br />
If you want to use the Data Server provider, the reference path is as follows:<br />
/R:”C:\Program Files (x86)\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER DRIVER\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll”<br />
Chapter 8. Working with .NET data providers 257
Example 8-3 shows how to compile and execute the sample with Data Server<br />
Provider for .NET.<br />
Example 8-3 Data Server connect.cs sample<br />
C:\work>csc.exe /R:"C:\Program Files (x86)\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER<br />
DRIVER\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll" /nologo connect.cs<br />
C:\work>connect "Server=kodiak:9089;Database=stores_demo;User<br />
ID=informix;password=password;"<br />
Connected<br />
Server Type: IDS/NT64, Server Version: 11.50.0000<br />
Database: STORES_DEMO<br />
C:\work><br />
Data Server Provider for .NET includes a connection tool called testconn20.exe<br />
that you can use to test the connection with the database. Example 8-4 shows a<br />
typical output of the testconn20.exe utility.<br />
Example 8-4 Output of the testconn20.exe utility<br />
C:\work>testconn20.exe "server=kodiak:9089; database=stores_demo; uid=informix;<br />
pwd=password;"<br />
Step 1: Printing version info<br />
.NET Framework version: 2.0.50727.3603<br />
DB2 .NET provider version: 9.0.0.2<br />
DB2 .NET file version: 9.7.2.2<br />
Capability bits: ALLDEFINED<br />
Build: 20100514<br />
Factory for invariant name <strong>IBM</strong>.Data.DB2 verified<br />
VSAI assembly version: 9.1.0.0<br />
VSAI file version: 9.7.1.53<br />
Elapsed: 1.015625<br />
Step 2: Validating db2dsdriver.cfg against db2dsdriver.xsd schema file<br />
C:\PROGRA~1\<strong>IBM</strong>\<strong>IBM</strong>DAT~2\cfg\db2dsdriver.cfg against C:\Program<br />
Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER DRIVER\cfg\db2dsdriver.xsd<br />
Elapsed: 0.015625<br />
Step 3: Connecting using "server=kodiak:9089; database=stores_demo;<br />
uid=informix; pwd=password;"<br />
Server type and version: IDS 11.50.0000<br />
Elapsed: 0.46875<br />
Step 4: Selecting rows from informix.systables to validate existance of<br />
packages<br />
SELECT * FROM informix.systables<br />
258 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Elapsed: 0.171875<br />
Step 5: Calling GetSchema for tables to validate existance of schema functions<br />
Elapsed: 0.21875<br />
Test passed.<br />
C:\work><br />
8.3 Developing a .NET application<br />
This section describes the connection string attributes for two <strong>Informix</strong> .NET<br />
providers and provides samples of basic database operations.<br />
8.3.1 Connecting to the database<br />
In this section, we describe the connection string attributes for <strong>Informix</strong> .NET<br />
Provider and Data Server Provider for .NET<br />
<strong>Informix</strong> .NET Provider connection attributes<br />
Table 8-3 lists the connection attributes that <strong>Informix</strong> .NET Provider supports.<br />
Table 8-3 <strong>Informix</strong> .NET Provider connection string attributes<br />
Attribute Description Default<br />
Client Locale,<br />
Client_Locale<br />
Locale used on the application en_us.1252<br />
Connection Lifetime Time in seconds that a connection is<br />
allowed in the pool<br />
Database, DB Database to connect to<br />
Database Locale,<br />
DB_LOCALE<br />
Locale of the database en_US.819<br />
DELIMIDENT If set, any string within double quotes (")<br />
is treated as an identifier, and any string<br />
within single quotes (’) is treated as a<br />
string literal<br />
Enlist Enables or disables automatic enlistment<br />
in a distributed transaction<br />
Chapter 8. Working with .NET data providers 259<br />
0<br />
y<br />
true
Attribute Description Default<br />
Exclusive, XCL if set the database is open in exclusive<br />
mode<br />
Host Name or IP address of the system on<br />
which the <strong>Informix</strong> server is running<br />
Max Pool Size Maximum number of connections<br />
allowed in the pool<br />
Min Pool Size Minimum number of connections allowed<br />
in the pool<br />
Optimize OpenFetchClose,<br />
OPTOFC<br />
Packet Size, Fetch Buffer<br />
Size, FBS<br />
260 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Reduces the number of client-server<br />
messages when using cursors<br />
Size in bytes of the buffers used to send<br />
data to or from the server<br />
Password, PWD Password associated with the User ID<br />
Persist Security Info If set the security-sensitive information,<br />
such as the password, is returned as part<br />
of the connection string<br />
No<br />
localhost<br />
100<br />
0<br />
0<br />
32767<br />
false<br />
Pooling Enable Connection Pooling true<br />
Protocol, PRO Communication protocol<br />
Server Name or alias of the <strong>Informix</strong> server<br />
Service Service name or port number used by the<br />
server for incoming connection<br />
Skip Parsing If set, there is no internal SQL parsing it<br />
increases performance but the SQL must<br />
be valid<br />
UserDefinedTypeFormat Changes the mapping of UDTs to either<br />
DbType.String or DbType.Binary<br />
Leave Trailing Spaces If set, disable the automatic trailing of<br />
spaces done in a varchar column<br />
User ID, UID Login account.<br />
false<br />
false
Data Server Provider for .NET connection attributes<br />
Table 8-4 lists the most common connection attributes that Data Server Provider<br />
for .NET uses.<br />
Table 8-4 Data Server Provider for .NET connection string attributes<br />
Attribute Description Default Value<br />
ConnectTimeout,<br />
Connect Timeout<br />
The time (in seconds) to wait for the<br />
database connection to be established.<br />
Connection Lifetime Time in seconds that a connection is<br />
allowed in the pool.<br />
Database, DB Database to connect to.<br />
Enlist Enables or disables automatic enlistment<br />
in a distributed transaction.<br />
Max Pool Size Maximum number of connections<br />
allowed in the pool.<br />
Min Pool Size Minimum number of connections allowed<br />
in the pool.<br />
Password, PWD Password associated with the User ID<br />
Persist Security Info If set the security-sensitive information,<br />
such as the password, is returned as part<br />
of the connection string.<br />
Pooling Enable Connection Pooling within the<br />
.NET Provider<br />
Query Timeout Time to wait for an SQL query response 5<br />
Server Name or alias of the <strong>Informix</strong> server<br />
followed by service name or the port<br />
number<br />
User ID, UID Login account<br />
The configuration parameters for Data Server Provider for .NET and most of the<br />
components are stored in the db2sdriver.cfg file. For a complete list of the<br />
configuration parameters in this file, refer to:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.swg.im.dbcli<br />
ent.config.doc/doc/c0054698.html<br />
If the connection string does not contain all the required information, the provider<br />
takes the missing arguments from the db2sdriver.cfg configuration file.<br />
Chapter 8. Working with .NET data providers 261<br />
0<br />
60<br />
true<br />
0<br />
0<br />
false<br />
true
The majority of the connection string attributes of Data Server Provider for .NET<br />
have similar meanings and format as <strong>Informix</strong> .NET Provider string attributes<br />
except the Server attribute.<br />
For <strong>Informix</strong> .NET Provider, the Server keyword contains the name (or alias) of<br />
the <strong>Informix</strong> server, which is normally the same as the value of the<br />
INFORMIXSERVER variable.<br />
For Data Server Provider for .NET, the Server keyword contains the host name<br />
where the <strong>Informix</strong> server is running and the service name or port number that is<br />
used to listen for DRDA client connections.<br />
Example 8-5 shows the Server attribute format. In this example, we connect to<br />
an <strong>Informix</strong> database server that is running in a system named kodiak.ibm.com.<br />
The <strong>Informix</strong> server DRDA alias is using the port number 9089.<br />
Example 8-5 Server attribute for the Data Server Provider for .NET<br />
C:\work>connect<br />
"Server=kodiak.ibm.com:9089;Database=stores_demo;UID=informix;password=password<br />
;"<br />
Connected<br />
Server Type: IDS/NT64, Server Version: 11.50.0000<br />
Database: STORES_DEMO<br />
C:\work><br />
8.3.2 Data type mapping<br />
The data types that are used by the .NET Framework differ from the data types<br />
that are used by the <strong>IBM</strong> <strong>Informix</strong> database. In this section, we describe the<br />
optimal data types to use when accessing data in an <strong>Informix</strong> database. The<br />
optimal data type depends on the .NET method that is used to access the data.<br />
You can use types other than those that we describe here. For example, you can<br />
use the IfxDataReader.GetString method to obtain any <strong>Informix</strong> data type. The<br />
types that we mention here are the most efficient and least likely to change in<br />
value in data conversion.<br />
262 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>Informix</strong> .NET type mapping<br />
Table 8-5 shows the mapping for the specific <strong>IBM</strong> <strong>Informix</strong> data types.<br />
Table 8-5 Type mapping for <strong>Informix</strong> specific data<br />
<strong>Informix</strong> data type For data reader For data set<br />
BIGINT Int64 Int64<br />
BIGSERIAL Int64 Int64<br />
BLOB IfxBlob Byte[]<br />
BOOLEAN Boolean Boolean<br />
BYTE Byte[] Byte[]<br />
CLOB IfxClob Byte[]<br />
COMPLEX (ROW, LIST) String String<br />
DECIMAL(p
Data Server Provider for .NET type mapping<br />
Table 8-6 shows the type mapping with the Data Server Provider for .NET when<br />
using specific <strong>Informix</strong> data types.<br />
Table 8-6 Type mapping for <strong>Informix</strong> specific data<br />
<strong>Informix</strong> data type Optimal for data reader Optimal for data set<br />
LVARCHAR IfxString String<br />
BLOB, BYTE IfxBlob Byte[]<br />
CLOB, TEXT IfxClob String<br />
BOOLEAN, SMALLINT IfxInt16 Int16<br />
BIGINT, BIGSERIAL, INT8,<br />
SERIAL8<br />
8.3.3 Performing database operations<br />
This section describes the main classes in the <strong>Informix</strong> .NET Provider and<br />
demonstrates how to use these classes with basic examples.<br />
IfxConnection<br />
The IfxConnection object represents a unique connection with the database<br />
server. You can specify the connectivity details about the database when creating<br />
the connection object or by setting the ConnectionString property later.<br />
Because <strong>Informix</strong> .NET Provider uses native resources (that are not managed by<br />
the .NET CLR), always close or dispose any open IfxConnection class when it is<br />
not longer needed.<br />
264 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
IfxInt64 Int64<br />
DECIMAL(p
Table 8-7 lists the public method of the IfxConnection class.<br />
Table 8-7 IfxConnection public methods<br />
We described the connection string attributes in 8.3.1, “Connecting to the<br />
database” on page 259. Example 8-6 demonstrates how to connect to the<br />
database<br />
Example 8-6 The connect_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
}<br />
Method Description<br />
void Open() Opens a database connection<br />
void Close() Closes the connection to the database<br />
void ChangeDatabase(String) Changes the current database<br />
void EnlistTransaction() Enlists the connection in a DTC transaction<br />
IfxTransaction BeginTransaction()<br />
BeginTransaction(IsolationLevel)<br />
}<br />
IfxConnection conn;<br />
Begins a database transaction<br />
IfxCommand CreateCommand() Creates an instance of an IfxCommand object that<br />
is associated with this IfxConnection<br />
IfxBlob GetIfxBlob() Creates an instance of an IfxBlob object that is<br />
associated with this connection<br />
IfxClob GetIfxClob() Creates an instance of an IfxClob object that is<br />
associated with this connection<br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
Console.WriteLine("Connected to "+conn.Database);<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
Chapter 8. Working with .NET data providers 265
Example 8-7 shows the output of Example 8-6.<br />
Example 8-7 Output of the connect_sample.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
connect_sample.cs<br />
C:\work>connect_sample<br />
Connected to stores_demo<br />
C:\work><br />
IfxCommand<br />
The IfxCommand object represents an SQL statement or stored procedure to<br />
execute against a database server.<br />
Table 8-8 shows methods that the IfxCommand class provides for executing an<br />
SQL statement.<br />
Table 8-8 IfxCommand public methods<br />
Method Description<br />
Int32 ExecuteNonQuery() Executes an SQL statement against the<br />
IfxConnection object<br />
IfxDataReader ExecuteReader()<br />
ExecuteReader(DataCommandBehavior<br />
behavior)<br />
266 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Executes the command in the<br />
CommandText property against the<br />
IfxConnection object and builds an<br />
IfxDataReader object<br />
Object ExecuteScalar() Executes the query, and returns the first<br />
column of the first row<br />
void Cancel() Attempts to cancel the execution of a<br />
command<br />
IfxParameter CreateParameter() Creates a new instance of an<br />
IfxParameter object<br />
void Prepare() Creates a prepared (or compiled) version<br />
of the command against the database
Example 8-8 demonstrates how to run a simple DELETE SQL statement.<br />
Example 8-8 The command_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
}<br />
}<br />
IfxConnection conn;<br />
int drows=0;<br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "DELETE FROM customer WHERE customer_num = 103";<br />
drows = cmmd.ExecuteNonQuery();<br />
Console.WriteLine("Deleted rows: "+drows.ToString());<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
Example 8-9 shows the output of Example 8-8.<br />
Example 8-9 Output of the command_sample.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
command_sample.cs<br />
C:\work>command_sample<br />
Deleted rows: 1<br />
C:\work><br />
IfxDataAdapter<br />
The IfxDataAdapter class represents a set of data commands that are used to<br />
communicate between a data set and the database. A data set is a copy of the<br />
database data that is stored in memory.<br />
Chapter 8. Working with .NET data providers 267
Table 8-9 lists the methods that the IfxDataAdapter class provides for accessing<br />
the data.<br />
Table 8-9 IfxDataAdapter public methods<br />
Method Description<br />
Int32 Fill(DataSet) Adds or refreshes rows in the data set<br />
DataTable FillSchema(DataSet,<br />
SchemaType)<br />
IDataParameter<br />
GetFillParameters()<br />
Example 8-10 demonstrates how to retrieve data from the database using the<br />
IfxDataAdapter class. In this example, we retrieve the data rows directly from the<br />
data set.<br />
Example 8-10 The dataadapter_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
268 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM state WHERE code='CA'";<br />
IfxDataAdapter dadap = new IfxDataAdapter();<br />
DataSet dset = new DataSet();<br />
dadap.SelectCommand = cmmd;<br />
dadap.Fill(dset);<br />
Adds a data table to the data set, and<br />
configures the schema to match that in the<br />
database based on the specified SchemaType<br />
Returns the parameters set by the user when<br />
executing a SELECT statement<br />
Int32 Update(DataSet) Executes the SQL statement that is associated<br />
with the InsertCommand, UpdateCommand, or<br />
DeleteCommand for each inserted, updated, or<br />
deleted row in the specified data set<br />
foreach(DataRow dr in dset.Tables[0].Rows) {<br />
Console.WriteLine(String.Format("\tCode\tState\n"));<br />
Console.WriteLine(String.Format("\t{0}\t{1}", dr["code"], dr["sname"]));
}<br />
}<br />
}<br />
conn.Close();<br />
Example 8-11 shows the data that is returned by the data set object.<br />
Example 8-11 Output of the dataadapter_sample.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
dataadapter_sample.cs<br />
C:\work>dataadapter_sample.exe<br />
Code State<br />
C:\work><br />
CA California<br />
IfxDataReader<br />
The IfxDataReader class provides fast read-only, forward-only access to a set of<br />
rows, similar to SQL cursors data. To create an IfxDataReader object, the<br />
application calls the ExecuteReader() method of the IfxCommand object.<br />
The IfxDataReader class is a part of the System.Data.Common.DbDataReader. It<br />
provides all common DbDataReader methods as well as additional methods to<br />
handle <strong>Informix</strong> specific data types.<br />
Table 8-10 contains the additional public methods of the IfxDataReader class.<br />
Table 8-10 Additional methods of IfxDataReader<br />
Method Description<br />
Boolean GetBoolean() Gets the value of the specified column as a Boolean<br />
TimeSpan GetTimeSpan() Gets the time span value of the specified field<br />
IfxBlob GetIfxBlob() Gets the IfxBlob value of the specific field<br />
IfxClob GetIfxClob() Gets the IfxClob value of the specific field<br />
IfxDateTime GetIfxDateTime() Gets the IfxDateTime value of the specific field<br />
IfxDecimal GetIfxDecimal Gets the GetIfxDecimal value of the specific field<br />
Chapter 8. Working with .NET data providers 269
Method Description<br />
IfxMonthSpan GetIfxMonthSpan Gets the IfxMonthSpan value of the specific field<br />
IfxTimeSpan GetIfxTimeSpan Gets the IfxTimeSpan value of the specific field<br />
Refer to <strong>IBM</strong> <strong>Informix</strong> .NET Provider Reference Guide for a complete list of all the<br />
IfxDataReader public methods:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.netpr.doc/n<br />
etpr031023156.htm#netpr031023156<br />
Note: Even if the IfxDataReader.Read() method returns only one row each<br />
time it is called, the provider can retrieve more than one row from the<br />
database. <strong>Informix</strong> .NET Provider does this to increase performance. The<br />
application can set the number of rows that the provider fetches from the<br />
database using the IfxCommand class RowFetchCount property.<br />
Example 8-12 demonstrates how to use a IfxDataReader class to access a<br />
database table.<br />
Example 8-12 The datareader_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
270 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM state WHERE code='CA'";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
while (drdr.Read()) {<br />
Console.WriteLine(String.Format("\tCode\tState\n"));<br />
Console.Write(String.Format("\t{0}", drdr.GetString(0)));<br />
Console.WriteLine(String.Format("\t{0}", drdr.GetString(1)));<br />
}<br />
drdr.Close();<br />
conn.Close();
}<br />
}<br />
Example 8-13 shows the output of Example 8-12 on page 270.<br />
Example 8-13 Output of the datareader_sample.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
datareader_sample.cs<br />
C:\work>datareader_sample.exe<br />
Code State<br />
C:\work><br />
CA California<br />
IfxError<br />
The IfxError class collects information that is relevant to a warning or error that<br />
the database returns. You can use the IfxError and IfxErrorCollection<br />
classes to retrieve additional information when an error occurs.<br />
An application can access this information using the properties of the IfxError<br />
object listed in Table 8-11.<br />
Table 8-11 IfxError properties<br />
Property Type Description<br />
Message String Text message<br />
NativeError Int32 <strong>IBM</strong> <strong>Informix</strong> error code<br />
SQLState String ANSI SQL error code<br />
Example 8-14 demonstrates a typical use of the IfxError class. The<br />
IfxException class contains one or more IfxError objects.<br />
Example 8-14 The error_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class Connect {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
Chapter 8. Working with .NET data providers 271
}<br />
}<br />
int drows=0;<br />
272 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=wrong_db");<br />
conn.Open();<br />
Console.WriteLine("Connected");<br />
conn.Close();<br />
}<br />
catch (IfxException e) {<br />
Console.WriteLine("----------------------------");<br />
if (e.Errors.Count > 0) {<br />
IfxError ifxErr = e.Errors[0];<br />
Console.WriteLine("Message :" + ifxErr.Message);<br />
Console.WriteLine("Native error :" + ifxErr.NativeError);<br />
Console.WriteLine("SQL state :" + ifxErr.SQLState );<br />
}<br />
Console.WriteLine(e.StackTrace);<br />
Console.WriteLine("----------------------------");<br />
}<br />
IfxParameter<br />
The IfxParameter class is used to pass parameters to the IfxCommand method.<br />
IfxParameter classes are stored as a collection in a IfxParameterCollection<br />
object.<br />
Table 8-12 contains the public attributes of an IfxParameter object.<br />
Table 8-12 IfxParameter attributes<br />
Property Type Description<br />
DbType DbType Defines the DbType of the parameter<br />
Direction ParameterDirection Defines the direction of the parameter<br />
(Input, Output, or both)<br />
IfxType IfxType Defines the IfxType class of the<br />
parameter<br />
IsNullable Boolean Specifies whether the parameter is<br />
allowed to be null<br />
ParameterName String Name of the parameter, which is a unique<br />
reference in the parameter collection
Example 8-15 shows how to use IfxParameter class to perform an UPDATE<br />
operation.<br />
Example 8-15 The parameter_sample.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
}<br />
Property Type Description<br />
SourceColumn String Specifies the column that is mapped to<br />
the data set<br />
SourceVersion DataRowVersion Defines the DataRowVersion to use when<br />
you load Value<br />
Value Object Defines the value of the parameter<br />
}<br />
IfxConnection conn;<br />
int urows=0;<br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "UPDATE state SET sname = ? where code = ?";<br />
IfxParameter pcode = new IfxParameter(“code”, DbType.String);<br />
IfxParameter psname = new IfxParameter(“sname”, DbType.String);<br />
pcode.Value="CA";<br />
psname.Value="CALIFORNIA";<br />
cmmd.Parameters.Add(psname);<br />
cmmd.Parameters.Add(pcode);<br />
urows = cmmd.ExecuteNonQuery();<br />
Console.WriteLine("Updated rows: "+urows.ToString());<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
Chapter 8. Working with .NET data providers 273
Example 8-16 shows the output of Example 8-15 on page 273.<br />
Example 8-16 Output of the parameter_sample.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
parameter_sample.cs<br />
C:\work>parameter_sample.exe<br />
Updated rows: 1<br />
C:\work><br />
IfxTransaction<br />
The IfxTransaction class represents an SQL transaction to be made at a<br />
database. The application creates an transaction by calling the<br />
BeginTransaction method of the IfxConnection object.<br />
The IfxConnection.BeginTransaction() method returns an IfxTransaction<br />
object. When the application decides how to resolve the transaction, it uses the<br />
Commit and Rollback methods of the IfxTransaction object.<br />
Any IfxCommand that is involved in a transaction must have the transaction<br />
property set to the IfxTransaction object.<br />
Example 8-17 demonstrates how to create a transaction.<br />
Example 8-17 Creating a transaction using the IfxTransaction class<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
int drows=0;<br />
274 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
conn.Open();<br />
IfxTransaction trans = conn.BeginTransaction();<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.Transaction = trans;<br />
cmmd.CommandText = "DROP TABLE state";<br />
drows = cmmd.ExecuteNonQuery();
}<br />
}<br />
trans.Rollback();<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
You can specify the following isolation levels with the BeginTransaction()<br />
method:<br />
► ReadUncommitted<br />
► ReadCommitted<br />
► RepeatableRead<br />
► Serializable<br />
The default Isolation level is ReadCommited.<br />
Example 8-18 demonstrates how to run a distributed transaction.<br />
Example 8-18 The transact_cts.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
using System.Transactions;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
try {<br />
conn = new IfxConnection("Server=demo_on;database=stores_demo");<br />
// Transaction options<br />
TransactionOptions tsopt = new TransactionOptions();<br />
tsopt.IsolationLevel =<br />
System.Transactions.IsolationLevel.RepeatableRead;<br />
tsopt.Timeout = new TimeSpan(0, 60, 0);<br />
using (TransactionScope tscope = new TransactionScope(<br />
TransactionScopeOption.RequiresNew, tsopt,<br />
EnterpriseServicesInteropOption.Full))<br />
{<br />
conn.Open();<br />
Chapter 8. Working with .NET data providers 275
276 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "DROP TABLE state";<br />
cmmd.ExecuteNonQuery();<br />
// Rollback the distributed transaction not calling Complete()<br />
// tscope.Complete();<br />
}<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
}<br />
8.3.4 Handling <strong>Informix</strong> specific data types<br />
In this section, we discuss how to work with <strong>IBM</strong> <strong>Informix</strong> specific data types<br />
such as smart large objects or decimal using <strong>Informix</strong> .NET Provider.<br />
IfxBlob and IfxClob<br />
CLOB and BLOB are <strong>Informix</strong> data types that are used to store large amounts of<br />
character or binary data.The application handles these smart large objects using<br />
the IfxClob and IfxBlob classes.<br />
The method that is implemented on these classes allows random access of its<br />
contents. The application can read or write to certain positions in the large object<br />
without reading or writing through all of the data up to that position.<br />
BLOBs and CLOBs are both smart large object type. Both types share many of<br />
the same methods. BLOBs differ from CLOBs in that the data in a CLOB is<br />
treated as text characters but the data in a BLOB is not. The data in a BLOB is<br />
considered to be binary data, and no translation or conversion of any kind is<br />
performed on it when it is moved to or from the database server.<br />
Properties and methods<br />
Table 8-13 lists the IfxBlob and IfxClob public properties.<br />
Table 8-13 Properties of IfxBlob and IfxClob<br />
Property Type Description<br />
EstimatedSize Int64 Estimates final size of the large object<br />
ExtentSize Int32 Finds the next extent size for this large object (disk<br />
space)
Property Type Description<br />
Flags Int32 Flags for this large object<br />
IsNull Boolean Determines whether the large object is NULL<br />
IsOpen Boolean Determines whether the large object is open<br />
LastAccessTime Int32 Determines the last time that the large object was<br />
accessed<br />
LastChangeTime Int32 Determines the last time that the status was<br />
changed<br />
LastModificationTime Int32 Determines the last time that the large object was<br />
modified<br />
MaxBytes Int64 Defines the maximum size for the large object<br />
Position Int64 Defines the current position on the large object<br />
ReferenceCount Int32 Provides the number of records in the database<br />
that currently contain a reference to this large<br />
object<br />
SBSpace String Defines the Sbspace in which the large object is<br />
stored<br />
Size Int64 Defines the size of the large object in bytes<br />
Table 8-14 lists the available public methods from the IfxClob and IfxBlob<br />
classes.<br />
Table 8-14 Public methods of IfxClob and IfxBlob<br />
Method Description<br />
void Open(mode) Opens a large object in a specific mode<br />
IfxSmartLOBLocator GetLocator() Returns the IfxSmartLOBLocator that is<br />
associated with this instance<br />
Int64 Read(buff), Read(buff,<br />
buffOffset, numBytesToRead,<br />
sLOBOffset, whence)<br />
Int64 Write(buff), Write(buff,<br />
buffOffset, numBytesToWrite,<br />
sLOBOffset, whence)<br />
Reads the complete data or a portion of a<br />
large object as a Byte[] or Char[]<br />
Writes the complete buffer or a portion into<br />
a large object<br />
void Truncate(offset) Truncates everything in the large object<br />
past the position offset<br />
Chapter 8. Working with .NET data providers 277
Method Description<br />
Int64 Seek(offset, whence) Changes the current position within the<br />
large object<br />
void FromFile(filename, appendTosLOB,<br />
fileLocation)<br />
String ToFile(filename, mode,<br />
fileLocation)<br />
void Lock(sLOBOffset, whence, range,<br />
lockMode)<br />
void Unlock(sLOBOffset, whence,<br />
range)<br />
Creating a smart large object<br />
You can use the GetIfxClob() and GetIfxBlob() methods of the<br />
IfxConnection() object to create a large object. To create a large object:<br />
1. Create an instance of a the large object, IfxClob or IfxBlob.<br />
2. Open the large object.<br />
3. Write data into the large object.<br />
4. Execute the SQL Statement using the IfxClob or IfxBlob classes as a<br />
parameter for the statement.<br />
5. Close the large object.<br />
Example 8-19 demonstrates how to update a CLOB column. The code reads a<br />
text file, sample_blob.cs, and uses the contents for the CLOB data.<br />
Example 8-19 The lo_create.cs sample<br />
using System;<br />
using System.IO;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
int urows=0;<br />
IfxClob vclob;<br />
char[] vclobBuff;<br />
278 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Reads an operating system file and writes<br />
the complete content into the large object.<br />
Writes the contents of the large object to<br />
an operating system file<br />
Locks the complete large object or only a<br />
portion of the large object<br />
Unlocks a large object<br />
void Release() Frees database server resources<br />
void Close() Closes the large object
try {<br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Create and Open the Clob<br />
vclob = conn.GetIfxClob();<br />
vclob.Open(IfxSmartLOBOpenMode.ReadWrite);<br />
// Read a text file and insert into the clob<br />
vclobBuff = File.ReadAllText("lo_create.cs").ToCharArray();<br />
vclob.Write(vclobBuff);<br />
// Create the UPDATE Ifxcommand<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "UPDATE catalog set advert_descr = ? WHERE" +<br />
" catalog_num = ?;";<br />
// Bind the IfxClob value and execute the UPDATE statement<br />
IfxParameter padvt_desc = new IfxParameter(null, vclob);<br />
IfxParameter pcatalog_n = new IfxParameter(null, "10072");<br />
cmmd.Parameters.Add(padvt_desc);<br />
cmmd.Parameters.Add(pcatalog_n);<br />
urows = cmmd.ExecuteNonQuery();<br />
Console.WriteLine("Updated rows: "+urows.ToString());<br />
// Close the IfxClob object<br />
vclob.Close();<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
}<br />
If the application requires more control over the smart large object, it can use the<br />
specific properties of the ifxClob and IfxBlob classes to set up values, such as<br />
in which smart BLOB space the large object should create or the maximum size.<br />
Example 8-20 Illustrates how to use the properties of the IfxBlob class.<br />
Example 8-20 Large object extended properties<br />
...<br />
// Create and Open the Clob<br />
vclob = conn.GetIfxClob();<br />
vclob.EstimatedSize = 5000;<br />
vclob.ExtentSize = 1000;<br />
vclob.Flags = (int) IfxSmartLOBCreateTimeFlags.DontKeepAccessTime |<br />
Chapter 8. Working with .NET data providers 279
...<br />
280 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
(int) IfxSmartLOBCreateTimeFlags.NoLog;<br />
vclob.MaxBytes = 10000;<br />
vclob.SBSpace = "sbspace";<br />
vclob.Open(IfxSmartLOBOpenMode.ReadWrite);<br />
Selecting a smart large object<br />
Reading a large object using the large object extensions requires the following<br />
steps:<br />
1. Execute the SQL statement, and retrieve the large object column.<br />
2. Open the large object.<br />
3. Read from the large object into an application buffer.<br />
4. Close the large object.<br />
Example 8-21 shows how to read a large object column from a database table.<br />
Example 8-21 The lo_select.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
IfxClob vclob;<br />
int maxSize = 2000;<br />
char[] vclobBuff = new char[maxSize];<br />
try {<br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Select the large object from the database<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM catalog WHERE catalog_num = 10072";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
while (drdr.Read()) {<br />
// Get the large object. The clob is the 6th column<br />
vclob = drdr.GetIfxClob(5);<br />
// Open the large object<br />
vclob.Open(IfxSmartLOBOpenMode.ReadOnly);<br />
vclob.Read(vclobBuff, 0, maxSize, 0, IfxSmartLOBWhence.Current);
Console.WriteLine(vclobBuff);<br />
// Close the large object<br />
vclob.Close();<br />
}<br />
}<br />
}<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
Random access<br />
One of the benefits of smart large objects over a normal large object is the option<br />
of reading partial data.<br />
The Read(buff, buffOffset, BytesToRead, loOffset, whence) method allows<br />
you to position the data pointer anywhere in the large object data and read from<br />
there.<br />
Example 8-22 shows how to read the last 10 bytes of a large object.<br />
Example 8-22 Partial large object read<br />
...<br />
// Read only the last 10 bytes of large object<br />
int bytesToRead = 10;<br />
vclob.Read(vclobBuff, 0, bytesToRead, (-1) * bytesToRead,<br />
IfxSmartLOBWhence.End);<br />
...<br />
Smart large objects support random I/O access. Using the Seek(offset,<br />
whence) method, you can position the data pointer anywhere in the large object<br />
and perform I/O operations with the data.<br />
Example 8-23 demonstrates how to update only a portion of a large object using<br />
the Seek() method.<br />
Example 8-23 A lo_seek.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
Chapter 8. Working with .NET data providers 281
IfxConnection conn;<br />
IfxClob vclob;<br />
int urows=0;<br />
int maxSize = 100;<br />
char[] vclobBuff = new char[maxSize];<br />
try {<br />
282 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Select the large object from the database<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM catalog WHERE catalog_num = 10072";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
drdr.Read();<br />
// Get the large object. The clob is the 6th column<br />
vclob = drdr.GetIfxClob(5);<br />
// Open the large object<br />
vclob.Open(IfxSmartLOBOpenMode.ReadWrite);<br />
// Move the pointer 15 bytes from the beginning of the large object.<br />
vclob.Seek(15,IfxSmartLOBWhence.Begin);<br />
// Update the large object.<br />
vclobBuff="//------------//".ToCharArray();<br />
vclob.Write(vclobBuff);<br />
drdr.Close();<br />
// Update the column in the table<br />
cmmd.CommandText = "UPDATE catalog set advert_descr = ? WHERE" +<br />
" catalog_num = ?;";<br />
// Bind the IfxClob value and execute the UPDATE statement<br />
IfxParameter padvt_desc = new IfxParameter(null, vclob);<br />
IfxParameter pcatalog_num = new IfxParameter(null, "10072");<br />
cmmd.Parameters.Add(padvt_desc);<br />
cmmd.Parameters.Add(pcatalog_num);<br />
urows = cmmd.ExecuteNonQuery();<br />
Console.WriteLine("Updated rows: "+urows.ToString());<br />
// Close the large object<br />
vclob.Close();<br />
}<br />
}<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
Note: Data Server Provider for .NET does not support random I/O access.
IfxDateTime<br />
The IfxDateTime class represents the <strong>Informix</strong> DATETIME data type. The<br />
IfxDateTime class provides support for all the precisions that are allowed in an<br />
<strong>Informix</strong> DATETIME.<br />
The <strong>Informix</strong> DATETIME data type is composed of the following time units:<br />
► Year<br />
► Month<br />
► Day<br />
► Hour<br />
► Minute<br />
► Second<br />
► Fractions of a second<br />
In an <strong>Informix</strong> database, the maximum precision for a DATETIME column is YEAR<br />
TO FRACTION(5). A DATETIME column can be defined with any precision, from a<br />
year to fraction of a second allowing any subset of these units.<br />
Table 8-15 lists the public properties of the IfxDateTime class.<br />
Table 8-15 Public properties of the IfxDateTime class<br />
Property Type Description<br />
Date IfxDateTime A Year to Day IfxDateTime instance<br />
Day Int32 The day portion of the value<br />
EndTimeUnit IfxTimeUnit The end time unit of the instance<br />
Hour Int32 The hour portion of the value<br />
MaxValue IfxDateTime Maximum value allowed for this IfxDateTime<br />
Millisecond Int32 Millisecond unit in this IfxDateTime<br />
MinValue IfxDateTime Smallest value allowed for this IfxDateTime<br />
Minute Int32 Minute unit in this IfxDateTime<br />
Month Int32 Month unit in this ifxDateTime<br />
Now IfxDateTime Current date with a range of Year to Fraction (5)<br />
Second Int32 Second unit of this IfxDateTime<br />
StartTimeUnit IfxTimeUnit Start time unit of this IfxDateTime<br />
Ticks Int64 Ticks from midnight on 1 Jan 0001<br />
Today IfxDateTime Current time with a range of Year to Day<br />
Chapter 8. Working with .NET data providers 283
Property Type Description<br />
Year Int32 Year unit of the value<br />
Table 8-16 lists the public methods of the IfxDateTime class.<br />
Table 8-16 Public methods of the IfxDateTime class<br />
Method Description<br />
Add(IfxTimeSpan or IfxMonthSpan) Current value plus by object passed<br />
AddDays(days) Current value plus days passed<br />
AddMilliseconds(milliseconds) Current value plus milliseconds passed<br />
AddMinutes(minutes) Current value plus minutes passed<br />
AddMonths(months) Current value plus months passed<br />
AddSeconds(seconds) Current value plus seconds passed<br />
AddYears(years) Current value plus years passed<br />
Compare(ifxDT1, ifxDT1) Compare two IfxDateTime instances<br />
CompareTo(obj) Compare two IfxDateTime instances<br />
DaysInMonth(year, month) Number of days in the month of the year<br />
Equals(ifxDT1, ifxDT2) True if ifxDT1 is equal to ifxDT2<br />
GreaterThan(ifxDT1, ifxDT2) True if ifxDT1 is later than ifxDT2<br />
GreaterThanOrEqual(ifxDT1,<br />
ifxDT2)<br />
284 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
True if ifxDT1 is later or equal than ifxDT2<br />
LessThan(ifxDT1, ifxDT2) True if ifxDT1 is earlier than ifxDT2<br />
LessThanOrEqual(ifxDT1, ifxDT2) True if ifxDT1 is earlier or equal than ifxDT2<br />
NotEquals(ifxDT1, ifxDT2) True if ifxDT1 is different than ifxDT2<br />
Parse(dateTimeStr)<br />
Parse(dateTimeStr, start, end)<br />
Parse(dateTimeStr, format, start,<br />
end)<br />
New IfxDateTime with a value based on<br />
dateTimeStr<br />
ToString(), ToString (format) Value of the instance as a string
To access a DATETIME column in the database, the application uses the<br />
IfxGetDateTime() method from the IfxDataReader() object.<br />
Example 8-24 demonstrates how to select a DATETIME data type from a table.<br />
Example 8-24 The datetime.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
IfxDateTime vdtime;<br />
try {<br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Select the datetime from the database<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM cust_calls WHERE "<br />
+ "customer_num = 106";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
drdr.Read();<br />
// Get the IfxDateTime from the recordset, 2nd columnd<br />
vdtime = drdr.GetIfxDateTime(1);<br />
Console.WriteLine("Call:\t"+vdtime);<br />
Console.WriteLine("Hour :\t "+vdtime.Hour);<br />
Console.WriteLine("Minute:\t"+vdtime.Minute);<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
}<br />
Example 8-25 shows the Hour and Minute units extracted from the IfxDateTime<br />
object.<br />
Example 8-25 Output of the datetime.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /nologo<br />
datetime.cs<br />
C:\work>datetime<br />
Call: 2008-06-12 08:20<br />
Chapter 8. Working with .NET data providers 285
Hour : 8<br />
Minute: 20<br />
C:\work>c<br />
Arithmetic operations between two IfxDateTime objects might return an<br />
lfxDateSpan() object or an IfxMonthSpan() object, depending on the precision<br />
that is used by the IfxDateTime objects.<br />
Example 8-26 shows the use of the IfxDateSpan() class.<br />
Example 8-26 The datetime2.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
IfxDateTime vdtime,vrestime;<br />
IfxTimeSpan vtimetaken;<br />
try {<br />
286 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Select the datetime from the database<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM cust_calls WHERE "<br />
+ "customer_num = 106";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
drdr.Read();<br />
// Get the IfxDateTime from the recordset<br />
vdtime = drdr.GetIfxDateTime(1);<br />
vrestime = drdr.GetIfxDateTime(5);<br />
// Stores the difference in an IfxTimeSpan object.<br />
vtimetaken=vrestime-vdtime;<br />
Console.WriteLine("Initiated at:\t"+vdtime);<br />
Console.WriteLine("Resolved at:\t"+vrestime);<br />
Console.WriteLine("Minutes taken:\t"+vtimetaken.Minutes);<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
}
IfxDecimal<br />
An IfxDecimal object represents the <strong>Informix</strong> decimal data type. The DECIMAL<br />
data type on an <strong>Informix</strong> database can take two forms:<br />
► DECIMAL(p) floating point<br />
► DECIMAL(p,s) fixed point<br />
The IfxDecimal object in <strong>Informix</strong> .NET Provider supports both versions.<br />
Table 8-17 contains the description of the public properties of an IfxDecimal<br />
object.<br />
Table 8-17 Public properties of the IfxDecimal class<br />
Property Type Description<br />
E IfxDecimal Irrational number e<br />
IsFloating Boolean Whether it is a floating number<br />
IsNull Boolean Whether it is NULL<br />
IsPositive Boolean Whether it is positive<br />
MaxPrecision Byte Maximum precision supported (32)<br />
MaxValue IfxDecimal Largest value allowed<br />
MinusOne IfxDecimal -1<br />
MinValue IfxDecimal Smallest value allowed<br />
Null IfxDecimal Null<br />
One IfxDecimal 1<br />
Pi IfxDecimal Irrational number pi<br />
Zero IfxDecimal 0<br />
Table 8-18 contains the description for the IfxDecimal public methods.<br />
Table 8-18 Public methods of the IfxDecimal object<br />
Method Description<br />
Abs(IfxDec) Absolute value of IfxDec<br />
Add(IfxDec1, IfxDec2) Sum of IfxDec1 and IfxDec2<br />
Ceiling(IfxDec) Smallest integer that is not less than IfxDec<br />
Clone() Creates a duplicate of this instance<br />
Chapter 8. Working with .NET data providers 287
Method Description<br />
Compare(IfxDec1, IfxDec2) Compares two IfxDecimal values<br />
CompareTo(obj) Compares current instance with object<br />
Divide(Dividend, Divisor) Dividing result for Dividend by Divisor<br />
Equals(obj) Equal<br />
Equals(IfxDec1, IfxDec2) True if IfxDec1 is the same as IfxDec2<br />
Floor(IfxDec) Largest integer not larger than IfxDec<br />
GreaterThan(IfxDec1, IfxDec2) True if IfxDec1 > IfxDec2<br />
GreaterThanOrEqual(IfxDec1,IfxDe<br />
c2)<br />
288 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
True if IfxDec1 >= IfxDec2<br />
LessThan(IfxDec1, IfxDec2) True if IfxDec1 < IfxDec2<br />
LessThanOrEqual(IfxDec1,<br />
IfxDec2)<br />
True if IfxDec1
An application uses the GetIfxDecimal() method from the IfxDataReader()<br />
class to access a DECIMAL column from the database.<br />
Example 8-27 demonstrates how to retrieve a DECIMAL column<br />
Example 8-27 The decimal.cs sample<br />
using System;<br />
using System.Data;<br />
using <strong>IBM</strong>.Data.<strong>Informix</strong>;<br />
class sample {<br />
static void Main(string[] args) {<br />
IfxConnection conn;<br />
IfxDecimal vtax;<br />
try {<br />
conn = new IfxConnection("Server=demo_on;Database=stores_demo");<br />
conn.Open();<br />
// Select the datetime from the database<br />
IfxCommand cmmd = conn.CreateCommand();<br />
cmmd.CommandText = "SELECT * FROM state WHERE "<br />
+ "code = 'CA'";<br />
IfxDataReader drdr;<br />
drdr = cmmd.ExecuteReader();<br />
drdr.Read();<br />
// Get the IfxDecimal from the recordset<br />
vtax = drdr.GetIfxDecimal(2);<br />
Console.WriteLine("Value Sales Tax :\t "+vtax);<br />
Console.WriteLine("Ceiling Sales Tax :\t " + IfxDecimal.Ceiling(vtax));<br />
Console.WriteLine("Negative Sales Tax :\t " + IfxDecimal.Negate(vtax));<br />
conn.Close();<br />
}<br />
catch (Exception e) {<br />
Console.WriteLine(e.Message);<br />
}<br />
}<br />
}<br />
Example 8-28 shows the output of the previous example.<br />
Example 8-28 Output of the decimal.cs sample<br />
C:\work>csc.exe /R:%INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.<strong>Informix</strong>.dll /platform:x86<br />
decimal.cs<br />
Chapter 8. Working with .NET data providers 289
C:\work>decimal<br />
Value Sales Tax : 0.0825<br />
Ceiling Sales Tax : 1.0<br />
Negative Sales Tax : -0.0825<br />
C:\work><br />
8.3.5 Troubleshooting<br />
In this section, we discuss typical errors that occur when using the .NET<br />
providers and how to enable the tracing facility to collect diagnostic information.<br />
Typical errors<br />
If the application fails to connect to the <strong>Informix</strong> database server, verify that<br />
connection string details are correct for the server and version of the .NET<br />
provider that you are using.<br />
Both .NET providers use the ODBC or CLI layer to communicate with the<br />
database server. You can test the connection with these components before<br />
using a .NET provider.<br />
When using <strong>Informix</strong> .NET Provider, always test the connectivity first using the<br />
iLogin utility that is included with Client SDK.<br />
Data Server Provider for .NET includes the Testconn11.exe and Testconn20.exe<br />
executables that you can use to diagnose connectivity and setup problems when<br />
using Data Server Provider for .NET.<br />
Table 8-19 lists typical errors and how to resolved them.<br />
Table 8-19 Typical errors<br />
Error Reason Solution<br />
ERROR [IM009] [<strong>Informix</strong> .NET<br />
provider]<br />
Unable to load translation<br />
shared library (DLL)<br />
System.BadImageFormatExcepti<br />
on: An attempt was made to<br />
load a program with an<br />
incorrect format<br />
290 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
The <strong>Informix</strong> .NET Provider<br />
failed to load the ODBC<br />
shared library.<br />
A 32-bit application is<br />
attempting to load a 64-bit<br />
<strong>IBM</strong>.Data.<strong>Informix</strong><br />
assembly.<br />
Verify that the values for the<br />
INFORMIXDIR environment variable<br />
and PATH are correct.<br />
INFORMIXDIR should point to the<br />
Client SDK installation directory, and<br />
PATH should contain the<br />
%INFORMIXDIR%\bin directory.<br />
Register the proper version of the<br />
.NET assembly into the GAC using<br />
the gacutil.exe tool.
Error Reason Solution<br />
Unhandled Exception:<br />
System.EntryPointNotFoundExc<br />
eption: Unable to find an<br />
entry point named<br />
'InterlockedIncrement' in DLL<br />
'kernel32.dll'.<br />
Unhandled Exception:<br />
System.DllNotFoundException:<br />
Unable to load DLL<br />
'IfxDotNetIntrinsicModule.dl<br />
l': The specified module<br />
could not be found.<br />
8.3.6 Tracing<br />
Your application can set the following types of tracing when using <strong>Informix</strong> .NET<br />
Provider:<br />
► <strong>Informix</strong> .NET Trace<br />
► SQLIDEBUG<br />
<strong>Informix</strong> .NET Trace<br />
This trace is specific to <strong>Informix</strong> .NET Provider. To use this feature, the<br />
application must use the Trace version of the <strong>IBM</strong>.Data.<strong>Informix</strong> assembly<br />
located in the %INFORMIXDIR%\netf20 directory. The name of shared library is<br />
<strong>IBM</strong>.Data.Trace.dll.<br />
Register the assembly in the GAC and enable the trace by setting the<br />
IFXDOTNETTRACE and IFXDOTNETTRACEFILE environment variables.<br />
Example 8-29 demonstrates how to register the trace library and generate a<br />
.NET trace file.<br />
Example 8-29 Trace setup<br />
C:\work>gacutil /i %INFORMIXDIR%\bin\netf20\<strong>IBM</strong>.Data.IfxTrace.dll /nologo<br />
Assembly successfully added to the cache<br />
C:\work>set IFXDOTNETTRACE=2<br />
A 64-bit application is<br />
attempting to load a 32-bit<br />
<strong>IBM</strong>.Data.Inforimx.dll<br />
assembly.<br />
Failed to load<br />
IfxDotNetIntrinsicModule<br />
assembly that is required by<br />
the 64-bit version of <strong>Informix</strong><br />
.NET Provider.<br />
C:\work>set IFXDOTNETTRACE=trace.txt<br />
C:\work>decimal<br />
Value Sales Tax : 0.0825<br />
Register the proper version of the<br />
.NET assembly into the GAC using<br />
the gacutil.exe tool.<br />
When using the 64-bit version of<br />
<strong>Informix</strong> .NET Provider, the PATH<br />
environment variable needs to define<br />
the %INFORMIXDIR%\bin and<br />
%INFORMIXDIR%\bin\netf20<br />
directories.<br />
Chapter 8. Working with .NET data providers 291
Ceiling Sales Tax : 1.0<br />
Negative Sales Tax : -0.0825<br />
C:\work>dir trace.txt<br />
Volume in drive C is W2003<br />
Volume Serial Number is 50DA-70D7<br />
Directory of C:\work<br />
25/06/2010 13:52 13,172 trace.txt<br />
1 File(s) 13,172 bytes<br />
0 Dir(s) 76,259,311,616 bytes free<br />
C:\work><br />
The .NET trace file contains information about all the .NET classes that are used<br />
and their return values. Example 8-30 shows some of the entries in the .NET<br />
trace file.<br />
Example 8-30 .NET Trace file<br />
...<br />
3532:1 Entry: IfxDecimal.ToString()<br />
3532:1 Exit: IfxDecimal.ToString<br />
IfxDecimal 0.0825<br />
3532:1 Entry: IfxDecimal.ToString()<br />
532:1 Exit: IfxDecimal.ToString<br />
IfxDecimal -1.0<br />
)<br />
3532:1 Entry: IfxDecimal.get_IsNull()<br />
3532:1 Exit: IfxDecimal.get_IsNull<br />
532:1 Exit: IfxDecimal.Multiply<br />
3532:1 Exit: IfxDecimal.Negate<br />
3532:1 Entry: IfxConnection.Close()<br />
3532:1 Entry: IfxConnection.GetLatch<br />
(<br />
String IfxConnection.Close<br />
)<br />
...<br />
292 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The level of tracing is determine by the value of the IFXDOTNETTRACE variable:<br />
0 No tracing<br />
1 Tracing of API entry and exit, with return code<br />
2 Tracing of API entry and exit, with return code, plus tracing of parameters<br />
to the API<br />
Levels 3 and 4 are for internal use only.<br />
SQLIDEBUG and DRDADEBUG<br />
Refer to “SQLIDEBUG” on page 119 for the description and use of the<br />
SQLIDEBUG.<br />
8.4 Visual Studio Add-In for Visual Studio<br />
This section gives an overview of Visual Studio Add-in. The <strong>IBM</strong> Database<br />
Add-Ins for Visual Studio are a collection of features that integrate into Microsoft<br />
Visual Studio development environment so that you can work with <strong>IBM</strong> data<br />
servers.<br />
Visual Studio Add-in Version 9.5 is compatible with Visual Studio 2005, and it<br />
uses <strong>Informix</strong> .NET Provider to connect to the database server.<br />
Version 9.7 is compatible with Visual Studio 2005 and Visual Studio 2008 and<br />
uses Data Server Provider for .NET.<br />
Although it is not included in the Client SDK bundle, Visual Studio Add-in is<br />
available as a separate product. For more information, refer to:<br />
https://www.software.ibm.com/webapp/iwm/web/reg/download.do?source=swg-vsai<br />
Chapter 8. Working with .NET data providers 293
Visual Studio Add-In provides tools and wizards that simplify the .NET<br />
development for the following common tasks:<br />
► Explore catalog information of the database server.<br />
► Generate Data Definition Language (DDL) for database objects.<br />
► Drag server objects onto .NET application components to generate the<br />
required ADO.NET code automatically.<br />
To use Visual Studio Add-In, you first define the <strong>Informix</strong> server in the Data<br />
Connections group of the Visual Studio environment. Select the Add Connection<br />
to add a new server definition.<br />
In the Add Connection panel, specify the server name, server log on ID and<br />
password, and the database name.<br />
When the database is connected, you can drag the <strong>Informix</strong> tables from the<br />
Server Explorer pane to the .Net data set pane.<br />
For more details about using <strong>IBM</strong> Database Add-Ins and Data Server Provider<br />
for .NET for application development, refer to the <strong>IBM</strong> Information Management<br />
and Visual Studio .NET zone at:<br />
http://www.ibm.com/developerworks/data/zones/vstudio<br />
294 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 9. Working with PHP<br />
PHP is a powerful server-side scripting language that was invented and that is<br />
designed for creating dynamic web applications with non-static content. The PHP<br />
code can be a stand-alone program or an insert inside Hypertext Markup<br />
Language (HTML) or Extensible Hypertext Markup Language (XHTML). The<br />
PHP syntax is based mostly on and is similar to the C, Java, and Perl<br />
programming language. You can use PHP based on an open-source license.<br />
This chapter discusses the use of PHP with <strong>Informix</strong> as the database server. We<br />
discuss various PHP database extensions that you can use to connect to<br />
<strong>Informix</strong>. We also discuss other components, such as Apache Web Server and<br />
OpenAdmin Tool (OAT), and how to bring these components together. We<br />
describe in detail the use of programming interfaces that are provided by<br />
selected PHP database extensions through the use of various examples.<br />
This chapter includes the following topics:<br />
► <strong>Informix</strong> and PHP extensions<br />
► Setup and configuration<br />
► Developing a PHP application<br />
9<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 295
9.1 <strong>Informix</strong> and PHP extensions<br />
The PHP Data Objects (PDO) extension defines a lightweight, consistent<br />
interface for accessing databases in PHP. Each database driver that implements<br />
the PDO interface can expose database-specific features as regular extension<br />
functions.<br />
<strong>Informix</strong> database can be accessed by PHP using the following PDO drivers:<br />
► PDO_INFORMIX<br />
This driver is also called PHP Driver for <strong>IBM</strong> <strong>Informix</strong>. To compile and use<br />
the driver, you must install <strong>Informix</strong> Client Software Development Kit (Client<br />
SDK). You can download this driver from:<br />
http://pecl.php.net/package/PDO_INFORMIX<br />
► PDO_<strong>IBM</strong><br />
This driver is also know as PHP Driver for Data Server clients. To use this<br />
driver, you must install <strong>IBM</strong> Data Server Driver for ODBC and CLI on the<br />
same computer. You can download this from:<br />
http://pecl.php.net/package/PDO_<strong>IBM</strong><br />
In addition to these two drivers, the following PHP extensions allow you to access<br />
an <strong>IBM</strong> <strong>Informix</strong> database:<br />
► PHP_INFORMIX<br />
This extension provides support only for standard <strong>Informix</strong> data types. This<br />
extension is developed by the open source community and is available at:<br />
http://cvs.php.net/viewvc.cgi/pecl/informix/<br />
► <strong>IBM</strong>_DB2<br />
This extension is available with <strong>IBM</strong> Data Server Driver and is written,<br />
maintained, and supported by <strong>IBM</strong>. It supports the <strong>Informix</strong> and DB2<br />
databases.<br />
Your application development environment, including hardware and software,<br />
determines which PHP drivers and extensions to use. If the platform of your<br />
choice is not supported by <strong>IBM</strong> Data Server Driver or if you are using some of the<br />
data types that are not supported by <strong>IBM</strong> Data Server Driver, then use the<br />
<strong>Informix</strong> driver. The Data Server Driver for ODBC and CLI requires an <strong>Informix</strong><br />
database of Version v11.x or later. If your database server is exclusively <strong>Informix</strong>,<br />
then you can use the <strong>Informix</strong> driver. You can also use both drivers<br />
simultaneously if needed.<br />
296 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
9.2 Setup and configuration<br />
9.2.1 Installing OAT<br />
To develop a PHP application for <strong>Informix</strong>, your application development system<br />
must have the following software:<br />
► PHP drivers and extensions<br />
Choose one of the PHP drivers or extensions that <strong>Informix</strong> supports. The<br />
PHP Driver for <strong>IBM</strong> <strong>Informix</strong> requires Client SDK and the PHP Driver for Data<br />
Server clients requires the <strong>IBM</strong> Data Server Driver for ODBC and CLI. Refer<br />
to 2.2, “Client setup” on page 34 for the details.<br />
► Web server<br />
You can either install Apache Web Server or use OpenAdmin Tool (OAT),<br />
which is a PHP-based web browser administration tool. Installing OAT is an<br />
easier option, because it contains everything that is required for PHP<br />
connectivity. At the time of writing this book, OAT version 2.2.7 has the<br />
following components:<br />
– Apache 2.2.4<br />
– PHP 5.2.4<br />
– PDO_INFORMIX 1.2.6<br />
In this section, we discuss OAT installation and driver configuration. For<br />
information about how to install the latest Apache Web Server, refer to the<br />
following website:<br />
http://httpd.apache.org/<br />
OpenAdmin Tool (OAT) is available for Windows, Linux, and Macintosh OS X<br />
(64-bit only) at:<br />
http://www.openadmintool.org<br />
This website leads you to the <strong>IBM</strong> site, where you must register and log in.<br />
After you complete the download, the installation procedure is as follows:<br />
1. Start the installation with one of the following methods:<br />
– GUI mode<br />
Launch the installation executable using one of the following commands:<br />
On Windows systems: Run install.exe.<br />
On Linux: Run install.bin.<br />
On MAC OS X: Extract the install.zip file, and run install.app.<br />
Chapter 9. Working with PHP 297
– Console mode<br />
Enter one of the following commands:<br />
On Windows systems: install.exe -i console<br />
On Linux: install.bin -i console<br />
2. Accept the license agreement to continue.<br />
3. Select the installation directory. The default directory is as follows:<br />
– For Windows systems: C:\Program Files\OpenAdmin<br />
– For Linux: /opt/OpenAdmin<br />
– For MAC OS X: /Applications/OpenAdmin/<br />
4. Choose an available port number for the web server.<br />
5. For Windows operating system users, provide an Apache service name.<br />
6. Specify the host name, which is the name of the computer where the<br />
database server is located.<br />
7. In the Security Features panel, enable password-protection for OAT<br />
administration pages.<br />
8. In the OAT Administrator login setup panel, enter a user name and password.<br />
9. In the Plug-in page, ensure that the plug-ins that you want to install are<br />
selected. Accept the license agreement.<br />
10.In the Pre-Installation Summary panel, review your selections and proceed<br />
with the installation. When the installation is complete, the following message<br />
displays:<br />
OpenAdmin Tool has been installed successfully. Please visit<br />
http://servername:portnumber/openadmin/ to use the OpenAdmin Tool<br />
Where:<br />
– servername is the name of the system where the web server is running.<br />
This name can be localhost on Windows.<br />
– portnumber is the port number that you provided in step 4.<br />
11.Click Done.<br />
– For Windows systems: You need to reboot. You can access OAT from the<br />
Start � All Programs � OpenAdmin Tool for IDS.<br />
– For Linux and MAC OS X: The OAT configuration page opens in your<br />
default web browser.<br />
For an insight into OAT, read the release notes and the README file that are<br />
bundled with the product.<br />
298 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
9.2.2 Verifying the PDO_INFORMIX setup<br />
We discuss the installation of the PDO_INFORMIX driver in 2.2.3, “Setting up<br />
<strong>IBM</strong> Data Server drivers” on page 43. The PDO_INFORMIX driver is installed<br />
automatically when you install OAT.<br />
We use the PDO_INFORMIX driver and the Apache Web Server that is installed<br />
with OAT in a Windows system for the examples in this chapter.<br />
On successful installation of OAT, the php_pdo_informix.ddl shared library (.so<br />
extension in Linux) is placed in the PHP extension directory. For example, if your<br />
installation directory is C:\Program FIles\OpenAdmin, the shared library is in the<br />
C:\Program Files\OpenAdmin\PHP_5.2.4\ext directory.<br />
To verify that the PHP extensions are working, enter the following URL in a web<br />
browser:<br />
http://localhost:8080/phpinfo.php<br />
9.2.3 Verifying the PDO setup<br />
We discuss the installation of the PDO_<strong>IBM</strong> driver in 2.2.3, “Setting up <strong>IBM</strong> Data<br />
Server drivers” on page 43. Depending on your platform copy, add the .dll or<br />
.so to the OAT extension directory and change the php.ini to include the<br />
extension php_pdo_ibm.dll or php_pdo_ibm.so.<br />
You can see which PDO driver is loaded by the Apache Web Server by typing the<br />
following address in your browser:<br />
http://localhost:8080/phpinfo.php<br />
Chapter 9. Working with PHP 299
9.2.4 Verifying connectivity<br />
You can use a simple PHP program to verify the connectivity to the <strong>Informix</strong><br />
server using the installed <strong>Informix</strong> PHP drivers or extensions.<br />
Example 9-1 shows a simple PHP script that connects to the database server<br />
using the PDO_INFORMIX driver. In the conn_string variable, set the proper<br />
host name, user name, password, and respective locales. The first keyword in<br />
the connection string identifies the PDO driver that is used. The<br />
PDO_INFORMIX driver uses informix, and the PDO_<strong>IBM</strong> driver uses ibm.<br />
Example 9-1 The connect.php program<br />
You can use the same script to test the connectivity using the PDO_<strong>IBM</strong> driver by<br />
just changing the connection string. Example 9-2 shows a typical connection<br />
string using the PD_<strong>IBM</strong> driver.<br />
Example 9-2 PDO_<strong>IBM</strong> connection string<br />
$conn_string = "ibm: DRIVER={<strong>IBM</strong> DB2 ODBC<br />
DRIVER};DATABASE=stores_demo;HOSTNAME=kodiak;PORT=9089;PROTOCOL=TCPIP;";<br />
At this point, you know that your environment is set up properly, and you are<br />
ready to begin using PHP with <strong>Informix</strong>.<br />
9.3 Developing a PHP application<br />
PHP is a powerful, server-side scripting language that was invented and<br />
designed for creating dynamic web applications with non-static content. The PHP<br />
code can be a stand-alone program or an insert inside Hypertext Markup<br />
Language (HTML) or Extensible Hypertext Markup Language (XHTML). The<br />
PHP syntax is based mostly on and similar to C, Java, and Perl. You can use<br />
PHP with an open-source license. You can run the PHP program directly from the<br />
command line.<br />
We chose PHP for our applications for the following reasons:<br />
► Easy to use<br />
PHP is a scripting language that is included directly in HTML. Thus, getting<br />
started is easy. There is no need to compile PHP programs or spend time<br />
learning tools to create PHP. You can simply insert statements and get quick<br />
turnaround as you make changes.<br />
► Fully functional<br />
The PHP language has built-in functions to access your favorite database.<br />
With PHP, your HTML pages can reflect current information from databases.<br />
You can use information for the user who is viewing your HTML web page to<br />
customize the page specifically for that user. You can create classes for<br />
object-oriented programming or use flat file or Lightweight Directory Access<br />
Protocol (LDAP) databases. It also includes a spelling checker, XML<br />
functions, image generation functions, and more.<br />
► Compatible and quick<br />
Because PHP generates plain HTML, PHP is compatible with all web<br />
browsers.<br />
Chapter 9. Working with PHP 301
► Secure<br />
Although PHP is open source, it is a secure environment. Web clients can<br />
only see the pure HTML code. The logic of the PHP program is never<br />
exposed to the client; therefore, security exposures are reduced.<br />
► Open source<br />
PHP is an open-source programming language. It is easy to get started and<br />
find examples from websites. For example:<br />
http://www.sourceforge.net<br />
9.3.1 Connecting to a database<br />
Regardless of which PHP driver you use, PDO_INFORMIX or PDO_<strong>IBM</strong>, you<br />
must decide the type of connection to use for any database connection and the<br />
user type to use for authentication.<br />
Connection type<br />
The following connection type behavior is controlled by the web server:<br />
► Persistent connection<br />
For the persistent connection, the web server leaves the connection to the<br />
database open after the PHP script completes its work. The next attempt to<br />
connect to the database with the same parameters reuses the connection.<br />
► Standard connection<br />
For the standard connection, the database connection is closed when the<br />
script completes its execution.<br />
Here are some considerations for persistent connection:<br />
► When using the persistent connection, the same connection is used for the<br />
next request with the same connection parameters. This connection might<br />
cause connection pooling, depending on the implementation and how you<br />
decide whether the connection is reused. Unintentional connection pooling is<br />
most likely a source of serious problems, even if you use transactions.<br />
► Opening up a persistent connection makes sense only in a web server<br />
environment if the connection is not closed in the same script with a<br />
connection close call. Calling the PHP command line processor on the shell<br />
closes the connection to the <strong>Informix</strong> database server at the end of the<br />
execution of the script.<br />
302 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
► The web server spawns more than one process to handle the incoming web<br />
requests. So, reusing the existing persistent connection is valid only for a<br />
specific web server process. It results in the database server having more<br />
connections open than actual running web server processes.<br />
► Restarting the database server without cleaning up the remaining persistent<br />
connections in the web server environment produces no errors at connection<br />
time when the connection is reused but fails at the first database statement.<br />
This type of error is hard to diagnose.<br />
The user type<br />
The user type is controlled by the database and has to do with authentication at<br />
the database server. You can choose an authentication with user ID and<br />
password in the connection string or a trusted user connect where the database<br />
server does not apply an authentication. This connection is controlled by settings<br />
on operating system resources, such as .rhosts or /etc/hosts.equiv files.<br />
Because you have to specify user names and passwords in the PHP scripts, pay<br />
attention to these files in regard to security. Use encapsulation for files that<br />
contain passwords, and put these files in directories that are secured with the<br />
.htaccess or httpd.conf mechanism of the Apache Web Server.<br />
<strong>Informix</strong> PDO requires, at connection time, a defined set of parameters:<br />
► Database name<br />
► Database server name<br />
We suggest that you use a user ID and password for untrusted user sessions.<br />
Some optional parameters can influence the cursor behavior. In this section, we<br />
focus on the required connection parameters.<br />
Example 9-3 gives an overview about the various parameter settings in an<br />
attempt to connect to the <strong>Informix</strong> database server. The examples shown are for<br />
the PDO_INFORMIX driver. Choose one of the connection statements that<br />
meets your requirements.<br />
Example 9-3 The PDO_INFORMIX driver connection strings<br />
/*--- standard connect ---*/<br />
$dbc = new PDO("informix:; database=sysmaster;<br />
server=ol_svr_custom;","informix", "123456");<br />
/*--- standard connect trusted user ---*/<br />
$dbc = new PDO("informix:; database=sysmaster; server=ol_svr_custom;");<br />
/*--- persistent connect untrusted user ---*/<br />
$dbc = new PDO("informix:; database=sysmaster;<br />
server=ol_svr_custom;","informix","123456",array(PDO::ATTR_PERSISTENT=> true));<br />
Chapter 9. Working with PHP 303
*--- persistent connect trusted user ---*/<br />
$dbc = new PDO("informix:; database=sysmaster;<br />
server=ol_svr_custom;",NULL,NULL,array(PDO::ATTR_PERSISTENT=> true));<br />
The PDO_<strong>IBM</strong> driver is based on the Data Server Driver CLI driver. Thus, it uses<br />
the same connectivity information as the ODBC and CLI driver. You can store this<br />
information, as well as user credentials, in the db2cli.ini file that is located in<br />
the user profile directory. Example 9-4 shows an example db2cli.ini file.<br />
Example 9-4 An example db2cli.ini file<br />
[dsc_dsn]<br />
Protocol=TCPIP<br />
Port=9089<br />
Hostname=kodiak<br />
Database=stores_demo<br />
PWD=password<br />
UID=password<br />
Having the details inside the db2cli.ini file makes it easier to manage the<br />
information that is needed for the connection. You can use either of the<br />
connection strings mentioned in the Example 9-5. The second conn_string uses<br />
the dbscli.ini file shown in Example 9-4 on page 304.<br />
Example 9-5 PDO_<strong>IBM</strong> connection strings<br />
$conn_string = "ibm: DRIVER={<strong>IBM</strong> DB2 ODBC DRIVER};DATABASE=stores_demo;<br />
HOSTNAME=kodiak;PORT=9089;PROTOCOL=TCPIP;";<br />
$conn_string = "ibm: DSN=dsc_dsn";<br />
9.3.2 Performing database operations<br />
<strong>Informix</strong> supports both static and dynamic SQL statements for the client<br />
applications such as PHP programs. In this section, we provide an in-depth<br />
discussion about the abilities of handling dynamic and static SQL of PDO<br />
<strong>Informix</strong>. We focus on the INSERT statement but also give short examples for the<br />
DELETE and UPDATE statements.<br />
304 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Static SQL statements<br />
Example 9-6 shows various methods to execute a static INSERT with <strong>Informix</strong><br />
PDO.<br />
Example 9-6 INSERT, UPDATE, and DELETE using the PDO_INFORMIX driver<br />
In a web application, the input data frequently comes from an HTML form that is<br />
entered by a web user. Example 9-7 shows, based on a simple HTML form, how<br />
the data flows from the HTML over the PHP script into the database table.<br />
Example 9-7 HTML sample<br />
basic html format<br />
<br />
Example 9-8 shows the PHP script that handles the HTML form. The $_POST<br />
array contains all the settings for the buttons and the values for the text fields<br />
from the HTML.<br />
Example 9-8 PHP Script handling the insert<br />
$dbc->query($statement);<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" prepare insert failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
?><br />
Example 9-9 shows how to perform an update and a delete using the PDO<br />
<strong>Informix</strong> extension.<br />
Example 9-9 Update a PHP script<br />
Dynamic SQL statements<br />
Dynamic SQL statements allow an <strong>Informix</strong> client side program to build an SQL<br />
statement at run time, so that the content of the statement can be determined by<br />
user input. <strong>Informix</strong> PDO provides several methods to use dynamic SQL. You<br />
must use placeholders, such as “?” or “:parameter” to prepare a statement for<br />
dynamic usage. These placeholders are later substituted with the values.<br />
The “prepare once, execute multiple times” capability of dynamic statements<br />
allows you to specify different values at each execution time. To determine which<br />
statements and clauses allow placeholders, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.sqls.doc/sqls<br />
.htm<br />
This document also provides a comprehensive reference of <strong>Informix</strong> SQL<br />
statements.<br />
Example 9-10 shows different INSERT statements using <strong>Informix</strong> PDO. To learn<br />
more about using placeholders (“?”) and bind parameters, refer to PHP manual<br />
at:<br />
http://php.net/manual/en/pdostatement.bindparam.php<br />
Example 9-10 INSERT statements using <strong>Informix</strong> PDO<br />
printf(" prepare insert2 failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
/*--- dynamic inserts with : placeholders and bindParam ---*/<br />
$stmt=<br />
$dbc->prepare("INSERT INTO customer VALUES (0,:p1,:p2,:p3,:p4,:p5,:p6,:p7,:p8,:p9)");<br />
$stmt->bindParam(':p1', $_POST["fname"]);<br />
$stmt->bindParam(':p2', $_POST["lname"]);<br />
$stmt->bindParam(':p3', $_POST["company"]);<br />
$stmt->bindParam(':p4', $_POST["address1"]);<br />
$stmt->bindParam(':p5', $_POST["address2"]);<br />
$stmt->bindParam(':p6', $_POST["city"]);<br />
$stmt->bindParam(':p7', $_POST["state"]);<br />
$stmt->bindParam(':p8', $_POST["zipcode"]);<br />
$stmt->bindParam(':p9', $_POST["phone"]);<br />
$stmt->execute();<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" prepare insert2 failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
/*--- dynamic inserts with "?" placeholders and bindValue ---*/<br />
$stmt=<br />
$dbc->prepare("INSERT INTO customer VALUES (99,?,?,?,?,?,?,?,?,?)");<br />
$stmt->bindValue(1, $_POST["fname"],PDO::PARAM_STR);<br />
$stmt->bindValue(2, $_POST["lname"],PDO::PARAM_STR);<br />
$stmt->bindValue(3, $_POST["company"],PDO::PARAM_STR);<br />
$stmt->bindValue(4, $_POST["address1"],PDO::PARAM_STR);<br />
$stmt->bindValue(5, $_POST["address2"],PDO::PARAM_STR);<br />
$stmt->bindValue(6, $_POST["city"],PDO::PARAM_STR);<br />
$stmt->bindValue(7, $_POST["state"],PDO::PARAM_STR);<br />
$stmt->bindValue(8, $_POST["zipcode"],PDO::PARAM_STR);<br />
$stmt->bindValue(9, $_POST["phone"],PDO::PARAM_STR);<br />
$stmt->execute();<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" prepare insert3 failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
/*--- dynamic inserts with : placeholders and bindValue ---*/<br />
$stmt=<br />
$dbc->prepare("INSERT INTO customer VALUES<br />
(199,:p1,:p2,:p3,:p4,:p5,:p6,:p7,:p8,:p9)");<br />
$stmt->bindValue(':p1', $_POST["fname"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p2', $_POST["lname"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p3', $_POST["company"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p4', $_POST["address1"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p5', $_POST["address2"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p6', $_POST["city"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p7', $_POST["state"],PDO::PARAM_STR);<br />
$stmt->bindValue(':p8', $_POST["zipcode"],PDO::PARAM_STR);<br />
Chapter 9. Working with PHP 309
$stmt->bindValue(':p9', $_POST["phone"],PDO::PARAM_STR);<br />
$stmt->execute();<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" prepare4 insert failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
?>l<br />
9.3.3 Handling complex data types<br />
In this section, we discuss how to use complex data types in a PHP program. We<br />
cover row types, collection types (such as SET, LIST, and MULTISET), and the<br />
BLOB and SBLOB data types. We explain how to work with the complex data<br />
types using examples with <strong>Informix</strong> PDO.<br />
Named row types<br />
Example 9-11 shows the Data Definition Language (DDL) to create a ROW type<br />
and the table that uses the ROW type.<br />
Example 9-11 DDL for creating ROW type and table<br />
/*Create a row type and a table by name customer_rtype as below<br />
CREATE ROW TYPE address_rtype (<br />
street_num int,<br />
street_name char(20),<br />
city char(20),<br />
state char(20),<br />
zipcode char(10)<br />
);<br />
CREATE TABLE customer_rtype (<br />
customer_num serial,<br />
lname char(15),<br />
fname char(15),<br />
company char(20),<br />
address address_rtype,<br />
phone char(18)<br />
);<br />
310 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The PHP script in Example 9-12 shows SQL statements using ROW type. This<br />
script inserts two rows and selects the data from the database with and without a<br />
filter on the row type. It then updates one of the rows and delete the rows from<br />
the table.<br />
Example 9-12 Insert complex types sample<br />
*--- Verification ---*/<br />
$stmt1=$dbc->query(' SELECT customer_num,fname, address::lvarchar FROM customer_rtype<br />
WHERE address = row(1234,"Almeda blvd","Fremont","CA","12345")::address_rtype');<br />
$row=$stmt1->fetch(PDO::FETCH_ASSOC);<br />
while($row) {<br />
print_r($row);<br />
printf("");<br />
$row=$stmt1->fetch(PDO::FETCH_ASSOC);<br />
}<br />
printf(" [ DELETE ] ");<br />
/*--- close the buisness ---*/<br />
$dbc->query(' DELETE FROM address_rowtype ');<br />
$dbc->query(' DELETE FROM customer_rtype ');<br />
?><br />
Example 9-13 shows the output from the SELECT statements to give you an idea<br />
of how the array with row types looks in PHP.<br />
Example 9-13 Output of ROW type example<br />
[ INSERT ]<br />
[ SELECT without row_type filter ]<br />
Array ( [CUSTOMER_NUM] => 1 [FNAME] => Gomes [CITY] => San Fransisco [STATE] => CA<br />
[PHONE] => 408-908-8887 )<br />
Array ( [CUSTOMER_NUM] => 2 [FNAME] => jones [CITY] => Fremont [STATE] => CA [PHONE] =><br />
345-908-8887 )<br />
[ SELECT with filter ]<br />
Array ( [CUSTOMER_NUM] => 2 [STATE] => CA [PHONE] => 345-908-8887 )<br />
[ UPDATE ]<br />
[ SELECT ]<br />
Array ( [CUSTOMER_NUM] => 2 [FNAME] => jones [] => ROW(1234 ,'Almeda blvd ','Fremont<br />
','CA ','12345 ') )<br />
[ DELETE ]<br />
312 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Collection data types<br />
The collection data types are another set of complex data types provided by the<br />
<strong>Informix</strong>. Collection data types include SET, LIST, and MULTISET. In this section,<br />
we cover these collection data types. Example 9-14 shows an example that<br />
inserts into and selects from a table containing collection data types.<br />
Example 9-14 Using collection data types in PHP<br />
$row=$stmt1->fetch(PDO::FETCH_ASSOC);<br />
while($row) {<br />
print_r($row);<br />
printf("");<br />
$row=$stmt1->fetch(PDO::FETCH_ASSOC);<br />
}<br />
?><br />
Example 9-15 shows the output of Example 9-14 on page 313.<br />
Example 9-15 The output of collection data type<br />
[ Insert ]<br />
[ SELECT without filter]<br />
Array ( [LIST] => LIST{-1 ,0 ,-2 ,3 ,0 ,0 ,32767 ,249 } [SET] => SET{-1 ,0 ,-2<br />
,3 } [MULTISET] => MULTISET{-1 ,0 ,0 ,-2 ,3 ,0 } )<br />
Array ( [LIST] => LIST{-1 ,0 ,-2 ,3 ,0 ,0 ,55555 ,249 } [SET] => SET{-11 ,0 ,-2<br />
,3 } [MULTISET] => MULTISET{-1 ,0 ,0 ,-2 ,3 ,0 ,9 ,10 } )<br />
[ SELECT with filter]<br />
Array ( [LIST] => LIST{-1 ,0 ,-2 ,3 ,0 ,0 ,32767 ,249 } [SET] => SET{-1 ,0 ,-2<br />
,3 } [MULTISET] => MULTISET{-1 ,0 ,0 ,-2 ,3 ,0 } )<br />
BLOB and SBLOB data types<br />
With BLOB and SBLOB data types, <strong>Informix</strong> IDS can handle large objects as<br />
binary data in a BYTE data type and text objects in a TEXT data type. The BYTE<br />
and TEXT data types, commonly known as BLOB data types or simple large<br />
objects, provides the capability to store images and entire documents in the<br />
database. BLOB data types can be stored within all the other data in the table<br />
space or in a separate specified BLOB space.<br />
BLOB data types can be used in several operational areas. Commonly,<br />
document retrieval systems and geographic information systems are based on<br />
this data type. To retrieve BLOB data with SQL, you need to define keywords,<br />
which are stored together with the BLOB in the data row. The benefit of storing<br />
large data in the database is, in addition to an easy search using keywords and a<br />
combination of data stored in different rows but belonging together, the<br />
opportunity to backup and restore or delete and update for specific data. Storing<br />
the data in a file in the operating system is much more of a maintenance effort.<br />
314 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
More advanced than the BLOB data types, smart large objects (commonly<br />
known as SBLOB), provide more flexibility for searching data, such as random<br />
I/O access to the data, which was impossible with simple large objects).<br />
There are two types of SBLOBs, BLOB, and CLOB. The BLOB data type is used<br />
to stored binary data when the CLOB data type is used only for character data.<br />
There types require an additional set of functions defined in the server that<br />
provides an API for access. They are stored in an SBLOB space (smart BLOB<br />
space) in the database server.<br />
You must create an sbspace to store the BLOB types in the server. You can<br />
create them with following command:<br />
onspaces -c -S sbspace -p -o 0 -s 4000<br />
We change the catalog table in the stores database to use BLOB and CLOB<br />
instead of BYTE and TEXT using the following command:.<br />
ALTER TABLE catalog MODIFY (cat_descr TEXT, cat_descr CLOB, cat_picture BYTE,<br />
cat_picture BLOB)<br />
Example 9-16 shows how to insert and retrieve from data with SBLOB data<br />
types, that is BLOB and CLOB. The example shows how to insert the large data<br />
from files into a table and how to select the same data back into files in a local file<br />
system.<br />
Example 9-16 Smart large object sample<br />
}<br />
/*--- Get the BLOB from the database back ---*/<br />
$query=$dbc->query("SELECT * FROM catalog where stock_num=302 and<br />
manu_code='KAR' ");<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" select blobs failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
$count=1;<br />
$row=$query->fetch(PDO::FETCH_ASSOC);<br />
while ( $row ) {<br />
$file = fopen ("README.$count.txt","w");<br />
$test=fread($row["CAT_DESCR"],100000);<br />
while($test) {<br />
fwrite ( $file, $test) ;<br />
$test=fread($row["CAT_DESCR"],100000);<br />
}<br />
fclose ( $file);<br />
$file = fopen ("PICTURE.$count.jpg","w");<br />
$test1=fread($row["CAT_PICTURE"],100000);<br />
while($test1) {<br />
fwrite ( $file, $test1) ;<br />
$test1=fread($row["CAT_PICTURE"],100000);<br />
}<br />
fclose ( $file);<br />
$count++;<br />
$row=$query->fetch(PDO::FETCH_ASSOC);<br />
}<br />
?><br />
Using TEXT and BYTE (BLOB data types) is similar to SBLOB types. Users can<br />
try Example 9-16 on page 315 and Example 9-17 using TEXT and BYTE instead<br />
of CLOB and BLOB respectively.<br />
The BLOB data to be inserted can be read from the file without placing it in a<br />
variable first, which simplifies the code as shown in Example 9-17.<br />
Example 9-17 Insert BLOB with file<br />
$stmt= $dbc->prepare("INSERT INTO catalog VALUES (0,302,'KAR',?,?,'All sports<br />
Goods')");<br />
$file = fopen ("C:\README.txt","r");<br />
$file1 = fopen ("C:\picture.jpg","r");<br />
$stmt->bindParam(1,$file, PDO::PARAM_LOB);<br />
$stmt->bindParam(2,$file1, PDO::PARAM_LOB);<br />
$stmt->execute();<br />
fclose($file);<br />
fclose($file1);<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" execute insert blobs failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
/*---try to get the BLOB from the database back ---*/<br />
$query=$dbc->query("SELECT * FROM catalog where stock_num=302 and<br />
manu_code='KAR' ");<br />
$error=$dbc->errorInfo();<br />
if ( $error["1"]) {<br />
printf(" select blobs failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
$count=1;<br />
$row=$query->fetch(PDO::FETCH_ASSOC);<br />
while ( $row ) {<br />
$file = fopen ("C:\README.$count.txt","w");<br />
$test=fread($row["CAT_DESCR"],100000);<br />
while($test) {<br />
fwrite ( $file, $test) ;<br />
$test=fread($row["CAT_DESCR"],100000);<br />
}<br />
fclose ( $file);<br />
$file = fopen ("C:\PICTURE.$count.jpg","w");<br />
$test1=fread($row["CAT_PICTURE"],100000);<br />
while($test1) {<br />
fwrite ( $file, $test1) ;<br />
$test1=fread($row["CAT_PICTURE"],100000);<br />
}<br />
fclose ( $file);<br />
$count++;<br />
$row=$query->fetch(PDO::FETCH_ASSOC);<br />
}<br />
?><br />
Chapter 9. Working with PHP 317
In Example 9-17 on page 316, the data was selected as a stream in string data<br />
type. Example 9-18 shows how to bind variables to a SELECT. The data is<br />
placed in the variables by column.<br />
Example 9-18 Select BLOB with bind<br />
printf(" prepare update blob columns failed with %s \n",$error["1"]);<br />
exit(1);<br />
}<br />
$descr="This is an PDO descr clob text";<br />
$file= fopen ("C:\picture1.jpg","r");<br />
$stmt->bindParam(1, $descr);<br />
$stmt->bindParam(2, $file);<br />
$stmt->execute();<br />
$error=$dbc->errorInfo();<br />
?><br />
9.3.4 Working with PHP extensions<br />
In addition to the PDO drivers for the PHP PDO extension, there are two PHP<br />
extensions that allow you to connect to an <strong>Informix</strong> database server:<br />
► PHP_INFORMIX<br />
► <strong>IBM</strong>_DB2<br />
These extensions provide a set of additional PHP functions to work with an<br />
<strong>Informix</strong> database. In addition to the normal create, read, update, and write<br />
database operations, they also offer extensive access to the database metadata.<br />
Table 9-1 lists a few of the functions that are included in the <strong>Informix</strong> PHP<br />
extensions as examples.<br />
Table 9-1 PHP extension functions<br />
Function Description<br />
ifx_connect() Opens <strong>Informix</strong> Server connection<br />
ifx_fieldproperties() Lists of SQL field properties<br />
ifxus_open_slob() Opens an SLOB object<br />
ifx_create_blob() Creates an BLOB object<br />
ifx_fetch_row() Gets row as an associative array<br />
ifx_query() Sends <strong>Informix</strong> query<br />
db2_connect() Returns a connection to a database<br />
db2_client_info() Returns information describing DB2 database client<br />
db2_primary_keys() Returns a result set listing primary keys for a table<br />
db2_special_columns() Returns a result set listing the unique row identifier<br />
columns for a table<br />
Chapter 9. Working with PHP 319
Function Description<br />
db2_bind_param() Binds a PHP variable to an SQL statement parameter<br />
db2_commit() Commits a transaction<br />
For a complete list of all the functions that are implemented in the<br />
PHP_INFORMIX and <strong>IBM</strong>_DB2 extensions, refer to:<br />
http://www.php.net/manual/en/ref.ifx.php<br />
http://www.php.net/manual/en/ref.ibm-db2.php<br />
The PHP_INFORMIX extension<br />
A PHP script can connect to an <strong>Informix</strong> database using the ifx_connect()<br />
function that is provided in the PHP_INFORMIX extension.<br />
Example 9-20 shows a simple PHP script that tests connectivity with the<br />
PHP_INFORMIX extension.<br />
Example 9-20 The connect.php script<br />
C:\work>type connect.php<br />
<br />
C:\work>php connect.php stores_demo informix password<br />
Connection succeeded.<br />
C:\work><br />
You can use functions such as ifx_query() and ifx_affected_rows() to run<br />
SQL statements and to retrieve the number of rows affected. Example 9-21<br />
demonstrates how to delete a row from the state table using the ifx_query()<br />
function.<br />
Example 9-21 The delete.php script<br />
C:\work>type delete.php<br />
Deleted 1 records<br />
C:\work><br />
The application can select data from an <strong>Informix</strong> database using the<br />
ifx_prepare() and ifx_fetch_row() functions.<br />
Example 9-22 shows how to select the first three rows from the state table using<br />
a prepared statement and the ifx_fetch_row() function.<br />
Example 9-22 The select.php script<br />
C:\work>cat select.php<br />
<br />
C:\work>php select.php<br />
code = AK sname = Alaska<br />
code = HI sname = Hawaii<br />
code = CA sname = California<br />
C:\work><br />
For more information and examples regarding the PHP_INFORMIX extension,<br />
refer to:<br />
http://www.php.net/manual/en/book.ifx.php<br />
Chapter 9. Working with PHP 321
The <strong>IBM</strong>_DB2 extension<br />
This PHP extension provides access to <strong>IBM</strong> Data Servers, including <strong>IBM</strong> <strong>Informix</strong><br />
and <strong>IBM</strong> DB2.<br />
In the same way as the PDO driver, PDO_<strong>IBM</strong>, the <strong>IBM</strong>_DB2 extension requires<br />
the <strong>IBM</strong> CLI driver to communicate with the database server. The <strong>IBM</strong> CLI driver<br />
is included as part of the <strong>IBM</strong> Data Server Driver for ODBC and CLI package.<br />
This PHP extension provides functions such as db2_connect(), db2_exec(), and<br />
db2_server_info() that you can use to perform typical tasks against an <strong>Informix</strong><br />
database server.<br />
Example 9-23 demonstrates how to use the db2_connect() and<br />
db2_server_info() functions to retrieve metadata information from the database.<br />
Example 9-23 The connect_ibm.php script<br />
C:\work>type "C:\Documents and Settings\Administrator\db2cli.ini"<br />
[dsc_dsn]<br />
Protocol=TCPIP<br />
Port=9089<br />
Hostname=kodiak<br />
Database=stores_demo<br />
C:\work>cat connect_ibm.php<br />
<br />
C:\work>php connect_ibm.php<br />
Connection succeeded.<br />
Database name = IDS/NT64<br />
Datbase version = 11.50.0000<br />
C:\work><br />
322 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The <strong>IBM</strong>_DB2 extension can use the connection details in the db2cli.ini<br />
configuration file. Refer to the 3.2.2, “<strong>IBM</strong> Data Server Driver for ODBC and CLI”<br />
on page 70 for more information regarding the db2cli.ini configuration file.<br />
You can use several functions to select data from the database. Example 9-24<br />
shows how to use the db2_fecth_object() function to retrieve the first three rows<br />
of the state table as a PHP object.<br />
Example 9-24 The select_ibm.php script<br />
C:\work>cat select_ibm.php<br />
<br />
C:\work>php select_ibm.php<br />
Connection succeeded.<br />
AK, Alaska<br />
HI, Hawaii<br />
CA, California<br />
C:\work><br />
You can find a full description of the <strong>IBM</strong>_DB2 extension at:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9/topic/com.ibm.db2.udb.apdv.p<br />
hp.doc/doc/t0023132.htm<br />
Chapter 9. Working with PHP 323
9.3.5 Exception handling<br />
Error handling or exception handling is important for all applications, including<br />
web-based applications. Consider an <strong>Informix</strong> PHP application that shows the<br />
following error message while the user is using the application:<br />
Warning: odbc_connect(): SQL error: [unixODBC][<strong>Informix</strong>][<strong>Informix</strong><br />
ODBCDriver][<strong>Informix</strong>]User (informix) password “123456” is not able to<br />
connect for the database server, Server is down, SQL state 28000 in<br />
SQLConnect in /usr/local/apache2/htdocs/odbc/error/error.php on line 4<br />
In this case, fail in exception handling creates a security exposure. In the this<br />
section, we describe many aspects related to exception handling using <strong>Informix</strong><br />
PDO.<br />
PHP 5 has introduced, as a part of the new object-oriented programming<br />
interface, the exception handling mechanism that is already used for other<br />
programming languages. We strongly suggest that you consider exceptions for<br />
the usage of <strong>Informix</strong> PDO. Additionally, you can advance this interface by<br />
creating, throwing, and catching your own exceptions.<br />
In comparison with the procedural-oriented interface, <strong>Informix</strong> PDO defines the<br />
following methods of expressing an error in a database environment:<br />
► Exceptions raised internally by the PDO based on an error condition<br />
► Database-generated errors, which can be captured and handled by calling the<br />
PDO class errorInfo() or errorCode() function<br />
Example 9-25 shows two exceptions raised by <strong>Informix</strong> PDO functions. One<br />
exception is a connection request to the database that failed because the<br />
specified database does not exist. The other exception is an attempt to start a<br />
transaction twice.<br />
Example 9-25 Without exception handling sample<br />
<br />
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE=HY000,<br />
SQLDriverConnect: -329 [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]Database not<br />
found or no system permission.' in C:\Program<br />
Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php:2 Stack trace: #0 C:\Program<br />
Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php(2): PDO->__construct('informix:;<br />
data...', 'informix', '123456') #1 {main} thrown in C:\Program<br />
Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php on line 2<br />
324 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Fatal error: Uncaught exception 'PDOException' with message 'There is already<br />
an active transaction' in C:\Program<br />
Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php:4 Stack trace: #0 C:\Program<br />
Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php(4): PDO->beginTransaction() #1<br />
{main} thrown in C:\Program Files\OpenAdmin\Apache_2.2.4\htdocs\excep1.php on<br />
line 4<br />
If these exceptions are not caught and processed, the application terminates.<br />
Example 9-26 shows the use of the basic exception handler provided by PHP 5<br />
to cover these errors. After an exception is caught, the action that is taken<br />
depends on where the error originates. For example, if the error occurs in<br />
connecting to the database phase and the application cannot continue, the action<br />
is to generate an “out of order” web page with contact details. If the error is a<br />
minor database error, logging the error and retrying the activity is an appropriate<br />
action.<br />
Example 9-26 Simple exception handle code<br />
<br />
Output:<br />
Error: There is already an active transaction.<br />
Chapter 9. Working with PHP 325
The error information that is generated when executing the SQL statements in<br />
the database server is different from the exceptions that are generated by the<br />
<strong>Informix</strong> PDO extension. You can use the <strong>Informix</strong> PDO errorInfo() function to<br />
capture the status of the last executed SQL statement in the database. This<br />
function returns an array with the following elements:<br />
► The SQLSTATE<br />
► The SQLCODE<br />
► The error message<br />
For the details about the meaning of the codes, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.esqlc.doc/s<br />
ii-11-40709.htm#sii-11-40709<br />
The errorCode() function is available to retrieve the SQL statement status. This<br />
function returns only the SQLSTATE information. Example 9-27 shows how to<br />
use the errorInfo() function and its output.<br />
Example 9-27 ErrorInfo() sample<br />
<br />
Output:<br />
Array<br />
(<br />
[0] => 42S02<br />
[1] => -206<br />
[2] => [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]The specified table<br />
(nonexistingtable) is not in the database. (SQLPrepare[-206] at at<br />
ext\pdo_informix\informix_driver.c:118)<br />
)<br />
In addition to using the generic exceptions provided by the PHP 5, you can<br />
extend the exception class of your own exceptions. For example, you can define<br />
different severities for SQL errors. Critical database errors are, for example tables<br />
326 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
that do not exist or the database connections, cannot be established. The<br />
application cannot continue with these critical errors. Non-critical errors, such as<br />
locking errors, can be handled by a retry.<br />
Example 9-28 demonstrates how to define your own exceptions with PHP 5 and<br />
<strong>Informix</strong> PDO. This example extends the standard exception class with two new<br />
database exception classes and, depending on the severity, different actions are<br />
taken.<br />
Example 9-28 Custom exceptions<br />
9.3.6 Troubleshooting<br />
printf("NonCritError!: %s \n",$ncde->getMessage()) ;<br />
} catch (PDOExecption $ncde) {<br />
printf("Error!: %s \n",$e->getMessage()) ;<br />
exit;<br />
}<br />
?><br />
Output:<br />
CritError!: [<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]The specified table (carr) is not<br />
in the database. (SQLPrepare[-206] at ext\pdo_informix\informix_driver.c:131)<br />
The following common errors can occur in setting the database connectivity:<br />
► Missing environment variable setting<br />
Setting the environment variable INFORMIXDIR is required for starting the<br />
Apache. If this variable is not set properly, the database connection also fails.<br />
Example 9-29 shows the error message when you have improper<br />
INFORMIXDIR set. The message occurs at the first line of any PHP program<br />
which is generally the connection string. If you get this message, verify that<br />
the INFORMIXDIR setting is correct.<br />
Example 9-29 Unspecified error<br />
Error!: SQLSTATE=HY000, SQLDriverConnect: -23101<br />
[<strong>Informix</strong>][<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]Unspecified System Error =<br />
-23101.<br />
► Mismatched settings<br />
Another important item affecting the connectivity is the setup of the <strong>Informix</strong><br />
runtime environment. <strong>Informix</strong> Connect or Client SDK provides the<br />
connectivity at run time. If the settings between the environment variables and<br />
the sqlhosts do not match, you receive messages similar to the one shown in<br />
Example 9-30.<br />
Example 9-30 Wrong connection information<br />
/usr/local/bin/php pdoconnect.php<br />
Error!: SQLSTATE=HY000, SQLDriverConnect: -25555<br />
[<strong>Informix</strong>] [<strong>Informix</strong> ODBC Driver][<strong>Informix</strong>]Server ol_svr_custom is not<br />
listed as a dbserver name in sqlhosts.<br />
328 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 10. User-defined routines<br />
In this chapter, we how to create and use user-defined routines (UDRs). A UDR<br />
is a routine that you create that can be invoked in an SQL statement by the<br />
database server or from another UDR. A routine is a collection of program<br />
statements that perform a particular task. By understanding UDRs, you can take<br />
the next step and extend the database server, either a little bit or in steps that<br />
lead to something bigger, such as a bladelet or DataBlade module.<br />
This chapter includes the following topics:<br />
► An overview of UDRs and database extensions<br />
► Developing UDRs<br />
► DataBlades and bladelets<br />
10<br />
Extending the database server requires an understanding of the components<br />
that are required to implement the extension. An extension can be as simple as<br />
one UDR or as complex as a new data type with many supporting routines. We<br />
first discuss UDRs and user-defined types (UDTs) to provide the scope for what<br />
is needed. Then we provide examples.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 329
10.1 An overview of UDRs and database extensions<br />
<strong>IBM</strong> <strong>Informix</strong> database servers have the built-in ability to retrieve, store,<br />
manipulate, and sort a number of standard data types, some of which are unique<br />
to <strong>Informix</strong> servers. A developer can choose to modify or extend the built-in data<br />
types and to modify or extend how various operations work with the resulting new<br />
data type extensions.<br />
You can extend the data types using the following methods:<br />
► Extend operations that are used to process built-in data types.<br />
► Create complex data types based on built-in data types.<br />
► Create UDTs, both distinct and opaque data types.<br />
► Create new operations to process extended data types.<br />
Database extensions and extended data types allow the developer to make<br />
customized routines and functions transparent, because you have the capability<br />
to actually build it into the database server. The transparency is visible as:<br />
► Better performance through optimized routines, faster queries, and reduced<br />
network traffic.<br />
► Simpler applications in streamlined code and easy to upgrade applications.<br />
► Transaction control with automatic recovery, backup, and rollback capability<br />
that is provided by the server.<br />
► Scalability because database extensions scale automatically with the<br />
database size and user count.<br />
In the context of discussing UDRs and database extensions, we also mention<br />
DataBlade modules. A DataBlade module is a software package that extends the<br />
functionality of the <strong>IBM</strong> <strong>Informix</strong> database server. Each package includes SQL<br />
statements and supporting code written in an external language or in Stored<br />
Procedure Language. A DataBlade module enables the same level of support for<br />
new data types as the database server provides for built-in data types.<br />
DataBlade modules can also use SQL queries or the DataBlade API to access<br />
data types and routines in other DataBlade modules.<br />
Note: Extended Parallel Server and Standard Engine support stored<br />
procedures but not UDRs.<br />
330 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
10.1.1 Considerations for UDRs<br />
A UDR can be written using the <strong>IBM</strong> <strong>Informix</strong> Stored Procedure Language (SPL)<br />
or with an external language. A routine written with SPL is simple to use and<br />
easy to implement. An SPL-based UDR has flow-control extensions (conditional<br />
clauses and while loops) and works with SQL statements. After the routine is<br />
created and is ready to use, the database server parses, optimizes, and stores it<br />
in system catalog tables so that it is ready to execute.<br />
The system catalog tables are used to keep track of the information the database<br />
server uses to manage the database server. UDR-related information is stored in<br />
a small group of catalog tables that provide information about the UDR layouts.<br />
UDRs include the following common catalog tables:<br />
► sysprocedures table<br />
Used to track the name and owner and to indicate whether the UDR is a<br />
user-defined function or a user-defined procedure (functions return values but<br />
procedures do not).<br />
► sysprocauth table<br />
Tracks who can execute the procedure.<br />
► sysprocbody table<br />
Contains the actual code for the SPL routines.<br />
UDRs can also be written with an external language. The body of an<br />
external-language routine allows language-based operations such as flow control<br />
and looping, while also using special <strong>Informix</strong> library calls to access the database<br />
server.<br />
The database server stores information for external-language routines in the<br />
following system catalog tables:<br />
► sysprocedures table<br />
The information kept in this table is same as the SPL UDR.<br />
► sysroutinelangs table<br />
This table tracks the language information.<br />
► syslangauth table<br />
This table tracks the users of the server who can use the particular external<br />
language.<br />
You need to use an appropriate compiler to parse and compile an<br />
external-language routine into an executable format. We discuss this with<br />
Chapter 10. User-defined routines 331
10.1.2 About UDRs<br />
specific examples of the language and API methods that are available for writing<br />
these extensions.<br />
The external languages that can be used are:<br />
► C<br />
To execute SQL statements in C UDRs, you must use the DataBlade API, and<br />
you cannot use ESQL/C. To write routines in C, you need a C compiler. YOu<br />
can find additional information about writing UDRs in C in the <strong>IBM</strong> <strong>Informix</strong><br />
DataBlade API Programmer’s Guide, Version 11.50, SC23-9429, and <strong>IBM</strong><br />
<strong>Informix</strong> DataBlade API Function Reference, Version 11.50, SC23-9428.<br />
► Java<br />
To write Java routines, you must use <strong>IBM</strong> <strong>Informix</strong> database server with<br />
J/Foundation and with the Java Development Kit (JDK) to compile your Java<br />
routines. You can find additional information about how to write Java UDRs in<br />
J/Foundation <strong>Developer's</strong> Guide, Version 10.0, G251-2291.<br />
Table 10-1 lists the UDR types that are supported by SPL, C, and Java in <strong>IBM</strong><br />
<strong>Informix</strong> database server.<br />
Table 10-1 Supported UDR tasks by languages<br />
UDR task SPL routines C routines Java routines<br />
Cast function Yes Yes Yes<br />
Cost function No Yes No<br />
End-user routine Yes Yes Yes<br />
Iterator function No Yes Yes<br />
Negator function Yes Yes Yes<br />
Opaque data type support No Yes Yes<br />
Parallelizable UDR No Yes Yes<br />
Statistics function No Yes Yes<br />
Selectivity function No Yes No<br />
User-defined aggregate Yes Yes Some<br />
Operator function Yes Yes Yes<br />
Operator-Class function Yes Yes Yes<br />
332 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The following terms are used in this table:<br />
► Cast: A routine to convert from one data type to another. Built-in data types<br />
have automatic casts between most data types. For UDTs, casting must be<br />
defined from scratch.<br />
► Cost function: A routine that informs the optimizer of cost factors for execution<br />
of a particular UDR.<br />
► End-user function: A routine that is used to encapsulate multiple SQL<br />
statements into one function.<br />
► Iterator function: A routine that is designed to return to its calling SQL<br />
statement several times, returning a value each time.<br />
► Negator function: A routine used for a NOT condition that involves a Boolean<br />
UDR.<br />
► Opaque data type support: When you create a new data type, you must<br />
provide several basic support functions for your UDT. The following functions<br />
are required:<br />
– Text input and output routines<br />
– Binary send and receive routines<br />
– Text import and export routines<br />
– Binary import and export routines<br />
► Parallel UDR: A routine that can run in parallel within parallel queries.<br />
► Statistics function: A routine to create distribution statistics for a UDT.<br />
► Selectivity function: A routine to determine the percentage of rows for which a<br />
Boolean UDR is expected to return true.<br />
► User-defined aggregate: A SQL invoked routine that takes values selected<br />
and returns information about those rows (a summarizing method).<br />
► Operator function: A routine that is used within expressions with a symbol,<br />
such as “+,-,,=”. Built-in data type operators cannot be extended. All UDTs<br />
require some operators to function within an SQL context.<br />
► Operator-Class function: A set of operators that the server associates with<br />
how to build an access method (that is, an index), how to arrange values in<br />
the access method, how to select values based on operator function, and<br />
ways to allow the query optimizer to consider using the access method to<br />
return results for a query.<br />
For more details about any of these functions and for details about functionality<br />
that we do not discuss here, see <strong>IBM</strong> <strong>Informix</strong> User-Defined Routines and Data<br />
Types <strong>Developer's</strong> Guide, Version 11.50, SC23-9438.<br />
Chapter 10. User-defined routines 333
Invoking a UDR<br />
You can invoke a UDR implicitly or explicitly. Implicit invocation is the result of an<br />
operator function, an implicit cast, or some type of query processing. For this<br />
book, we mainly discuss explicit invocation. You can use either EXECUTE<br />
PROCEDURE or EXECUTE FUNCTION statements to run a UDR.<br />
When the database server executes an SQL statement that contains a UDR, it<br />
loads the UDR executable code as a shared-object into memory. It determines<br />
which shared-object file to load from the externalname column of the row in the<br />
sysprocedures system catalog table that describes the UDR. The sysprocedures<br />
entry is created when you register the UDR, as a result of the CREATE<br />
FUNCTION or CREATE PROCEDURE statement.<br />
In more general terms, when you invoke a UDR, the database server parses the<br />
statement into syntactic parts, call the system catalog to resolve the routine<br />
parts, generate a query plan, and then execute the query. If the query involves<br />
more than one database, each database requires that all the UDRs and UDTs<br />
must be registered in the participating databases.<br />
10.1.3 Considerations for extending data types<br />
A significant aspect of UDR is the support for an extended data type. <strong>IBM</strong><br />
<strong>Informix</strong> database servers have several built-in data types. Why do we need<br />
more?<br />
An extended data type is a new data type that has different core properties, new<br />
functionality, and new operator methods that go beyond a basic data type.<br />
As an example, consider a datetime data type. Imagine, as a programmer if you<br />
had to add “a day and a half” to a specific datetime event. Intuitively, we<br />
recognize that the value “‘day and a half” is an interval of time, but the database<br />
serve does not recognize this construct as an interval. We need to convert it<br />
manually to a usable interval, and then add it to a datetime value.<br />
With a UDR, an unconventional interval name such as this can be passed<br />
through a function, which translates it to an “normal” interval automatically,<br />
applies the addition operation, and returns a datetime value with the result. To do<br />
this, we apply a functional behavior change, which extends the data type. The<br />
result of processes such as this can simplify a repeating data task and make the<br />
development task easier.<br />
334 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
A quick review of the data type hierarchy is useful. Figure 10-1 shows a summary<br />
of these data types.<br />
Figure 10-1 Hierarchy of data types in <strong>Informix</strong> servers<br />
The extended data types can be either UDR types or complex data types. We are<br />
especially interested in UDTs, which can be described as follows:<br />
► Distinct: Internally, this data type is stored the same as a source data type,<br />
but it is overlaid with different casts and functions defined beyond the basic<br />
source type. The server sees distinct types as different from the source type.<br />
It is necessary to tell the server:<br />
– Source data type information and how the internal structure is defined.<br />
– The functions of how this data type interacts with its internal structure.<br />
– The operations that are valid with this distinct data type.<br />
– Any secondary access methods on how to handle this type.<br />
– Cast functions to move data in and out of the distinct type are automatic.<br />
► Opaque: A fundamental, user-defined data type. It cannot be broken into<br />
smaller pieces, although it can serve as the building block for other data<br />
types. The internal structure of the opaque data type is invisible to the server.<br />
When you define and use an opaque type, the developer must provide all of<br />
the following:<br />
– How the internal structure is defined.<br />
– The functions that enable routines to interact with its internal structure.<br />
– The operations that are valid with this distinct data type.<br />
– Any secondary access methods on how to handle this type.<br />
– Cast functions to move data in and out of the distinct type need to be<br />
provided.<br />
Chapter 10. User-defined routines 335
10.2 Developing UDRs<br />
To develop a UDR, plan it first and then write the routine. We follow the<br />
recommendations given in the UDR and Data Types Developers Guide for<br />
planning our routines:<br />
► Use a sensible name.<br />
► Avoid modal arguments (an argument in the function determines how the<br />
function will work).<br />
► Always declare routine parameter data types.<br />
► Declare the type that is returned when the routine is returning a value<br />
The source for an external routine resides in a separate text file. To prepare UDR<br />
source code:<br />
► You will compile the C-language UDR and store the executable version in a<br />
shared-object (.so or .o on UNIX) file.<br />
► Compile the Java-language UDR and store the executable version in a .jar<br />
file.<br />
You must install shared object files and .jar files on all database servers that<br />
need to run the UDRs, including database servers involved in Enterprise<br />
Replication (ER) and High-Availability Data Replication (HDR). The shared object<br />
files and .jar files need to be installed under the same absolute path name.<br />
Tip: Although not required, use the DataBlade Developer’s Kit (DBDK) to help<br />
write UDRs is advantageous. DBDK books and software can help enforce<br />
standards that facilitate migration between different versions of the database<br />
server. Because external-language routines are external to the database, be<br />
aware that you must compile the UDR source code and store it where the<br />
database server can access it when the routine is invoked.<br />
For information about C UDRs, refer to <strong>IBM</strong> <strong>Informix</strong> DataBlade API<br />
Programmer’s Guide, Version 11.50, SC23-9429 and <strong>IBM</strong> <strong>Informix</strong> DataBlade<br />
API Function Reference, Version 11.50, SC23-9428. For information about Java<br />
UDRs, refer to the J/Foundation <strong>Developer's</strong> Guide, Version 10.0, G251-2291.<br />
10.2.1 UDR examples in SQL<br />
You can use <strong>Informix</strong> Stored Procedure Language (SPL) statements to write<br />
routines and then store these SPL routines in the database. SPL extends SQL<br />
and helps to reduce SQL coding by minimizing the visible code in large SQL<br />
336 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
operations. It has the advantage that it is run as a server-side routine, the<br />
executable code stays inside the engine, and it is optimized only as needed. The<br />
end result is lower application startup costs and better performance. As an SQL<br />
extension, SPL can do flow control, such as looping and branching. SPL routines<br />
can also execute routines written in C or other external languages, and other<br />
UDR routines can execute SPL routines.<br />
In this section, we provide examples of UDRs written in SQL. As we do so,<br />
consider the following rules for SQL UDRs:<br />
► If you use any parameters, they must be declared as built-in or user-defined<br />
data types.<br />
► An SPL routine does not have access to the user state of its execution<br />
sequence. If the routine is going to be called more than once and if you want<br />
to retain information about previous executions in the transaction, use an SPL<br />
routine that states WITH RESUME as a part of the RETURN statement for<br />
multiple executions of the same SPL routine within the same routine<br />
sequence.<br />
► When an SPL routine is executed, the parameters (also known as the<br />
dependency list) is checked. If it is determined that an item in the dependency<br />
list needs reoptimization, optimization occurs at this point. If an item needed<br />
in the execution of the SQL statement is missing (for example, a column or<br />
table is dropped), an error is returned.<br />
► UDRs can be overloaded, meaning that a function can have more than one<br />
way to operate, depending on the list of data types that are provided as<br />
parameters. There is a precedence hierarchy to decide how the parameter list<br />
is executed. A precedence hierarchy to decide the execution sequence of the<br />
parameters can be important if you have more than one UDR with the same<br />
name but a different parameter list. For more information, see <strong>IBM</strong> <strong>Informix</strong><br />
User-Defined Routines and Data Types <strong>Developer's</strong> Guide, Version 11.50,<br />
SC23-9438.<br />
Using a stored procedure method for a UDR is simple, as long as you can<br />
recognize the incoming and outgoing parameters properly. The incoming<br />
parameters, which are provided in the function definition, must have data types<br />
defined when the parameter is first created and declared. The parameter values,<br />
which are to be returned from the function, also should have declared data types.<br />
By following this general rule, you can avoid many of the initial problems you can<br />
get with SPL UDRs.<br />
For the routines with SPL, we use the <strong>Informix</strong> stores sample database, defined<br />
as stores@demo_on.<br />
Chapter 10. User-defined routines 337
One variable in, one result out<br />
For this example, we want a function that provides a count of all the orders that<br />
are received in a numerical month (N) from the orders table of the stores<br />
database. This example demonstrates what happens when selecting an<br />
aggregating value, once.<br />
Example 10-1 shows the function code and ways to invoke the function to get<br />
output.<br />
Example 10-1 CREATE FUNCTION new_orders (month_num INT)<br />
CREATE FUNCTION new_orders ( month_num INT )<br />
RETURNING INT ;<br />
DEFINE nrows INT;<br />
SELECT COUNT(order_date) INTO nrows FROM orders<br />
WHERE month(order_date)=month_num;<br />
RETURN nrows;<br />
END FUNCTION;<br />
# -- Execute it as a function:<br />
EXECUTE FUNCTION new_orders(5);<br />
(expression)<br />
7<br />
# -- Execute it as a select statement.<br />
# -- Note that we have to force “first 1” as a criteria so we can assure we are<br />
# -- only getting one value. Otherwise we would get an error.<br />
SELECT FIRST 1 new_orders(5) FROM orders;<br />
(expression)<br />
7<br />
One variable in, several results out<br />
Example 10-2 performs three SELECT statements, and each statement does an<br />
aggregation, returning one value from each select. It returns three values and<br />
labels each value in the returned result. This example shows how to aggregate<br />
three separately selected value returns in one call and still not use a cursor.<br />
Example 10-2 Selecting three and returning three<br />
CREATE FUNCTION new_orders ( month_num INT )<br />
RETURNING INT as TotalOrders,INT as ShippedOrders,INT as Backorders;<br />
DEFINE nrows INT;<br />
DEFINE mrows INT;<br />
DEFINE b_rows INT;<br />
338 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
SELECT COUNT(ship_date) INTO mrows FROM orders<br />
WHERE month(ship_date)=month_num;<br />
SELECT COUNT(order_date) INTO nrows FROM orders<br />
WHERE MONTH(order_date)=month_num;<br />
SELECT COUNT(order_date) INTO b_rows FROM orders<br />
WHERE MONTH(order_date)=month_num AND ship_date IS NULL;<br />
RETURN nrows,mrows,b_rows;<br />
END FUNCTION;<br />
# -- Since we are returning three values with labels, calling the execute<br />
# -- function is the most appropriate way to make our SQL call.<br />
EXECUTE FUNCTION new_orders(5);<br />
totalorders shippedorders backorders<br />
7 3 1<br />
Using the WITH RESUME clause to return a cursor result<br />
Example 10-3 collects better details for the new orders. To return a detailed<br />
listing of orders made, it uses the WITH RESUME clause in the function. In this<br />
case, we assume (and expect) more than one row is returned. So, the WITH<br />
RESUME clause is needed.<br />
Example 10-3 A WITH RESUME ROUTINE that uses a cursor<br />
CREATE FUNCTION new_orders ( month_num INT )<br />
RETURNING INT as Num, INT as ord_num;<br />
DEFINE ord_num INT;<br />
DEFINE Num INT;<br />
LET Num=1;<br />
FOREACH cursor1 FOR<br />
SELECT order_num INTO ord_num FROM orders<br />
WHERE month(order_date)=month_num<br />
RETURN Num, ord_num WITH RESUME;<br />
LET Num=Num+1;<br />
END FOREACH;<br />
END FUNCTION;<br />
EXECUTE FUNCTION new_orders(5);<br />
------num ord_num-----<br />
1 1001<br />
2 1002<br />
3 1003<br />
4 1004<br />
Chapter 10. User-defined routines 339
10.2.2 UDRs in Java<br />
5 1005<br />
6 1006<br />
7 1007<br />
Multi-table select using a cursor<br />
Example 10-4 performs a multi-table join with a summary expression return and<br />
a group by. We ask for all the orders placed in a specific month, the name of the<br />
person placing the order, and the total amount for each order.<br />
Example 10-4 A multi-table select using a cursor<br />
CREATE FUNCTION new_orders ( month_num INT )<br />
RETURNING INT as Ord_Num, char(15) as ord_fname,<br />
char(15) as ord_lname, money(8,2) as Amt;<br />
DEFINE ord_num INT;<br />
DEFINE ord_fname char(15);<br />
DEFINE ord_lname char(15);<br />
DEFINE Amt money(8,2);<br />
FOREACH cursor1 FOR<br />
SELECT o.order_num, c.fname,c.lname,sum(i.quantity*i.total_price)<br />
INTO ord_num, ord_fname, ord_lname, Amt<br />
FROM orders o, customer c, items i<br />
WHERE month(order_date)=month_num<br />
AND o.customer_num=c.customer_num<br />
AND i.order_num=o.order_num<br />
group by o.order_num,c.fname,c.lname<br />
RETURN ord_num, ord_fname, ord_lname, Amt WITH RESUME;<br />
END FOREACH;<br />
END FUNCTION;<br />
You must distinguish between JDBC and Java Virtual Machine (JVM)<br />
applications. You can use JDBC to write stand-alone applications. If you want to<br />
connect with to databases that support Java, you typically write stand-alone<br />
JDBC applications, because these applications require specific driver methods to<br />
communicate with other database servers. When you write a Java UDR, you<br />
must use the <strong>IBM</strong> <strong>Informix</strong> JDBC Driver, which is based on the JDBC 2.0 API.<br />
The generated code is processed by a JVM that runs as a process inside <strong>IBM</strong><br />
<strong>Informix</strong> Server. The generated code (.jar file) is stored in an sbspace and can<br />
also refer to a .jar file in a storage location outside of the server.<br />
Java allows you to create UDRs, cast support functions, aggregates, and opaque<br />
data type support routines. However, Java routines cannot handle row or<br />
collection data types.<br />
340 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
For ordinary UDR with <strong>IBM</strong> <strong>Informix</strong> database servers, you can use the Java<br />
Developers Kit (version 1.5 at this writing). A JVM comes pre-installed with the<br />
<strong>IBM</strong> <strong>Informix</strong> database server (with J/Foundation). To confirm it is present, make<br />
sure that you have an existing directory path to the<br />
INFORMIXDIR/extend/kraratoa directory.<br />
Java-based UDR, when developed, is placed into a Java archive (.jar) file. The<br />
.jar file is stored inside an sbspace, or it can have additional supporting files on<br />
the file system. If the .jar file is large or perhaps proprietary, you can leave it on<br />
the file system. Store smaller .jar files or files that you might want to update<br />
frequently in the sbspace.<br />
Configuration<br />
Make sure that you use the JVM system that came with your server engine, and<br />
make sure the onconfig file and environment variables are set to accurate<br />
working paths. The environment settings for our testing setup had the following<br />
variables in the environment:<br />
JRE_HOME=/usr/lib/jvm/java/jre<br />
JAVA_BINDIR=/usr/lib/jvm/java/bin<br />
JAVA_HOME=/usr/lib/jvm/java<br />
SDK_HOME=/usr/lib/jvm/java<br />
JJDK_HOME=/usr/lib/jvm/java<br />
JAVA_ROOT=/usr/lib/jvm/java<br />
The JAVA_ROOT environment variable is mostly determined by the developer. It is<br />
the path to the file system directory where you are developing your .jar files:<br />
CLASSPATH=location_for_your_java_classes:.:.:.<br />
The INFORMIXDIR/etc/ONCONFIG file also has a small group of parameters that<br />
must have verified settings. The following parameters are standard:<br />
JVPJAVAHOME $INFORMIXDIR/extend/krakatoa/jre<br />
JVPHOME $INFORMIXDIR/extend/krakatoa<br />
JVPPROPFILE $INFORMIXDIR/extend/krakatoa/.jvpprops<br />
JVPJAVALIB /bin<br />
JVPJAVAVM jvm<br />
The JVPJAVALIB and JVPJAVAVM parameters in the onconfig file are important for<br />
development. The following parameter specifies the name of the log file to which<br />
Java problems are written (make sure the path exists):<br />
JVPLOGFILE $INFORMIXDIR/jvp.log<br />
If you are going to use external .jar files, you must add them to JVPCLASSPATH:<br />
JVPCLASSPATH $INFORMIXDIR/extend/krakatoa/krakatoa.jar:$INFORMIXDIR/extend/krakatoa/jdbc.jar<br />
Chapter 10. User-defined routines 341
Note: The paths for .jar files that are added to JVPCLASSPATH are visible<br />
only after you add them to the onconfig file and restart the database engine.<br />
Routine examples in Java<br />
In the examples that follow, we create our Java routines in /work/, which defines<br />
the UNIX directory that we use for our working CLASSPATH. You must change<br />
the directory path in some of the code expressions if you use a different directory.<br />
After you compile the code, the source code is not used for execution, but keep it<br />
in a safe place in case you want to improve it later.<br />
A function extension<br />
This example illustrates a simple way to extend functionality. This routine<br />
provides an SQL function to “multiply a value times 10.” The developing<br />
procedure is as follows:<br />
1. Create a simple class file named Times.java in the working JVPCLASSPATH<br />
directory. Example 10-5 shows the source code.<br />
Example 10-5 A Java function to multiply a value times 10<br />
/*Times.java */<br />
public class Times {<br />
public static int TimesTen(int x) {<br />
return x * 10;<br />
}<br />
}<br />
2. At a command prompt, compile the file using the following command:<br />
javac Times.java<br />
3. Compress it into a .jar file:<br />
jar cvf Times.jar Times.class<br />
As this runs, it shows output similar to the following:<br />
added manifest<br />
adding: Times.class(in = 245) (out= 187)(deflated 23%)<br />
4. Register the routine:<br />
Start dbaccess to connect to the database and run the following SQL to<br />
register and add the .jar file into our sbspace:<br />
execute procedure sqlj.install_jar ("file://work/Times.jar" , "Times_jar");<br />
Routine executed.<br />
342 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5. With the .jar file in storage and accessible, from dbaccess, we create a<br />
function that calls our routine in the .jar file. See Example 10-6.<br />
Example 10-6 Calling routine<br />
create function times_ten(value int) returning int<br />
with (class = "jvp")<br />
external name "Times_jar:Times.TimesTen"<br />
language JAVA;<br />
6. The procedure is ready to run. Test it in dbaccess:<br />
EXECUTE function times_ten(13)<br />
(expression)<br />
130<br />
For an alternate method, try:<br />
SELECT ship_charge, times_ten(ship_charge) X10 FROM orders;<br />
A Java UDR with two input parameters<br />
This example demonstrates how to create a Java routine with multiple input<br />
parameters. Java language and <strong>IBM</strong> <strong>Informix</strong> servers express decimals in<br />
different ways. This example creates a new sales tax function, Salestax, that<br />
includes a new Java function and the existing Times_ten function created in “A<br />
function extension” on page 342.<br />
We need to include an external standard Java library that has a math class. You<br />
might find that multiplying decimal values in Java is different than what you might<br />
be used to. In addition, Java uses a different naming convention than <strong>Informix</strong><br />
data types. If you get the class wrong or the library is incorrect, things simply do<br />
not work, and you will get errors.<br />
Chapter 10. User-defined routines 343
We use the following procedure to create the salestax function:<br />
1. Create a Java class called Tax.java, using an editor such as vi.<br />
Example 10-7 shows the source code.<br />
Example 10-7 The Tax.java class<br />
/*Tax.java*/<br />
import java.math.BigDecimal;<br />
public class Tax {<br />
public static BigDecimal salestax(BigDecimal x,BigDecimal xtax )<br />
{<br />
BigDecimal ratePlusOne = xtax.add(BigDecimal.valueOf(1));<br />
BigDecimal afterTax = x.multiply(ratePlusOne);<br />
afterTax = afterTax.setScale(2, BigDecimal.ROUND_HALF_UP);<br />
return (afterTax);<br />
}<br />
}<br />
2. Compile the file. At a command prompt, run the following command:<br />
javac Tax.java<br />
3. Compress the file, and add it to the existing .jar file (named Times.jar).<br />
jar cvf Times.jar Tax.class<br />
As this runs, it shows a line similar to the following:<br />
added manifest<br />
adding: Tax.class(in = 546) (out= 318)(deflated 41%)<br />
4. Add the updated .jar file to sbspace.<br />
For this example, we want to update an existing function that already exists in<br />
the sbspace .jar file. Because we cannot replace an existing function in a<br />
sbspace .jar file directly, update an sbspace .jar function, we must replace<br />
the entire .jar file. To do this, we must remove (drop) the sbspace .jar file,<br />
then replace it with an updated version from our working path.<br />
A .jar file in an sbspace must be empty to drop the .jar file. Drop all the<br />
UDRs in the .jar file to empty it. Otherwise, you receive the following error<br />
message:<br />
“Invalid jar removal. All dependent UDRs not dropped”.<br />
Our Times_jar file exists in the database. To drop the function, we start<br />
dbaccess, connect to the database, then run the following command:<br />
DROP FUNCTION times_ten;<br />
Now, we can remove the .jar file:<br />
EXECUTE PROCEDURE sqlj.remove_jar ("Times_jar");<br />
344 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
5. Add the updated .jar file into sbspace. Run the following SQL:<br />
EXECUTE PROCEDURE sqlj.install_jar ("file://work/Times.jar" , "Times_jar");<br />
6. The updated .jar file is now in sbspace storage. Re-create the dropped<br />
Times_ten function:<br />
create function times_ten(value int) returning int<br />
with (class = "jvp")<br />
external name "Times_jar:Times.TimesTen"<br />
language JAVA;<br />
7. Add the new function to call on the same .jar file:<br />
create function salestax(value decimal,xtax decimal)<br />
returning decimal(8,2)<br />
with (class = "jvp")<br />
external name "Times_jar:Tax.salestax"<br />
language JAVA;<br />
8. Now, can test the expanded function from dbaccess:<br />
EXECUTE FUNCTION salestax(250.00,.065);<br />
(expression)<br />
266.25<br />
As an alternative test using SQL, try:<br />
SELECT o.order_num,<br />
salestax (sum(i.quantity*i.total_price), .065) Amt_w_Tax<br />
FROM orders o, items i WHERE month(order_date)=5<br />
AND i.order_num=o.order_num group by o.order_num<br />
Creating a routine that uses external Java APIs<br />
This example demonstrates how to create a Java UDR that requires support from<br />
one or more Java APIs that lie outside the database server. The import<br />
references in the code indicate that an external Java API exists to help support<br />
the UDR. The references on the import list remain outside the database server.<br />
The JVM runs using the .jar file calls in the sbspace storage location and sends<br />
calls to the external Java functions based on the import reference in the .jar file.<br />
Note that the import reference does not have a full directory path. Any Java API<br />
or .jar file that is not residing in an sbspace is external to the instance. To help<br />
the server instance find external .jar files, you must supply all JAR API path<br />
locations in the onconfig file.<br />
Note: The onconfig file must be updated so the JVM knows the directory path<br />
for any supporting APIs. The full path location for the supporting Java file<br />
specified on an import list must be included in JVPCLASSPATH.<br />
Chapter 10. User-defined routines 345
For our example, JVPCLASSPATH is set to:<br />
/usr3/11.50/extend/krakatoa/krakatoa_g.jar:/usr3/11.50/extend/krakatoa/jdbc_g.j<br />
ar:/usr3/11.50/extend/krakatoa/jre/lib/rt.jar:/work/mailapi.jar:/work/activatio<br />
n.jar:/work/smtp.jar<br />
For our example to work, the engine needs the Java mail API classes. The files<br />
that are required are mailapi.jar, activation.jar, and smtp.jar, which are<br />
available at:<br />
http://java.sun.com/products/javamail/downloads/index.html<br />
To create the sendmail routine:<br />
1. Install the downloaded .jar files, and add .jar files with full path to<br />
JVPCLASSPATH.<br />
In this example, the paths are /work/mailapi.jar, /work/activation.jar,<br />
and /work/smtp.jar.<br />
2. Create the file MailClient.java as shown in Example 10-8. You have to<br />
update the italicized references in the example with your own information.<br />
Example 10-8 An SQL based sendmail() UDR<br />
---- MailClient.java --import<br />
javax.mail.*;<br />
import javax.mail.internet.*;<br />
import java.io.*;<br />
import java.util.Properties;<br />
public class MailClient<br />
{<br />
public static void send(String to, String text)<br />
{<br />
try<br />
{<br />
MailClient client = new MailClient();<br />
346 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Properties props = System.getProperties();<br />
props.put("mail.smtp.host", "smtp.server.com");<br />
Session session = Session.getDefaultInstance(props, null);<br />
Message message = new MimeMessage(session);<br />
message.setFrom(new InternetAddress("Name_showing@fromfield"));<br />
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));<br />
message.setSubject("Message from the database");<br />
message.setText(text);<br />
Transport.send(message);<br />
}<br />
catch(Exception e)<br />
{<br />
e.printStackTrace(System.out);
}<br />
}<br />
}<br />
---- MailClient.java ---<br />
3. Compile and compress your file:<br />
javac MailClient.java<br />
jar cvf MailClient.jar MailClient.class<br />
4. Install the .jar file into the sbspace:<br />
EXECUTE PROCEDURE sqlj.install_jar ("file://work/MailClient.jar" ,<br />
"MailClient_jar");<br />
Make sure JVPCLASSPATH is set as indicated in step 1.<br />
5. Create the sendmail procedure from dbaccess:<br />
CREATE PROCEDURE sendemail(to LVARCHAR, message LVARCHAR)<br />
WITH (class = "jvp")<br />
EXTERNAL NAME "MailClient_jar:MailClient.send"<br />
LANGUAGE JAVA;<br />
6. Test the procedure:<br />
EXECUTE PROCEDURE sendemail('dba@mybiz.com','Error deleting from table');<br />
Troubleshooting tips<br />
Sometimes you might have trouble getting a Java UDR to run. The following<br />
points of exposure for errors are possible:<br />
► At the time of a Java compile<br />
If you get an error here, the issue relates to a Java language problem, most<br />
likely resulting from syntax or a Java method. Consult a Java Programming<br />
Language Guide for assistance.<br />
► At the .jar installation point or later<br />
Check the jvp.log at the path in the onconfig file specified by the JVPLOG<br />
parameter. You should see no errors at the time of install, and no errors at run<br />
time. When you start the server instance the JVM starts, .jar files load into<br />
the process memory as needed. The MSGPATH file (online.log) often<br />
reveals the success or failure of JVM and .jar file loading. If the files cannot<br />
load, they cannot run.<br />
► UDR runtime errors<br />
If there are errors, study the Java error messages returned, and determine<br />
the cause for whatever did not resolve.<br />
Troubleshooting can be a trial and error approach. When you have an<br />
understanding of how the provided UDR examples work, do them over again,<br />
Chapter 10. User-defined routines 347
10.2.3 UDRs in C<br />
and break something in the Java code. Go through the example with slightly<br />
broken syntax and review the results. For example, in the BigDecimal code<br />
example, change BigDecimal to Float or Double, and work through the example<br />
again.<br />
The C programming language allows you to create UDRs, cast support functions,<br />
aggregates, and opaque data type support routines. C can also handle row and<br />
collection data types. Working with C UDR adds an extra layer of difficulty<br />
because these routines have to be compiled using a compiler, specific to the<br />
system and operating system where you have products installed. Compiled C is<br />
not stored in sbspace, but rather it is stored outside the server.<br />
In addition to compiler difficulties, it is a good idea to develop and test UDRs on a<br />
development server and not in a production environment. C UDR code runs as a<br />
database server process that works closely with internal structures. The routine<br />
should not do anything that would negatively affect the database server. A poorly<br />
designed C UDR is likely to crash the server. If you plan to work with C UDRs,<br />
the following resources are good reference books:<br />
► <strong>IBM</strong> <strong>Informix</strong> User-Defined Routines and Data Types <strong>Developer's</strong> Guide,<br />
Version 11.50, SC23-9438<br />
► <strong>IBM</strong> <strong>Informix</strong> DataBlade API Programmer’s Guide, Version 11.50, SC23-9429<br />
► <strong>IBM</strong> <strong>Informix</strong> DataBlade API Function Reference, Version 11.50, SC23-9428<br />
If you are interested in using C++ Datablade modules, the <strong>IBM</strong> <strong>Informix</strong><br />
Developer Zone that provides the latest recommendations on C++ programming<br />
options at:<br />
http://www.ibm.com/software/data/developer/informix<br />
Routine examples in C<br />
In this section, we undertake a few examples written in C. With Java, we have<br />
portability that extends across platforms, but some of our functionality is limited.<br />
C is flexible because it can extend the database by way of data types, new<br />
functions, and new operators. For each database across an enterprise, the<br />
extensibility that you provide through C must be specifically compiled with each<br />
operating system. For the examples in this section, we use a Solaris system.<br />
A simple function using C<br />
For our first example, we start with a function similar to what we did in our Java<br />
example, a multiplying function. For each of our C examples, we must include the<br />
DataBlade API files (referenced in the example as mi.h and milib.h). The<br />
348 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
DataBlade APIs provide the interface to allow C language calls to interface with<br />
<strong>IBM</strong> <strong>Informix</strong> database SQL calls. To prevent a conflict with our times_ten Java<br />
UDR function, we name this example times_five.<br />
To create this routine:<br />
1. Create the text file that holds the C code. Example 10-9 shows the source file<br />
times.c.<br />
Example 10-9 A C UDR that multiplies times five<br />
#include <br />
#include "mi.h"<br />
#include "milib.h"<br />
mi_integer* TimesFive(mi_integer value);<br />
mi_integer* TimesFive(mi_integer value)<br />
{<br />
return (mi_integer *) (value * 5);<br />
}<br />
2. Compile and link the C code.<br />
The compile command (cc or gcc) depends on the operating system. The first<br />
way to identify your compiler is to examine the text of man cc (on UNIX), or<br />
check the command for your operating system compiler in the<br />
INFORMIXDIR/release/en_us/0333/ids_machine_notes_vers.txt file.<br />
At a minimum, your compile line for UDR preparation usually includes:<br />
-DMI_SERVBUILD -KPIC -I$INFORMIXDIR/incl/public -I/$INFORMIXDIR/incl<br />
Where<br />
– -DMI_SERVBUILD is the flag to indicate that this is a server-oriented C UDR<br />
application which uses the DataBlade API (required).<br />
– -KPIC is the flag to indicate that the symbol table is dynamic (for UNIX and<br />
Linux only).<br />
– -I$INFORMIXDIR/incl/public and -I/$INFORMIXDIR/incl are the location<br />
of the mi_ libs.<br />
The compile command for our code sample (on Solaris) is:<br />
cc -DMI_SERVBUILD -KPIC -I$INFORMIXDIR/incl/public -I/$INFORMIXDIR/incl -o<br />
times.o -c times.c<br />
Run the link command to put the compiled object file into our Blade library file:<br />
ld -dy -G -Bsymbolic -o times.bld times.o<br />
Chapter 10. User-defined routines 349
3. Set the bld file permissions as user informix:<br />
chmod 555 ./times.bld<br />
4. In dbaccess, after connecting to the database, create the times_five<br />
function:<br />
CREATE FUNCTION times_five(value int) RETURNING int<br />
WITH (handlesnulls)<br />
EXTERNAL NAME "/work/times.bld(TimesFive)"<br />
LANGUAGE C;<br />
Adjust the external work path as needed.<br />
5. Test the routine:<br />
execute function times_five(20);<br />
(expression)<br />
100<br />
In Java UDR, the .jar file serves as a library (collection-repository) for all of the<br />
compiled routines. In C, a collection of compiled routines is stored in a shared<br />
library (.so or .o) file. On Windows, a shared object file has a .dll extension<br />
(dynamic link library).<br />
Creating a C routine using large object column<br />
The next example explores the use of working with a large object column<br />
reference. Without a text search DataBlade, searching a character large object<br />
(CLOB) file for a particular value can be a laborious SQL task. This example<br />
shows how to access a CLOB file, copy the CLOB contents into an LVARCHAR,<br />
and search for a specific text item, while using a simple function. We create a C<br />
file that handles two parameters. The first parameter is a CLOB column<br />
reference, the second parameter is the LVARCHAR text value for our search. It<br />
returns a count for the number of successful finds.<br />
To implement this search routine:<br />
1. Create the CLOB column search routine source file un.c as shown in<br />
Example 10-10.<br />
Example 10-10 A UDR for searching a CLOB<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
typedef unsigned char byte;<br />
mi_integer contains(MI_LO_HANDLE* loptr, mi_lvarchar* pattern, MI_FPARAM* fp);<br />
350 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
mi_integer contains(MI_LO_HANDLE* loptr, mi_lvarchar* value, MI_FPARAM* fp)<br />
{<br />
mi_unsigned_integer crc = (mi_fp_argisnull(fp, 1) == MI_TRUE) ? 0 :<br />
(mi_unsigned_integer)1;<br />
MI_CONNECTION *conn;<br />
MI_LO_SPEC *lo_spec = NULL;<br />
MI_LO_FD lo_fd;<br />
MI_LO_STAT *lo_stat = NULL;<br />
char *buff = NULL;<br />
char *pattern = NULL;<br />
mi_integer buffsize = 4096;<br />
mi_integer found=0;<br />
mi_integer result=0;<br />
pattern = mi_lvarchar_to_string(value);<br />
if ((conn = mi_open(NULL, NULL, NULL)) == NULL) return (mi_integer) -1;<br />
if ((buff = (char*)mi_alloc(buffsize)) == NULL) return (mi_integer) -2;<br />
if ((lo_fd = mi_lo_open(conn, loptr, MI_LO_RDONLY)) == MI_ERROR)<br />
return (mi_integer) -3;<br />
do {<br />
if ((result = mi_lo_read(conn, lo_fd, buff, buffsize)) == MI_ERROR)<br />
break;<br />
if (result == 0)<br />
break;<br />
if (strstr(buff,pattern)!=NULL)<br />
{<br />
found=1;<br />
break;<br />
}<br />
if (result < buffsize)<br />
break;<br />
} while(1);<br />
mi_lo_close(conn,lo_fd);<br />
if (buff)<br />
mi_free(buff);<br />
return (mi_integer)found;<br />
}<br />
2. Compile and link the C routine. We use the following Solaris compile line:<br />
cc -DMI_SERVBUILD -KPIC -I$INFORMIXDIR/incl/public<br />
-I$INFORMIXDIR/incl/public -I$INFORMIXDIR/incl/esql -I/$INFORMIXDIR/incl -o<br />
un.o -c un.c<br />
Here is the link line:<br />
ld -dy -G -Bsymbolic -o un.bld un.o<br />
3. Set the bld file permissions as user informix:<br />
chmod 555 ./un.bld<br />
Chapter 10. User-defined routines 351
4. Create the function in dbaccess.<br />
create function contains(clob,lvarchar)<br />
returns integer<br />
external name '/work/un.bld(contains)'<br />
language C;<br />
5. Set up for testing. Create a table and populate it with our c file.<br />
CREATE TABLE tclob (c1 INT, c2 CLOB);<br />
INSERT INTO tclob VALUES (1,filetoclob('un.c','server'));<br />
6. Test the routine:<br />
SELECT c2 FROM tclob;<br />
SELECT CONTAINS(c2,'pattern') FROM tclob;<br />
SELECT CONTAINS(c2,'nopattern') FROM tclob;<br />
SELECT c1 FROM tclob WHERE CONTAINS(c2,'buff')=1;<br />
Troubleshooting tips<br />
To track down the cause of problems with C UDRs, the most affective approach<br />
is to use a debugger. To debug your UDR, use a debugger that can attach to the<br />
active server process and access the symbol tables of the dynamically loaded<br />
shared object files. On UNIX and Linux, the debugger and dbx utilities meet these<br />
criteria. To start a debugger, enter the following command at the shell prompt, in<br />
which pid is the process identifier of the CPU or virtual processor:<br />
debugger -pid<br />
This command starts the debugger on the server virtual-processor process<br />
without starting a new instance of the virtual processor. For more information<br />
about available debugger commands, see the debugger manual page, and learn<br />
more in the information center:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
dapip.doc/sii111026637.htm<br />
10.3 DataBlades and bladelets<br />
Your initial collection of UDRs increases over time. It might not be long until you<br />
have UDRs, special functions, and stored procedure UDRs that reduce work<br />
complexity and provide great functionality. If you choose not to write your own<br />
DataBlade, you can still choose from a nice selection of DataBlade modules. Any<br />
DataBlade module you use provides functions that might increase the usefulness<br />
of business data dramatically or that might perhaps generate income for you—as<br />
a software developer that develops and sells licensed DataBlades.<br />
352 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
10.3.1 Configuration<br />
<strong>IBM</strong> <strong>Informix</strong> has several books on the subject of planning, designing, and<br />
implementing DataBlades. With a taste of the programming examples presented<br />
earlier in this chapter, you might be ready to take the next step and program a<br />
DataBlade in C or Java. From a technical perspective, <strong>IBM</strong> <strong>Informix</strong> DataBlade<br />
Developers Kit User's Guide, Version 4.20, G229-6366 is specifically oriented to<br />
the development of DataBlade modules. It provides detailed help for<br />
programming DataBlade modules in Java and C.<br />
If you want to work with a DataBlade before you decide whether you have a need<br />
to write your own, the sections that follow provide an overview of the DataBlades<br />
that are available with <strong>IBM</strong> <strong>Informix</strong> Server.<br />
Configuration for a Datablade module requires the following steps:<br />
1. Prepare the database serve.<br />
2. Install the DataBlade.<br />
3. Register the DataBlade.<br />
Conceptually, the process is the same, regardless of the operating system and<br />
hardware. Small differences exist in interface or command lines, which we will<br />
point out.<br />
Prepare the database<br />
Database preparation involves setting up environment variables and making sure<br />
the database you are going to use with a DataBlade is set to a logged database<br />
in advance.<br />
The environment settings required are as follows:<br />
► On UNIX and Linux: LD_LIBRARY_PATH, INFORMIXSERVER, and<br />
ONCONFIG<br />
► On Windows: INFORMIXSERVER and ONCONFIG<br />
When you get to the step for BladeManager usage, you also require an<br />
environment setting for LD_LIBRARY_PATH.<br />
Although a logged database is not required for every DataBlades, a logged<br />
database can help avoid concurrency problems and thus is recommended. Also,<br />
while it is the case that not all DataBlade modules require a logged database,<br />
some of them do require logged sbspace. Keep this in mind as you install and<br />
configure the DataBlade that you select.<br />
Chapter 10. User-defined routines 353
Installing the DataBlade<br />
Any DataBlades that are installed with <strong>IBM</strong> <strong>Informix</strong> Servers are installed in<br />
separate subdirectories under the INFORMIXDIR/extend directory. Several<br />
subdirectories for DataBlades are established when the <strong>IBM</strong> <strong>Informix</strong> Server is<br />
installed. There might be some variation to the list, based on your exact version<br />
and operating system.<br />
Example 10-11 shows the subdirectory listing for 11.50.UC6 on Linux with<br />
J/Foundation.<br />
Example 10-11 A sample INFORMIXDIR/extend subdirectory<br />
opt/<strong>IBM</strong>/informix/extend:> ls<br />
binaryudt.1.0 ifxmngr LLD.1.20.UC2 spatial.8.21.UC3<br />
bts.2.00 ifxrltree.2.00 mqblade.2.0 web.4.13.UC4<br />
ifxbuiltins.1.1 krakatoa Node.2.0 wfs.1.00.UC1<br />
If you do not see the DataBlade directory reference for the one you want, you<br />
must acquire it by way of a download or CD.<br />
Installation on UNIX is simply a matter of uncompressing the new DataBlade<br />
module into a temporary directory. After the files are expanded, run the<br />
installation script, ./install. The ./install script creates a new module<br />
directory under the INFORMIXDIR/extend directory. Some file expansions can<br />
result in more than one new module. If there is more than one module, you must<br />
run the ./install script for each one.<br />
Installation on Windows systems requires that you go to the installation location<br />
and run setup.exe. Select the Typical installation option. The other dialog<br />
verification is the INFORMIXDIR location. With those items confirmed, the<br />
software is installed. When the installation is complete, the module directory is<br />
installed in the INFORMIXDIR\extend directory.<br />
Registering the DataBlade<br />
With a DataBlade directory in place, the active database server is not aware of<br />
the DataBlade directories until the software is registered in the database.<br />
Registration is the process of executing the SQL statements that create the<br />
DataBlade module database objects and identify the DataBlade module shared<br />
object file or dynamic link library to the database server.<br />
354 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
With the release of 11.50.XC4, <strong>IBM</strong> <strong>Informix</strong> provides the following distinct<br />
methods for DataBlade registration:<br />
► BladeManager<br />
Available with the first release of datablades, the BladeManager is an<br />
interface that automates the registration process by performing a series of<br />
SQL steps in the database engine.<br />
► sysbldprepare()<br />
This command is an <strong>Informix</strong> function for DataBlade registration. At its<br />
simplest, you can run the command inside dbaccess with your target<br />
database open to install a DataBlade. It has a few restrictions that are<br />
described in <strong>IBM</strong> <strong>Informix</strong> DataBlade Module Installation and Registration<br />
Guide, Version 4.20, G229-6368. To register the bts DataBlade with this<br />
interface, use the following command:<br />
EXECUTE FUNCTION sysbldprepare('bts.*','create');<br />
To register a DataBlade module using BladeManager:<br />
1. Start BladeManager:<br />
– On UNIX or at the MS-DOS prompt, the BladeManager is started with the<br />
blademgr command.<br />
– To start BladeManager on a Windows system, select Start � Programs �<br />
<strong>Informix</strong> program group name � BladeManager or double-click the<br />
BladeManager icon in the <strong>Informix</strong> program group.<br />
If the BladeManager fails to start, it is either not installed or you do not have<br />
the environment variables set, as noted in the previous section.<br />
2. Set confirmations.<br />
If you want an automatic confirmation after each step, turn the prompt on:<br />
set confirm on<br />
Commands run when you press the carriage return.<br />
3. Connect to an <strong>Informix</strong> instance:<br />
show servers<br />
set server demo_on < Use you own server name for demo_on><br />
If you want to connect as a different user, try:<br />
set user <br />
At the password prompt, enter your password. Validation does not occur until<br />
connection, on the next step.<br />
Chapter 10. User-defined routines 355
To connect to a database, run one of the following commands:<br />
list stores<br />
register module_name database_name<br />
unregister module_name database_name<br />
The module_name represents the name of the DataBlade module directory.<br />
These names typically follow the form of the DataBlade module name<br />
followed by the version number.<br />
When BladeManager registers a DataBlade module, it executes a series of SQL<br />
CREATE statements to register each database object in the module. You must<br />
have resource permissions on the database to register the DataBlade. In<br />
addition, if your server has implemented the ONCONFIG EXTEND role, you<br />
must be granted the EXTEND role by user informix.<br />
If the registration of a module fails, BladeManager returns the database to its<br />
prior state. To see the SQL statements that failed, look at the corresponding log<br />
file and check the procedure in Appendix A. Troubleshooting Registration<br />
Problems of <strong>IBM</strong> <strong>Informix</strong> DataBlade Module Installation and Registration Guide,<br />
Version 4.20, G229-6368, for possible solutions.<br />
Occasionally, DataBlade modules have more than one interface. If there are<br />
additional modules, there are also dependencies. You have to make sure that<br />
each of the interfaces are registered correctly in order for the DataBlade to work.<br />
BladeManager automatically checks for dependencies and registers any<br />
dependencies it might need. If the BladeManager cannot do the registration, it<br />
will prompt you to do so manually.<br />
Important: BladeManager does not verify the integrity of DataBlade modules<br />
that have additional interfaces, nor does it not check for the presence of<br />
required database objects.<br />
Datablade modules written in the Java language can only be registered in <strong>IBM</strong><br />
<strong>Informix</strong> Servers with J/Foundation database servers.<br />
356 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
10.3.2 <strong>IBM</strong> <strong>Informix</strong> provided DataBlades<br />
Table 10-2 the DataBlades that are available with <strong>IBM</strong> <strong>Informix</strong> database servers.<br />
Table 10-2 <strong>Informix</strong> provided DataBlades<br />
<strong>IBM</strong> <strong>Informix</strong><br />
DataBlade<br />
module name Description Special notes<br />
Large Object<br />
Locator<br />
A foundation Datablade module for large<br />
objects management that can be used by<br />
other modules that create or store<br />
large-object data.<br />
MQ DataBlade Allows <strong>IBM</strong> <strong>Informix</strong> database<br />
applications to communicate with other<br />
MQSeries® applications with MQ<br />
messaging.<br />
Binary DataBlade This module includes binary data types to<br />
store binary-encoded strings that can be<br />
indexed for quick retrieval.<br />
Basic Text Search Permits text search of words and phrases<br />
in an unstructured document repository<br />
stored in a column of a table.<br />
Node DataBlade This module is for the hierarchical data<br />
type, to represent hierarchical data within<br />
a relational database.<br />
Web Feature<br />
Service<br />
J/Foundation<br />
krakatoa<br />
This module is an add-on to allow Open<br />
Geospatial Consortium (OGC) web<br />
feature service as a presentation layer for<br />
the Spatial and Geodetic DataBlade<br />
modules<br />
A library of classes and interfaces that<br />
allow programmers to create and execute<br />
Java UDRs that access <strong>Informix</strong> database<br />
servers<br />
ifxbuilt-ins This is not really a DataBlade, but it sets<br />
up definitions and functions for the<br />
standard data types offered in the informix<br />
server.<br />
ifxmngr.2.00 This is the API that supports the<br />
BladeManager.<br />
Available on<br />
standard install<br />
(LLD.1.20.UC2)<br />
Available on<br />
standard install<br />
(mqblade.2.0)<br />
Available on<br />
standard install.<br />
(binaryudt.1.0)<br />
Available on<br />
standard install.<br />
(bts.2.00)<br />
Available on<br />
standard install.<br />
(Node.2.0)<br />
Available on<br />
standard install.<br />
(wfs.1.00.UC1)<br />
Available as a part<br />
of <strong>Informix</strong> Server<br />
with J/Foundation<br />
Available on<br />
standard install<br />
(ifxbuiltins.1.1)<br />
Available on<br />
standard UNIX<br />
install<br />
Chapter 10. User-defined routines 357
<strong>IBM</strong> <strong>Informix</strong><br />
DataBlade<br />
module name Description Special notes<br />
ifxrltree.2.00 This is a foundational, multidimensional,<br />
index called “Region tree” (R-tree) (also<br />
known as Range Tree). This blade is<br />
needed for both spatial and time related<br />
data management.<br />
Image Foundation This module is a foundation DataBlade<br />
which provides a base on which new or<br />
specialized image types and image<br />
processing technologies can be added or<br />
changed. Because the foundation is open,<br />
secure, and scalable, it provides a clear<br />
path toward reusing and repurposing<br />
valuable image assets.<br />
Excalibur Text<br />
Search<br />
358 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
This module enables provides extensive<br />
text-searching capabilities; It supports<br />
full-text indexing, including extensive<br />
fuzzy-search logic for indexing scanned<br />
text. Can search document types<br />
including: ASCII, Word, Excel, HTML,<br />
PowerPoint, WordPerfect, and PDF.<br />
Includes an adaptive pattern recognition<br />
process (APRP) and capabilities such as<br />
multiple stop-word lists, proximity<br />
searching and synonym lists.<br />
Geodetic This blade supports global space- and<br />
time based queries. It is designed to treat<br />
earth as a globe rather than a flat plane.<br />
Supports client-side Geographic<br />
Information Systems (GIS) software.<br />
Spatial This blade transforms locations and<br />
traditional 2-d map data into useful<br />
information. Uses SQL-based spatial data<br />
types and functions that can be used<br />
directly through standard SQL queries or<br />
with client-side Geographic Information<br />
Systems (GIS) software.<br />
Available on<br />
standard install<br />
No charge<br />
download<br />
License fee applies<br />
License fee applies<br />
No charge<br />
download
<strong>IBM</strong> <strong>Informix</strong><br />
DataBlade<br />
module name Description Special notes<br />
TimeSeries This module supports data for managing<br />
time-series and temporal data. A “time<br />
series” is any set of data that is accessed<br />
in sequence by time and can be<br />
processed and analyzed in a<br />
chronological order.<br />
Video Foundation This blade allows you to incorporate video<br />
servers, external control devices,<br />
compression codes, and cataloging tools<br />
to manage video content and metadata or<br />
information about the content. Allows<br />
metadata elements in the database, while<br />
allowing video content to be maintained<br />
on disk, video tape, video server, or other<br />
external storage devices.<br />
Web This module supports most web server<br />
APIs and has a web client application to<br />
build and run SQL queries to work with<br />
your database. Enables customized web<br />
applications. Allows you to track<br />
persistent session variables between<br />
AppPages.<br />
10.3.3 Developing a bladelet routine<br />
License fee<br />
applies.<br />
License fee<br />
applies.<br />
License fee applies<br />
We can define a bladelet as a small, unofficial DataBlade module. It is meant to<br />
be useful (and complete with source code) from the time you set it up, but it<br />
becomes your own application (with no support or warranty). If you have tried out<br />
our UDR development examples, you have a bladelet.<br />
As you might have observed from having to drop and re-create the JAR API in<br />
the server in our earlier example, you can understand that if you have a large<br />
number of UDRs and have gone to the trouble of creating user-defined data<br />
types, the whole package of tasks that are required to set up, change, or update<br />
a DataBlade object inside the server might not be a convenient task.<br />
On a large scale, if you have API dependencies, dozens of UDRs, and other<br />
DataBlade related objects, you will want to move them and install them as a<br />
package. <strong>IBM</strong> <strong>Informix</strong> provides a Windows-based interface for this, which has<br />
the ability to do the package preparation work for you. The package preparation<br />
Chapter 10. User-defined routines 359
interface, called BladeSmith, allows you to populate a properties definition<br />
dictionary.<br />
When the properties are all defined and you proceed, BladeSmith creates,<br />
assembles, and arranges a directory structure with a complete tree layout of all<br />
the components that are required and handled in a DataBlade registration<br />
process. The resulting directory tree layout and assembly pieces provide a<br />
prepared package that is ready to ship to an operating system of your choice.<br />
Likewise, any language source code that is output for the preparation task is<br />
parcelled out for the appropriate component nodes also.<br />
The dispensation for the BladeSmith file components and directory structure is<br />
laid out based on the type of component node, as described in Table 10-3.<br />
Table 10-3 BladeSmith file package creation layout<br />
Component node What is generated<br />
Source All source code in the coding languages you use for your<br />
DataBlade module objects<br />
Client Client code (ActiveX or Java)<br />
Server Server code in the coding language you specified for BladeSmith<br />
Individual language Source code for the represented language (C, Java, or SPL)<br />
For further information about creating datablade objects using BladeSmith<br />
generating files, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
dbdk.doc/sii-smith-27272.htm<br />
For further information or examples of existing bladelets and downloads<br />
developed by users, refer to:<br />
http://www.ibm.com/developerworks/data/zones/informix/library/samples/db_downlo<br />
ads.html<br />
There is also a downloads page that features bladelets at the International<br />
<strong>Informix</strong> Users Group site called “ORDBMS - Object-Relational Database<br />
Extensibility, DataBlades” at:<br />
http://www.iiug.org/software/index_ORDBMS.html<br />
360 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 11. Working with Ruby on Rails<br />
This chapter contains information about how to develop applications against an<br />
<strong>IBM</strong> <strong>Informix</strong> database using the Ruby programming language and the Ruby on<br />
Rails web development framework.<br />
In this chapter, we discuss the following topics:<br />
► A brief overview of Ruby on Rails<br />
► Setup and configuration<br />
► Database operations<br />
► Using the Rails Adapter with Ruby <strong>Informix</strong><br />
► Using the Rails Adapter with <strong>IBM</strong>_DB<br />
11<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 361
11.1 A brief overview of Ruby on Rails<br />
Ruby is a open source programming scripting language with a focus on simplicity<br />
and productivity. Ruby is similar to other scripting languages like Perl or Python<br />
with the difference that Ruby is an object-oriented language. Ruby is both<br />
technology- and platform-independent. You can find implementations of the Ruby<br />
run time on C, Java, and even .NET, making Ruby a useful option for any<br />
scripting need.<br />
For more information about the Ruby language, refer to:<br />
http://www.ruby-lang.org/en/about/<br />
Rails is an open source Ruby framework for developing database-backed web<br />
applications. Rails expands the object-orientated core design of Ruby, helping<br />
developers to build websites and applications with minimum coding efforts.<br />
Rails is based on two key principles:<br />
► Convention over configuration (CoC), where developers need to focus only<br />
on the exceptions to the conventions. Every other aspect of the application,<br />
from design to implementation, is done automatically by the defined<br />
conventions.<br />
The conventions define rules such as use a plural for the table names. For<br />
example, if the application uses an entity called Book, the table that stores<br />
this entity must be called Books, and if it stores details about a Person, the<br />
table must be called People.<br />
► Don’t Repeat Yourself (DRY). Information is located in one place only.<br />
Database object definitions, documentation, and configuration scripts all are<br />
kept in one location and are consulted when information about them is<br />
required.<br />
For more information, refer to:<br />
http://rubyonrails.org/documentation<br />
362 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
11.1.1 Architecture of Ruby on Rails<br />
Ruby on Rails is built with the Model, View, Controller (MVC) architecture that is<br />
typically used in web-based GUI programming. This architecture has three main<br />
concepts:<br />
► Model<br />
The business logic of the system, which encompasses the persistence layer<br />
because it interacts with a database back end.<br />
► View<br />
The GUI interface of the model that is visible to the user. One Model can have<br />
many views.<br />
► Controller<br />
The action taken by the user using the view. The controller takes inputs from<br />
the user through the view and executes the business logic encapsulated in<br />
the model.<br />
The Rails framework provides a set of utilities and components that are designed<br />
to facilitate the development of web applications:<br />
► Rake is a build tool that is bundled with the Ruby programming language. It is<br />
the equivalent to the make command on UNIX.<br />
► WEBrick is the web server that is bundled with Ruby on Rails.<br />
► ActiveRecord is the object-relational mapper of Rails and provides for<br />
persistence. It presents the database table as a class, which in Rails is called<br />
model.<br />
► Action Controller is the component that manages the controllers in a Rails<br />
application. It also processes and dispatches incoming requests.<br />
► Action View manages the views in a Rails application.<br />
11.1.2 Ruby Driver and Rails Adapter<br />
<strong>IBM</strong> <strong>Informix</strong> supports database access for client applications written in the Ruby<br />
programming language and web application development with the Rail<br />
framework.<br />
Ruby Driver<br />
To use an database with Ruby, the application requires a Ruby driver. This driver<br />
provides the layer that connect the Ruby run time with the database server.<br />
Chapter 11. Working with Ruby on Rails 363
The following drivers allow Ruby to connect to an <strong>IBM</strong> <strong>Informix</strong> database:<br />
► The Ruby <strong>Informix</strong> driver is an open source project supported by the open<br />
source community. It allows Ruby to connect to any <strong>IBM</strong> <strong>Informix</strong> database<br />
server. The Ruby driver is developed using the <strong>IBM</strong> <strong>Informix</strong> ESQL/C<br />
language that provides full support for all the <strong>Informix</strong> database features and<br />
data types. Because it uses the <strong>Informix</strong> Client Software Development Kit<br />
(Client SDK) libraries,<br />
► Ruby Driver for <strong>IBM</strong> Data Servers driver (<strong>IBM</strong>_DB) is provided, supported,<br />
and developed by <strong>IBM</strong> as an open source project. The Ruby driver is bundled<br />
together with the Rails Adapter in the Rails Adapter/Driver for <strong>IBM</strong> Data<br />
Servers package.<br />
Rails Adapter<br />
A Rails Adapter is a Ruby script that allows you to use a specific Ruby driver<br />
within the Rails framework. It provides the required Ruby objects, for example the<br />
ActiveRecord object, that enable the full use of the Ruby driver inside the Rails<br />
framework.<br />
<strong>IBM</strong> <strong>Informix</strong> supports the following Rails adapters:<br />
► informix_adapter.rb, used in conjunction with the Ruby <strong>Informix</strong> driver.<br />
Requires Client SDK libraries for the communication with the <strong>Informix</strong><br />
database server.<br />
► ibm_db_adapter.rb, used with the Ruby Driver for <strong>IBM</strong> Data Servers.<br />
Requires the <strong>IBM</strong> Data Server Driver for ODBC and CLI package.<br />
Both adapters are available from the Ruby repository as Ruby gems. Ruby gems<br />
are self-contained packages that contain all the libraries, source files, and scripts<br />
needed for the Ruby component.<br />
11.2 Setup and configuration<br />
This section describes how to set up and configure both Ruby drivers and Rails<br />
adapters for use with an <strong>IBM</strong> <strong>Informix</strong> database.<br />
11.2.1 Ruby <strong>Informix</strong> driver<br />
The Ruby <strong>Informix</strong> driver is available for download at the SourceForge website at:<br />
http://rubyforge.org/projects/ruby-informix<br />
364 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
You can use the Ruby gem utility to download the Ruby <strong>Informix</strong> driver<br />
automatically from the Ruby repository and install it in the Ruby environment.<br />
Run the following command from your Ruby session to install the Ruby <strong>Informix</strong><br />
driver:<br />
gem install ruby-informix<br />
Because the driver shared library, informixc.so, is built during the installation<br />
process, the environment should contain the correct settings for compiling<br />
ESQL/C applications. Refer to Chapter 4, “Working with ESQL/C” on page 125<br />
for more information about ESQL/C settings.<br />
You can find more information about the installation process in the README file<br />
inside the gem directory. See Example 11-1 for the list of files that are included<br />
with the Ruby <strong>Informix</strong> driver.<br />
Example 11-1 The gem directory<br />
Directory of C:\work\Ruby187\lib\ruby\gems\1.8\gems\ruby-informix-0.7.3<br />
03/07/2010 19:34 .<br />
03/07/2010 19:34 ..<br />
03/07/2010 19:19 8,383 Changelog<br />
03/07/2010 19:19 1,470 COPYRIGHT<br />
03/07/2010 19:29 ext<br />
03/07/2010 19:35 lib<br />
03/07/2010 19:19 4,500 README<br />
03/07/2010 19:19 test<br />
3 File(s) 14,353 bytes<br />
5 Dir(s) 76,168,769,536 bytes free<br />
Configuration<br />
The Ruby <strong>Informix</strong> uses the same connectivity information as other Client SDK<br />
components. It uses the INFORMIXDIR environment variable to locate the<br />
libraries and resources such as error message files or configuration files.<br />
By default, the Ruby driver connects to the database server specified in the<br />
INFORMIXSERVER environment variable. Same as the other Client SDK<br />
components, the information regarding the INFORMIXSERVER value is stored<br />
on the sqlhosts file or the Windows registry. For more information, refer to<br />
“Connectivity on UNIX” on page 27.<br />
The shared library search path variables, for example, LD_LIBRARY_PATH or<br />
SHLIB_PATH, must contain the $INFORMIX/libl and $INFORMIX/lib/esql<br />
directories. Otherwise, the Ruby driver might fail to load the ESQL/C libraries that<br />
it requires for work.<br />
Chapter 11. Working with Ruby on Rails 365
Data types<br />
The Ruby <strong>Informix</strong> driver provides the data types to be used against an <strong>IBM</strong><br />
<strong>Informix</strong> database. The driver provides specific types such as<br />
<strong>Informix</strong>::IntervalYTM or <strong>Informix</strong>::Slob to handle specific <strong>Informix</strong> types.<br />
Table 11-1 shows the data type mapping between the Ruby <strong>Informix</strong> driver and<br />
the <strong>Informix</strong> database.<br />
Table 11-1 Ruby <strong>Informix</strong> data type mapping<br />
<strong>Informix</strong> data type Ruby data type<br />
SMALLINT, INT, INT8, FLOAT, SERIAL<br />
and SERIAL8<br />
CHAR, NCHAR, VARCHAR,<br />
NVARCHAR<br />
Verifying connectivity<br />
Ruby includes an interactive shell called irb that you can use to run simple Ruby<br />
statements. The irb is located in the bin directory of the Ruby installation.<br />
The driver name used inside the Ruby scripts to reference the Ruby <strong>Informix</strong><br />
driver is informix.<br />
To test whether the Ruby driver can connect to a database, you must load the<br />
Ruby driver and then create a connection using the Ruby <strong>Informix</strong> object.<br />
366 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Numeric<br />
String<br />
DATE Date<br />
DATETIME TIME<br />
INTERVAL <strong>Informix</strong>::IntervalYTM, <strong>Informix</strong>::IntervalDTS<br />
DECIMAL, MONEY BigDecimal<br />
BOOL TrueClass, FalseClass<br />
BYTE, TEXT StringIO, String<br />
CLOB, BLOB <strong>Informix</strong>::Slob
Example 11-2 demonstrates how to load the Ruby <strong>Informix</strong> driver and connect to<br />
an <strong>IBM</strong> <strong>Informix</strong> database. The fist command, require 'informix', tells the<br />
Ruby run time to load the Ruby <strong>Informix</strong> driver. After that, it creates a Ruby<br />
<strong>Informix</strong> connection object and prints the database version information.<br />
Example 11-2 Testing connection with Ruby informix<br />
C:\work>irb<br />
irb(main):001:0> require 'informix'<br />
=> true<br />
irb(main):002:0> db=<strong>Informix</strong>.connect('stores_demo','informix','password')<br />
=> #<br />
irb(main):003:0> puts db.version<br />
<strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
=> nil<br />
irb(main):004:0><br />
11.2.2 Data Server Ruby driver<br />
The Data Server Ruby driver uses the Data Server CLI driver to connect the<br />
<strong>Informix</strong> database. It uses the DRDA protocol so the version of the <strong>IBM</strong> <strong>Informix</strong><br />
database server must be 11.10 or 11.50.<br />
The driver is included with <strong>IBM</strong> Data Server Driver and is also available to<br />
download directly from the RubyForge website:<br />
http://rubyforge.org/projects/rubyibm/<br />
You can install the complete package using the Ruby gem utility by running the<br />
following command from a Ruby session:<br />
gem install ibm_db<br />
For information about the build and setup process for Data Server Driver, consult<br />
the README file in the driver directory.<br />
Example 11-3 shows the Data Server Ruby driver directory.<br />
Example 11-3 The ibm_db gem directory<br />
Directory of C:\work\Ruby187\lib\ruby\gems\1.8\gems\ibm_db-0.10.0-x86-mswin32<br />
03/07/2010 17:30 .<br />
03/07/2010 17:30 ..<br />
03/07/2010 17:30 6,063 CHANGES<br />
03/07/2010 17:30 ext<br />
03/07/2010 17:30 1,656 init.rb<br />
03/07/2010 18:06 lib<br />
Chapter 11. Working with Ruby on Rails 367
03/07/2010 17:30 1,088 LICENSE<br />
03/07/2010 17:30 299 MANIFEST<br />
03/07/2010 17:30 13,402 README<br />
03/07/2010 17:30 test<br />
5 File(s) 22,508 bytes<br />
5 Dir(s) 76,167,290,880 bytes free<br />
You can find additional information regarding the Ruby driver for <strong>IBM</strong> Data<br />
Servers at:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/topic/com.ibm.db2.luw.apdv<br />
.ruby.doc/doc/t0052765.html<br />
On some platforms, such as Windows operating system platforms, the shared<br />
library for the Ruby driver is already included in the package. Therefore, there is<br />
no need for a C build environment.<br />
Example 11-4 shows the Ruby driver directory from Data Server Client on a<br />
Windows system after the installation.<br />
Example 11-4 Windows system Data Server Ruby directory<br />
C:\work>dir "C:\Program Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER DRIVER\ruby"<br />
Volume in drive C is W2003<br />
Volume Serial Number is 50DA-70D7<br />
Directory of C:\Program Files\<strong>IBM</strong>\<strong>IBM</strong> DATA SERVER DRIVER\ruby<br />
17/05/2010 02:16 .<br />
17/05/2010 02:16 ..<br />
30/05/2009 11:09 198,144 ibm_db-0.10.0-mswin32.gem<br />
1 File(s) 198,144 bytes<br />
2 Dir(s) 76,167,372,800 bytes free<br />
C:\work><br />
Configuration<br />
The Data Server Ruby driver uses the Data Server ODBC/CLI driver for the<br />
connection to the database.<br />
The configuration details are the same as with the ODCB/CLI driver. These<br />
details are usually kept in the db2profile.ini file. Refer to 2.2.3, “Setting up <strong>IBM</strong><br />
Data Server drivers” on page 43 for detailed information about Data Server<br />
Driver configuration.<br />
368 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
11.2.3 Rails adapters<br />
Data types<br />
There are no specific Ruby objects to use <strong>Informix</strong> data types. Data Server Ruby<br />
driver supports the same <strong>Informix</strong> data types as Data Server Driver for CLI.<br />
Verifying connectivity<br />
We use an irb session to test the connectivity against an <strong>IBM</strong> <strong>Informix</strong> database<br />
server.<br />
The reference name of the Data Server Ruby driver is ibm_db. You must load this<br />
driver before creating the Ruby connection object.<br />
Example 11-5 shows how to load the Ruby driver and how to connect to the<br />
<strong>Informix</strong> server.<br />
Example 11-5 Testing connection with data server Ruby driver<br />
C:\work>irb<br />
irb(main):001:0> require 'mswin32/ibm_db'<br />
=> true<br />
irb(main):002:0> db=<strong>IBM</strong>_DB.connect 'dsc_dsn','informix','password'<br />
=> #<br />
irb(main):003:0><br />
First, load the Ruby driver with require 'mswin32/ibm_db'. Then, open the<br />
connection using the <strong>IBM</strong>_DB::connect method.<br />
Note: Because we use the Windows version of the Ruby driver, we must prefix<br />
the driver name with the mswin32 directory.<br />
You can also made a dsn-less connection by specifying all the required<br />
parameters in the connection string as follows:<br />
<strong>IBM</strong>_DB.connect 'DRIVER={<strong>IBM</strong> DB2 ODBC DRIVER};DATABASE=stores_demo;<br />
HOSTNAME=kodiak;PORT=9089;PROTOCOL=TCPIP;UID=informix;PWD=password;', '', ''<br />
The Ruby <strong>Informix</strong> Rails Adapter provides the Ruby ActiveRecord object that<br />
makes the use of the Ruby driver to work on the Rails framework possible.<br />
The Rails Adapter for the Ruby <strong>Informix</strong> driver is a free download that is available<br />
at:<br />
http://rubyforge.org/projects/rails-informix/<br />
Chapter 11. Working with Ruby on Rails 369
The Rails Adapter can be installed automatically as a Ruby gem using the gem<br />
utility. Example 11-6 shows how to install the Ruby <strong>Informix</strong> adapter.<br />
Example 11-6 Installing the Ruby <strong>Informix</strong> adapter<br />
C:\work>gem install activerecord-informix-adapter -v 1.1.1<br />
Successfully installed activerecord-informix-adapter-1.1.1<br />
1 gem installed<br />
Installing ri documentation for activerecord-informix-adapter-1.1.1...<br />
Installing RDoc documentation for activerecord-informix-adapter-1.1.1...<br />
C:\work><br />
The configuration of the adapter depends on the version of Rails that is installed<br />
in the Ruby environment. On versions older than 2.x, you need to copy the<br />
informix_adapter.rb adapter script file into the connection_adapters directory.<br />
Example 11-7 shows the connection_adapter directory with both Rails adapters<br />
installed.<br />
Example 11-7 The connection _adapter directory<br />
Directory of<br />
C:\work\Ruby187\lib\ruby\gems\1.8\gems\activerecord-1.15.6\lib\active_record\co<br />
nnection_adapters<br />
3/07/2010 18:02 .<br />
3/07/2010 18:02 ..<br />
3/07/2010 17:01 abstract<br />
3/07/2010 17:01 5,235 abstract_adapter.rb<br />
3/07/2010 17:01 6,957 db2_adapter.rb<br />
3/07/2010 17:01 27,749 firebird_adapter.rb<br />
3/07/2010 17:01 30,751 frontbase_adapter.rb<br />
3/07/2010 17:30 70,854 ibm_db_adapter.rb<br />
3/07/2010 20:06 9,916 informix_adapter.rb<br />
3/07/2010 17:01 13,774 mysql_adapter.rb<br />
3/07/2010 17:01 11,531 openbase_adapter.rb<br />
3/07/2010 17:01 25,897 oracle_adapter.rb<br />
3/07/2010 17:01 21,513 postgresql_adapter.rb<br />
3/07/2010 17:01 13,162 sqlite_adapter.rb<br />
3/07/2010 17:01 22,087 sqlserver_adapter.rb<br />
3/07/2010 17:01 22,622 sybase_adapter.rb<br />
13 File(s) 282,048 bytes<br />
3 Dir(s) 76,166,725,632 bytes free<br />
370 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
When using versions of Rails older than 2.x, it is also required to include the<br />
reference name of the Ruby driver in the RAILS_CONNECTION_ADAPTER<br />
parameter in the ActiveRecord Ruby script (see Example 11-8).<br />
Example 11-8 The active_record file<br />
C:\work\Ruby187\lib\ruby\gems\1.8\gems\activerecord-1.15.6\lib>grep informix<br />
active_record.rb<br />
RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver<br />
db2 oracle sybase openbase frontbase informix ibm_db )<br />
C:\work\Ruby187\lib\ruby\gems\1.8\gems\activerecord-1.15.6\lib><br />
After creating a Rails project, you must update the project configuration file that<br />
contains the database information, database.yml, with the connection details of<br />
the <strong>IBM</strong> <strong>Informix</strong> database. This file is located in the /config directory<br />
and has three sections:<br />
► Development<br />
► Test<br />
► Production<br />
These sections point to databases on the respective environments. The<br />
database connectivity properties include:<br />
► adapter: The Ruby driver used. You do not have to give the complete version.<br />
► database: Database to which to connect.<br />
► username and password: To connect to the <strong>Informix</strong> server.<br />
► server: The system on which the <strong>Informix</strong> server is running.<br />
► port: The DRDA port on the <strong>Informix</strong> server.<br />
Example 11-9 shows a typical database.yml file with the details for an <strong>IBM</strong><br />
<strong>Informix</strong> database. The Rails application is named stores7, and we connect to<br />
an <strong>Informix</strong> server called demo_on.<br />
We use the Ruby <strong>Informix</strong> driver reference name for the adapter parameter<br />
informix. The server parameter identifies the <strong>Informix</strong> server to connect to. The<br />
information about the <strong>Informix</strong> server, such as host and port, is retrieved from the<br />
sqlhosts file or Windows registry.<br />
Example 11-9 The database.yml file for the Ruby informix Adapter<br />
C:\work\stores7\config>type database.yml<br />
development:<br />
adapter: informix<br />
database: stores7<br />
pool: 5<br />
timeout: 5000<br />
Chapter 11. Working with Ruby on Rails 371
server: demo_on<br />
username: informix<br />
password: password<br />
# Warning: The database defined as 'test' will be erased and<br />
# re-generated from your development database when you run 'rake'.<br />
# Do not set this db to the same as development or production.<br />
test:<br />
adapter: mysql<br />
database: stores7<br />
username: root<br />
password:<br />
host: localhost<br />
production:<br />
adapter: informix<br />
database: stores7<br />
pool: 5<br />
timeout: 5000<br />
server: demo_on<br />
username: informix<br />
password: password<br />
C:\work\stores7\config><br />
Rails Adapter for <strong>IBM</strong> Data Server<br />
The Rails Adapter for the <strong>IBM</strong> Data Server Ruby driver is bundled with the Ruby<br />
driver. It is installed when the Ruby driver is installed.<br />
In the same way as with the Ruby <strong>Informix</strong> Rails Adapter, if the Rails Adapter for<br />
<strong>IBM</strong> Data Server is installed on a version of Rails older than 2.x, the adapter<br />
script (informix_adapter.rb) must be copied in the connection_adapter<br />
directory, and the RAILS_CONNECTION_ADAPTER parameter in the<br />
active_record.rb Ruby script has to be updated.<br />
The database configuration file for a Rails project, database.yml, has to be<br />
updated with the details for the <strong>Informix</strong> server.<br />
Example 11-10 shows a database.yml file that is used in a Rails project that<br />
connects to an <strong>IBM</strong> <strong>Informix</strong> database.<br />
Example 11-10 The database.yml file for the Rails Adapter for <strong>IBM</strong> Data Servers<br />
C:\work\stores7\config>type database.yml<br />
development:<br />
adapter: ibm_db<br />
372 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
database: stores7<br />
pool: 5<br />
timeout: 5000<br />
host: kodiak<br />
port: 9089<br />
username: informix<br />
password: password<br />
# Warning: The database defined as 'test' will be erased and<br />
# re-generated from your development database when you run 'rake'.<br />
# Do not set this db to the same as development or production.<br />
test:<br />
adapter: mysql<br />
database: stores7<br />
username: root<br />
password:<br />
host: localhost<br />
C:\work\stores7\config><br />
The adapter parameter is set to the reference name of the Data Server Ruby<br />
driver ibm_db. It also contains the host name and the port parameter with the<br />
details about the DRDA <strong>Informix</strong> instance. These details are the same as used in<br />
the Data Server driver for ODBC/CLI.<br />
For a list of all the parameters, refer to:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.swg.im.dbcli<br />
ent.ruby.doc/doc/t0052780.html<br />
11.3 Database operations<br />
This section provides examples of using both Ruby drivers to perform basic<br />
operations against an <strong>IBM</strong> <strong>Informix</strong> database server. It also demonstrates how to<br />
use the Rails Adapter to create basic web applications.<br />
Chapter 11. Working with Ruby on Rails 373
Using Ruby <strong>Informix</strong> driver<br />
In this section, we discuss the basic database operation using the Ruby <strong>Informix</strong><br />
driver.<br />
Connection to the database<br />
Example 11-11 shows how to connect to an <strong>Informix</strong> database. The database<br />
name, user name, and password are passed as the parameters in the command<br />
line.<br />
Example 11-11 The ifx_connect.rb output<br />
C:\work>type ifx_connect.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect(ARGV[0],ARGV[1],ARGV[2])<br />
# print database information<br />
print "Connected to #{db.version}"<br />
db.close<br />
C:\work>ruby ifx_connect.rb stores_demo informix password<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
C:\work><br />
Getting information about a table<br />
Example 11-12 demonstrates how to use the Ruby connection object to retrieve<br />
metadata information about the state table. It uses the columns() method of the<br />
<strong>Informix</strong> connection object to retrieve a Ruby array with all the metadata<br />
information.<br />
Example 11-12 The ifx_metadata.rb output<br />
C:\work>cat ifx_metadata.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect(ARGV[0])<br />
# print database information<br />
print "Connected to #{db.version}\n"<br />
db.columns(ARGV[1]).each {<br />
|name| name.each {|elem| print " #{elem[0]} #{elem[1]}\n"}<br />
}<br />
db.close<br />
C:\work>ruby ifx_metadata.rb stores_demo state<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
precision 0<br />
374 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
type 0<br />
scale 0<br />
length 2<br />
xid 0<br />
nullable true<br />
name code<br />
stype CHAR<br />
...<br />
Executing a simple SQL statement<br />
Using the Ruby driver, you can run an SQL statement directly from the Ruby<br />
connection object or through a prepared statement object.<br />
Example 11-13 demonstrates how to run a simple SQL statement using a<br />
prepared statement Ruby object. The execute() method returns the number of<br />
affected rows.<br />
Example 11-13 The ifx_execute.rb output<br />
C:\work>cat ifx_execute.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect(ARGV[0])<br />
# print database information<br />
print "Connected to #{db.version}\n"<br />
# create a prepare object with the SQL passed<br />
stmt = db.prepare(ARGV[1])<br />
# Execute the prepared statement<br />
rc=stmt.execute()<br />
print "Result=#{rc}"<br />
db.close<br />
C:\work>ruby ifx_execute.rb stores_demo "DELETE from STATE where CODE='AR'"<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
Result=1<br />
C:\work><br />
Chapter 11. Working with Ruby on Rails 375
If the SQL statement returns only one row, you can use the execute() method to<br />
retrieve that value (see Example 11-14).<br />
Example 11-14 The ifx_execute.rb output<br />
C:\work>ruby ifx_execute.rb stores_demo "SELECT sname FROM state WHERE<br />
code='CA'"<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
Result=snameCalifornia<br />
C:\work><br />
Using parameters<br />
Example 11-15 shows how to prepare and execute a parametrized INSERT<br />
statement using the Ruby driver.<br />
Example 11-15 The ifx_parameters output<br />
C:\work>cat ifx_insert.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect('stores_demo')<br />
# print database information<br />
print "Connected to #{db.version}\n"<br />
# create a prepare object with the SQL passed<br />
stmt = db.prepare('INSERT INTO state(code,sname) VALUES (?,?)')<br />
# Execute the statement using parameters<br />
rc=stmt.execute(ARGV[0],ARGV[1])<br />
# Execute the prepared statement<br />
print "Result=#{rc}"<br />
db.close<br />
C:\work>ruby ifx_insert.rb AR Arizona<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
Result=1<br />
C:\work><br />
376 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Selecting data from the database<br />
When selecting multiple records from the database the application must create a<br />
cursor object to fetch the selected rows. Example 11-16 illustrates how to return<br />
information using an <strong>Informix</strong> cursor.<br />
Example 11-16 The ifx_cursor.rb output<br />
C:\work>cat ifx_cursor.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect('stores_demo')<br />
# display database information<br />
print "Connected to #{db.version}\n"<br />
# Create a Cursor object<br />
cur = db.cursor(ARGV[0])<br />
# Open the cursor and fetch some rows<br />
cur.open.each {|rows|<br />
puts rows*" = "<br />
}.close<br />
# Close connection<br />
db.close<br />
C:\work>ruby ifx_cursor.rb "SELECT FIRST 3 sname FROM state"<br />
Connected to <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6<br />
Alaska<br />
Hawaii<br />
California<br />
C:\work><br />
Using the Ruby <strong>Informix</strong> IfxSlob class<br />
The Slob class is the Ruby interface for handling smart large objects. It provides<br />
methods for every action applicable with a smart large object. By using the<br />
<strong>Informix</strong>::Slob class, it is possible to perform the same operations against a<br />
smart large object as with other programming languages and drivers. The Slob<br />
methods such as Seek() or Lock() allow random I/O to individuals part of the<br />
large object that can only be achieved using this Ruby driver.<br />
Chapter 11. Working with Ruby on Rails 377
Example 11-17 shows how to select a BLOB from a database table. It retrieves<br />
the catalog_advert CLOB column from the catalog table and prints out the<br />
content of the large object and the size of the large object.<br />
Example 11-17 The ifx_blob.rb output<br />
C:\work>cat ifx_blob.rb<br />
# load the informix driver<br />
require 'informix'<br />
# Connect to the database<br />
db = <strong>Informix</strong>.connect("stores_demo")<br />
# Creates an <strong>Informix</strong> Slob object<br />
slob = <strong>Informix</strong>::Slob<br />
# Opens a cursor to retrieve<br />
cur = db.cursor("SELECT catalog_num,advert_descr FROM catalog WHERE<br />
catalog_num=?")<br />
cur.open(ARGV[0]).each {|rows|<br />
slob = rows[1].open<br />
print "Number = #{rows[0]}\n"<br />
# Reads the blob data as a String<br />
print "Clob data= #{rows[1].read(rows[1].size)}\n"<br />
print "Clob Size= #{rows[1].size}\n"<br />
# Close the Slob object<br />
slob.close<br />
}<br />
# Close database connection<br />
db.close<br />
C:\work><br />
C:\work>ruby ifx_blob.rb 10001<br />
Number = 10001<br />
Clob data= Brown leather. Specify first baseman's or infield/outfield style.<br />
Clob Size= 98<br />
C:\work>ruby ifx_blob.rb 10027<br />
Number = 10027<br />
Clob data=<br />
Double or triple crankset with choice of chainrings or chunky bacon. For double<br />
crankset...<br />
Clob Size= 154<br />
C:\work>ruby ifx_blob.rb 10031<br />
Number = 10031<br />
Clob data= No buckle so no plastic touches your chin. Meets both ANSI and<br />
Snell...<br />
Clob Size= 123<br />
C:\work><br />
378 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
For examples and a full description of all the method implemented by the<br />
<strong>Informix</strong>::Slob class, refer to:<br />
http://ruby-informix.rubyforge.org/doc/classes/<strong>Informix</strong>/Slob.html<br />
Using the Ruby <strong>Informix</strong> INTERVAL<br />
The Ruby <strong>Informix</strong> driver provides a specific class to deal with the <strong>Informix</strong><br />
INTERVAL data type.<br />
Example 11-18 shows how to define and use the <strong>Informix</strong>::Interval class. In<br />
this example we create an Interval Year to Month with one year and one month<br />
as the value. The code performs a simple arithmetic operation adding the Interval<br />
to the current date.<br />
Example 11-18 The ifx_interval.rb output<br />
# Creates an <strong>Informix</strong> Interval object<br />
minterval = <strong>Informix</strong>::Interval.year_to_month(1, 1)<br />
print "Interval \t=#{minterval}\n"<br />
today = Date.today<br />
print "Current date \t=#{today}\n"<br />
print"Interval+today\t=#{minterval + today}\n"<br />
C:\work>ruby ifx_interval.rb<br />
Interval =1-01<br />
Current date =2010-07-04<br />
Interval+today =2011-08-04<br />
C:\work><br />
You can fine the full documentation about all the methods supported by the Ruby<br />
<strong>Informix</strong> driver at:<br />
http://ruby-informix.rubyforge.org/doc/<br />
Using Data Server Ruby driver<br />
In this section, we discuss the basic database operation using the Data Server<br />
Ruby driver.<br />
Connecting to the database<br />
The syntax for opening a connection with an <strong>Informix</strong> database server using the<br />
Data Server Ruby driver differs from the syntax used by the Ruby <strong>Informix</strong> driver.<br />
Chapter 11. Working with Ruby on Rails 379
Example 11-19 shows a simple Ruby script that creates a connection object and<br />
opens the connection. In the example code we also retrieve information about<br />
the <strong>Informix</strong> server using the <strong>IBM</strong>_DB::server_info class.<br />
Example 11-19 The dsc_connect.rb script<br />
C:\work>cat dsc_connect.rb<br />
# load the informix driver<br />
require 'mswin32/ibm_db'<br />
# Connect to the database<br />
db = <strong>IBM</strong>_DB.connect(ARGV[0],ARGV[1],ARGV[2])<br />
info = <strong>IBM</strong>_DB.server_info(db)<br />
# display database information<br />
print "Connected to #{info.DBMS_NAME} #{info.DBMS_VER}"<br />
C:\work>ruby dsc_connect.rb testdsc informix password<br />
Connected to IDS/NT64 11.50.0000<br />
C:\work><br />
The Data Server Ruby driver is based on calls to the CLI driver, this means it<br />
takes the connection details from the db2cli.ini configuration file.<br />
Example 11-20 shows the contents of the db2cli.ini file that we used for this<br />
test.<br />
Example 11-20 The db2cli.ini file<br />
C:\work>type "c:\Documents and Settings\Administrator\db2cli.ini"<br />
[dsc_dsn]<br />
Protocol=TCPIP<br />
Port=9089<br />
Hostname=kodiak<br />
Database=stores_demo<br />
PWD=password<br />
UID=informix<br />
C:\work><br />
380 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Executing an SQL statement<br />
Example 11-21 demonstrates how to execute an SQL statement using the<br />
prepare() and execute() methods of the <strong>IBM</strong>_DB driver. Both methods,<br />
prepare() and execute(), require the connection and statement objects as<br />
parameters.<br />
Example 11-21 The dsc_execute.rb output<br />
C:\work>cat dsc_execute.rb<br />
# load the informix driver<br />
require 'mswin32/ibm_db'<br />
# Connect to the database<br />
db = <strong>IBM</strong>_DB.connect(ARGV[0],'','')<br />
info = <strong>IBM</strong>_DB.server_info(db)<br />
# display database information<br />
print "Connected to #{info.DBMS_NAME} #{info.DBMS_VER}\n"<br />
# create a prepare object with the SQL passed<br />
stmt = <strong>IBM</strong>_DB.prepare(db,ARGV[1])<br />
# Execute the prepared statement<br />
rc=<strong>IBM</strong>_DB.execute(stmt)<br />
print "Result=#{rc}"<br />
C:\work>ruby dsc_execute.rb dsc_dsn "DELETE from STATE WHERE code='NW'"<br />
Connected to IDS/NT64 11.50.0000<br />
Result=true<br />
C:\work><br />
Parametrized SQL statement<br />
When using parameters for an SQL statement, the application can use the<br />
execute() method. You can provide the parameters as a second argument for<br />
the method call.<br />
Example 11-22 insert a new record into the state table with the values passed to<br />
the script through the command line.<br />
Example 11-22 The dsc_param.rb output<br />
C:\work>cat dsc_param.rb<br />
# load the informix driver<br />
require 'mswin32/ibm_db'<br />
# Connect to the database<br />
db = <strong>IBM</strong>_DB.connect('dsc_dsn','','')<br />
info = <strong>IBM</strong>_DB.server_info(db)<br />
# display database information<br />
print "Connected to #{info.DBMS_NAME} #{info.DBMS_VER}\n"<br />
sql = 'INSERT INTO state(code, sname) VALUES (?,?)'<br />
Chapter 11. Working with Ruby on Rails 381
# create a prepare object<br />
stmt = <strong>IBM</strong>_DB.prepare(db,sql)<br />
# display Statement and parameters<br />
print "SQL=#{sql}\n"<br />
# Execute the prepared statement<br />
rc=<strong>IBM</strong>_DB.execute(stmt,[ARGV[0],ARGV[1]])<br />
print "Result=#{rc}"<br />
C:\work><br />
C:\work>ruby dsc_param.rb "NW" "NewState"<br />
Connected to IDS/NT64 11.50.0000<br />
SQL=INSERT INTO state(code, sname) VALUES (?,?)<br />
Result=true<br />
C:\work><br />
Selecting data<br />
The Data Server Ruby driver has several methods that allow retrieving data from<br />
the database, such as fetch_array(), fetch_assoc(), and fetch_row().<br />
Example 11-23 shows a simple Ruby script that returns the first two columns of<br />
an SQL SELECT statement passed through the command line. The script uses<br />
the fecth_array() method to retrieve the rows as an array object.<br />
Example 11-23 Select data using fetch_array()<br />
C:\work>cat dsc_fetch.rb<br />
# load the informix driver<br />
require 'mswin32/ibm_db'<br />
# Connect to the database<br />
db = <strong>IBM</strong>_DB.connect(ARGV[0],'','')<br />
info = <strong>IBM</strong>_DB.server_info(db)<br />
# display database information<br />
print "Connected to #{info.DBMS_NAME} #{info.DBMS_VER}\n"<br />
# create a prepare object with the SQL passed<br />
stmt = <strong>IBM</strong>_DB.prepare(db,ARGV[1])<br />
# Execute the prepared statement<br />
<strong>IBM</strong>_DB.execute(stmt)<br />
while row = <strong>IBM</strong>_DB.fetch_array(stmt)<br />
puts "#{row[0]}:#{row[1]}"<br />
end<br />
C:\work>ruby dsc_fetch.rb dsc_dsn "SELECT FIRST 3 code,sname FROM state"<br />
Connected to IDS/NT64 11.50.0000<br />
AK:Alaska<br />
HI:Hawaii<br />
CA:California<br />
C:\work><br />
382 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Using smart large objects<br />
The Data Server Ruby driver handles smart large objects as normal data types. It<br />
does not support all the smart features that are normally available with other<br />
drivers. However, it simplifies the code that is needed to deal with these data<br />
types.<br />
Example 11-24 retrieves a CLOB column from the catalog table and displays the<br />
contents.<br />
Example 11-24 The dsc_blob.rb file<br />
C:\work>cat dsc_blob.rb<br />
# load the informix driver<br />
require 'mswin32/ibm_db'<br />
# Connect to the database<br />
db = <strong>IBM</strong>_DB.connect('dsc_dsn','','')<br />
info = <strong>IBM</strong>_DB.server_info(db)<br />
# display database information<br />
print "Connected to #{info.DBMS_NAME} #{info.DBMS_VER}\n"<br />
# create a prepare object with the SQL passed<br />
sql = "SELECT catalog_num,advert_descr FROM catalog WHERE catalog_num=?"<br />
stmt = <strong>IBM</strong>_DB.prepare(db,sql)<br />
# Execute the prepared statement<br />
<strong>IBM</strong>_DB.execute(stmt,ARGV)<br />
while row = <strong>IBM</strong>_DB.fetch_array(stmt)<br />
puts "#{row[0]}:#{row[1]}"<br />
end<br />
C:\work>ruby dsc_blob.rb 10001<br />
Connected to IDS/NT64 11.50.0000<br />
10001:Brown leather. Specify first baseman's or infield/outfield style.<br />
Specify right- or left-handed.<br />
C:\work><br />
You can find additional documentation about the methods for the Ruby for <strong>IBM</strong><br />
Data Server at:<br />
http://rubyibm.rubyforge.org/docs/driver/2.0.0/doc/<br />
You can also find information in the <strong>IBM</strong> DB2 Information Center:<br />
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.swg.im.dbcli<br />
ent.ruby.doc/doc/c0052760.html<br />
Chapter 11. Working with Ruby on Rails 383
11.4 Using the Rails Adapter with Ruby <strong>Informix</strong><br />
In this section, we demonstrate how to create a basic web application using the<br />
Rails framework with the Ruby <strong>Informix</strong> Adapter.<br />
One of the key concepts of Rails is Convention over Configuration, meaning that<br />
you must follow the convention rules when designing your database so that the<br />
Rails framework can generate code automatically to handle typical operations<br />
with the database.<br />
The following convention rules are required by the Rails framework:<br />
► Table name must be a plural name for the entity it contains. For example, if<br />
the table contains information about books, it should be called Books.<br />
► The table must contains an unique primary key column and should be called<br />
ID.<br />
► You must create an <strong>Informix</strong> SQL SEQUENCE for each of the tables used for<br />
both Ruby drivers. The name of the sequence must be tablename_seq.<br />
You can find additional information about Rails conventions at:<br />
http://guides.rubyonrails.org/<br />
11.4.1 Creating database objects<br />
The following examples use the orders and items tables from the stores7<br />
database. Due to the convention rules used on Rails, we must modify the<br />
schema of the tables to follow the Rails conventions.<br />
Example 11-25 shows the SQL script we use to change the name of the two<br />
tables, create views to incorporate the ID column, and create the SQL sequence<br />
required for object reference.<br />
Example 11-25 The setup.sql script<br />
-- Orders table<br />
RENAME TABLE orders TO order;<br />
CREATE VIEW orders(<br />
id,<br />
order_date,<br />
customer_num,<br />
ship_instruct,<br />
backlog,<br />
po_num,<br />
ship_date,<br />
ship_weigh,<br />
384 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
ship_charge,<br />
paid_date) AS<br />
SELECT * FROM order;<br />
CREATE SEQUENCE orders_seq;<br />
-- Items table<br />
RENAME TABLE items TO item;<br />
CREATE VIEW items (<br />
id,<br />
order_num,<br />
stock_num,<br />
manu_code,<br />
quantity,<br />
total_price) AS<br />
SELECT * FROM item;<br />
CREATE SEQUENCE items_seq;<br />
11.4.2 Creating the Rails application<br />
You must create a Rails application using Rails commands before adding any<br />
objects or definitions.<br />
Example 11-26 shows the output of the rail stores command.<br />
Example 11-26 Output of the rail stores command<br />
C:\work>rails stores<br />
create<br />
create app/controllers<br />
create app/helpers<br />
create app/models<br />
create app/views/layouts<br />
create config/environments<br />
create components<br />
create db<br />
create doc<br />
create lib<br />
create lib/tasks<br />
create log<br />
create public/images<br />
create public/javascripts<br />
create public/stylesheets<br />
...<br />
Chapter 11. Working with Ruby on Rails 385
For more information, refer to:<br />
http://rubyonrails.org/documentation<br />
11.4.3 Modifying the database configuration file<br />
The database.yml file must include the connection details of the database.<br />
Example 11-27 shows the database configuration file used by our application.<br />
Example 11-27 The database.yml file<br />
C:\work\stores>type config\database.yml<br />
development:<br />
adapter: informix<br />
database: stores7<br />
pool: 5<br />
timeout: 5000<br />
server: demo_on<br />
username: informix<br />
password: password<br />
# Warning: The database defined as 'test' will be erased and<br />
# re-generated from your development database when you run 'rake'.<br />
# Do not set this db to the same as development or production.<br />
test:<br />
adapter: mysql<br />
database: stores7<br />
username: root<br />
password:<br />
host: localhost<br />
production:<br />
adapter: informix<br />
database: stores7<br />
pool: 5<br />
timeout: 5000<br />
server: demo_on<br />
username: informix<br />
password: password<br />
C:\work\stores><br />
386 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
11.4.4 Creating the Rails model and controllers<br />
To make Rails aware of the database tables, you create model and controller in<br />
your application. We use the model and controller Ruby script to create model<br />
and controller for the two tables used in our application.<br />
Example 11-28 shows the batch script used to generate the model and controller<br />
for the Items and Orders table.<br />
Example 11-28 The objects.cmd script<br />
ruby script\generate model Order<br />
ruby script\generate controller Order<br />
ruby script\generate model Item<br />
ruby script\generate controller Item<br />
Example 11-29 shows the output of each of the commands in the objects.cmd<br />
batch script.<br />
Example 11-29 Output of the objects.cmd script<br />
C:\work\stores>ruby script\generate model Order<br />
exists app/models/<br />
exists test/unit/<br />
exists test/fixtures/<br />
create app/models/order.rb<br />
create test/unit/order_test.rb<br />
create test/fixtures/orders.yml<br />
exists db/migrate<br />
create db/migrate/004_create_orders.rb<br />
C:\work\stores>ruby script\generate controller Order<br />
exists app/controllers/<br />
exists app/helpers/<br />
create app/views/order<br />
exists test/functional/<br />
create app/controllers/order_controller.rb<br />
create test/functional/order_controller_test.rb<br />
create app/helpers/order_helper.rb<br />
C:\work\stores>ruby script\generate model Item<br />
exists app/models/<br />
exists test/unit/<br />
exists test/fixtures/<br />
create app/models/item.rb<br />
create test/unit/item_test.rb<br />
create test/fixtures/items.yml<br />
Chapter 11. Working with Ruby on Rails 387
388 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
exists db/migrate<br />
create db/migrate/005_create_items.rb<br />
C:\work\stores>ruby script\generate controller Item<br />
exists app/controllers/<br />
exists app/helpers/<br />
create app/views/item<br />
exists test/functional/<br />
create app/controllers/item_controller.rb<br />
create test/functional/item_controller_test.rb<br />
create app/helpers/item_helper.rb<br />
C:\work\stores><br />
After the objects are created, you must modify the controller Ruby file for each<br />
object to build the object scaffold. We use Rails 1.2.6 on our examples. Rails 2.x<br />
does not support dynamic scaffolding. This means it cannot retrieve the<br />
information for the table column dynamically. In this case, the scaffold for the<br />
objects must be created manually while creating the model object. We add the<br />
instruction scaffold: object_name to each of the files for creating scaffold.<br />
Example 11-30 shows the Ruby script file for the Item and Order controllers.<br />
Example 11-30 Controller script<br />
C:\work\stores>cat app/controllers/order_controller.rb<br />
class OrderController < ApplicationController<br />
scaffold :Order<br />
end<br />
C:\work\stores>cat app/controllers/item_controller.rb<br />
class ItemController < ApplicationController<br />
scaffold :Item<br />
end<br />
C:\work\stores><br />
For more information about the changes in Rails 2.x, refer to:<br />
http://rubyonrails.org/documentation
11.4.5 Starting the Rails web server<br />
To start the Rails web server use the ruby script/server command.<br />
Example 11-31 demonstrates how to start the server.<br />
Example 11-31 Rails web server<br />
C:\work\stores>ruby script/server<br />
=> Booting WEBrick...<br />
=> Rails application started on http://0.0.0.0:3000<br />
=> Ctrl-C to shutdown server; call with --help for options<br />
[2010-07-05 10:40:00] INFO WEBrick 1.3.1<br />
[2010-07-05 10:40:00] INFO ruby 1.8.7 (2010-01-10) [i386-mingw32]<br />
[2010-07-05 10:40:00] INFO WEBrick::HTTPServer#start: pid=1092 port=3000<br />
11.4.6 Demonstrating website application<br />
At this point, Rails should have constructed the application for you, and you can<br />
browse and change the information for your table.<br />
We can open a web browser and navigate to the local web server to browse the<br />
the Order and Item tables:<br />
http://127.0.0.1:3000/item<br />
http://127.0.0.1:3000/order<br />
Chapter 11. Working with Ruby on Rails 389
Figure 11-1 shows the Listing items page. The page contains links to perform all<br />
the typical operations that are associated with a database table (select, insert,<br />
update, and delete).<br />
Figure 11-1 Item listing web page<br />
Figure 11-2 shows the New Item page with all the fields from the Item table ready<br />
to be used to insert a new record into the table.<br />
Figure 11-2 Item New web page<br />
390 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Figure 11-3 shows the Listing Orders page.<br />
Figure 11-3 List Order webpage<br />
The web applications development with Ruby on Rails was designed to be an<br />
effortless task. With just four commands and a little configuration, we created a<br />
website that can handle the common table operations for a database application.<br />
11.5 Using the Rails Adapter with <strong>IBM</strong>_DB<br />
In this section, we demonstrate how to create a basic web application using the<br />
Rails framework with the <strong>IBM</strong>_DB Adapter. The sample program is a simple<br />
telephone directory application for a user to list, add, update, and delete phone<br />
entries.<br />
Rails can be used to generate the Data Definition Language (DDL) for the<br />
database objects. We show how to create tables with Ruby on Rails.<br />
Chapter 11. Working with Ruby on Rails 391
11.5.1 Creating the Rails application<br />
Use the rails command to create the Rails application. We create our<br />
application sample in the C:\RailsProjects directory.<br />
Example 11-32 shows how to create a rails application and the rails command<br />
output of our application.<br />
Example 11-32 Creating a Rails application<br />
C:\RailsProjects>rails sample<br />
create<br />
create app/controllers<br />
create app/helpers<br />
create app/models<br />
create app/views/layouts<br />
create config/environments<br />
create config/initializers<br />
...<br />
11.5.2 Modifying the database configuration file<br />
Update the database.yml database configuration file with the database<br />
connectivity details.<br />
Example 11-33 shows the configuration file for our development database. The<br />
name of the adapter, ibm_db, correspond to the Ruby Adapter for <strong>IBM</strong> Data<br />
Servers.<br />
Example 11-33 The database.yml file<br />
development:<br />
adapter: ibm_db<br />
database: ruby<br />
username: informix<br />
password: Ifmx4you<br />
host: kefka.lenexa.ibm.com<br />
port: 9089<br />
# Warning: The database defined as "test" will be erased and<br />
# re-generated from your development database when you run "rake".<br />
# Do not set this db to the same as development or production.<br />
test:<br />
production:<br />
392 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
11.5.3 Creating model, control, and view<br />
Ruby on Rails worked on a Model, Control, View architecture. You can create<br />
model, control, and view components in stages. Here, we show a quick way of<br />
using the scaffold command to have Rails create the complete application,<br />
including all the directories and necessary files.<br />
The telephone table of our application use strings for first name, last name, and<br />
phone number. This table layout forms the model for our application. We specify<br />
this model right on the scaffold command as shown in Example 11-34.<br />
We invoke the command from the root directory of our project with the ruby<br />
script\generate script. The syntax for the scaffold options is:<br />
scaffold .. <br />
The output shows that Rails creates the necessary models, views and<br />
controllers. This command also generates the script to create the tables that are<br />
necessary to associate with the model.<br />
Example 11-34 Creating a scaffold<br />
C:\RailsProjects\sample>ruby script\generate scaffold phonedir<br />
first_name:string last_name:string phone:string<br />
exists app/models/<br />
exists app/controllers/<br />
exists app/helpers/<br />
create app/views/phonedirs<br />
exists app/views/layouts/<br />
exists test/functional/<br />
exists test/unit/<br />
create test/unit/helpers/<br />
exists public/stylesheets/<br />
create app/views/phonedirs/index.html.erb<br />
...<br />
dependency model<br />
exists app/models/<br />
exists test/unit/<br />
exists test/fixtures/<br />
create app/models/phonedir.rb<br />
create test/unit/phonedir_test.rb<br />
create test/fixtures/phonedirs.yml<br />
create db/migrate<br />
create db/migrate/20100704234009_create_phonedirs.rb<br />
Chapter 11. Working with Ruby on Rails 393
11.5.4 Migrating the model<br />
Ruby on Rails calls the creation of the database objects backing the model as<br />
migration. The scaffold command created a migrate script for creating the table<br />
attached to the model.<br />
Example 11-35 shows the db/migrate/20100704234009_create_phonedirs.rb<br />
migration file that is created by the scaffold command. This script has both<br />
create and drop sections, which means that you can roll back any migration. We<br />
created a model by the name phonedir. Ruby on Rails then created the table<br />
phonedirs, which is a plural form of the model name, which is the Ruby on Rails<br />
naming convention.<br />
Example 11-35 The phonedir migration file<br />
class CreatePhonedirs < ActiveRecord::Migration<br />
def self.up<br />
create_table :phonedirs do |t|<br />
t.string :first_name<br />
t.string :last_name<br />
t.string :phone<br />
t.timestamps<br />
end<br />
end<br />
def self.down<br />
drop_table :phonedirs<br />
end<br />
end<br />
Use the rake utility to migrate the file and create table in the database. The rake<br />
utility is a Ruby build script with capabilities similar to the make utility. You can use<br />
the rake utility to generate the database schema using a migration file.<br />
Example 11-36 shows how to run the rake script to create the model in the<br />
database.<br />
Example 11-36 Creating model<br />
C:\RailsProjects\sample>rake db:migrate<br />
(in C:/RailsProjects/sample)<br />
== CreatePhonedirs: migrating ================================================<br />
-- create_table(:phonedirs)<br />
-> 0.0781s<br />
== CreatePhonedirs: migrated (0.0781s) =======================================<br />
394 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 11-37 uses the <strong>Informix</strong> dbschema utility to export the table schema to<br />
check the tables created Ruby on Rails migration. We started with an empty<br />
database and the output shows that two tables were created:<br />
► schema_migrations<br />
Ruby on Rails uses this table to keep track of the various version of the table,<br />
which allows you to roll back to the previous version.<br />
► phonedirs<br />
In this application table, Ruby added a few columns that we did not specify:<br />
– id: This serial column is for the primary key required by Ruby on Rails.<br />
– created_at and updated_at: Ruby on Rails uses these optionally.<br />
Example 11-37 The dbschema on database Ruby output<br />
% dbschema -d ruby<br />
DBSCHEMA Schema Utility INFORMIX-SQL Version 11.50.FC7<br />
grant dba to "informix";<br />
...<br />
create table "informix".schema_migrations<br />
(<br />
version varchar(255) not null<br />
);<br />
...<br />
create table "informix".phonedirs<br />
(<br />
id serial not null ,<br />
first_name varchar(255),<br />
last_name varchar(255),<br />
phone varchar(255),<br />
created_at datetime year to fraction(5),<br />
updated_at datetime year to fraction(5),<br />
primary key (id)<br />
);<br />
...<br />
create unique index "informix".unique_schema_migrations on "informix"<br />
.schema_migrations (version) using btree ;<br />
Chapter 11. Working with Ruby on Rails 395
11.5.5 Starting the Rails web server<br />
Example 11-38 shows how to start the Rails WEBrick server from the root<br />
directory. The http port number is 3000.<br />
Example 11-38 Starting the WEBrick server<br />
C:\RailsProjects\sample>ruby script\server<br />
=> Booting WEBrick<br />
=> Rails 2.3.8 application starting on http://0.0.0.0:3000<br />
=> Call with -d to detach<br />
=> Ctrl-C to shutdown server<br />
[2010-07-04 17:25:22] INFO WEBrick 1.3.1<br />
[2010-07-04 17:25:22] INFO ruby 1.9.1 (2010-01-10) [i386-mingw32]<br />
[2010-07-04 17:25:22] INFO WEBrick::HTTPServer#start: pid=292 port=3000<br />
11.5.6 Checking the application from website<br />
You can start the web application using the local web server address:<br />
http://localhost:3000/phonedirs<br />
Figure 11-4 shows the initial screen that our application opened with<br />
http://localhost:3000/phonedirs.<br />
Figure 11-4 Initial screen of the application<br />
396 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
We add one phone entry to our directory as shown in Figure 11-5.<br />
Figure 11-5 Creating a new phone listing<br />
Figure 11-6 shows that a phone entry is added.<br />
Figure 11-6 Listing phonedir after the new entry<br />
For more information regarding the development with Ruby and Ruby on Rails,<br />
refer to:<br />
http://rubyonrails.org/documentation<br />
Chapter 11. Working with Ruby on Rails 397
398 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 12. <strong>Informix</strong> 4GL Web Services<br />
In this chapter, we introduce the new Web Services feature of <strong>IBM</strong> <strong>Informix</strong> 4GL.<br />
This chapter provides an overview and configuring and building Web Services<br />
using <strong>Informix</strong> 4GL.<br />
In this chapter, we discuss the following topics:<br />
► Basic concepts<br />
► Setup and configuration<br />
► <strong>Informix</strong> 4GL Web Services tools<br />
► Developing a web service with I4GL<br />
► Consuming a web service with I4GL<br />
► Troubleshooting<br />
12<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 399
12.1 Basic concepts<br />
In this section, we provide an introduction of the products and technologies that<br />
we discuss in this chapter.<br />
12.1.1 <strong>IBM</strong> <strong>Informix</strong> 4GL<br />
<strong>Informix</strong> 4GL is a programming language developed by <strong>IBM</strong> for interacting with<br />
<strong>Informix</strong> database servers. It provides a rich environment for easy development<br />
of relational database applications. <strong>Informix</strong> 4GL provides all the component<br />
needed to develop character based applications using an <strong>Informix</strong> database, for<br />
example, project management, reports, debugger, and so on.<br />
<strong>Informix</strong> 4GL supports compilers that can convert the I4GL applications to C<br />
language or to generate platform-independent pseudo code that can be executed<br />
using a I4GL runner.<br />
For more information about <strong>Informix</strong> 4GL, refer to the 4GL Reference Manual at:<br />
http://publib.boulder.ibm.com/infocenter/ifxhelp/v0/index.jsp?topic=/com.ibm.to<br />
ols.doc/4gl.html<br />
12.1.2 Service-oriented architecture and Web Services<br />
Service-oriented architecture (SOA) is an architectural style that provides<br />
methods for systems development and integration, allowing applications<br />
developed with different technologies or programing languages to exchange data<br />
with one another.<br />
This exchange of data is accomplished through the use of Web Services. Web<br />
services are saleable functions that can be accessed independent of platforms<br />
and programming languages. These functions take a set of inputs and return a<br />
set of outputs to accomplish a specific task.<br />
Refer to the Service Oriented Architecture — SOA portal for more information:<br />
http://www.ibm.com/software/solutions/soa/<br />
12.1.3 Web Services development<br />
You can create a web service with any web-aware technology. Java is the most<br />
common language that is used for Web Services development. However, you can<br />
create Web Services with other language and technologies, such as C or .NET<br />
400 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>IBM</strong> provides the following options for SOA development:<br />
► <strong>IBM</strong> SOA Sandbox<br />
► <strong>IBM</strong> Rational® Application Developer<br />
► eKit: Enterprise Architect for SOA<br />
You can develop a web service that requires the use of an <strong>IBM</strong> <strong>Informix</strong> database<br />
using the following languages:<br />
► Java, using any of the JDBC drivers for an <strong>Informix</strong> database<br />
► Any .NET language, using any of the <strong>Informix</strong> .NET providers available<br />
► <strong>IBM</strong> <strong>Informix</strong> 4GL<br />
12.1.4 <strong>Informix</strong> 4GL and Web Services<br />
12.1.5 Components<br />
Starting form version 7.50 of <strong>IBM</strong> <strong>Informix</strong>, 4GL developers can manage and<br />
create Web Services using the 4GL language.<br />
One of the key benefits of using <strong>Informix</strong> 4GL is the easy interaction with the<br />
<strong>Informix</strong> database server. The use of SQL statements to access database<br />
objects does not require any specific code as with other programming languages,<br />
because SQL is embedded in the I4GL language.<br />
The ability to create Web Services directly with I4GL allows the reuse of existing<br />
code. Existing solutions that are developed with I4GL can be converted to web<br />
solutions without much effort.<br />
With <strong>Informix</strong> 4GL, you can publish existing I4GL functions as Web Services and<br />
use existubg Web Services from any I4GL application.<br />
<strong>Informix</strong> 4GL uses the Axis2 web service wrapper API to implement the interface<br />
that is required to communicate between the web server and the <strong>Informix</strong> 4GL<br />
libraries.<br />
A typical I4GL web service solution includes the following components:<br />
► Apache AXIS2C server<br />
► Web service<br />
► <strong>Informix</strong> 4GL<br />
► <strong>Informix</strong> Database Server<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 401
A web service can perform the following operations:<br />
► Create is the process of creating the web service and publishing it to make it<br />
available to consumers.<br />
The term used in <strong>Informix</strong> 4GL for this task is Publish.<br />
► Consume is the process of using the web service, providing input parameters,<br />
and retrieving the result as output parameters for the function.<br />
The term used in <strong>Informix</strong> 4GL for this task is Subscribe.<br />
Axis2 C functions are used for both operations as the wrapper code between the<br />
web service and <strong>Informix</strong> 4GL.<br />
12.2 Setup and configuration<br />
In this section, we discuss the setup and configuration needed to develop Web<br />
Services with <strong>Informix</strong> 4GL.<br />
12.2.1 Prerequisites and supported platforms<br />
The following prerequisites are required to use Web Services with <strong>Informix</strong> 4GL:<br />
► Apache Axis2/C version 1.5.1 (bundled with <strong>Informix</strong> 4GL)<br />
► Apache Axis2/Java version 1.3.1 (bundled with <strong>Informix</strong> 4GL)<br />
► <strong>IBM</strong> <strong>Informix</strong> database server version 10 or later<br />
► Java Runtime Environment (JRE) 1.5 or later<br />
► Perl 5.8.8<br />
<strong>IBM</strong> <strong>Informix</strong> 4GL 7.50 is supported in the following platforms:<br />
► HP-IA 11.23 and 11.31<br />
► AIX 5.3 and 6.1<br />
► Solaris 5.9 or 5.10<br />
► Red Hat Enterprise Linux 4 and 5<br />
► SUSE Linux Enterprise Server 10<br />
Note: The Web Services feature was added to <strong>Informix</strong> 4GL version 7.50.xC1<br />
but was available only for Linux platforms. Since version 7.50.xC3, all the<br />
platforms listed previously are supported.<br />
402 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
12.2.2 Environment<br />
The utilities for using Web Services with <strong>Informix</strong> 4GL are installed in the same<br />
directory as <strong>Informix</strong> 4GL.<br />
We do not discuss how to install and set up <strong>Informix</strong> 4GL in this book. For more<br />
information, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.4gl_install<br />
.doc/fgl_ing_010.htm<br />
To use any of the I4GL Web Services tools, you must set the variables listed in<br />
Table 12-1 in the development environment. These variables define the location<br />
of those 4GL, Java, and Axis2 resources that you need for application<br />
development and deployment.<br />
Table 12-1 Environment variables<br />
Variable Description<br />
AXIS2C_HOME Specifies the Axis2 installation directory<br />
CLASSPATH Path to the required Java classes<br />
DBPATH Path for support files. Must be $INFORMIXDIR/etc<br />
INFORMIXDIR Directory where the 4GL files are installed<br />
INFORMIXSERVER Default database server<br />
INFORMIXSQLHOSTS Optional: Specifies the location of the sqlhosts<br />
file, which contains database connectivity<br />
information<br />
JAVA_HOME Must be set to point to JRE 1.5 or later<br />
Load Library Path<br />
for example: LD_LIBRARY_PATH<br />
Specifies which directories to search for client or<br />
shared <strong>IBM</strong> <strong>Informix</strong> general libraries<br />
PATH Specifies which directories to search for<br />
executable programs. Must include the following<br />
path:<br />
$INFORMIXDIR/bin and $JAVA_HOME/bin<br />
SOA_ERR_LOG Optional: Specifies the directory where the log file<br />
(w4glerr.log) is created, defaults to /tmp<br />
PROGRAM_DESIGN_DBS Optional: Database used for storing web service<br />
definitions, defaults to syspgm4gl<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 403
Example 12-1 shows a typical shell script to set up these variables.<br />
Example 12-1 The setup.ksh script<br />
AXIS2C_HOME=$INFORMIXDIR/AXIS2C<br />
AXJDIR=$INFORMIXDIR/AXIS2C/AXIS2JARS<br />
CLASSPATH=$AXJDIR/wsdl4j-1.6.2.jar:$AXJDIR/backport-util-concurrent-2.2.jar:$AX<br />
JDIR/XmlSchema.jar:$AXJDIR/XmlSchema-1.3.1.jar:$AXJDIR/xbean-2.2.0.jar:$AXJDIR/<br />
axiom-dom-1.3.1.jar:$AXJDIR/axiom-impl-1.3.1.jar:$AXJDIR/axiom-api-1.3.1.jar:$A<br />
XJDIR/neethi-1.3.1.jar:$AXJDIR/axis.jar:$AXJDIR/commons-logging.jar:$AXJDIR/wsd<br />
l2ws.jar:$AXJDIR/commons-discovery.jar:$AXJDIR/jaxrpc.jar:$AXJDIR/saaj.jar:$AXJ<br />
DIR/wsdl4j.jar:$AXJDIR/axis2-java2wsdl-1.3.1.jar:$AXJDIR/axis2-codegen-1.3.1.ja<br />
r:$AXJDIR/axis2-kernel-1.3.1.jar<br />
DBPATH=$INFORMIXDIR/etc<br />
LD_LIBRARY_PATH=$INFORMIXDIR/AXIS2C/lib:$LD_LIBRARY_PATH<br />
export AXIS2C_HOME AXJDIR CLASSPATH DBPATH LD_LIBRARY_PATH<br />
Some of the I4GL Web Services utilities, such as w4gl, keep design and<br />
configuration information in a database on the <strong>Informix</strong> database server. The<br />
default name for this database is syspgm4gl. The database is created the first<br />
time the w4gl tool is invoked. You can specify your own database name using the<br />
environment variable PROGRAM_DESIGN_DBS.<br />
12.3 <strong>Informix</strong> 4GL Web Services tools<br />
This section describes the tools that are available within <strong>Informix</strong> 4GL to publish,<br />
deploy, package, and subscribe Web Services.<br />
12.3.1 The w4glc Web Services compiler<br />
The w4glc Web Services compiler is a script based on Perl. The w4glc compiler<br />
performs all the required task to use Web Services within 4GL, from the creation<br />
process to deploying and packaging.<br />
The w4glc script is used by other I4GL utilities in a non-interactive way. Any error<br />
or failure that is generated during the execution of the script is written to the<br />
SOA_ERR_LOG log file.<br />
To execute the w4glc utility, use the following syntax:<br />
w4glc {-option} <br />
The w4glc utility does not keep any information in the database. It uses only<br />
configuration files. The configuration file is a text file that specifies the details<br />
about the I4GL function that is created as a web service. It contains information<br />
404 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
such as the location of the 4GL source files, input and output parameters, and<br />
database connectivity information.<br />
Table 12-2 lists the parameters that are available with the w4glc utility.<br />
Table 12-2 The w4glc utility parameter list<br />
Option Description<br />
check Reads the configuration file and performs basic checks, such<br />
as ensuring that the identified source files exist<br />
compile Compiles generated intermediate code<br />
deploy Deploys the web service on the AXIS2C server<br />
force Overwrites the existing service with identical name<br />
generate Generates the intermediate code for publish/subscribe<br />
help Provides basic help information<br />
keep Retains intermediate source files for troubleshooting<br />
package Bundles a web service for production deployment<br />
silent Generates code without on-screen display<br />
version Prints the version number<br />
Example 12-2 shows the output for the generate option.<br />
Example 12-2 The w4glc utility generate output<br />
informix@irk:/work$ w4glc -generate ./ws_zipcode_irk.4cf<br />
Begin environment check ...<br />
Environment check is completed.<br />
Generating code. Please wait ...<br />
Generating Wrapper code ....<br />
The wrapper file is /tmp/w4gl_informix/zipcode_details_wrap.c<br />
Generating WSDL ....<br />
Generating headers ....<br />
Generating skeletal code ....<br />
Code generation completed.<br />
Removing /tmp/w4gl_informix/zipcode_details_wrap.c ...<br />
Removing /tmp/w4gl_informix/zipcode_details.wsdl ...<br />
Removing /tmp/w4gl_informix/axis2_skel_ws_zipcode.h ...<br />
Removing /tmp/w4gl_informix/axis2_svc_skel_ws_zipcode.c ...<br />
Removing /tmp/w4gl_informix/services.xml ...<br />
Removing /tmp/w4gl_informix/axis2_skel_ws_zipcode.c ...<br />
informix@irk:/work$<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 405
12.3.2 The w4gl utility<br />
For additional information about the I4GL Web Services compiler, refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.4gl_admin.d<br />
oc/fgl_wsg_500.htm<br />
The character-based interface w4gl utility is the main tool when using Web<br />
Services with <strong>Informix</strong> 4GL. It allows you to perform the same task as the<br />
command line tool, w4glc, but it also manages the data within the program<br />
design database, syspgm4gl.<br />
The design database contains the following information about the Web Services<br />
environment:<br />
► Information for the host system, such as the host name or temporary<br />
directories<br />
► Location for the Axis2 web server<br />
► Details for the <strong>Informix</strong> servers to which the Web Services connects<br />
► Definition for Web Services, such as function names or parameters types<br />
The web service definition that is specified through the utility is saved in<br />
database tables and is available for future reuse and modification, thus reducing<br />
development effort.<br />
The w4gl utility uses the same I4GL character-based interface as other 4GL tools<br />
to accomplish the creation and consumption of Web Services.<br />
Example 12-3 shows the main menu of the w4gl utility.<br />
Example 12-3 The w4gl utility main menu<br />
+------------------------------------------------------------------------------+<br />
|W4GL: Publish Subscribe Host name App server Exit |<br />
|Create and Deploy web services from I4GL functions. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
406 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
The w4gl utility offers the following options:<br />
► Use the Publish option to create a new web service.<br />
► Use the Subscribe option to consume a web service.<br />
► Use the Host name option to managed the host information that is stored in<br />
the design database.<br />
► Use the Application server option to managed the Axis2 information that is<br />
stored in the design database.<br />
For a detailed description of all the w4gl utility menu options, refer to the 4GL<br />
Web Services Administration Guide, which is available at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
4gl_admin.doc/fgl_wsg_006.htm<br />
12.3.3 Web Services Description Language Parser (wsdl_parser)<br />
Web Services Description Language (WSDL) is an XML-based language for<br />
describing network services. You can use WSDL to describe all the functions and<br />
parameters for a web service.<br />
When you subscribe a web service, the wsdl_parser tool parses the WSDL file to<br />
retrieve all the information that is required to define a web service.<br />
The syntax to invoke the wsdl_parser tool is:<br />
wsdl_parser sid wsdl_path ws_func i4gl_func target_dir target_file<br />
Table 12-3 describes each parameter. All of the parameters are required for the<br />
tool to work correctly.<br />
Table 12-3 The wsdl_parser tool parameters list<br />
Parameter Description<br />
sid An integer that uniquely identifies the subscriber<br />
wsdl_path Location of the file that describes the complete description of the<br />
web service; can be an online or a local copy of the WSDL file<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 407
Parameter Description<br />
ws_func Function to consume within the designated web service<br />
i4gl_func Name for the wrapper function to be used by the I4GL program<br />
target_dir The path where files are stored while the web service is being<br />
consumed<br />
target_file The file name that contains the generated subscriber client code<br />
Note: The sid parameter is used only when you invoke the wsdl_parser from<br />
the w4gl tool. This parameter is ignored if you use wsdl_parser from the<br />
command line.<br />
Example 12-4 shows the wsdl_parser tool used to generate the configuration file<br />
for a ws_zipcode web service. This example passes the WSDL definition directly<br />
from a web server as the following wsdl_path parameter:<br />
http://irk:9876/axis/services/ws_zipcode?wsdl<br />
Example 12-4 The wsdl_parser tool output<br />
informix@irk:/work$ wsdl_parser 0 http://irk:9876/axis/services/ws_zipcode?wsdl<br />
zipcode_details zipcode4gl `pwd`/publish zipcode4gl.c<br />
informix@irk:/work$ ls publish<br />
local.wsdl zipcode4gl.c_zipcode4gl.4cf<br />
informix@irk:/work$<br />
408 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
12.3.4 I4GL Web Services process<br />
Figure 12-1 illustrates how the I4GL Web Services tools work together.<br />
Figure 12-1 I4GL Web Services tools<br />
12.4 Developing a web service with I4GL<br />
In this section, we demonstrate the steps that are required to create a web<br />
service using <strong>IBM</strong> <strong>Informix</strong> 4GL. Publish and subscribe are two operations to<br />
perform with a web service:<br />
► Publish<br />
The tasks required to create a new web service with the w4gl utility are:<br />
a. Add the Host name and an Axis2 server information.<br />
b. Add the web service definition details.<br />
c. Generate the web service configuration file for publishing.<br />
d. Deploy a web service by registering the service in the Axis2 web server.<br />
e. Package a web service as a single file (a .tar file) that is ready for the<br />
production server.<br />
► Subscribe<br />
The tasks needed to consume a web service are with the w4gl tool are:<br />
a. Add details for the web service to consume.<br />
b. Compile the Web Services wrapper code that generates the configuration<br />
file for subscription.<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 409
In the remaining sections, we describe how to publish a simple I4GL function as<br />
a web service and show that the function can be used from other languages such<br />
as Java.<br />
12.4.1 Example I4GL function<br />
We use a basic I4GL function, state_name(), for the web service. This function<br />
connects to the database server and retrieves the name for a specific state code.<br />
Example 12-5 shows the 4GL code state_name() saved in the state_name.4gl<br />
file. This function queries the state table from the stores_demo database. The<br />
state_name() function takes one input parameter state code of type CHAR(2)<br />
and returns the state name that has CHAR(15) data type.<br />
Example 12-5 The state_name.4gl file<br />
FUNCTION state_name(code)<br />
DEFINE state_rec RECORD<br />
code CHAR(2),<br />
sname CHAR(15)<br />
END RECORD,<br />
code CHAR(2),<br />
sel_stmt CHAR(100);<br />
LET sel_stmt= "SELECT code, sname FROM state WHERE code = ?";<br />
PREPARE st_id FROM sel_stmt;<br />
DECLARE cur_id CURSOR FOR st_id;<br />
OPEN cur_id USING code;<br />
FETCH cur_id INTO state_rec.*;<br />
CLOSE cur_id;<br />
FREE cur_id;<br />
FREE st_id;<br />
RETURN state_rec.sname<br />
END FUNCTION<br />
To ensure that the 4GL code is correct, we compile the function with the I4GL<br />
compiler, c4gl, as shown in Example 12-6.<br />
Example 12-6 Compile 4GL function<br />
informix@irk:/work$ c4gl -c state_name.4gl<br />
informix@irk:/work$<br />
410 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
12.4.2 Host and application details<br />
Before adding any of the details for the web service, we must provide details<br />
about the system where the web service will run and the Axis2 server that is<br />
used. To complete these tasks, we use the “Host name” and “Application server”<br />
menus from the w4gl utility.<br />
Example 12-7 shows the HOST INFORMATION form with the host name irk and<br />
the /tmp/w4gl_informix temporary directory.<br />
Example 12-7 Host name menu<br />
+------------------------------------------------------------------------------+<br />
|HOST NAME: Query Next Previous Add Modify Remove Exit |<br />
|See the existing host name details. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| HOST INFORMATION |<br />
| |<br />
| Machine ID [ 1] |<br />
| Host Name [irk ] |<br />
| Temporary Directory [/tmp/w4gl_informix ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
Host information includes the following fields:<br />
► Machine ID is an automatic identifier number.<br />
► Host Name is the system name where the Axis2 server is installed.<br />
► Temporary Directory is the directory that is used by the w4glc utility for<br />
creating temporary files.<br />
After you define the host information, add the information for the Axis2<br />
application server using the APP SERVER menu.<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 411
Example 12-8 shows the APP SERVER form.<br />
Example 12-8 Application menu<br />
+------------------------------------------------------------------------------+<br />
|APP SERVER: Query Next Previous Add Modify Remove Exit |<br />
|Go to the next app server |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| APP SERVER INFORMATION |<br />
| Server ID [ 1] |<br />
| Server Name [axis ] |<br />
| Host Name [irk ] |<br />
| Port Number [ 9876] |<br />
| ENVIRONMENT VARIABLES |<br />
| INFORMIXDIR [/usr3/4gl750 ] |<br />
| INFORMIXSQLHOSTS [/usr3/sqlhosts ] |<br />
| CLIENT_LOCALE [en_us.utf8 ] |<br />
| DBDATE [Y4MD- ] |<br />
| Notes [ ] |<br />
| [ ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
An application server is identified by the following fields:<br />
► Server ID is an automatic identifier number.<br />
► Server Name is the name of the Axis2 server.<br />
► Host Name is the system name where the Axis2 server runs.<br />
► Port Number is the port used for incoming connections to the web service.<br />
The APP SERVER option is also used to stored specific environment information<br />
for the application server. This information is required because the Axis2 server<br />
binary loads the I4GL libraries that might need additional resource files located in<br />
the $INFORMIXDIR directory.<br />
12.4.3 Definition of the web service<br />
In this section, we demonstrate how to define a web service from the w4gl utility.<br />
To define a web service:<br />
1. Add the <strong>IBM</strong> <strong>Informix</strong> database that the web service will use.<br />
2. Add the definition for the web service, such as the name of the service or the<br />
name of the 4GL function.<br />
412 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
3. Add specific details for the web service such as input and output variables.<br />
4. Add the location of the 4GL source code file.<br />
Add <strong>Informix</strong> database details<br />
You add <strong>Informix</strong> database details using the Database menu from the w4gl utility.<br />
Example 12-9 shows the DATABASE INFORMATION form with details of the<br />
database that we used in our example.<br />
Example 12-9 Database option<br />
+------------------------------------------------------------------------------+<br />
|DATABASE: Query Next Previous Add Modify Remove Exit |<br />
|Go to the previous database record. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| DATABASE INFORMATION |<br />
| |<br />
| Database ID [ 1] |<br />
| Database Name [stores7 ] |<br />
| Database Server [irk1150 ] |<br />
| IDS Version [11.50 ] |<br />
| DB_LOCALE [en_US.819 ] |<br />
| Notes [ ] |<br />
| [ ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
This menu option uses the following fields:<br />
► Database ID is an automatic number that identifies a database definition.<br />
► Database name defines the database name to which the web server connects.<br />
► IDS Version is the <strong>Informix</strong> database server version number.<br />
► DB_LOCALE is the locale of the database. The default DB_LOCALE is en_US.819.<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 413
Add details about the web service<br />
You can add details about the web service using the Add option in the web<br />
service menu. Example 12-10 shows this option.<br />
Example 12-10 Add the web service menu<br />
+------------------------------------------------------------------------------+<br />
|WEB SERVICE: Query Next Previous Add Modify Remove Install ... |<br />
|Specify a new service record. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| |<br />
| |<br />
First, you add the web service name and the function name using the Detail<br />
menu option. Example 12-11 shows this input form with the details of our web<br />
service.<br />
Example 12-11 Web service details<br />
+------------------------------------------------------------------------------+<br />
|ADD: Detail Variable File Exit |<br />
|Specify the web service parameters. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| Webservice ID [ 4] |<br />
| Webservice Name [ws_statename ] |<br />
| Function Name [state_name ] |<br />
| Notes [Returns the state name for a given code ] |<br />
| [ ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
The following input details on this form describes a web service:<br />
► Webservice ID is an automatically generated identifier for the web service.<br />
► Webservice Name is the name of the web service.<br />
► Function Name is the he name of the 4GL function.<br />
414 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Input and output variables<br />
Next, you add the input and output variables that the web service uses with the<br />
Variable menu option. Example 12-12 shows this menu option. Our function<br />
takes an CHAR(2) input parameter and returns a CHAR(15) value.<br />
Example 12-12 Web service VARIABLE option<br />
+------------------------------------------------------------------------------+<br />
|VARIABLE: Input Output Exit |<br />
|Exit the Variable menu. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| [ 1] Input parameter - Variable name Data type |<br />
| |<br />
| [ 1][code ][CHAR(2) ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| |<br />
| [ 1] Output parameter - Variable name Data type |<br />
| |<br />
| [ 1][sname ][CHAR(15) ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| [ ][ ][ ] |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 415
Source file information<br />
Finally, you define a web service. We stored the location of the I4GL source file<br />
that contains our function using the File menu option. Example 12-13 shows the<br />
fields used by the File option.<br />
Example 12-13 Web service File option<br />
+------------------------------------------------------------------------------+<br />
|ADD: Detail Variable File Exit |<br />
|Exit the web services Add menu. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| Service Name [ws_state_name ] |<br />
| Function Name [state_name ] |<br />
| |<br />
| File Number [ 1] |<br />
| Directory [/work ] |<br />
| File Name [state_name.4gl ] |<br />
| |<br />
| File Number [ ] |<br />
| Directory [ ] |<br />
| File Name [ ] |<br />
| |<br />
| File Number [ ] |<br />
| Directory [ ] |<br />
| File Name [ ] |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
An I4GL function can require more than one I4Gl file. You must supply all the<br />
required file names and their directories. We supplied the 4GL file<br />
state_name.4gl that contains the state_name() function.<br />
12.4.4 Generate the configuration file<br />
With the details of the web service stored in the design database, you can then<br />
generate the configuration file (.4cf) for a Publish operation. The Generate<br />
option is inside the Install menu option.<br />
416 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Example 12-14 shows the Generate form. You must complete every field in the<br />
form. If you stored the definition for these items in the design database, you can<br />
retrieve them using the Ctrl+B key shortcut.<br />
Example 12-14 Generate form<br />
+------------------------------------------------------------------------------+<br />
|INSTALL: Generate Deploy Package Exit |<br />
|Generate the configuration file for a web service. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| +-----------------------------------------------------+ |<br />
| |DATABASES: Query Previous query Exit | |<br />
| Service ID [ 4] M|Make a new selection | |<br />
| |-----------------------------------------------------| |<br />
| Service Name [ws_s| DATABASE INFORMATION | |<br />
| Host Name [irk | | |<br />
| Temp Directory [/tmp|ID Database Name | |<br />
| [ |[ 1] [stores7 ]| |<br />
| App-Server Name [axis|[ ] [ ]| |<br />
| Port Number [ 98|[ ] [ ]| |<br />
| Database Name [ |[ ] [ ]| |<br />
| Database Server [ |[ ] [ ]| |<br />
| |[ ] [ ]| |<br />
| | | |<br />
| |Arrow key - Press Esc to Accept or press Ctrl+C to Ca| |<br />
| +-----------------------------------------------------+ |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
After you enter all the information, use the Generate option to create the<br />
configuration file. Example 12-15 shows the output of the Generate option.<br />
Example 12-15 Generate option<br />
+------------------------------------------------------------------------------+<br />
|INSTALL: Generate Deploy Package Exit |<br />
|Generate the configuration file for a web service. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| GENERATE CONFIGURATION |<br />
| |<br />
| Service ID [ 4] Machine ID [ 1] Server ID [ 1] Database ID [ 1] |<br />
| |<br />
| Service Name [ws_state_name ] |<br />
| Host Name [irk ] |<br />
| Temp Directory [/tmp/w4gl_informix ] |<br />
| [ ] |<br />
| App-Server Name [axis ] |<br />
| Port Number [ 9876] |<br />
| Database Name [stores7 ] |<br />
| Database Server [irk1150 ] |<br />
| |<br />
| Generated configuration file ws_state_name_irk.4cf |<br />
+------------------------------------------------------------------------------+<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 417
The Generate operation creates the configuration file that is required for<br />
publishing a web service. The name of the file is constructed using the web<br />
service name, the host name, and a .4cf extension. In our example, the name of<br />
the file is ws_state_name_irk.4cf.<br />
Example 12-16 shows the configuration file for the ws_state_name web service.<br />
Example 12-16 The ws_state_name_irk.4cf file<br />
[SERVICE]<br />
TYPE = publisher<br />
INFORMIXDIR = /usr3/4gl750uc3<br />
DATABASE = stores7<br />
CLIENT_LOCALE = en_us.utf8<br />
DB_LOCALE = en_US.819<br />
INFORMIXSERVER = irk1150<br />
HOSTNAME = irk<br />
PORTNO = 9876<br />
I4GLVERSION = 7.50.xC3<br />
WSHOME = /usr3/4gl750uc3/AXIS2C<br />
WSVERSION = axis<br />
TMPDIR = /tmp/w4gl_informix<br />
SERVICENAME = ws_state_name<br />
[FUNCTION]<br />
NAME = state_name<br />
[INPUT]<br />
[VARIABLE] NAME = code TYPE = CHAR(2) [END-VARIABLE]<br />
[END-INPUT]<br />
[OUTPUT]<br />
[VARIABLE] NAME = sname TYPE = CHAR(15) [END-VARIABLE]<br />
[END-OUTPUT]<br />
[END-FUNCTION]<br />
[DIRECTORY]<br />
NAME = /work<br />
FILE = state_name.4gl,<br />
[END-DIRECTORY]<br />
[END-SERVICE]<br />
You can use a configuration file (4cf) with the w4glc utility to perform tasks, such<br />
as generate and compile, directly from the command line. Example 12-17 shows<br />
how to perform all the steps that the w4gl deploy options performs using the<br />
w4glc utility.<br />
Example 12-17 Using w4glc<br />
informix@irk:/work$ w4glc -generate -compile -deploy ws_state_name_irk.4cf<br />
Begin environment check ...<br />
Environment check is completed.<br />
Generating code. Please wait ...<br />
418 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Generating Wrapper code ....<br />
The wrapper file is /tmp/w4gl_informix/state_name_wrap.c<br />
Generating WSDL ....<br />
Generating headers ....<br />
Generating skeletal code ....<br />
Code generation completed.<br />
Generating shared object for service ws_state_name ....<br />
Compiling code. Please wait...<br />
Executing: c4gl --shared -o /tmp/w4gl_informix/libws_state_name.so<br />
-I/usr3/4gl750/AXIS2C/include/axis2-1.5.0 -L/usr3/4gl750/AXIS2C/lib<br />
-laxis2_engine -laxutil -laxis2_axiom /tmp/w4gl_informix/state_name_wrap.c<br />
/tmp/w4gl_informix/axis2_skel_ws_state_name.c<br />
/tmp/w4gl_informix/axis2_svc_skel_ws_state_name.c /work/state_name.4gl<br />
/usr3/4gl750/lib/tools/w4glutil.a /usr3/4gl750/lib/tools/lib4gl.a<br />
Compilation done.<br />
Deploying service ws_state_name ...<br />
Deploying the service. Please wait ...<br />
Copying /tmp/w4gl_informix/libws_state_name.so ...<br />
Copying /tmp/w4gl_informix/services.xml ...<br />
Copying /tmp/w4gl_informix/state_name.wsdl ...<br />
Service name: ws_state_name<br />
The wrapper file is /tmp/w4gl_informix/state_name_wrap.c<br />
Generating WSDL ....<br />
Generating headers ....<br />
Generating skeletal code ....<br />
Code generation completed.<br />
Generating shared object for service ws_state_name ....<br />
Compiling code. Please wait...<br />
Executing: c4gl --shared -o /tmp/w4gl_informix/libws_state_name.so<br />
-I/usr3/4gl750/AXIS2C/include/axis2-1.5.0 -L/usr3/4gl750/AXIS2C/lib<br />
-laxis2_engine -laxutil -laxis2_axiom /tmp/w4gl_informix/state_name_wrap.c<br />
/tmp/w4gl_informix/axis2_skel_ws_state_name.c<br />
/tmp/w4gl_informix/axis2_svc_skel_ws_state_name.c /work/state_name.4gl<br />
/usr3/4gl750/lib/tools/w4glutil.a /usr3/4gl750/lib/tools/lib4gl.a<br />
Compilation done.<br />
Deploying service ws_state_name ...<br />
Deploying the service. Please wait ...<br />
Copying /tmp/w4gl_informix/libws_state_name.so ...<br />
Copying /tmp/w4gl_informix/services.xml ...<br />
Copying /tmp/w4gl_informix/state_name.wsdl ...<br />
Service name: ws_state_name<br />
Deployed at : /usr3/4gl750/AXIS2C/services/ws_state_name<br />
Removing /tmp/w4gl_informix/state_name_wrap.c ...<br />
Removing /tmp/w4gl_informix/state_name.wsdl ...<br />
Removing /tmp/w4gl_informix/axis2_skel_ws_state_name.h ...<br />
Removing /tmp/w4gl_informix/axis2_svc_skel_ws_state_name.c ...<br />
Removing /tmp/w4gl_informix/services.xml ...<br />
Removing /tmp/w4gl_informix/axis2_skel_ws_state_name.c ...<br />
Removing /tmp/w4gl_informix/libws_state_name.so ...<br />
The output of the command line tool shows all the steps completed while creating<br />
and compiling the wrapper function. When errors occur during the deployment<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 419
process, running each individual step with the w4glc utility might help to diagnose<br />
the reason for the problem.<br />
12.4.5 Deployment of the web service<br />
This process generates the auxiliary code that is required to link the Axis2<br />
application server with the I4GL function. During deployment, the C code for the<br />
web service is created and compiled automatically using the 4GL libraries. The<br />
result of this process is a shared library ready that can be used in the application<br />
server.<br />
This process also creates a WSDL file for the web service and copies this file,<br />
together with the web service shared library, into the application server directory.<br />
The Deploy option is part of the Install menu. Example 12-18 shows the<br />
deployment of the ws_state_name web service.<br />
Example 12-18 Deploy form<br />
+------------------------------------------------------------------------------+<br />
|INSTALL: Generate Deploy Package Exit |<br />
|Deploy the web service. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| CONFIGURATION TO DEPLOY |<br />
| |<br />
| File Name [ws_state_name_irk.4cf ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| Deployed ws_state_name. |<br />
+------------------------------------------------------------------------------+<br />
420 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
After a successful deployment, the web service is ready to be consume by any<br />
application. The files are copied into the Axis2 services directory automatically.<br />
Example 12-19 shows the web service files on the application server.<br />
Example 12-19 Application Server services directory<br />
informix@irk:/work$ ls $INFORMIXDIR/AX*/services/ws_state_name<br />
libws_state_name.so services.xml state_name.wsdl<br />
informix@irk:/work$<br />
12.4.6 Packaging of the web service<br />
Packaging a web service is the process of creating a compressed file with all the<br />
components that are required by the web service. You can use the compressed<br />
file, in .tar format, on other Axis2 servers. Example 12-20 shows the Package<br />
menu option.<br />
Example 12-20 Package option<br />
+------------------------------------------------------------------------------+<br />
|INSTALL: Generate Deploy Package Exit |<br />
|Package the web service. |<br />
|[1 of 1]------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| SERVICE PACKAGING |<br />
| |<br />
| File Name [ws_state_name_irk.4cf ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| Packaging successful. Check $TMPDIR directory (in config file) for .tar file |<br />
+------------------------------------------------------------------------------+<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 421
The compressed file is left in the $TMPDIR directory. Example 12-21 shows the<br />
contents of the packaged file for the ws_state_name web service.<br />
Example 12-21 Packaged .tar file<br />
informix@irk:/work$ tar tvf /tmp/w4gl_informix/ws_state_name.tar<br />
drwxr-xr-x informix/informix 2010-07-05 21:01 ws_state_name/<br />
-rw-r--r-- informix/informix 2010-07-05 21:01 ws_state_name/services.xml<br />
-rw-r--r-- informix/informix 2010-07-05 21:01 ws_state_name/state_name.wsdl<br />
-rwxr-xr-x informix/informix 2010-07-05 21:01 ws_state_name/libws_state_name.so<br />
informix@irk:/work$<br />
12.4.7 Starting the Axis2 application server<br />
An Axis2 application server is included with the <strong>Informix</strong> 4GL package. It is<br />
located in the AXIS2C directory under the $INFORMIXDIR directory.<br />
The Axis2 application server must be started to consume a web service. It is not<br />
required during the development or deploy process.<br />
Example 12-22 demonstrates how to start the Axis2 application server.<br />
Example 12-22 Starting Axis2<br />
informix@irk:/usr3/4gl750/AXIS2C/bin$ ./axis2_http_server -p 9876<br />
Started Simple Axis2 HTTP Server ...<br />
For more information about the Axis2 application server, refer to:<br />
http://ws.apache.org/axis2/c/docs/axis2c_manual.html<br />
12.4.8 Consuming the I4GL web service<br />
The main feature of a web service is that it can be used by any application in any<br />
platform. A web service is not tied to the technology or programming language<br />
that is used to develop the service. Any application that supports SOAP can<br />
make use of the functions that are implemented inside a web service. SOAP is a<br />
simple XML-based protocol that is used to exchange information over an HTTP<br />
link.<br />
In this section, we demonstrate how to use the ws_state_name web service using<br />
a basic Java application.<br />
422 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Getting a list of available Web Services<br />
You can get a list of available Web Services in the Axis2 application server by<br />
opening a web browser and connecting to the Axis2 server:<br />
http://hostname:port/axis/services<br />
Figure 12-2 shows the Web Services that are available in our example.<br />
Figure 12-2 Axis2 Web Services<br />
You can use the ?wsdl keyword to retrieve the WSDL file as follows:<br />
http://hostname:port/axis/services/ws_state_name?wsdl<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 423
Figure 12-3 shows the WSDL information for the ws_state_name service.<br />
Figure 12-3 WSDL information for the ws_state_name service<br />
Java application<br />
The main advantages of Web Services is that the application that uses the<br />
services does not need to know anything about how the service is implemented.<br />
The information needed for consuming a web service is defined in the WSDL file,<br />
such as the name of the operations that it supports and the parameters that it<br />
requires.<br />
Example 12-23 demonstrates how to use the ws_state_name I4GL web service<br />
from a simple Java application.<br />
Example 12-23 The state_name.java file<br />
informix@irk:/work$ cat state_name.java<br />
import org.apache.axis.client.Call;<br />
import org.apache.axis.client.Service;<br />
import javax.xml.namespace.QName;<br />
public class state_name {<br />
public static void main(String [] args) {<br />
try {<br />
String endpoint = "http://irk:9876/axis/services/ws_state_name";<br />
String qname = "http://www.ibm.com/ws_state_name";<br />
424 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
Service service = new Service();<br />
Call call = (Call) service.createCall();
}<br />
call.setTargetEndpointAddress( new java.net.URL(endpoint) );<br />
call.setOperationName(new QName(qname, "state_name"));<br />
String ret = (String) call.invoke( new Object[] { args[0] } );<br />
System.out.println("Sent 'CA', got '" + ret + "'");<br />
} catch (Exception e) {<br />
System.err.println(e.toString());<br />
}<br />
}<br />
informix@irk:/work$ javac state_name.java<br />
informix@irk:/work$ java state_name CA<br />
Sent 'CA', got 'California '<br />
informix@irk:/work$ java state_name AR<br />
Sent 'CA', got 'Arkansas '<br />
informix@irk:/work$<br />
12.5 Consuming a web service with I4GL<br />
In this section, we demonstrate how to consume a web service using <strong>IBM</strong><br />
<strong>Informix</strong> 4GL.<br />
12.5.1 Web service to consume<br />
You can use the w4gl utility to insert details about the specific web service that<br />
you want to consume. The Subscribe menu option allows you to manage<br />
definitions for Web Services in the design database.<br />
Example 12-24 shows the Subscribe form with the details referring to the<br />
ws_state_name web service.<br />
Example 12-24 Subscribe form<br />
+------------------------------------------------------------------------------+<br />
|SUBSCRIBE: Query Next Previous Add Modify Remove Compile Exit |<br />
|Specify a new web service definition. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| Subscription ID [ 3] |<br />
| WSDL Path [http://irk:9876/axis/services/ws_state_name?wsdl ] |<br />
| [ ] |<br />
| Webservice Function[state_name ] |<br />
| I4GL Function [statename ] |<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 425
| Target Directory [/work/publish ] |<br />
| Target File Name [statename ] |<br />
| Notes [Wrapper for ws_state_name Web Service ] |<br />
| [ ] |<br />
| [ ] |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
This form includes the following details:<br />
► Subscription ID is an automatic generated identifier of the web service.<br />
► WSDL Path is the complete path of the WSDL file for the service. This path can<br />
be a local file or a URL.<br />
► Web service Function is the function name that is provided by the web<br />
service.<br />
► I4GL Function is the name of the wrapper C function. This name is the<br />
function that our I4GL used to invoke the web service operation.<br />
► Target Directory is the directory where the wrapper and configuration files<br />
are created.<br />
► Target File Name is the name of the C source file with the I4GL function.<br />
The configuration file for a publish operation is generated automatically by the<br />
w4gl utility, but you can also create it using the wsdl_parser as shown in<br />
Example 12-25.<br />
Example 12-25 Generating the configuration file<br />
informix@irk:/work$ wsdl_parser 0<br />
http://irk:9876/axis/services/ws_state_name?wsdl state_name wsstatecode<br />
`pwd`/publish statecode.c<br />
informix@irk:/work$ ls publish<br />
local.wsdl statecode.c_wsstatecode.4cf<br />
informix@irk:/work$ cat publish/statecode.c_wsstatecode.4cf<br />
[SERVICE]<br />
TYPE = subscriber<br />
I4GLVERSION = 7.50.xC4<br />
WSHOME = 0<br />
TARGET_DIR = /work/publish<br />
I4GL_FUNCTION = wsstatecode<br />
TARGET_FILE = statecode.c<br />
[WSDL_INFO]<br />
426 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
WSDL_PATH = /work/publish/local.wsdl<br />
WSDL_NAME_SPACE = http://www.ibm.com/state_name<br />
[FUNCTION]<br />
SERVICENAME = ws_state_name<br />
NAME = state_name<br />
[INPUT]<br />
[VARIABLE] NAME = code TYPE = char(2) [END-VARIABLE]<br />
[END-INPUT]<br />
[OUTPUT]<br />
[VARIABLE] NAME = sname TYPE = char(15) [END-VARIABLE]<br />
[END-OUTPUT]<br />
[END-FUNCTION]<br />
[END-WSDL_INFO]<br />
[END-SERVICE]<br />
12.5.2 Compiling the wrapper code<br />
Next, you need to generate and compile the wrapper code using the Compile<br />
menu option of the w4gl utility. Example 12-26 shows the w4gl utility after the<br />
wrapper code is compiled for the ws_state_name web service.<br />
Example 12-26 Compile option<br />
+------------------------------------------------------------------------------+<br />
|SUBSCRIBE: Query Next Previous Add Modify Remove Compile Exit |<br />
|Compile the subscriber client code. |<br />
|--------------------------------[ irkpgm4gl ]-------------------[Help: CTRL-W]|<br />
| |<br />
| Subscription ID [ 3] |<br />
| WSDL Path [http://irk:9876/axis/services/ws_state_name?wsdl ] |<br />
| [ ] |<br />
| Webservice Function[state_name ] |<br />
| I4GL Function [statename ] |<br />
| Target Directory [/work/publish ] |<br />
| Target File Name [statename ] |<br />
| Notes [Wrapper for ws_state_name Web Service ] |<br />
| +---------------------------------------------------------------------------+|<br />
| | ||<br />
| |Subscriber code has been compiled successfully. ||<br />
| +---------------------------------------------------------------------------+|<br />
| |<br />
| |<br />
| |<br />
| |<br />
| |<br />
+------------------------------------------------------------------------------+<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 427
After this process is complete, the C files for the wrapper function, including the<br />
object file that contains the function code, are left in the Target Directory that is<br />
specified in the web service definition.<br />
Example 12-27 shows the files for the ws_state_name web service. You can use<br />
these files from an <strong>Informix</strong> 4GL program to consume the web service.<br />
Example 12-27 Web service files<br />
informix@irk:/work/publish$ ls<br />
axis2_stub_ws_state_name_statename.h statename.c_statename.4cf<br />
statename.c statename.o<br />
informix@irk:/work/publish$<br />
12.5.3 Using the web service from an I4GL application<br />
To consume a web service from I4GL, you have to link the object file for the<br />
wrapper function in the 4GL code or use the C source file that is generated<br />
during the subscribe compile process.<br />
Example 12-28 shows a simple 4GL program that uses the wrapper function for<br />
the ws_state_name web service. The wrapper function can be called like any<br />
other C function in I4GL.<br />
Example 12-28 The I4GL subscriber code: wsstate.4gl<br />
informix@irk:/work/publish$ cat wsstate.4gl<br />
MAIN<br />
DEFINE sname CHAR(15)<br />
WHENEVER ERROR STOP<br />
CALL statename("CA") RETURNING sname<br />
DISPLAY "State name: ",sname<br />
END MAIN<br />
informix@irk:/work/publish$ c4gl wsstate.4gl statename.c -o wsstate<br />
$INFORMIXDIR/lib/tools/w4glutil.a -I$AXIS2C_HOME/include/axis2-1.5.0<br />
-L$AXIS2C_HOME/lib -laxis2_engine<br />
informix@irk:/work/publish$ ./wsstate<br />
State name: California<br />
informix@irk:/work/publish$<br />
For more information about <strong>Informix</strong> 4GL, refer to the 4GL Reference Manual at:<br />
http://publib.boulder.ibm.com/infocenter/ifxhelp/v0/index.jsp?topic=/com.ibm.to<br />
ols.doc/4gl.html<br />
428 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
12.6 Troubleshooting<br />
In this section, we discuss typical problems that can occur when developing a<br />
web service using the <strong>Informix</strong> 4GL Web Services tools and how to obtain<br />
diagnostic information through the use of tracing or log files.<br />
12.6.1 Typical problems<br />
This section lists typical problems that can occur.<br />
Connection<br />
Connection errors normally occur when there is something wrong in the<br />
configuration details for the development environment or the web service.<br />
Any web service developed in I4GL requires the use of the <strong>Informix</strong> 4GL<br />
communication libraries for the database connection. I4GL tools such as w4gl<br />
keep details about the environment in the Design database. Therefore, it is<br />
critical that the communication with the <strong>IBM</strong> <strong>Informix</strong> database was configured<br />
correctly before beginning any project.<br />
Environment variables such as INFORMIXDIR and INFORMIXSQLHOSTS must<br />
contain valid details for the <strong>Informix</strong> database server to use during development.<br />
Also, consider the following problems that are related to connection:<br />
► Confirm <strong>Informix</strong> database server details. <strong>Informix</strong> Web Services tools and<br />
Web Services running on the Axis2 server use the database information that<br />
is defined in the Design database.<br />
► Verify connection information. Connection information regarding the Axis2<br />
server, such as the host name or port number, is stored as part of an<br />
Application Server definition. Check that these values are valid.<br />
► Consuming Web Services with a 4GL application requires a TCP connection<br />
from the I4GL program to the application server that is running the web<br />
service. Make sure that the location of the web service is correct and that the<br />
application server can be reached from the I4GL process.<br />
Compilation errors<br />
A common reason for having compilation errors is due to an incorrect setup or<br />
incorrect use of the <strong>Informix</strong> 4GL Web Services utilities.<br />
Environment variables such as PATH and LD_LIBRARY_PATH (or the suitable<br />
variable for the platform) might cause compilation and runtime errors when<br />
developing a web service with <strong>Informix</strong> 4GL. When a compile problem occurs<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 429
while using one of the I4GL web service tools, the information about the error is<br />
written into the W4GL log file. The default name and location of this file is<br />
/tmp/w4glerr.log.<br />
Example 12-29 shows an error message that is generated by the w4gl utility<br />
during the deploy process.<br />
Example 12-29 A w4gl error.<br />
The file "state_name.err" has been written.<br />
-CDCAK0012: The web service not deployed. Check error log '/tmp/w4glerr.log'<br />
The Deploy menu option performs several operations automatically, including<br />
generating the wrapper code, compiling, and moving the Web Services files into<br />
the Axis2 server. To know the specific task that is failing, use the web compiler<br />
utility (w4glc) to perform each individual task manually.<br />
By default, the w4glc script deletes the temporary files that are used to perform a<br />
task. Use the -keep flag to avoid file deletion.<br />
Example 12-30 shows how to run the generate process using the w4glc utility.<br />
Example 12-30 Using w4glc generate to avoid file deletion<br />
informix@irk:/work$ w4glc -generate -keep ws_state_name_irk.4cf<br />
Begin environment check ...<br />
Environment check is completed.<br />
Generating code. Please wait ...<br />
Generating Wrapper code ....<br />
The wrapper file is /tmp/w4gl_informix/state_name_wrap.c<br />
Generating WSDL ....<br />
Generating headers ....<br />
Generating skeletal code ....<br />
Code generation completed.<br />
With the wrapper files in the temporary directory, you can execute the compile<br />
process from the command line using the w4glc utility and examine the output for<br />
any errors. Example 12-31 shows the output of the compile process when an<br />
compilation error occurs.<br />
Example 12-31 Compilation output<br />
informix@irk:/work$ w4glc -compile -keep ws_state_name_irk.4cf<br />
Begin environment check ...<br />
Environment check is completed.<br />
Generating shared object for service ws_state_name ...<br />
Compiling code. Please wait...<br />
430 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Executing: c4gl -keep --shared -o /tmp/w4gl_informix/libws_state_name.so<br />
-I/usr3/4gl750/AXIS2C/include/axis2-1.5.0 -L/usr3/4gl750/AXIS2C/lib<br />
-laxis2_engine -laxutil -laxis2_axiom...<br />
The compilation was not successful. Errors found: 1.<br />
The file "state_name.err" has been written.<br />
Error executing: c4gl -keep --shared -o /tmp/w4gl_informix/libws_state_name.so<br />
-I/usr3/4gl750/AXIS2C/include/axis2-1.5.0 ...<br />
/usr3/4gl750/lib/tools/w4glutil.a /usr3/4gl750/lib/tools/lib4gl.a at<br />
/usr3/4gl750/lib/globals.pm line 583.<br />
Error executing: c4gl -keep --shared -o /tmp/w4gl_informix/libws_state_name.so<br />
-I/usr3/4gl750/AXIS2C/include/axis2-1.5.0 -L/usr3/4gl750/AXIS2C/lib<br />
-laxis2_engine -laxutil ...<br />
informix@irk:/work$<br />
Similar to any I4GL program, if the error was inside the <strong>Informix</strong> 4GL code, a .err<br />
file is created that contains the error message. Example 12-32 shows the content<br />
of the state_name.err file.<br />
Example 12-32 The state_name.err file<br />
informix@irk:/work$ cat state_name.err<br />
FUNCTION state_name(code)<br />
DEFINE state_rec RECORD<br />
code CHAR(2),<br />
sname CHAR(15)<br />
END RECORD,<br />
code CHAR(2),<br />
sel_stmt CHAR(100);<br />
LET sel_stmt= "SELECT code, sname FROM state WHERE code = ?";<br />
PEPARE st_id FROM sel_stmt;<br />
|________^<br />
|<br />
| A grammatical error has been found on line 11, character 10.<br />
| The construct is not understandable in its context.<br />
| See error number -4373.<br />
DECLARE cur_id CURSOR FOR st_id;<br />
OPEN cur_id USING code;<br />
FETCH cur_id INTO state_rec.*;<br />
...<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 431
Keeping the wrapper C files in the temporary directory might be useful when the<br />
compilation error appears inside the Axis2 functions. Example 12-33 shows the<br />
temporary files that are created with the generate option for the ws_state_name<br />
web service.<br />
Example 12-33 Temporary files<br />
informix@irk:/work$ ls /tmp/w4gl_informix/<br />
axis2_skel_ws_state_name.c services.xml tmpXMLReqFile<br />
axis2_skel_ws_state_name.h state_name_wrap.c ws_state_name.tar<br />
axis2_svc_skel_ws_state_name.c state_name.wsdl<br />
informix@irk:/work$<br />
While compiling any web service, during creation or consume, make sure all the<br />
required libraries and include files are passed to the compiler correctly. These<br />
requirements can vary from platform to platform. Always check the release notes.<br />
The release and documentation files for <strong>Informix</strong> 4GL are located in the<br />
release/en_us/0333 directory inside the INFORMIXDIR variable. These files<br />
contain additional information that is relevant to the version of I4GL that is<br />
installed, such as known issues or compiler requirements, that might help<br />
diagnose the problem.<br />
Consuming the web service<br />
Errors when consuming the web service might be caused by an invalid web<br />
service definition. Items such as the function name or parameters type are<br />
defined in the configuration file and are used to create the WSDL file that the<br />
application server uses to define the web service. You can retrieve the WSDL<br />
information for a web service using the location of the service and the ?wsdl<br />
suffix from any web browser.<br />
432 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Figure 12-4 shows the WSDL information for the ws_state_name web service.<br />
Figure 12-4 WSDL for ws_state_name service<br />
Make sure the operations, parameters, and data types are correct for the I4GL<br />
code that the function uses.<br />
Testing a web service<br />
<strong>Informix</strong> I4GL does not provide any specific tool for testing Web Services.<br />
Although you can create a simple 4GL code to consume the web service,<br />
depending on the complexity of the web service, it can be more useful to perform<br />
a complete testing.<br />
You can use open source tools, such as soapUI, to test your web service before<br />
publish it in the production environment. For more information about soapUI,<br />
refer to:<br />
http://www.soapui.org/<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 433
12.6.2 Tracing<br />
A developer can use one of the following types of tracing when diagnosing an<br />
<strong>Informix</strong> 4GL web service problem:<br />
► Application server trace can be used to diagnose problems between a client<br />
application and the Axis2 application server.<br />
► Database trace can be used when diagnostic problems are specific to<br />
operations with the database server, such as SQL errors or incorrect data<br />
returned.<br />
Application server trace<br />
The Axis2c application server provides a method to trace all the exchanged<br />
messages between the application server and a client that is consuming a web<br />
service.<br />
You can set this trace using the -l log_level option when you start the Axis2<br />
server. To launch the Axis2 server with trace enable, run the following command:<br />
axis2_http_server -p 9876 -l 6 -f /tmp/w4gl_informix/axis_trace.log<br />
Use the -l to specify the logging level for the application server. The maximum<br />
value is 6, which enables full tracing.<br />
You can specify the location of the trace file using the -f log_file option.<br />
Example 12-34 shows the contents of a typical trace file.<br />
Example 12-34 A typical application_trace sample<br />
[debug] phase_resolver.c(139) Service name is : ws_zipcode<br />
[debug] phase_resolver.c(1123) Operation name is : zipcode_details<br />
[debug] phase_holder.c(139) Add handler AddressingOutHandler to phase<br />
[debug] phase_holder.c(139) Add handler AddressingOutHandler to phase<br />
[debug] phase_resolver.c(222) svc name is:ws_state_name<br />
[debug] phase_resolver.c(139) Service name is : ws_state_name<br />
[debug] phase_resolver.c(1123) Operation name is : state_name<br />
[debug] phase_holder.c(139) Add handler AddressingOutHandler to phase<br />
...<br />
For more information about the logging options with the Axis2c application<br />
server, refer to:<br />
http://ws.apache.org/axis2/c/docs/axis2c_manual.html#simple_axis_server<br />
434 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Database trace<br />
<strong>IBM</strong> <strong>Informix</strong> 4GL uses the SQLI protocol to exchange data with the <strong>Informix</strong><br />
server. Thus, any web service that is developed using <strong>Informix</strong> 4GL also uses the<br />
SQLI protocol for any communication with the database server.<br />
You can use the SQLIDEBUG environment variable to collect all the messages<br />
between the application server and the <strong>Informix</strong> database server.<br />
To enable this trace, create the SQLIDEBUG environment variable before<br />
starting the Axis2 Application server.<br />
Example 12-35 demonstrates how to set the SQLIDEBUG variable and how to<br />
run sqliprint to un-encode the SQLI file.<br />
Example 12-35 The SQLIDEBUG variable client side<br />
informix@irk:/usr3/4gl750/AXIS2C/bin$ export SQLIDEBUG=2:/tmp/sqlitrace<br />
informix@irk:/usr3/4gl750/AXIS2C/bin$ ./axis2_http_server -p 9876<br />
Started Simple Axis2 HTTP Server ...<br />
...<br />
...<br />
informix@irk:/work$ ls /tmp/sqlitrace*<br />
/tmp/sqlitrace_17008_0_8c819d0<br />
informix@irk:/works$ sqliprint -o tracefile.txt /tmp/sqlitrace_17008_0_8c819d0<br />
informix@irk:/works$<br />
Note: The sqliprint tool is included with <strong>Informix</strong> Client Software<br />
Development Kit (Client SDK).<br />
You can also use the SQLIDEBUG trace at the server side. For more information,<br />
refer to 3.3.6, “Troubleshooting” on page 117.<br />
Chapter 12. <strong>Informix</strong> 4GL Web Services 435
436 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Chapter 13. Application development<br />
considerations<br />
13<br />
In this chapter, we examine some of the considerations a developer might need<br />
to address in a multi-user environment. A single user workstation that connects<br />
to an exclusive-use database does not the issue of two or more independent<br />
uses of the same data at the same time. However, in a multi-user environment,<br />
concurrency is a challenge.<br />
<strong>IBM</strong> <strong>Informix</strong> database servers are designed to provide features to help in<br />
handling concurrency and sorting facilities. The application developer should<br />
design applications to take advantage of these built-in features, rather than<br />
attempt to implement their own facilities in the application. In this chapter we<br />
examine the factors that cause concurrency problems, and focus on ways to<br />
keep the scope and duration of locks to a minimum. To do this, we consider<br />
isolation levels, sharing data, and data contention issues that can occur when<br />
two or more attempts are made to access or change the same row of data.<br />
In the last two sections, we focus on configuration parameters that effect the<br />
application development, and how to monitor issues when the application<br />
developers work with the database administrator to tune the engine and the<br />
application to work effectively.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 437
13.1 Concurrency and locking<br />
13.1.1 Types of locks<br />
Concurrency involves two or more independent uses of the same data at the<br />
same time. In a database system with many users, each user needs to be able to<br />
access and modify data. Unless the developer and database system impose<br />
controls, there can be negative consequences. Programs might access old data<br />
that is in the process of being changed by another user, and changes might<br />
seem to disappear even though it seems like the change was performed<br />
successfully.<br />
To take advantage of database server controls, tables in a multiple user<br />
environment should be logging tables. At a minimum, if you must use a<br />
nonlogging table within a transaction, either set Repeatable Read isolation level<br />
or lock the table in exclusive mode.<br />
To avoid concurrency problems, the database server imposes a system of locks.<br />
A lock is a claim, or reservation, that a program can place on a piece of data. The<br />
database server assures that no other program can modify it, as long as the lock<br />
is in place. When another application requests the data, the database server<br />
either makes the program wait or turns it back with an error.<br />
The application developer can control the effect of lock access using a<br />
combination of SQL statements and with the buffering mode selected for the<br />
database. The most used SQL statements are SET LOCK MODE, SET<br />
ISOLATION, and SET TRANSACTION. We discuss these in more detail later.<br />
For now, we need a better understanding of the types of locks that we can<br />
encounter.<br />
<strong>IBM</strong> <strong>Informix</strong> offers several server edition. <strong>IBM</strong> <strong>Informix</strong> Extended Parallel Server,<br />
<strong>Informix</strong> Online, and Standard Engine will have different syntax for concurrency<br />
related commands. You can find more information about the command syntax for<br />
the discussion in this chapter in <strong>IBM</strong> <strong>Informix</strong> Guide to SQL: Syntax, v11.50,<br />
SC27-3611.<br />
A lock is implemented as a variable associated with a data item. It can be<br />
explicitly placed by an application or, more frequently, is implicitly handled by the<br />
database management system. The lock is used to mark a data item as<br />
reserved; the type of lock designation determines what actions are permitted by<br />
users in regard to the data item.<br />
438 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
An <strong>IBM</strong> <strong>Informix</strong> instance can have several types of locks:<br />
► A shared lock reserves its object for reading only. It prevents the object from<br />
changing while the lock remains. More than one program can place a shared<br />
lock on the same object. More than one object can read the record while it is<br />
locked in shared mode. In the lock list output visible from onstat -k, a thread<br />
with a shared lock is be designated with an S. If an object is currently locked in<br />
exclusive mode and the user thread wants to acquire a shared lock, the<br />
designation is “IS” (intent-shared).<br />
When a session first connects to a database, <strong>IBM</strong> <strong>Informix</strong> Servers place a<br />
shared lock on the database, to prevent another session from acquiring an<br />
exclusive lock on the same database. SELECT queries place a shared lock at<br />
the table level, because it is faster for the engine to find a table level lock than<br />
it is to search through potentially thousands of row locks.<br />
► Intent-exclusive locks are set automatically by <strong>Informix</strong>. If a row in a table is<br />
updated, an exclusive lock is placed on the row and an intent-exclusive lock is<br />
placed on the table. This assures that no other session can place a shared or<br />
exclusive lock on the table as long as an individual row is locked exclusively.<br />
In the lock list output visible from onstat -k, a thread with an intent exclusive<br />
lock is designated with an IX. A related designation that is sometimes visible<br />
is SIX. This designation indicates the object is currently shared, with<br />
Intent-exclusive when the chance arrives.<br />
► An exclusive lock reserves its object for the use of a single application. This<br />
lock type is used when the application needs to change the object. You<br />
cannot place an exclusive lock where any other kind of lock exists. After you<br />
place an exclusive lock, you cannot place another lock on the same object. In<br />
the lock list output visible from onstat -k, a thread with an exclusive lock is<br />
designated with an X.<br />
► A promotable (or update) lock establishes an intent to update. You can only<br />
place it where no other updatable or exclusive lock exists. You can place an<br />
updatable lock on records that already have shared locks. When the<br />
application is about to change the locked object, you can promote the update<br />
lock to an exclusive lock, but only if no other locks, including shared locks, are<br />
on the record at the time the lock would change from update to exclusive. If a<br />
shared lock was on the record when the update lock was set, you must drop<br />
the shared lock before the update lock can be promoted to an exclusive lock.<br />
In the lock list output visible from onstat -k, a thread with an update lock is<br />
designated with an U.<br />
Chapter 13. Application development considerations 439
13.1.2 Lock duration<br />
The length of time a lock remains in effect is known as lock duration. The<br />
duration of a lock is determined by the application, the closing of a database, and<br />
the type of transaction method used by the application and database<br />
If the database does not use transactions (no transaction log exists and you do<br />
not use a COMMIT WORK statement), an explicit table lock remains until it is<br />
removed by the execution of the UNLOCK TABLE statement.<br />
If database transactions are in use, the end-of-lock-duration event occurs when<br />
the transaction ends or a COMMIT WORK is issued in the application. The<br />
ending transaction causes a release of all table, row, page, and index locks that<br />
were on hand during the transaction.<br />
13.1.3 Lock granularity<br />
With <strong>IBM</strong> <strong>Informix</strong> Servers, the developer can apply locks to databases, tables,<br />
disk pages, data rows, or index-key values. At a database level, an exclusive lock<br />
is simple to enforce for a developer, but it has a big impact on users. No one gets<br />
access until the database lock processing is completed. Concurrency drops to<br />
zero, and performance is maximized for the exclusive use of a single user<br />
running an application.<br />
At the other end of the granularity scope for locks, a transaction can exclusively<br />
lock a row, and have no impact on other users in their work efforts (if they are not<br />
trying to access the same row, and the lock mode is row). Concurrency is<br />
maximized, but performance will tend to slide as the number of users for a<br />
database or table increases.<br />
Table locks<br />
It is the responsibility of both database administrator and developer to select and<br />
enable the best level of lock granularity for users and for their database system.<br />
At the database level, administrative activities such as imports and exports are<br />
usually the task of the database administrator. For such activity, the database<br />
administrator would use a command such as<br />
DATABASE database_name EXCLUSIVE;<br />
Another task, principally for the database administrator, is to enable a change for<br />
a table or an index structure. The task of enabling work on an entire table can be<br />
done with a command similar to one of the following command:<br />
► LOCK TABLE table_name IN EXCLUSIVE MODE;<br />
► LOCK TABLE table_name in SHARE MODE;<br />
440 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
When the task is finished (end of statement or transaction reached), the table is<br />
implicitly unlocked. It could be unlocked explicitly with the following command:<br />
UNLOCK TABLE table_name;<br />
Note: A table lock on a table can decrease update concurrency radically. Only<br />
one update transaction can access that table at any given time, and that<br />
update transaction locks out all other transactions. However, multiple<br />
read-only transactions can simultaneously access the table. This behavior is<br />
useful in a data warehouse environment where the data is loaded and then<br />
queried by multiple users.<br />
Page locks<br />
Lock mode PAGE is the default for <strong>Informix</strong> tables, and it is considered the<br />
optimal level in lock efficiency when rows are being accessed and modified in<br />
physical order. If your tables are large in row count and small in row size, a page<br />
level lock can be severely limiting, because it will lock a large number of rows on<br />
a page, and discourage user access.<br />
In this case, it would be more efficient to change the default lock mode. For all<br />
new tables, this can be done by way of the <strong>Informix</strong> onconfig file, using the<br />
parameter DEF_TABLE_LOCKMODE. For example:<br />
DEF_TABLE_LOCKMODE ROW;<br />
Note: Use a small (or default) page size if your application contains small<br />
sized rows. Increasing the page size for an application that randomly<br />
accesses small rows can decrease performance. In addition, a page lock on a<br />
larger page will lock more rows, which is likely to reduce concurrency in some<br />
situations.<br />
Tables that use page locks cannot support the USELASTCOMMITTED<br />
concurrency feature.<br />
Row and key locks<br />
Row and key locks generally provide the best overall performance when you are<br />
updating a relatively small number of rows, because they increase concurrency.<br />
However, the database server incurs some overhead in obtaining a lock. For an<br />
operation that changes a large number of rows, obtaining one lock per row might<br />
not be cost effective. For operations that consistently change a large number of<br />
rows, page locks might be a better option.<br />
Chapter 13. Application development considerations 441
If a table is not created with row locking and you want row or key locks, you must<br />
alter the table. Here is an example to show how to create a table with row locking<br />
turned on:<br />
CREATE TABLE table_namer(field1 serial,field2 char(20)...)<br />
LOCK MODE ROW;<br />
The ALTER TABLE statement can also change the lock mode. An example for<br />
this command syntax is:<br />
ALTER TABLE table_name LOCK MODE (ROW);<br />
When the lock mode is ROW and you insert or update a row, the database server<br />
creates a row lock. In some cases, you place a row lock by simply reading the<br />
row with a SELECT statement.<br />
When the lock mode is ROW and you insert, update, or delete a key (performed<br />
automatically when you insert, update, or delete a row), the database server also<br />
creates a lock on the key in the index.<br />
Key-value locks<br />
When a user deletes a row within a transaction, the row cannot be locked<br />
because it becomes a non-existent row. However, the database server must<br />
somehow record that a row existed until the end of the transaction. The database<br />
server uses key-value locking to lock the deleted row. Key locks are used<br />
identically to row locks. When the table uses row locking, key locks are<br />
implemented as locks on imaginary rows.<br />
When the table uses page locking, a key lock is placed on the entire index page<br />
that contains the key or that would contain the key if it existed. A page lock on an<br />
index page can decrease concurrency more substantially than a page lock on a<br />
data page. Index pages are dense and hold a large number of keys. By locking<br />
an index page, you make a potentially large number of keys unavailable to other<br />
users until you release the lock.<br />
Note: To determine the current lock mode for a table:<br />
► On UNIX, try “oncheck -pt dbname:tablename | grep Locking”<br />
► On Windows systems, examine the output of “oncheck -pt<br />
dbname:tablename”<br />
Database locks<br />
The act of opening a database places a shared lock on the database name. The<br />
statements which open a database are CONNECT, DATABASE, or CREATE<br />
DATABASE. As long as a database is open, the shared lock on the database<br />
442 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
prevents any other program from dropping the database or putting an exclusive<br />
lock on it.<br />
Locking a database for exclusive use are not needed very often, because this<br />
would prevent other users and programs from accessing it for the duration of the<br />
lock. The usual reason for a database lock would be the need for a major<br />
structure change across several tables, when implementing a series of related<br />
indexes on several tables, or when you have an application that needs<br />
uninterrupted access to the database for a period of time. To lock a database in<br />
exclusive mode, the syntax is:<br />
DATABASE database_name EXCLUSIVE;<br />
Tasks that run from the sysadmin database (dbscheduler tasks, such as Auto<br />
Update Statistics), occasionally might prevent exclusive database access. In this<br />
case, you must temporarily disable the dbscheduler. In the sysadmin database,<br />
stop the scheduler API with:<br />
execute function task(scheduler shutdown)<br />
Restart the scheduler API with:<br />
execute function task(scheduler start)<br />
Smart large object locks<br />
Smart large objects are quite different in the way they work from the rest of the<br />
structures and processes in <strong>IBM</strong> <strong>Informix</strong> databases. The locking and locking<br />
granularity for smart large objects is also different. The database server uses one<br />
of the following granularity levels for locking smart large objects:<br />
► The sbspace chunk header partition<br />
► The smart large object<br />
► A byte range of the smart large object<br />
The default locking granularity for a smart BLOB is at the level of the smart large<br />
object. When you update a smart large object, the database server locks the<br />
smart large object that is being updated. Concurrently, there is an update lock<br />
placed on the sbspace chunk header partition while the object is being updated.<br />
Byte locks<br />
Byte locks, also known as byte-range locks, are used to lock a specific byte range<br />
of a smart large object. Byte-range locking is advantageous because it allows<br />
multiple users to update the same smart large object simultaneously, as long as<br />
they are updating different parts of it. Also, users can read a part of a smart large<br />
object while another user is updating or reading a different part of the same<br />
smart large object.<br />
Chapter 13. Application development considerations 443
How the database server manages byte-range locks<br />
The database server manages byte-range locks in the lock table in a similar<br />
fashion to other locks placed on rows, pages, and tables. However, the lock table<br />
must store the byte range as well. If a user places additional locks on a byte<br />
range, any new byte locks in the range that are contiguous are consolidated into<br />
one lock range.<br />
Likewise, if a user unlocks a portion of the bytes included within a byte-range<br />
lock, the database server will split into multiple byte-range locks.<br />
To enable use of byte-range locks<br />
By default, the database server places a lock on the entire smart large object. At<br />
the time of sbspace creation, there is an option to use byte-range locking.<br />
When the DBA sets the default locking mode for the sbspace to byte-range<br />
locking, the database server locks only the necessary bytes when it updates any<br />
smart large objects stored in the sbspace. To set byte-range locking for the<br />
sbspace that stores the smart large object, the database administrator must use<br />
the onspaces utility.<br />
The following example sets byte-range locking for a new sbspace:<br />
onspaces -c -S sblob -g 2 -p /ifmx/sblob1 -o 0 -s 1000 -Df LOCK_MODE=RANGE<br />
When byte-range locking is set for the individual smart large object, the database<br />
server implicitly locks only the necessary bytes when it selects or updates the<br />
smart large object. The application developer can set byte-range locking for the<br />
smart large object when it is opened, using one of the following methods:<br />
► Set the MI_LO_LOCKRANGE flag in the mi_lo_open() DataBlade API function.<br />
► Set the LO_LOCKRANGE flag in the ifx_lo_open() ESQL/C function.<br />
To lock a byte range explicitly, use one of the following functions:<br />
► mi_lo_lock()<br />
► ifx_lo_lock()<br />
These functions lock the range of bytes that is specified for the smart large<br />
object. If the developer specifies an exclusive lock with either function, UPDATE<br />
statements do not place locks on the smart large object if they update the locked<br />
bytes.<br />
The database server releases exclusive byte-range locks placed with<br />
mi_lo_lock() or ifx_lo_lock() at the end of the transaction. The database<br />
server releases shared byte-range locks placed with mi_lo_lock() or<br />
ifx_lo_lock() based on the same rules as locks placed with SELECT<br />
444 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
statements, depending upon the isolation level. The application can also release<br />
shared byte-range locks with mi_lo_unlock() or ifx_lo_unlock().<br />
For more information about these DataBlade API functions, see <strong>IBM</strong> <strong>Informix</strong>:<br />
DataBlade API Programmer's Guide:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.d<br />
apip.doc/dapip.htm<br />
You can fine additional details about the ESQL/C functions in the <strong>IBM</strong> <strong>Informix</strong>:<br />
ESQL/C Programmer's Manual at:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.e<br />
sqlc.doc/esqlc.htm<br />
13.2 Locking issues and performance<br />
Row and key locks generally provide the best performance whenever a database<br />
system only needs to update a small number of rows at a time. However, the<br />
database incurs additional overhead in obtaining a lock as the granularity gets<br />
smaller. The developer should help decide the granularity based on the character<br />
of the trnasactions.The following are guidelines for your decision making:<br />
► For low row count transactions, use row level locking.<br />
► For large processes that update an entire table, set locking to page or lock the<br />
table in exclusive mode before processing.<br />
► For massive updates to many tables or the whole database, lock the database<br />
in exclusive mode before processing.<br />
The type of isolation can affect overall performance because it affects<br />
concurrency. Before you execute a SELECT statement, you can set the isolation<br />
level with one of these options:<br />
► The SET ISOLATION statement (an extension to ANSI SQL-92 standard)<br />
– Can be executed more than once in a transaction.<br />
– Can change the enduring isolation level for a session<br />
– Has an additional isolation level (Cursor Stability)<br />
► SET TRANSACTION (ANSI/ISO-compliant)<br />
– Can only be executed once in a transaction.<br />
► Dirty Read isolation (or ANSI Read Uncommitted) level does not place any<br />
locks on any rows fetched during a SELECT statement. Dirty Read isolation is<br />
appropriate for static tables that are used for queries. Use Dirty Read isolation<br />
with care if update activity occurs at the same time. With Dirty Read, the<br />
Chapter 13. Application development considerations 445
13.2.1 Deadlocks<br />
reader can read a row that has not been committed to the database and might<br />
be eliminated or changed during a rollback.<br />
A deadlock is a situation in which a pair of programs blocks the progress of each<br />
other. Each program has a lock on some object that the other program wants to<br />
access. In a database with local database queries only, a deadlock arises only<br />
when all programs concerned set their lock modes to wait for locks.<br />
An <strong>Informix</strong> database server detects deadlocks immediately when they only<br />
involve data at a single network server. Before a lock is granted, the database<br />
server examines the lock list for each user. If a user holds a lock on the resource<br />
that the requestor wants to lock, the database server traverses the lock wait list<br />
for the user to see if the user is waiting for any locks that the requestor holds. If<br />
so, the requestor receives a deadlock error. It prevents the deadlock from<br />
occurring by returning an error code (error -143 ISAM error: deadlock detected)<br />
to the second program to request a lock. The error code is the one the program<br />
receives if it sets its lock mode to not wait for locks. If your program receives an<br />
error code related to locks even after it sets lock mode to wait, you know the<br />
cause is an impending deadlock.<br />
The best way to avoid deadlocks is to set lock mode to wait for a specific number<br />
of seconds (N), where N is the number of seconds it is reasonable for your<br />
applications to wait on a lock. Example syntax:<br />
SET LOCK MODE TO WAIT 30;<br />
Deadlock errors can be unavoidable when applications update the same rows<br />
frequently. However, certain applications might always be in contention with each<br />
other. Examine applications that are producing a large number of deadlocks and<br />
try to run them at different times. To monitor the number of deadlocks, monitor<br />
the deadlks field in the output of onstat -p.<br />
Distributed transactions<br />
Deadlock handling is more challenging when you are using a distributed query,<br />
which involves more than one database server. In this case, the server cannot<br />
monitor the locks of the other database, so deadlocks cannot be detected before<br />
they occur.<br />
Occasionally, there might be a need for each database to wait on a lock from<br />
another local transaction. When the wait occurs, the entire distributed transaction<br />
might need to wait until the action is cleared up. If both servers get into a<br />
wait-for-the-other-server to complete mode, you have a multi-server deadlock<br />
that neither server can get out of easily.<br />
446 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
To avoid this situation, set the DEADLOCK_TIMEOUT parameter in the onconfig<br />
file. This parameter tells the server how much time to wait before returning an<br />
error code (error -143 ISAM error: deadlock detected). For more information<br />
about using this configuration parameter, see <strong>IBM</strong> <strong>Informix</strong> Dynamic Server<br />
Administrator's Guide, v11.50, SC27-3606.<br />
Distributed transactions, also known as global transactions, can have additional<br />
problems beyond concurrency and locking. In a distributed transaction, we can<br />
have queries that include multiple, separate databases spanning multiple host<br />
systems across a network. With the added task of reaching out to different<br />
servers, we are confronted with delays caused by network traffic, connectivity<br />
issues, additional user authentication, and user activity on the other database<br />
server that can slow or temporarily prevent quick access to the remote database<br />
server.<br />
In the context of a query that is doing an update, insert, select, or delete, this new<br />
group of interfering factors can occasionally present problems. Developers<br />
should include coding conventions to accommodate distributed (XA) transactions<br />
and include code to avoid certain problems. We have included an ESQL/C<br />
routine (xa_tool.ec) in Appendix B, “Accommodating distributed transactions” on<br />
page 469 that is designed to help provide a start for handling XA problems.<br />
13.3 Isolation levels<br />
The number and duration of locks placed on data during a SELECT statement<br />
depends on the level of isolation that the user sets. The type of isolation can<br />
affect overall performance because it affects concurrency.<br />
You can set the isolation level with the SET ISOLATION or the ANSI SET<br />
TRANSACTION statement before you execute the SELECT statement. The main<br />
differences between the two statements are that SET ISOLATION has an<br />
additional isolation level, Cursor Stability, and SET TRANSACTION cannot be<br />
executed more than once in a transaction as SET ISOLATION can.<br />
Dirty Read isolation<br />
Dirty Read isolation (or ANSI Read Uncommitted) does not place any locks on<br />
any rows fetched during a SELECT statement. Dirty Read isolation is appropriate<br />
for static tables that are used for queries. It offers the best performance of all<br />
isolation levels, because the database server does not check or place any locks<br />
for queries.<br />
If update activity can occur at the same time as a dirty read, there is a chance<br />
that a rollback might need to occur during the dirty read. The reader could read a<br />
Chapter 13. Application development considerations 447
ow that has not been committed (this is known as a phantom read) to the<br />
database and the data would be lost during a subsequent rollback. Because of<br />
potential problems with uncommitted data that is rolled back, use Dirty Read<br />
isolation with care.<br />
Databases that do not have logging turned on (and thus do not allow<br />
transactions) use Dirty Read as a default isolation level. In fact, Dirty Read is the<br />
only isolation level allowed for databases that do not have logging turned on.<br />
Committed Read isolation<br />
Committed Read isolation (or ANSI Read Committed) removes the problem of<br />
phantom reads. A reader with this isolation level checks for locks before it returns<br />
a row. By checking for locks, the reader cannot return any uncommitted rows.<br />
The database server does not actually place any locks for rows read during<br />
Committed Read. It simply checks for any existing rows in the internal lock table.<br />
Committed Read is the default isolation level for databases with logging, and it is<br />
an appropriate isolation level for most activities.<br />
Cursor Stability isolation<br />
A reader with Cursor Stability isolation acquires a shared lock on the row that is<br />
currently fetched. This action assures that no other user can update the row until<br />
the user fetches a new row.<br />
If you do not use a cursor to fetch data, Cursor Stability isolation behaves in the<br />
same way as Committed Read. No locks are actually placed.<br />
Repeatable Read isolation<br />
Repeatable Read isolation (also known as ANSI Serializable and ANSI<br />
Repeatable Read) is the strictest isolation level. With Repeatable Read, the<br />
database server locks all rows examined (not just fetched) for the duration of the<br />
transaction.<br />
Repeatable Read is useful during any processing in which multiple rows are<br />
examined, but nothing will change during the transaction. The original application<br />
holds a read lock on each account that it examines until the end of the<br />
transaction, so the attempt by the second application to change the first account<br />
fails (or waits, depending upon SET LOCK MODE).<br />
Note: With Repeatable Read, because even examined rows are locked, if the<br />
database server reads the table sequentially, a large number of rows<br />
unrelated to the query result can be locked.<br />
448 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Use Repeatable Read isolation for tables when the database server can use an<br />
index to access a table. If an index exists and the optimizer chooses a sequential<br />
scan instead, you can use directives to force use of the index. However, forcing a<br />
change in the query path might negatively affect query performance.<br />
13.4 Configuration options<br />
We have brought configuration parameters into our discussion several times. In<br />
this section, we examine <strong>IBM</strong> <strong>Informix</strong> Server configuration options that directly<br />
affect application development. We point out what the developers should know,<br />
and how these parameters affect the application and transactions. For more<br />
information about any of the parameters in the onconfig file or environment<br />
variables relating to configuration, see <strong>IBM</strong> <strong>Informix</strong> Dynamic Server<br />
Administrator's Reference, v11.50, SC27-3607.<br />
13.4.1 Server identification<br />
When the <strong>Informix</strong> server needs to resolve a database instance name, the<br />
oninit process uses the contents of a file known as the sqlhosts file. Each line<br />
in the sqlhosts file provides a database server naming reference, called the<br />
DBSERVERNAME, along with a protocol, a host system reference, and a<br />
listening protocol service reference. On Windows systems, this information is<br />
stored in the registry and is updated and accessed using the setnet32.exe utility.<br />
Multiple DBSERVERNAME references allow for an evenly distributed connection<br />
distribution over a variety of protocols<br />
In addition to the information in the sqlhosts file, the DBSERVERNAME crosses<br />
reference to the onconfig file, which holds the configuration information<br />
describing how the database server is to operate. When a client application<br />
connects to a database server, it specifies a DBSERVERNAME, which helps the<br />
server to know which protocol will be used for the connectivity.<br />
The DBSERVERNAME used in the application connection must be consistent<br />
with a name specified in the sqlhosts file and a DBSERVERNAME that is<br />
defined in the onconfig file.<br />
In the onconfig file, there is a default (main instance) DBSERVERNAME and one<br />
or more alternate DBSERVERNAME parameters that are available. The DBA<br />
specifies the default name to be used with the DBSERVERNAME parameter in<br />
the onconfig file. For alternative application processing, the DBA can allow for or<br />
specify alternative protocols, referenced by an a different DBSERVERNAME.<br />
The alternate DBSERVERNAME or names are specified by the<br />
DBSERVERALIASES parameter in the onconfig file. An application developer<br />
Chapter 13. Application development considerations 449
should check with the DBA to determine whether to use the DBSERVERNAME<br />
or one of the DBSERVERALIASES for connecting with his application. Example<br />
SQL statements include CONNECT, DATABASE, CREATE TABLE, and ALTER<br />
TABLE, which can specify the database server instance.<br />
13.4.2 Storage space identifiers<br />
When tables are created, the database creator has syntax options available to<br />
define where tables and indexes are to be placed physically in a disk structure.<br />
Typically, the database administrator defines the disk structure layout. The disk<br />
layout is assigned by way of dbspaces, and each dbspace is further broken down<br />
into allocations of chunks.<br />
The dbspace is the parent structure unit, used to hold storage information for<br />
tables and indexes, with low level definitions to describe where the actual data<br />
pointers for row data resides, as well as information about indexes, fragments,<br />
and table scope. The dbspace area that tracks pointer locations for data is known<br />
as the table space.<br />
When defining the table and index schema, the database administrator can<br />
specify a dbspace location to hold the table or the index. Other space<br />
specifications for default storage are available if none are specified at creation<br />
time. These default locations define where to store binary large objects (BLOBs),<br />
character large objects (CLOBs), and temporary storage locations.<br />
The default location for the location of temp tables and sorting space, if not<br />
predefined, is the dbspace where a table is accessed, or the root dbspace<br />
(rootdbs). If a default onconfig file definition for the location of temp dbspace is<br />
defined, we have more control over the location used<br />
The DBSPACETEMP parameter<br />
The DBSPACETEMP parameter specifies a list of dbspaces that the database<br />
server uses to globally manage the storage of temporary tables. When a<br />
temporary space is available, it improves performance by enabling the database<br />
server to spread I/O for temporary tables across multiple disks efficiently. The<br />
database server also uses temporary dbspaces during backups to store the<br />
before-images of data that are overwritten while the backup is occurring. More<br />
than one dbspace can be specified for this parameter. Simply list them as<br />
comma-separated values.<br />
The DBSPACETEMP parameter can contain dbspaces with a non-default page<br />
size, but all of the dbspaces in the DBSPACETEMP list must have the same page<br />
size.<br />
450 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
When using a logged database, file activity in temporary dbspaces is not logged.<br />
If the developer writes a query that requires tempspace, it is useful to include the<br />
phrase WITH NO LOG, so that you can force the query to use the designated<br />
temporary dbspace.<br />
The SBSPACENAME parameter<br />
The SBSPACENAME parameter specifies the name of the default sbspace. If<br />
your database tables include smart-large-object columns that do not explicitly<br />
specify a storage space, that data is stored in the sbspace that SBSPACENAME<br />
specifies. The default sbspace is also used by the built-in encryption and<br />
decryption functions to store BLOB or CLOB values.<br />
An sbspace is a specialized type of dbspace, used for storing smartblobs and<br />
binary large object dbspaces. (Binary data or some type of multidimensional data<br />
is common to any datablade that uses R-tree indexing.)<br />
When a create table statement includes a column that defines a CLOB or BLOB<br />
object, the column information will be stored in an sbspace. If the PUT clause is<br />
not specified in the create table statement when the defined CLOB or BLOB is<br />
created, the default location where the column data will be stored is the sbspace<br />
designated by the SBSPACENAME parameter.<br />
If you are using <strong>IBM</strong> <strong>Informix</strong> with J/Foundation, you must provide a smart large<br />
object where the database server can store the Java archive (.jar) files. These<br />
.jar files contain the Java user-defined routines (UDRs). If you use Java UDRs,<br />
you will want to create additional, separate sbspaces for storing smart large<br />
objects.<br />
The SBSPACETEMP parameter<br />
The SBSPACETEMP parameter specifies the name of the default temporary<br />
sbspace for storing temporary smart large objects without metadata or user-data<br />
logging. If you store temporary smart large objects in a standard sbspace, the<br />
metadata is logged. For more information about using temporary smart large<br />
objects, see <strong>IBM</strong> <strong>Informix</strong> DataBlade API Programmer's Guide, V11.50,<br />
SC23-9429.<br />
The SYSSBSPACENAME parameter<br />
The SYSSBSPACENAME parameter specifies the name of the sbspace in which<br />
the database server stores statistics that the UPDATE STATISTICS statement<br />
collects for certain user-defined data types. Normally, the database server places<br />
statistics in the sysdistrib system catalog table.<br />
Chapter 13. Application development considerations 451
Note: For information about writing user-defined statistics, refer to <strong>IBM</strong><br />
<strong>Informix</strong> User-Defined Routines and Data Types <strong>Developer's</strong> Guide, V11.5,<br />
SC23-9438.<br />
For information about providing statistics data for a smart BLOB column, refer<br />
to <strong>IBM</strong> <strong>Informix</strong> DataBlade API Programmer's Guide, V11.50, SC23-9429.<br />
13.4.3 Limiters and limits<br />
In addition to the storage location identifiers mentioned in 13.4.2, “Storage space<br />
identifiers” on page 450, a number of parameters in the onconfig file impose<br />
resource limits. Limits help the engine and system to utilize appropriate<br />
resources rather than consume all the resources on the system and end up in a<br />
crash. When the resources are no longer needed, they are released to be used<br />
by other processes. These configuration limits can be adjusted in cooperation<br />
with the database administrator, to accommodate for application concerns that<br />
might need them.<br />
The MULTIPROCESSOR parameter<br />
The MULTIPROCESSOR parameter is used to determine the type of processing<br />
method the engine will use. If it is set to 0, locking is done in a way that is suitable<br />
for a single- processor computer, and processor affinity is ignored.<br />
If the nearby onconfig file SINGLECPU_VP parameter is non-zero (On),<br />
MULTIPROCESSOR and user-defined VPCLASSes (of any kind) will not work.<br />
The VPCLASS parameter<br />
The VPCLASS parameter allows you to designate a class of virtual processors<br />
(VPs), create a user-defined VP, and specify the following information for it:<br />
► The number of virtual processors that the database server should start<br />
initially.<br />
► The maximum number of virtual processors allowed for this class.<br />
► The assignment of virtual processors to CPUs if processor affinity is available.<br />
► The disabling of priority aging by the operating system if the operating system<br />
implements priority aging.<br />
You can have multiple VPCLASS parameter definitions. Use one VPLCLASS<br />
reference for each class of virtual processor, one per line. Basic guidelines for<br />
defining a VPLCASS are in <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Administrator's<br />
Reference, V11.50, SC27-3607.<br />
452 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
For information about creating a user-defined virtual process, see <strong>IBM</strong> <strong>Informix</strong><br />
User-Defined Routines and Data Types <strong>Developer's</strong> Guide, V11.5, SC23-9438<br />
or J/Foundation <strong>Developer's</strong> Guide, V11.5, SC23-9434.<br />
The NETTYPE parameter<br />
The NETTYPE parameter provides tuning options for the protocols that<br />
DBSERVERNAME entries define in the sqlhosts file or registry. Each<br />
DBSERVERNAME entry in the sqlhosts file or registry is defined in relation to<br />
either the DBSERVERNAME parameter or the DBSERVERALIASES parameter<br />
in the ONCONFIG file.<br />
The NETTYPE configuration parameter describes a network connection as<br />
follows:<br />
► The protocol (or type of connection).<br />
► The number of poll threads assigned to manage the connection.<br />
► The expected number of concurrent connections.<br />
► The class of virtual processor that will run the poll threads.<br />
► You can specify a NETTYPE parameter for each protocol that you want the<br />
database server to use.<br />
► There is a special NETTYPE setting available if you want to enable the<br />
database server to use multiplexed connections on UNIX. To do this, you<br />
designate a NETTYPE with the value sqlmux, as in the following example.<br />
NETTYPE sqlmux<br />
The LOCKS parameter<br />
The LOCKS parameter specifies the initial size of the lock table. Every SQL<br />
session that connects to a database, and accesses tables and rows, generates a<br />
lock. If the lock is non-exclusive, it is shared; if something needs to change, it is<br />
exclusive. If there is an intent to change an object, it is an intent-exclusive lock.<br />
The lock table holds an entry for each type of lock entry, and each of these lock<br />
entries will have a small allocation in resident memory.<br />
When the number of locks exceeds the lock table value, on 32-bit servers, the<br />
database server will increase the size of the lock table by doubling the lock table<br />
value, up to 99 times, or to the maximum value for the server allowance<br />
(whichever comes first). On 32-bit servers, the maximum limit is 8,000,000 locks.<br />
On 64-bit servers, the lock table limit is based on the maximum starting locks<br />
value (500,000)+99 allowed increments of 1,000,000 locks for a total of<br />
599,000,000 locks.<br />
Chapter 13. Application development considerations 453
The amount of memory storage per lock ranges from 100 to 200 bytes,<br />
depending on the byte-word size and the platform.<br />
Note: All lock table increments are kept in virtual memory. If the server engine<br />
has limited shared memory, locks can become a memory resource drain.<br />
The STACKSIZE parameter<br />
The STACKSIZE parameter specifies the stack size for database server user<br />
threads. Setting a value for STACKSIZE that is too large wastes virtual memory<br />
space and can cause swap-space problems.<br />
For 32-bit platforms, the default STACKSIZE value of 32 KB is sufficient for<br />
non-recursive database activity. For 64-bit platforms, the recommended<br />
STACKSIZE value is 64 KB.<br />
In recursive SQL routines, the server checks for the possibility of stack-size<br />
overflow and automatically expands the stack.<br />
UDRs should increase the stack size for a routine as needed, using the stack<br />
modifier in the CREATE FUNCTION statement.<br />
The USELASTCOMMITTED parameter<br />
This parameter specifies the isolation level whenever the LAST COMMITTED<br />
feature of the COMMITTED READ isolation level is implicitly in effect. The LAST<br />
COMMITTED feature can reduce the risk of locking conflicts between concurrent<br />
transactions on tables that have exclusive row locks.<br />
Important: The USELASTCOMMITTED parameter works only with tables<br />
that have been created or altered to have ROW as their locking granularity.<br />
For the USELASTCOMMITTED parameter to work as expected, and given that a<br />
table has been created or altered to have ROW as their locking granularity, the<br />
following considerations also apply:<br />
► If SET TRANSACTION is enabled with READ COMMITTED or READ<br />
UNCOMMITTED, USELASTCOMMITTED will work.<br />
► Tables created without any explicit lock mode setting will use the default<br />
setting in DEF_TABLE_LOCKMODE.<br />
The DEF_TABLE_LOCKMODE parameter<br />
The DEF_TABLE_LOCKMODE parameter sets the lock mode for every newly<br />
created table for all sessions connected to a logging or nonlogging database, and<br />
has no effect on existing tables in the database.<br />
454 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
The specified value can be ROW, which sets the lock mode to ROW for every<br />
new table connected to a database. If the specified value is PAGE (default), any<br />
exclusive locked table remains inaccessible.<br />
There is also an environment variable on the client side,<br />
DEF_TABLE_LOCKMODE, which can be set with a lock mode value (PAGE or<br />
ROW). The environment variable has rules of precedence involved if the<br />
onconfig value is also set. For more information, see <strong>IBM</strong> <strong>Informix</strong> Dynamic<br />
Server Administrator's Reference, V11.50, SC27-3607.<br />
The DEADLOCK_TIMEOUT parameter<br />
The DEADLOCK_TIMEOUT parameter specifies the maximum number of<br />
seconds that a database server thread can wait to acquire a lock. Use this<br />
parameter only for distributed queries that involve a remote database server.<br />
Note: This parameter applies only to distributed queries. There is a separate,<br />
automated mechanism for deadlocks that are internal to a local database.<br />
The OPTCOMPIND parameter<br />
The OPTCOMPIND parameter determines the method used by the engine to<br />
process a query. All queries are optimized to run based on the guideline<br />
recommended by this parameter, unless the developer forces a different<br />
guideline using a directive.<br />
When the OPTCOMPIND parameter is set to one of the following values, a given<br />
query is optimized using the guideline assigned:<br />
0 When appropriate indexes exist for tables in the query, the optimizer<br />
chooses index scans, without consideration of the cost, over table<br />
scans.<br />
1 As long as the isolation is not Repeatable Read, the optimizer will<br />
use cost based decisions. If Repeatable Read is in use, it behaves as<br />
though OPTCOMPIND=0. Setting 1 is recommended for optimal<br />
performance.<br />
2 The optimizer uses cost to determine an execution path regardless of<br />
isolation level. Index scans are not given preference; the optimizer<br />
decision is purely based on cost. Setting 2 is the default if the<br />
parameter is not set.<br />
Note: Use the same OPTCOMPIND value in the development and in the<br />
production environment. Performance and query behavior can have variations<br />
if the OPTCOMIND value is different in the development and production<br />
environments.<br />
Chapter 13. Application development considerations 455
The DIRECTIVES parameter<br />
The DIRECTIVES parameter enables or disables the use of SQL directives. SQL<br />
directives allow you to specify behavior for the query optimizer in developing<br />
query plans for SELECT, UPDATE, and DELETE statements.<br />
Set DIRECTIVES to 1, which is the default value, to enable the database server<br />
to process directives.<br />
Set DIRECTIVES to 0 to disable the database server from processing directives.<br />
Note: Client programs have the option to set the IFX_DIRECTIVES<br />
environment variable to ON or OFF to enable or disable processing of<br />
directives by the database server. The setting of the IFX_DIRECTIVES<br />
environment variable overrides the setting of the DIRECTIVES configuration<br />
parameter. If you do not set the IFX_DIRECTIVES environment variable, all<br />
sessions for a client inherit the database server configuration for processing<br />
SQL directives.<br />
The MAX_PDQPRIORITY parameter<br />
The MAX_PDQPRIORITY parameter limits the PDQ resources that the database<br />
server can allocate to a given DSS query. MAX_PDQPRIORITY is a factor that is<br />
used to scale the value of PDQ priority set by users. For example, if the database<br />
administrator sets MAX_PDQPRIORITY to 80, and a user sets the<br />
PDQPRIORITY environment variable to 50 and issues a query, the database<br />
server silently processes the query with a PDQ priority of 40. The database<br />
administrator can use the onmode utility to change the value of<br />
MAX_PDQPRIORITY while the database server is online.<br />
In <strong>Informix</strong> database server, PDQ resources include memory, CPU, disk I/O, and<br />
scan threads. the MAX_PDQPRIORITY parameter lets the database<br />
administrator run decision support concurrently with OLTP, without a<br />
deterioration of OLTP performance. However, if the MAX_PDQPRIORITY<br />
parameter is too low, the performance of decision- support queries can degrade.<br />
Table 13-1 lists the MAX_PDQPRIORITY value that you can set.<br />
Table 13-1 The MAX_PDQPRIORITY value<br />
Value Database Server Action<br />
0 Turns off PDQ. DSS queries use no parallelism.<br />
1 Fetches data from fragmented tables in parallel (parallel scans) but<br />
uses no other form of parallelism.<br />
100 Uses all available resources for processing queries in parallel.<br />
456 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Value Database Server Action<br />
An integer<br />
between 1-100<br />
13.4.4 Java configuration parameters<br />
The configuration parameters listed in Table 13-2 allow you to use J/Foundation,<br />
which incorporates an embedded Java Virtual Machine (JVM) on the database<br />
server. For more information about these parameters, see J/Foundation<br />
<strong>Developer's</strong> Guide, V11.5, SC23-9434.<br />
Table 13-2 Java configuration parameters<br />
Parameter name Description<br />
Sets the percentage of the user-requested PDQ resources to be<br />
allocated to the query.<br />
AFCRASH 0x10 When the 0x10 bit is on for AFCRASH, all the messages that the<br />
JVM generates are logged into the JVM_vpid file, where vpid is<br />
the process ID of the Java virtual processor. This file is stored in<br />
the directory where the JVPLOG file is stored.<br />
JVPDEBUG When set to 1, tracing messages are written to the JVPLOG file.<br />
JVPHOME Directory where the classes of the <strong>IBM</strong> <strong>Informix</strong> JDBC Driver are<br />
installed.<br />
JVPLOGFILE Absolute path name for your Java VP log files.<br />
JVPPROPFILE Absolute path name for the Java VP properties file.<br />
JVPJAVAVM Libraries to use for the JVM.<br />
JVPJAVAHOME Directory where the Java Runtime Environment (JRE) for the<br />
database server is installed.<br />
JVMTHREAD Thread package (green or native) to use for the JVM.<br />
JVPVJAVALIB Path from JVPJAVAHOME to the location of the Java VM<br />
libraries.<br />
JVPCLASSPATH Initial Java class path setting.<br />
VPCLASS jvp=n Number of Java virtual processors that the database server<br />
should start.<br />
For additional onconfig file parameters and how they relate to development, see<br />
Appendix A, “Parameters in the onconfig file” on page 467.<br />
Chapter 13. Application development considerations 457
13.5 Working with your database administrator<br />
There is an assumption throughout this book that our main audience is the<br />
developer, with a distinctive separate role from the database administrator (DBA).<br />
The database server administrator has the task of allocating and handling<br />
resources effectively in the server side of a client -server environment. When a<br />
performance issue occurs or when a transaction process is not working as it<br />
should, you need to work with the database administrator to find a solution.<br />
Present the problem to the database administrator, who might have a good idea<br />
about a particular configuration area that might relate to the problem. There are a<br />
few areas we can discuss here, which might help target the common problems<br />
that developers experience.<br />
Configuration parameters that affect logging<br />
Checkpoints, logging, and page cleaning are necessary to maintain database<br />
consistency. A direct trade-off exists between the frequency of checkpoints or the<br />
size of the logical logs and the time that it takes to recover the database in the<br />
event of a failure. A database administrator has to consider ways to reduce the<br />
overhead for these activities based on the acceptable delay during recovery.<br />
Sometimes these decisions are influenced by the behavior of the application.<br />
Here we discuss the configuration parameters that effect logging.<br />
LOGBUFF and PHYSBUFF<br />
The LOGBUFF and PHYSBUFF configuration parameters effect logging I/O<br />
activity because they specify the respective sizes of the logical-log and<br />
physical-log buffers that are in shared memory. The size of these buffers<br />
determines how quickly they fill and therefore how often they need to be flushed<br />
to disk. If checkpoints tend to have long duration, you can seek further<br />
information related to buffer tuning and checkpoint tuning information in the <strong>IBM</strong><br />
<strong>Informix</strong> Dynamic Server Performance Guide, V11.50, SC27-3618-00.<br />
LOGFILES<br />
The LOGFILES parameter specifies the number of logical-log files. This<br />
parameter only becomes important with respect to the long transaction high<br />
water mark (LTXHWM and LTXEHWM), if we are having trouble with transactions<br />
that need to be rolled back. See the sections on DYNAMIC_LOGS, LTXHWM<br />
and LTXEHWM, later in this chapter for more details.<br />
LOGSIZE<br />
Choose a log size based on how much logging activity occurs and the amount of<br />
risk in case of catastrophic failure. If you cannot afford to lose more than an<br />
hour's worth of data, create many small log files that each hold an hour's worth of<br />
458 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
transactions. If your system is stable with high logging activity, choose larger logs<br />
to improve performance.<br />
Note: A backup process can hinder transaction processing if data is located<br />
on the same disk as the logical-log files. It is better to keep logical logs on<br />
separate disks from data, if possible. If extra disks are not an option for<br />
separate logical log space, however, you can wait for periods of low user<br />
activity before you back up the logical-log files.<br />
You need to adjust the size of the logical log when your transactions include<br />
simple large objects or smart large objects, as the following sections describe.<br />
Estimating logical-log size when logging simple large objects<br />
To obtain better overall performance for applications that perform frequent<br />
updates of TEXT or BYTE data in blobspaces, reduce the size of the logical log.<br />
Blobpages cannot be reused until the logical log to which they are allocated is<br />
backed up. When TEXT or BYTE data activity is high, the performance impact of<br />
more frequent checkpoints is balanced by the higher availability of free<br />
blobpages.<br />
When you use volatile blobpages in blobspaces, smaller logs can improve<br />
access to simple large objects that must be reused. Simple large objects cannot<br />
be reused until the log in which they are allocated is flushed to disk. In this case,<br />
you can justify the cost in performance because those smaller log files are<br />
backed up more frequently.<br />
Estimating logical-log size when logging smart large objects<br />
If you plan to log smart large object user data, you must ensure that the log size<br />
is considerably larger than the amount of data being written. Smart large object<br />
metadata is always logged even if the smart large objects are not logged.<br />
Use the following guidelines when you log smart large objects:<br />
► If you are appending data to a smart large object, the increased logging<br />
activity is roughly equal to the amount of data written to the smart large<br />
object.<br />
► If you are updating a smart large object (overwriting data), the increased<br />
logging activity is roughly twice the amount of data written to the smart large<br />
object. The database server logs both the before-image and after-image of a<br />
smart large object for update transactions. When updating the smart large<br />
objects, the database server logs only the updated parts of the before and<br />
after image.<br />
Chapter 13. Application development considerations 459
► Metadata updates affect logging less. Even though metadata is always<br />
logged, the number of bytes that are logged is usually much smaller than the<br />
smart large objects.<br />
The DYNAMIC_LOGS parameter<br />
The default value for the DYNAMIC_LOGS configuration parameter is 2, which<br />
means that the database server allocates a new logical log file automatically after<br />
the current log file when it detects that the next log file contains an open<br />
transaction. The database server automatically checks whether the log after the<br />
current log still contains an open transaction at the following times:<br />
► Immediately after it switches to a new log file while writing log records (not<br />
while reading and applying log records)<br />
► At the beginning of the transaction cleanup phase which occurs as the last<br />
phase of logical recovery<br />
Logical recovery happens at the end of fast recovery and at the end of a cold<br />
restore or roll forward. For more information about the phases of fast recovery,<br />
see <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Administrator's Guide, Version 11.50,<br />
SC27-3606.<br />
If you set DYNAMIC_LOGS to 0, the database server still checks whether the<br />
next active log contains an open transaction when it switches log files. If it finds<br />
an open transaction in the next log to be active, it issues the following warning:<br />
WARNING: The oldest logical log file (%d) contains records from an open<br />
transaction (0x%p), but the Dynamic Log Files feature is turned off.<br />
The LTXHWM and LTXEHWM parameters<br />
The LTXHWM parameter indicates how full the logical log is when the database<br />
server starts to check for a possible long transaction and to roll it back.<br />
LTXEHWM indicates the point at which the database server suspends new<br />
transaction activity to locate and roll back a long transaction.<br />
With the dynamic log file feature, long transaction high watermarks are no longer<br />
as critical because the database server does not run out of log space unless you<br />
use up the physical disk space available on the system. Under normal conditions,<br />
you should keep the default values for LTXHWM and LTXEHWM. If a rollback is<br />
ever needed, it can indicate a serious problem within an application.<br />
460 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
After Version 9.4, LTXHWM and LTXEHWM are not in the onconfig.std file and<br />
default to the following values, depending on the value of the DYNAMIC_LOGS<br />
configuration parameter:<br />
► With DYNAMIC_LOGS set to 1 or 2, the long transaction high watermark<br />
default values are 80 for LTXHWM and 90 for LTXEHWM. Because the<br />
database server does not run out of logs, other users can still access the log<br />
during the rollback of a long transaction.<br />
► With DYNAMIC_LOGS to 0, the default values are 50 for LTXHWM and 60 for<br />
LTXEHWM.<br />
You might want to change these default values for one of the following reasons:<br />
► To allow other transactions to continue update activity (which requires access<br />
to the log) during the rollback of a long transaction. In this case, you increase<br />
the value of LTXEHWM to raise the point at which the long transaction<br />
rollback has exclusive access to the log.<br />
► To perform scheduled transactions of unknown length, such as large loads<br />
that are logged. In this case, you increase the value of LTXHWM so that the<br />
transaction has a chance to complete before reaching the high watermark.<br />
13.5.1 Parameters for negotiation<br />
While the database administrator should make final decisions for server<br />
parameters, there are several parameters for which you need agreement to<br />
achieve the most effective results.<br />
The VPCLASS parameter<br />
If you decide to use UDRs, a UDR can use the processing power in the CPU<br />
class that users currently have at their disposal, or you can define the UDR with<br />
its own processor class when you create the function. You define a new class of<br />
virtual processors to isolate UDR execution from other transactions that execute<br />
on the CPU virtual processors. This method is typically used when you write<br />
UDRs to support user-defined data types. The class name that you specify in the<br />
VPCLASS parameter must match the name specified in the CLASS modifier of<br />
the CREATE FUNCTION statement.<br />
Note: Because a user-defined CPU class is treated as a virtual processor, the<br />
onconfig file SINGLE_CPU_VP parameter must be off (0) and<br />
MULTIPROCESSOR must be on (1).<br />
Chapter 13. Application development considerations 461
Guidelines for setting VPCLASS:<br />
► For uniprocessor computers, it is recommended that you use one CPU virtual<br />
processor.<br />
► VPCLASS cpu,num=1.<br />
► For multiprocessor systems with four or more CPUs that are primarily used as<br />
database servers, it is recommended that you set the VPCLASS num option to<br />
one less than the total number of processors. For example, if you have four<br />
CPUs, use the following specification:<br />
VPCLASS cpu,num=3<br />
Process priority aging<br />
On some operating systems, priority aging occurs when the operating system<br />
lowers the priority of long-running processes as they accumulate processing<br />
time. Always disable the priority aging parameter of VPCLASS because it can<br />
cause the performance of the database server processes to decline over time.<br />
For further information, check the system notes for your database server.<br />
Processor affinity<br />
Processor affinity distributes the computation impact of CPU virtual processors<br />
and other processes. On computers that are dedicated to the database server,<br />
assigning CPU virtual processors to all but one of the CPUs achieves maximum<br />
CPU utilization. On computers that support both database server and client<br />
applications, you can bind applications to certain CPUs through the operating<br />
system. By doing so, you effectively reserve the remaining CPUs for use by<br />
database server CPU virtual processors, which you bind to the remaining CPUs<br />
with the VPCLASS configuration parameter.<br />
Set the aff option of the VPCLASS parameter to the numbers of the CPUs on<br />
which to bind CPU virtual processors. For example, on an 8 CPU system, the<br />
following VPCLASS setting assigns CPU virtual processors to processors 4 to 7,<br />
and CPU’s 0, 1, 2, and 3 would be most available to the application:<br />
VPCLASS cpu,num=4,aff=4-7<br />
For additional methods that can be used with the VPCLASS, see I<strong>IBM</strong> <strong>Informix</strong><br />
Dynamic Server Administrator's Guide, V11.50, SC27-3606.<br />
13.5.2 Monitoring isolation levels<br />
As a general discovery tool, the onstat -g sql command can be used for<br />
determining the isolation levels and lock modes for SQL statements actively<br />
running against the server, their associated databases and any SQL errors that<br />
might have occurred. The onstat -g sql command can also be run with an<br />
462 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
optional session ID. The output contains the most recent and the current SQL<br />
statements being run by the session.<br />
13.5.3 Monitoring locks<br />
The onstat -k command displays the current database locks held within the<br />
system. The type of lock is determined by the logging mode, isolation levels, and<br />
application design. Example 13-1shows the output of a sample onstat -k<br />
command.<br />
Example 13-1 The onstat -k command output<br />
<strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.UC6 -- On-Line -- Up 03:55:17 --<br />
15360 Kbytes<br />
Locks<br />
address wtlist owner lklist type tblsnum rowid key#/bsiz<br />
a095f78 0 a4d9e68 0 HDR+S 100002 203 0<br />
1 active, 2000 total, 2048 hash buckets, 0 lock table overflows<br />
The information in this output explains lock aspects. The type column explains<br />
the lock types associated with each open lock. The abbreviations in the Type<br />
column defines lock types as follows:<br />
HDR Header<br />
B Byte lock<br />
S Shared<br />
I Intent<br />
X Exclusive<br />
U Update<br />
IX Intent exclusive<br />
IS Intent-shared<br />
SIX Shared, Intent exclusive<br />
Chapter 13. Application development considerations 463
The lock level is determined by the value of the tblsnum (tblspace ID) and the row<br />
ID as show in Table 13-3 and Table 13-4.<br />
Table 13-3 Determining lock level from Row ID<br />
If RowID Lock level is<br />
Equals 0 Table<br />
Ends in 00 Page<br />
– X=transaction cleanup<br />
– Y=condition<br />
Watch for persistent L, S, T, or Y.<br />
3. Look at flag position 3 (thread activity):<br />
– A=DBSpace backup<br />
– B=Begin work<br />
– P=Preparing/prepared<br />
– X=XA prepare<br />
– C=Commiting/committed<br />
– R=Aborting/aborted<br />
– H=Heuristic aborting/aborted<br />
Watch for P, X, C, R, H.<br />
For more information about onstat commands and the meanings of the columns,<br />
refer to:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.<br />
adref.doc/ids_adr_0608.htm<br />
Chapter 13. Application development considerations 465
466 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
A<br />
Appendix A. Parameters in the onconfig<br />
file<br />
In 13.4, “Configuration options” on page 449, we discuss several parameters that<br />
have a direct impact on developer methods and understandings. The list<br />
provided in that section is limited to the direct-impact related parameters. It is<br />
likely that application developers will not have much use for the remaining<br />
onconfig parameters most of the time.<br />
Normally, this remaining configuration information is a concern only for the DBAs.<br />
Occasionally, there will come a time when performance of an application might<br />
come into question. Sometimes slow performance is caused by the application,<br />
and is under control of the developer. At other times it might be an onconfig file<br />
setting in the <strong>IBM</strong> <strong>Informix</strong> server that requires adjustment.<br />
Table A-1 on page 468 lists the parameters that might help developers and DBAs<br />
to narrow the target area for further investigation. The table is not an exhaustive<br />
list. Parameters that do not apply or that we have already discussed in this book<br />
are not listed. For further information about any of these parameters, see <strong>IBM</strong><br />
<strong>Informix</strong> Dynamic Server Administrator's Reference, v11.50, SC27-3607.<br />
From the developer’s point of view, it makes sense to categorize the configuration<br />
parameters from a functional perspective, rather than a strict listing by name.<br />
Toward this objective, the parameter names are listed in the table by function<br />
groupings to help narrow down the parameter list that can be considered for an<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 467
area of specific interest. The functional groupings do not reflect specific naming<br />
conventions used in the onconfig file.<br />
Table A-1 Parameters in the onconfig file<br />
Related function area Parameters<br />
Storage Space ROOTNAME, ROOTOFFSET, ROOTSIZE, MIRROR,<br />
MIRROROFFSET, LOGFILES, LOGSIZE,<br />
DBSPACETEMP, SBSPACETEMP, SBSPACENAME,<br />
SYSSBSPACENAME, CLEANERS, STAGEBLOB,<br />
TBLTBLFIRST, TBLTBLNEXT, DATASKIP<br />
Path locations ROOTPATH, JVPJAVALIB, JVPJAVAVM, MSGPATH,<br />
PLOG_OVERFLOW_PATH<br />
Buffer Movement BUFFERPOOL,PHYSBUFF, PHYSFILE<br />
Memory Cache DD_HASHSIZE, DD_HASHMAX, DS_HASHSIZE,<br />
DS_POOLSIZE, PC_HASHSIZE, PC_POOLSIZE,<br />
STMT_CACHE, STMT_CACHE_HITS,<br />
STMT_CACHE_SIZE, STMT_CACHE_NOLIMIT,<br />
STMT_CACHE_NUMPOOL, PLCY_POOLSIZE,<br />
PLCY_HASHSIZE, USRC_POOLSIZE,<br />
USRC_HASHSIZE<br />
Memory control RESIDENT, SHMVIRTSIZE, SHMADD, EXTSHMADD,<br />
SHMTOTAL, SHMVIRT_ALLOCSEG,<br />
SHMNOACCESS,VP_MEMORY_CACHE_KB,<br />
ONLIDX_MAXMEM<br />
Virtual processor and<br />
connection<br />
468 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
NETTYPE, LISTEN_TIMEOUT,<br />
MAX_INCOMPLETE_CONNECTIONS, FASTPOLL,<br />
MULTIPROCESSOR, VPCLASS, SINGLE_CPU_VP,<br />
AUTO_AIOVPS, DIRECT_IO<br />
SQL Control LOCKS, OPTCOMPIND, DIRECTIVES,<br />
EXT_DIRECTIVES, OPT_GOAL, IFX_FOLDVIEW,<br />
RA_PAGES, RA_THRESHOLD<br />
Transaction control DEF_TABLE_LOCKMODE, BLOCKTIMEOUT,<br />
TXTIMEOUT, HETERO_COMMIT,<br />
DEADLOCK_TIMEOUT, DYNAMIC_LOGS, LOGBUFF,<br />
LTXHWM, LTXEHWM, TEMPTAB_NOLOG,<br />
AUTO_REPREPARE<br />
Decision support DS_MAX_QUERIES, DS_TOTAL_MEMORY,<br />
DS_MAX_SCANS, DS_NONPDQ_QUERY_MEM
B<br />
Appendix B. Accommodating distributed<br />
transactions<br />
This appendix discusses the use of distributed transactions with an <strong>IBM</strong> <strong>Informix</strong><br />
database server.<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 469
B.1 Distributed transactions<br />
A transaction is a series of actions performed as a single logical unit of work in<br />
which either all of the actions are performed or none of them are.<br />
A distributed transaction is a transaction that runs in multiple processes, usually<br />
on several systems, and normally involves actions against two or more<br />
databases. Each participant of a distributed transaction must agree to commit<br />
the changes before the distributed transaction can be committed.<br />
There are three core components on a distributed transaction:<br />
► Application program (AP)<br />
Application program implements the desired business function. It specifies a<br />
sequence of operations that involve resources such as databases. An<br />
application program participates in one or more units of work, and might<br />
decide to commit or roll back each unit of work independently.<br />
► Resource manager (RM)<br />
Resource manager manages access to shared resources such as databases.<br />
The resource manager provides the services to manage the data involved in<br />
the distributed transaction.<br />
► Transaction manager (TM)<br />
Transaction manager manages global transactions and coordinates the<br />
decision to commit them or roll them back ensuring their atomicity. The<br />
transaction manager also coordinates recovery activities of the resource<br />
managers when necessary, such as after a component failure.<br />
The XA standards, set forth by the Open Group's X/Open Distributed Transaction<br />
Processing (DTP) model, define the interfaces between the transaction manager,<br />
the application program, and the resource manager in a DTP environment.<br />
These interfaces are implemented in the <strong>Informix</strong> TP/XA Interface Library.<br />
B.2 TP/XA Transaction Manager XA Interface Library<br />
TP/XA is a library of functions that allows the <strong>IBM</strong> <strong>Informix</strong> database server act<br />
as a resource manager in the X/Open DTP environment.The TP/XA library<br />
facilitates communication between a third-party transaction manager and the<br />
database server.<br />
TP/XA is supplied with <strong>IBM</strong> <strong>Informix</strong> ESQL/C, which is included with <strong>Informix</strong><br />
Client Software Development Kit (Client SDK).<br />
470 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
In addition to the TP/XA library, a header file xa.h, is supplied that contains the<br />
definition for the functions and common structures, such as XID or xa_switch_t,<br />
which are used by the transaction manager and resource manager.<br />
Table B-1 describes the functions used to work with XA transactions.<br />
Table B-1 TP/XA macro definitions<br />
Function Description<br />
xa_open() Initializes the resource manager (database server) for an XA<br />
transaction<br />
xa_close() Close a currently open resource manager<br />
xa_start() Starts an XA transaction<br />
xa_end() Dissociates from an XA transaction<br />
xa_rollback() Tells the resource manager to roll back an XA transaction<br />
xa_prepare() Asks the resource manager to prepare to commit an XA<br />
transaction<br />
xa_commit() Tells the resource manager to commit an XA transaction<br />
xa_recover() Obtains a list of XIDs that are currently in a prepared or<br />
heuristically completed state<br />
xa_forget() Tells the resource manager to forget a heuristically completed<br />
transaction<br />
xa_complete() Test Completion of asynchronous XA Request. This function is<br />
provided only for compliance with the X/Open XA Specification<br />
B.3 XA_TOOL ESQL/C sample<br />
Example B-1is a basic ESQL/C application which demonstrates how to use<br />
some of the TP/XA functions such as xa_prepare() and xa_rollback() to<br />
perform operations with a distributed transaction.<br />
Example B-1 The xa_tool.ec application<br />
/*************************************************************<br />
*<br />
* WARNING: USE OF THIS PROGRAM MAY HAVE UNDESIRABLE AFFECTS<br />
* TO THE DATABASE SERVER, INCLUDING DATA CORRUPTION!!<br />
*<br />
* TITLE: xa_tool.ec<br />
*<br />
Appendix B. Accommodating distributed transactions 471
* Compile with:<br />
* on UNIX systems:<br />
* esql -o xa_tool xa_tool.ec -lifxa<br />
* on Windows systems<br />
* esql xa_tool.ec<br />
*<br />
* This program will connect to a global transaction branch<br />
* and let the user manipulate the transaction.<br />
*<br />
* Usage:<br />
* xa_tool <br />
*<br />
* The fID, gtl, bql, and hex data should be provided from the<br />
* transaction desired to be manipulated. The information is<br />
* found by execution of the onstat -G command. Example output<br />
* of the onstat -G command:<br />
*<br />
* Global Transaction Identifiers<br />
* address flags fID gtl bql data<br />
* cb2a964 0x8442a 0 2 4 4D4E4F000000<br />
* 1 active, 128 total<br />
*<br />
* If transaction cb2a964 is the transaction that is desired<br />
* to be manipulated, then this program should be executed<br />
* with the following command:<br />
*<br />
* xa_tool 0 2 4 4D4E4F000000<br />
*<br />
*<br />
*************************************************************<br />
*/<br />
#include <br />
#include <br />
#include <br />
#include "xa.h"<br />
extern struct xa_switch_t infx_xa_switch;<br />
#define xa_open(info, rmid, flags) \<br />
((*infx_xa_switch.xa_open_entry)(info,rmid,flags))<br />
#define xa_start(gtrid,rmid, flags) \<br />
((*infx_xa_switch.xa_start_entry)(gtrid,rmid,flags))<br />
#define xa_rollback(gtrid,rmid, flags) \<br />
((*infx_xa_switch.xa_rollback_entry)(gtrid,rmid,flags))<br />
#define xa_commit(gtrid,rmid, flags) \<br />
((*infx_xa_switch.xa_commit_entry)(gtrid,rmid,flags))<br />
#define xa_rollback(gtrid,rmid, flags) \<br />
((*infx_xa_switch.xa_rollback_entry)(gtrid,rmid,flags))<br />
#define xa_end(gtrid,rmid, flags) \<br />
((*infx_xa_switch.xa_end_entry)(gtrid,rmid,flags))<br />
#define xa_prepare(gtrid,rmid, flags)\<br />
((*infx_xa_switch.xa_prepare_entry)(gtrid,rmid,flags))<br />
472 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
#define xa_close(info,rmid, flags)\<br />
((*infx_xa_switch.xa_close_entry)(info,rmid,flags))<br />
#define xa_forget(gtrid,rmid, flags)\<br />
((*infx_xa_switch.xa_forget_entry)(gtrid,rmid,flags))<br />
#define xa_recover(gtrid, count, rmid, flags)\<br />
((*infx_xa_switch.xa_recover_entry)(gtrid,count,rmid,flags))<br />
/* Doesn't matter what database is opened... */<br />
#define OPEN_DATABASE "sysmaster"<br />
/* Initialize the xid */<br />
void<br />
setup_myxid (XID *xid, int x_fID, int x_gtl, int x_bql, char* x_data)<br />
{<br />
int ii, c;<br />
xid->formatID = x_fID;<br />
xid->gtrid_length = x_gtl;<br />
xid->bqual_length = x_bql;<br />
}<br />
for(ii=0; iidata[ii] = (char) c;<br />
void xa_tool(XID *xid)<br />
{<br />
int choice = 0;<br />
int cc;<br />
while( choice != 'Q')<br />
{<br />
printf("\tP -- XA_PREPARE\n");<br />
printf("\tC -- XA_COMMIT\n");<br />
printf("\tR -- XA_ROLLBACK\n");<br />
printf("\tF -- XA_FORGET\n");<br />
printf("\tQ -- terminate program\n");<br />
printf("Enter Choice: ");<br />
choice = getchar();<br />
choice = toupper(choice);<br />
printf("%c\n\n", choice);<br />
switch(choice)<br />
{<br />
case 'P':<br />
printf("Executing XA_PREAPRE\n");<br />
if ((cc = xa_prepare(xid, 0, TMNOFLAGS)) != XA_OK)<br />
Appendix B. Accommodating distributed transactions 473
}<br />
{<br />
printf("XA_PREPARE failed with %d\n", cc);<br />
}<br />
else<br />
printf("... XA_PREPARE finished\n\n");<br />
break;<br />
case 'C':<br />
printf("Executing XA_COMMIT\n");<br />
if ((cc = xa_commit(xid, 0, TMNOFLAGS)) != XA_OK)<br />
{<br />
printf("XA_COMMIT failed with %d\n", cc);<br />
}<br />
else<br />
printf("... XA_COMMIT finished\n\n");<br />
break;<br />
case 'R':<br />
printf("Executing XA_ROLLBACK\n");<br />
if ((cc = xa_rollback(xid, 0, TMNOFLAGS)) != XA_OK)<br />
{<br />
printf("XA_ROLLBACK failed with %d\n", cc);<br />
}<br />
else<br />
printf("... XA_ROLLBACK finished\n\n");<br />
break;<br />
case 'F':<br />
printf("Executing XA_FORGET\n");<br />
if ((cc = xa_forget(xid, 0, TMNOFLAGS)) != XA_OK)<br />
{<br />
printf("XA_FORGET failed with %d\n", cc);<br />
}<br />
else<br />
printf("... XA_FORGET finished\n\n");<br />
break;<br />
case 'Q':<br />
break;<br />
default:<br />
printf("%c is not a valid option!\n\n", choice);<br />
choice = 0;<br />
}<br />
if (choice != 'Q') {<br />
choice = getchar();<br />
choice = 0;<br />
}<br />
}<br />
int main(int arc, char *argv[])<br />
{<br />
$int cc;<br />
XID xid;<br />
int xid_fID, xid_gtl, xid_bql;<br />
char xid_data[300];<br />
474 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
}<br />
if (arc != 5)<br />
{<br />
printf("Error: Incorrect number of parameters.\n");<br />
printf("Usage: %s \n", argv[0]);<br />
exit(2);<br />
}<br />
printf("\n\n");<br />
xid_fID = atoi(argv[1]);<br />
xid_gtl = atoi(argv[2]);<br />
xid_bql = atoi(argv[3]);<br />
strcpy(xid_data, argv[4]);<br />
/* setup the XID which is used to identify the transaction */<br />
setup_myxid(&xid, xid_fID, xid_gtl, xid_bql, xid_data);<br />
/* establish connection to the database */<br />
printf("Calling xa_open ... \n");<br />
if ((cc = xa_open(OPEN_DATABASE, 0, TMNOFLAGS)) != XA_OK)<br />
{<br />
printf("xa_open failed with %d, sqlcode = %d\n", cc, SQLCODE);<br />
exit(1);<br />
}<br />
else<br />
printf("... xa_open finished\n\n");<br />
/** This is for xa_tool... **/<br />
xa_tool(&xid);<br />
/* close the connection */<br />
printf("Calling xa_close...\n");<br />
if ((cc = xa_close("", 0, TMNOFLAGS)) != XA_OK)<br />
{<br />
printf("xa_close failed with %d\n", cc);<br />
exit(1);<br />
}<br />
else<br />
printf("... xa_close finished\n\n");<br />
You also can use the xa_tool example to complete unresolved transactions left<br />
in the database server. Local transactions are rolled back automatically by the<br />
<strong>Informix</strong> database server if the connection between the client application and the<br />
database server is lost. This roll back occurs because the database server is in<br />
direct control of the transaction.<br />
Appendix B. Accommodating distributed transactions 475
When using distributed transactions, the database server cannot resolved a<br />
transaction automatically. The Transaction Manager is the only process in charge<br />
of committing or aborting the global transaction. Thus, if there is a<br />
communication error or a failure within the resource manager, there is a chance<br />
of leaving unresolved transactions in the database server. These transactions<br />
can be resolved using the xa_commit(), xa_rollback(), and xa_forget()<br />
functions.<br />
Example B-2 shows how to compile and use the xa_tool.ec program to rollback<br />
an XA transaction left in the <strong>Informix</strong> database server.<br />
Example B-2 The xa_tool.ec program output<br />
D:\Infx\ids1150>onstat -G<br />
<strong>IBM</strong> <strong>Informix</strong> Dynamic Server Version 11.50.FC6 -- On-Line -- Up 05:04:17<br />
Global Transaction Identifiers<br />
address flags isol timeout fID gtl bql data<br />
83246d88 -L--G COMMIT 0 0 2 4 4D4E4F000000<br />
1 active, 128 total<br />
D:\Infx\ids1150><br />
C:\work>esql -nologo xa_tool.ec<br />
<strong>IBM</strong> <strong>Informix</strong> CSDK Version 3.50, <strong>IBM</strong> <strong>Informix</strong>-ESQL Version 3.50.TC7<br />
xa_tool.c ...<br />
C:\work>xa_tool 0 2 4 4D4E4F000000<br />
Calling xa_open ...<br />
... xa_open finished<br />
P -- XA_PREPARE<br />
C -- XA_COMMIT<br />
R -- XA_ROLLBACK<br />
F -- XA_FORGET<br />
Q -- terminate program<br />
Enter Choice: R<br />
R<br />
Executing XA_ROLLBACK<br />
... XA_ROLLBACK finished<br />
P -- XA_PREPARE<br />
C -- XA_COMMIT<br />
R -- XA_ROLLBACK<br />
F -- XA_FORGET<br />
Q -- terminate program<br />
476 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Enter Choice: q<br />
Q<br />
Calling xa_close...<br />
... xa_close finished<br />
C:\work><br />
For more information related to the TP/XA library, refer to the TP/XA Transaction<br />
Manager XA Interface Library User Manual at:<br />
http://publibfp.boulder.ibm.com/epubs/pdf/5193.pdf<br />
Appendix B. Accommodating distributed transactions 477
478 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Related publications<br />
We consider the publications that we list in this section particularly suitable for a<br />
more detailed discussion of the topics that we cover in this book.<br />
<strong>IBM</strong> <strong>Redbooks</strong> publications<br />
For information about ordering these publications, see “How to get <strong>IBM</strong><br />
<strong>Redbooks</strong> publications” on page 480. Note that some of the documents that we<br />
reference here might be available in softcopy only.<br />
► Embedding <strong>Informix</strong> Dynamic Server: An Introduction, SG24-7666<br />
Other publications<br />
The following publications are also relevant as further information sources:<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Administrator’s Guide, v11.50, SC23-7748<br />
► <strong>IBM</strong> <strong>Informix</strong> Guide to SQL: Reference, v11.50, SC23-7750<br />
► Embedded SQLJ User’s Guide, Version 2.90, G251-2278<br />
► <strong>IBM</strong> <strong>Informix</strong> Storage Manager Administrator’s Guide, v2.2, G229-6388<br />
► <strong>IBM</strong> <strong>Informix</strong> Security Guide, v11.50, SC23-7754<br />
► <strong>IBM</strong> <strong>Informix</strong> GLS User’s Guide, G229-6373<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Administrator’s Reference, SC23-7749<br />
► <strong>IBM</strong> <strong>Informix</strong> Guide to SQL: Syntax, v11.50, SC23-7751<br />
► <strong>IBM</strong> <strong>Informix</strong> Guide to SQL: Tutorial, G229-6427<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Performance Guide, v11.50, SC23-7757<br />
► <strong>IBM</strong> <strong>Informix</strong> High-Performance Loader User’s Guide, v11.50, SC23-9433<br />
► <strong>IBM</strong> <strong>Informix</strong> Migration Guide, SC23-7758<br />
► <strong>IBM</strong> <strong>Informix</strong> JDBC Driver Programmer’s Guide, SC23-9421<br />
► <strong>IBM</strong> <strong>Informix</strong> ESQL/C Programmer’s Manual, v3.50, SC23-9420<br />
► <strong>IBM</strong> Data Server Provider for .NET Programmer’s Guide, SC23-9848<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 479
Online resources<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Installation Guide for UNIX, Linux, and Mac OS<br />
X, GC23-7752<br />
► <strong>IBM</strong> <strong>Informix</strong> DB-Access User’s Guide, SC23-9430<br />
► <strong>IBM</strong> <strong>Informix</strong> ODBC Driver Programmer’s Manual, SC23-9423<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Installation Guide for Windows, GC23-7753<br />
► Guide to <strong>Informix</strong> MaxConnect, Version 1.1, G251-0577<br />
► <strong>IBM</strong> <strong>Informix</strong> Backup and Restore Guide, v11.50, SC23-7756<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server Enterprise Replication Guide, v11.50,<br />
SC23-7755<br />
► “Expand transaction capabilities with savepoints in <strong>Informix</strong> Dynamic Server”<br />
by Uday B. Kale in <strong>IBM</strong> developerWorks, 26 March 2009:<br />
http://www.ibm.com/developerworks/data/library/techarticle/dm-0903idssavepo<br />
ints/index.html<br />
The following website is also relevant as further information sources:<br />
► <strong>IBM</strong> <strong>Informix</strong> Dynamic Server v11.50 Information Center:<br />
http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp<br />
How to get <strong>IBM</strong> <strong>Redbooks</strong> publications<br />
Help from <strong>IBM</strong><br />
You can search for, view, or download <strong>Redbooks</strong>, Redpapers, Technotes, draft<br />
publications and Additional materials, as well as order hardcopy <strong>Redbooks</strong><br />
publications, at this website:<br />
ibm.com/redbooks<br />
<strong>IBM</strong> Support and downloads<br />
ibm.com/support<br />
<strong>IBM</strong> Global Services<br />
ibm.com/services<br />
480 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
Index<br />
Symbols<br />
.4cf extension 418<br />
.ec 128<br />
.ecp 128<br />
.NET connection string attributes<br />
Client_Locale 259<br />
Connection Lifetime 259<br />
DB_LOCALE 259<br />
DELIMIDENT 259<br />
Enlist 259<br />
Fetch Buffer Size 260<br />
Host 260<br />
Max Pool Size 260<br />
OPTOFC 260<br />
Packet Size 260<br />
Pooling 260<br />
PWD 260<br />
Skip Parsing 260<br />
UserDefinedTypeFormat 260<br />
XCL 260<br />
Numerics<br />
4GL 15<br />
A<br />
access method 333<br />
ACE Report Writer 9<br />
ad hoc queries 7<br />
adapter script 370<br />
ADO interface<br />
IAccessor 220<br />
IColumnsInfo 220<br />
ICommand 220<br />
IDBCreateCommand 220<br />
IDBCreateSession 220<br />
IDBDataSourceAdmin 220<br />
IDBProperties 220<br />
IErrorLookup 220<br />
IGetDataSource 220<br />
IRowsetIdentity 220<br />
ISessionProperties 220<br />
ITransaction 220<br />
ADO object 217<br />
adOpenDynamic cursor 224<br />
adOpenForwardOnly cursor 224<br />
adOpenKeyset cursor 224<br />
adOpenStatic cursor 224<br />
annotation 211<br />
ANSI serializable 448<br />
antlr-2.7.6.jar 193<br />
Apache Web Server 295, 297, 299, 303<br />
application programming interface 14<br />
application-programming interface 35<br />
attribute 190<br />
authorized user 5<br />
AutoCommit mode 171<br />
AXIS2C_HOME 403<br />
B<br />
backup 330<br />
begin work 140<br />
BeginTransaction method 274<br />
BeginTransaction() 265<br />
bigint data type 80<br />
bigserial data type 80<br />
binary import routine 333<br />
binary large object 450<br />
binary receive routine 333<br />
binary send routine 333<br />
bind parameter 308<br />
bladelet 359<br />
BLOB 80, 178, 180, 232, 276, 314, 450<br />
BOOLEAN 80<br />
Boolean UDR 333<br />
buffered logging 20<br />
built-in data type 330<br />
bulk load 9<br />
byte 80<br />
byte column 7<br />
byte-range lock 443<br />
byte-range locking 444<br />
C<br />
call level interface 15<br />
CallableStatement object 173<br />
© Copyright <strong>IBM</strong> Corp. 2010. All rights reserved. 481
cast function 332<br />
cast support function 348<br />
certified file 183<br />
character large object 350, 450<br />
chunk 450<br />
CLASSPATH environment variable 157<br />
classpath environment variable 52, 195<br />
CLI 15<br />
CLI parameters<br />
database 73<br />
hostname 73<br />
port 73<br />
protocol 73<br />
client connectivity 7<br />
client locale 62<br />
Client SDK 9, 11, 15, 21–22, 29, 34–38, 42, 58–59,<br />
65–66, 70, 75, 117, 125, 127, 151, 156, 216–217,<br />
222, 250, 254, 256, 293, 296, 328, 364–365, 435,<br />
470<br />
client_locale environment variable 150<br />
CLOB 80, 178–180, 233, 276, 278, 450<br />
collection 10<br />
collection data type 145, 348<br />
collection variable 145<br />
commands<br />
esql 14, 125–128, 130<br />
commit work 140<br />
committed read 448<br />
compiler 130<br />
complex data type 335<br />
component object model 216<br />
concentrator 5<br />
concurrency 438<br />
concurrent query 19<br />
concurrent session 5<br />
connection management 192<br />
connection object 171<br />
connection pooling 155<br />
connection string 257<br />
connection string attribute 221<br />
connection_adapters directory 370<br />
connectionstring property 264<br />
constraint 190<br />
controller 363<br />
convention over configuration 384<br />
cost factor 333<br />
cost function 332<br />
cursor behavior 62<br />
cursor stability 447–448<br />
482 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
customer object 190<br />
customized routine 330<br />
D<br />
data access layer 190<br />
Data accessibility 143<br />
data consistency 8<br />
data consumer 14<br />
Data Definition Language (DDL) 134, 168, 198,<br />
294, 310, 391<br />
data logging 143<br />
data management 7<br />
Data Manipulation Language (DML) 198<br />
data mart 7<br />
data provider 14, 254<br />
data query language 190<br />
data source 155<br />
data source name 61<br />
data type extension 330<br />
data warehouse 7<br />
database driver 44<br />
database extension 330<br />
database instance name 449<br />
database locale 62<br />
database lock 442<br />
database operation 76<br />
datetime 80<br />
db_locale 63<br />
db_locale environment variable 150<br />
db2cli.ini 73<br />
db2jcc.jar 52, 194<br />
db2jcc4.jar 52<br />
db2sdriver.cfg configuration file 261<br />
DbDataReader method 269<br />
dbschema utility 395<br />
dbspace 450<br />
dbspacetemp parameter 450<br />
deadlock 446<br />
deadlock_timeout parameter 455<br />
def_table_lockmode parameter 441, 454<br />
delete 76<br />
dependency list 337<br />
directives parameter 456<br />
dirty read isolation 445<br />
discarded-logging 194<br />
display environment variable 38<br />
distinct 10, 80<br />
distinct data type 330, 335
distributed transaction 20, 93, 447, 470<br />
distributed transactions 154<br />
distribution statistic 333<br />
dmg format 26<br />
dom4j-1.6.1.jar 194<br />
DRDA protocol 156<br />
DRDADEBUG trace 188<br />
driver manager 11<br />
driver package 44<br />
drsoctcp 31<br />
dyld_library_path environment variable 131<br />
dynamic link library 350<br />
dynamic sql 136<br />
dynamic_logs configuration parameter 460<br />
E<br />
editions 2<br />
element type 145<br />
encryption 182<br />
end-user routine 332<br />
environment variable<br />
ld_library_path 131–132, 353, 365<br />
lib 132<br />
libpath 131<br />
shlib_path 131, 365<br />
esql command 14, 125–128, 130<br />
exception 206<br />
exclusive lock 439<br />
exclusive mode 438–439<br />
execute into command 134<br />
executeQuery() 168<br />
executeUpdate() 168<br />
export routine 333<br />
F<br />
fast data loading 7<br />
fetch buffer size 62<br />
filetoblob 144<br />
finderr utility 14, 37, 126<br />
flat file 9<br />
flow-control extension 331<br />
functional test file 16<br />
G<br />
gem utility 370<br />
generator element 201<br />
get diagnostics statement 149<br />
get() method 206<br />
GetIfxMonthSpan 270<br />
global transaction 470<br />
global transactions 447<br />
H<br />
header file 16, 471<br />
Hibernate dialect 196<br />
Hibernate service 195<br />
Hibernate session instance 203<br />
hibernate.cfg.xml 195<br />
hibernate.property 195<br />
hibernate3.jar 193<br />
hibernate-jpa-2.0-api-1.0.0.Final.jar 193<br />
HibernateUtil class 202<br />
high availability clustering 3<br />
high availability replication 19<br />
host name 61<br />
host variable 146<br />
HQL criteria 209<br />
HQL restriction 209<br />
HTTP link 422<br />
I<br />
i4gl_func 408<br />
<strong>IBM</strong>.Data.DB2 255<br />
<strong>IBM</strong>.Data.<strong>Informix</strong> 255<br />
ICommand interface 225<br />
idssecuritylabel 80<br />
ifx_lo_lock() 444<br />
ifx_lo_open 82<br />
ifx_lo_t 142<br />
ifx_lo_write 82<br />
IfxBlob GetIfxBlob() 265, 269<br />
IfxClob GetIfxClob() 265, 269<br />
IfxCommand 269<br />
IfxCommand class 266<br />
IfxCommand CreateCommand() 265<br />
IfxCommand method 272<br />
IfxConnection class 265<br />
IfxConnection object 274<br />
IfxDataReader 269<br />
IfxDataReader ExecuteReader() 266<br />
IfxDateTime class 283<br />
IfxDateTime GetIfxDateTime() 269<br />
IfxDateTime public method 284<br />
IfxDecimal GetIfxDecimal 269<br />
IfxDecimal object 287<br />
Index 483
IfxDecimal public method 287<br />
ifxdotnettrace variable 293<br />
IfxError 271<br />
IfxException class 271<br />
ifxjdbc.jar 194<br />
IfxMonthSpan 270<br />
ifxoledbc COM class ID 216<br />
ifxoledbctrace environment variable 248<br />
IfxParameter 273<br />
IfxParameter CreateParameter() 266<br />
IfxParameterCollection object 272<br />
IfxTimeSpan GetIfxTimeSpan 270<br />
IfxTransaction 265<br />
IfxTransaction object 274<br />
iifxoledbc.dll 216<br />
iLogin utility 37, 117, 126, 217, 290<br />
Implict invocation 334<br />
incoming parameter 337<br />
<strong>Informix</strong> classes 12<br />
<strong>Informix</strong> Client Software Development Kit , see Client<br />
SDK<br />
<strong>Informix</strong> Connect 9, 34–35, 328<br />
<strong>Informix</strong> OLE DB Provider 37<br />
informixc.so 365<br />
informixdir environment variable 21, 59, 127<br />
informixserver 353<br />
informixserver environment variable 133, 365<br />
informix-sqli 164<br />
inheritance 190<br />
insert 76<br />
installation directory 21<br />
installation ini file 27<br />
installIDSDriver command 44<br />
int8 80<br />
intent locks 439<br />
intent-exclusive lock 439<br />
intent-shared 439<br />
interval day 80<br />
interval day to hour 81<br />
interval day to minute 81<br />
interval day to second 81<br />
interval hour 81<br />
interval hour to minute 81<br />
interval hour to second 81<br />
interval minute 81<br />
interval minute to second 81<br />
interval month 81<br />
interval second 81<br />
interval year 81<br />
484 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
interval year to month 81<br />
Isolation Level 62, 70<br />
isolation level 20, 438, 447, 463<br />
ISupportErrorInfo interface 234<br />
Iterator function 332<br />
J<br />
Java annotation 191<br />
Java archive file 341<br />
Java class file 195<br />
Java compiler 195<br />
Java interface 12<br />
Java library 193, 195<br />
Java Naming and Directory Interface 166<br />
java property 195<br />
Java runtime 166<br />
Java virtual machine (JVM) 37, 157, 195, 340, 457<br />
java_bindir environment variable 341<br />
java_home environment variable 341<br />
java_root environment variable 341<br />
java.io.Serializable 12<br />
java.lang.object 13<br />
java.sql.ParameterMetaData 12<br />
javassist-3.9.0.GA.jar 194<br />
javax.sql extensions 163<br />
javax.sql.ConnectionEventListener 12<br />
javax.sql.ConnectionPoolDataSource 12<br />
javax.sql.DataSource 12<br />
javax.sql.PooledConnection 12<br />
javax.sql.XADataSource 12<br />
JDBC API interfaces 155<br />
JDBC class 155<br />
JDBC method 155<br />
JDBC standard 8<br />
jjdk_home environment variable 341<br />
JNDI, see Java Naming and Directory Interface.<br />
jre_home environment variable 341<br />
jta-1.1.jar 194<br />
JVM 37, 157, 195, 340, 457<br />
jvp.log 347<br />
K<br />
key 441<br />
Key lock 442<br />
keystore file 183<br />
key-value locking 442
L<br />
library file 126<br />
licensing metric 5<br />
limited use socket 5<br />
list 145<br />
load() 206<br />
load() method 206<br />
loc_t 142<br />
lock 438<br />
lock duration 440<br />
lock list 446<br />
lock mode page 441<br />
locks parameter 453<br />
logbuff 458<br />
logfiles parameter 458<br />
logging mode 463<br />
logical device 8<br />
logical volume manager 8<br />
logical-log buffer 20, 458<br />
logical-log record 20<br />
long identifier 10<br />
loopback connection 8<br />
LPAR 5<br />
ltxehwm parameter 460<br />
ltxhwm parameter 460<br />
LU socket 6<br />
lvarchar 81<br />
M<br />
make file 16<br />
mapping 191<br />
max_pdqpriority parameter 456<br />
MaxBytes 277<br />
memory addressability 2<br />
messages files 16<br />
method 190<br />
method properties<br />
position 277<br />
ReferenceCount 277<br />
mi_lo_lock() 444<br />
migration 394<br />
model 363<br />
module 16<br />
money 81<br />
msgpath file 347<br />
multimedia capability 8<br />
multiplexed connection 18–19<br />
multiplexer 5<br />
multiprocessor parameter 452<br />
multiset 145<br />
mutexes 18<br />
mutually exclusive 18<br />
N<br />
native-protocol 53<br />
nchar 81<br />
negator function 332<br />
nettype parameter 453<br />
network connection 18<br />
nvarchar 81<br />
O<br />
obdcinst.ini file 66<br />
object ExecuteScalar() 266<br />
object file 126<br />
object processing 191<br />
object-orientated programming 190<br />
object-relational mapper 363<br />
ODBC data source 60<br />
ODBC driver 59<br />
ODBC driver configuration files<br />
odbc 66<br />
odbcinst.ini 66<br />
sqlhosts 66<br />
ODBC libraries<br />
libifcli.a or libcli.a 66<br />
libifcli.so or iclis09b.so 66<br />
libifdrm.so or idmrs09a.so 66<br />
libthcli.a 66<br />
libthcli.so or iclit09b.so 66<br />
odbc.ini file 66<br />
ODBC/CLI driver 45<br />
OLE DB consumer 216<br />
OLE DB provider 216<br />
OLE DB providers<br />
DBTYPE_DBDATE 223<br />
DBTYPE_DBTIME 223<br />
DBTYPE_DBTIMESTAMP 224<br />
olsoctcp 31<br />
onconfig file 341<br />
onconfig parameter<br />
dbservername 449<br />
jvpclasspath 341<br />
jvphome 341<br />
jvpjavahome 341<br />
jvpjavalib 341<br />
Index 485
jvpjavavm 341<br />
jvplog 347<br />
jvplogfile 341<br />
jvppropfile 341<br />
oninit 18<br />
opaque 10<br />
opaque data type 330, 332, 335<br />
OpenAdmin Tool (OAT) 9, 17, 295, 297–299<br />
operator 333<br />
operator function 332<br />
operator-class function 332<br />
optcompind parameter 19, 455<br />
OUT parameter 173<br />
outgoing parameter 337<br />
P<br />
packaging file 16<br />
page level lock 441<br />
page locking 442<br />
parallel subquery 19<br />
parallelism 19<br />
parallelizable UDR 332<br />
parsing 139<br />
partition 5<br />
path environment variable 127<br />
path variable 59<br />
persistence 191, 363<br />
persistence data 190<br />
persistence framework 190<br />
persistence object 199<br />
persistent connectivity layer 5<br />
phantom read 448<br />
physbuff configuration parameter 458<br />
physical log image 20<br />
physical-log buffer 458<br />
pipe connection 18<br />
placeholder 88, 308<br />
Plain Old Java Objects (POJO) 199<br />
POJO, see Plain Old Java Object (POJO).<br />
polymorphism 190<br />
precedence hierarchy 337<br />
prepare statement 20<br />
prepared statement 175<br />
PreparedStatement object 169<br />
preprocessor 10<br />
primary key 201<br />
priority aging 462<br />
process memory 347<br />
486 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
processor affinity 462<br />
processor value unit 5<br />
processor-based pricing 5<br />
program_design_dbs 404<br />
Project Object Model (POM) 194<br />
public method 265<br />
publish 402<br />
pure-Java driver 53<br />
pure-Java drivers 154<br />
Q<br />
query optimization 139<br />
query plan 334<br />
query-intensive analytical application 7<br />
R<br />
rails command 392<br />
recovery 330<br />
<strong>Redbooks</strong> Web site 480<br />
Contact us xiv<br />
regsvr32 tool 245<br />
repeatable read 438, 448<br />
resource manager 470<br />
result set 173<br />
rollback 21, 330, 447<br />
rollback method 274<br />
rollback work statement 140<br />
row and collection functions<br />
ifx_rc_count 106<br />
ifx_rc_create 106<br />
ifx_rc_delete 106<br />
ifx_rc_describe 106<br />
ifx_rc_fetch 106<br />
ifx_rc_free 106<br />
ifx_rc_insert 106<br />
ifx_rc_isnull 106<br />
ifx_rc_setnull 106<br />
ifx_rc_typespec 106<br />
ifx_rc_update 106<br />
rowset 229<br />
R-tree 358<br />
Ruby/<strong>Informix</strong> Adapter 384<br />
S<br />
sbspacename parameter 451<br />
sbspacetemp parameter 451<br />
scaffold command 393
scalability 6, 330<br />
scalable data warehousing 7<br />
sdk_home environment variable 341<br />
secure sockets layer 19<br />
secured connection method 19<br />
select 76<br />
selectivity function 332<br />
serial8 81<br />
server definition 31<br />
server instance 18<br />
server name 61<br />
server_locale environment variable 150<br />
service-oriented architecture 400<br />
Session.createCiteria() 206<br />
Session.createQuery() 206<br />
Session.delete() method 210<br />
Session.get() 205<br />
Session.load() 205<br />
Session.save() 205<br />
SessionFactory class 202<br />
set 145<br />
set isolation 438<br />
set lock mode 438<br />
set transaction 438<br />
setnet32.exe utility 21, 29–31, 42, 61, 66, 117, 126,<br />
222, 245, 247, 449<br />
shared library 67<br />
shared lock 439<br />
shared memory 18, 20<br />
simple large objects 314<br />
Simple Logging Facade for Java (SLF4J) 194–195<br />
simple-logging 194<br />
single install 5<br />
single password CSM 19<br />
single-user product version 8<br />
SLF4J see Simple Logging Facade for Java.<br />
slf4j-api-1.5.8.jar 194<br />
slob method 377<br />
smart blob space 95, 143, 178<br />
smart lager objects 179<br />
smart large object 10<br />
smart large object functions<br />
ifx_lo_alter 97<br />
ifx_lo_close 97<br />
ifx_lo_col_info 97<br />
ifx_lo_create 97<br />
ifx_lo_def_create_spec 97<br />
ifx_lo_open 97<br />
ifx_lo_read 97<br />
ifx_lo_readwithseek 97<br />
ifx_lo_seek 97<br />
ifx_lo_specget_estbytes 97<br />
ifx_lo_specget_extsz 97<br />
ifx_lo_specget_flags 97<br />
ifx_lo_specget_maxbytes 97<br />
ifx_lo_specget_sbspace 97<br />
ifx_lo_specset_estbytes 97<br />
ifx_lo_specset_extsz 97<br />
ifx_lo_specset_flags 97<br />
ifx_lo_specset_maxbytes 97<br />
ifx_lo_specset_sbspace 98<br />
ifx_lo_stat 98<br />
ifx_lo_stat_atime 98<br />
ifx_lo_stat_cspec 98<br />
ifx_lo_stat_ctime 98<br />
ifx_lo_stat_refcnt 98<br />
ifx_lo_stat_size 98<br />
ifx_lo_tell 98<br />
ifx_lo_truncate 98<br />
ifx_lo_write 98<br />
ifx_lo_writewithseek 98<br />
smart large objects 178–180, 182, 232, 276, 278,<br />
280–281, 315, 377, 383<br />
soa_err_log log file 404<br />
SOAP 422<br />
socket connection 18<br />
source file 126<br />
source files 16<br />
SQL communications area 148<br />
SQL dialect 196<br />
SQL optimization 197<br />
sql_autocommit_off 91<br />
sql_bigint 80–81<br />
sql_bit 80<br />
sql_c_binary 80<br />
sql_ifmx_udt_blob 80<br />
sql_ifmx_udt_clob 80<br />
sql_infx_attr_lo_automatic 104<br />
sql_infx_bigint 80<br />
sql_infx_udt_fixed 80–81<br />
sql_infx_udt_varying 81<br />
sql_integer 81<br />
sql_interval_day 80<br />
sql_interval_hour 81<br />
sql_interval_month 81<br />
sql_interval_second 81<br />
sql_interval_year 81<br />
sql_longbinary 104<br />
Index 487
sql_longvarbinary 80, 96<br />
sql_longvarchar 81, 96, 104<br />
sql_timestamp 80<br />
sqlca 148<br />
sqlexec 30<br />
SQLExecDirect() 82<br />
SQLExecDirect() function 82<br />
SQLGetDiagRec() 84<br />
sqlhosts file 21, 27–29, 33–34, 42, 66, 127, 157,<br />
365, 449, 453<br />
sqlidebug trace 119, 151<br />
sqliprt tool 120<br />
sqlj.zip 52<br />
sqlj4.zip 52<br />
SQLSetConnectAttr() 91<br />
SQLSTATE 149<br />
stacksize parameter 454<br />
standard data type 330<br />
startup cost 337<br />
Statement object 168<br />
static SQL statement 136<br />
statistics function 332<br />
stored procedure 133<br />
structured query language 154<br />
subscribe 402<br />
sysdistrib system catalog table 451<br />
syslangauth table 331<br />
syspgm4gl 406<br />
sysprocauth table 331<br />
sysprocbody table 331<br />
sysprocedures table 331<br />
sysroutinelangs table 331<br />
syssbspacename parameter 451<br />
system catalog table 331<br />
T<br />
table space 450<br />
tar file 26<br />
target_dir 408<br />
target_file 408<br />
temporary space 450<br />
temporary table 450<br />
text export routine 333<br />
text import routine 333<br />
Text input routine 333<br />
text output routine 333<br />
third-party vendor 11<br />
thread 439<br />
488 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong><br />
TimeSpan GetTimeSpan() 269<br />
transaction 470<br />
transaction control 330<br />
transaction management 192<br />
transient 210<br />
transient object 210<br />
translation library 62<br />
translation option 62<br />
Type 4 154<br />
type constructor 145<br />
U<br />
UDR 173, 330–332, 334, 336–337, 340, 343, 345,<br />
347–348, 352, 451<br />
UDRManager 13<br />
UDRMetaData 13<br />
UDTManager 13<br />
UDTMetaData 13<br />
uncommitted data 448<br />
unit of work 470<br />
unresolved transaction 475<br />
updatable lock 439<br />
update 76<br />
uselastcommitted 441, 454<br />
user thread 464<br />
user-defined aggregate 332<br />
user-defined data type 335<br />
user-defined routine , see UDR.<br />
utilities<br />
finderr 14, 37, 126<br />
setnet32.exe 21, 29–31, 42, 61, 66, 117, 126,<br />
222, 245, 247, 449<br />
V<br />
validity checking 139<br />
version numbering convention 2<br />
virtual processor 18<br />
virtual server 5<br />
virtual-processor process 352<br />
VMB character 62<br />
void Prepare() 266<br />
vpclass parameter 452, 461<br />
W<br />
w4glc parameters<br />
check 405<br />
compile 405
deploy 405<br />
force 405<br />
generate 405<br />
help 405<br />
keep 405<br />
package 405<br />
silent 405<br />
version 405<br />
web server 363<br />
web service description language 407<br />
web service function 426<br />
workload 3<br />
ws_func 408<br />
WSDL Path 426<br />
wsdl_path 407<br />
X<br />
xa.h 471<br />
XML configuration file 190<br />
XML formatted file 195<br />
XML mapping file 197<br />
XML-based protocol 422<br />
Index 489
490 <strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
(1.0” spine)<br />
0.875”1.498”<br />
460 788 pages<br />
<strong>IBM</strong> <strong>Informix</strong> Developer’s <strong>Handbook</strong>
<strong>IBM</strong> <strong>Informix</strong><br />
Developer’s <strong>Handbook</strong><br />
Learn application<br />
development with<br />
supported APIs,<br />
drivers, and interfaces<br />
Understand <strong>Informix</strong><br />
supported<br />
programming<br />
environments<br />
Follow practical<br />
examples to develop<br />
an <strong>Informix</strong><br />
application<br />
SG24-7884-00 ISBN 0738434701<br />
Back cover<br />
<strong>IBM</strong> <strong>Informix</strong> is a low-administration, easy-to-use, and embeddable<br />
database that is ideal for application development. It supports a<br />
wide range of development platforms, such as Java, .NET, PHP, and<br />
web services, enabling developers to build database applications in<br />
the language of their choice. <strong>Informix</strong> is designed to handle RDBMS<br />
data and XML without modification and can be extended easily to<br />
handle new data sets.<br />
This <strong>IBM</strong> <strong>Redbooks</strong> publication provides fundamentals of <strong>Informix</strong><br />
application development. It covers the <strong>Informix</strong> Client installation<br />
and configuration for application development environments. It<br />
discusses the skills and techniques for building <strong>Informix</strong><br />
applications with Java, ESQL/C, OLE DB, .NET, PHP, Ruby on Rails,<br />
DataBlade, and Hibernate.<br />
The book uses code examples to demonstrate how to develop an<br />
<strong>Informix</strong> application with various drivers, APIs, and interfaces. It<br />
also provides application development troubleshooting and<br />
considerations for performance.<br />
This book is intended for developers who use <strong>IBM</strong> <strong>Informix</strong> for<br />
application development. Although some of the topics that we<br />
discuss are highly technical, the information in the book might also<br />
be helpful for managers or database administrators who are<br />
looking to better understand their <strong>Informix</strong> development<br />
environment.<br />
INTERNATIONAL<br />
TECHNICAL<br />
SUPPORT<br />
ORGANIZATION<br />
®<br />
BUILDING TECHNICAL<br />
INFORMATION BASED ON<br />
PRACTICAL EXPERIENCE<br />
<strong>IBM</strong> <strong>Redbooks</strong> are developed by<br />
the <strong>IBM</strong> International Technical<br />
Support Organization. Experts<br />
from <strong>IBM</strong>, Customers and<br />
Partners from around the world<br />
create timely technical<br />
information based on realistic<br />
scenarios. Specific<br />
recommendations are provided<br />
to help you implement IT<br />
solutions more effectively in<br />
your environment.<br />
For more information:<br />
ibm.com/redbooks<br />
®