IBM Software GroupIBM Information Management Oracle to IBM Informix Server Porting Guide A discussion of porting issues and Informix Server 11 Prepared By: Business Partner Technical Enablement Competitive Technologies and Enablement IBM Data Servers, IBM Software Group June 2012 Version 1.6 1 IBM Software Group IBM Information Management © Copyright IBM Corporation 2012. All Rights Reserved. Trademarks IBM, AIX, DB2, DB2 Universal Database, Informix, and iSeries are trademarks of the International Business Machines Corporation in the United States, other countries or both. UNIX and Unixbased trademarks and logos are trademarks or registered trademarks of The Open Group. Intel and Intel-based trademarks and logos are trademarks or registered trademarks of Intel Corporation. Windows is a trademark of Microsoft Corporation in the United States, and other countries, or both. Linux is a registered trademark of Linus Torvalds in the United States, other countries, or both. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other company, product or service names may be the trademarks or service marks of others. Disclaimer References in this publication to IBM products or services do not imply that IBM intends to make them available in all countries in which IBM operates. The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. The use of this information or the implementation of any of these techniques is your responsibility and depends on your ability to evaluate and integrate them into your operational environment. While each item may have been reviewed by IBM for accuracy in a specific situation, there is no guarantee that the same or similar results will be obtained elsewhere. Customers attempting to adapt these techniques to their own environments do so at their own risk. This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time without notice. Information concerning non-IBM products was obtained from the suppliers of those products, their published announcements or other publicly available sources. IBM has not tested those products and cannot confirm the accuracy of performance, compatibility or any other claims related to nonIBM products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of those products. The information in this white paper is provided AS IS without warranty. Such information was obtained from publicly available sources, is current as of December 2011, and is subject to change. Any performance data included in the paper was obtained in the specific operating environment and is provided as an illustration. Performance in other operating environments may vary. More specific information about the capabilities of products described should be obtained from the suppliers of those products. Version 1.6 2 IBM Software Group IBM Information Management Document History Revision History Authoring Original Document (May 2000) Glen Black, Frank Martino & Tim Tobey Informix Software Incorporated, Partner Engineering Organization Revisions for Informix Dynamic Server.2000, Version 6 (July 2000) Tim Tobey Informix Software Incorporated, Partner Engineering Organization Updated to IBM Informix Dynamic Server Database Version 9, Release 4. (February 2004) Christine Normile Informix Dynamic Server Product Manager IBM Software Group, Information Management Updated material for Sequence Objects (December 2004) Sam Marino DB2 Business Partner Technical Enablement IBM Information Management Solutions, IBM Software Group Updated to IBM Informix Dynamic Server Database Version 10.00 (January 2006) Sam Marino Business Partner Technical Enablement – Data Services IBM Information Management, IBM Software Group Updated to IBM Informix Dynamic Server Database Version 11.10 (December 2007) Sam Marino IBM Business Partner Technical Enablement – Data Services IBM Information Management, IBM Software Group Updated material for delimited identifiers, metadata extraction, type conversion, and miscellaneous (June 2012) Chris Golledge IBM Information Management, IBM Software Group Contributions Technical Reviewers Keshava Murthy IBM Informix Database Server Development IBM Software Group, Information Management Lakshman Sakaray IBM Database Migration Toolkit IBM Software Group, Information Management Version 1.6 3 IBM Software Group IBM Information Management Table of Contents CHAPTER 1: INTRODUCTION ............................................................................................ 8 PURPOSE ........................................................................................................................................ 8 PRODUCT OVERVIEW ....................................................................................................................... 8 ORGANIZATION................................................................................................................................ 8 CONVENTIONS ................................................................................................................................ 9 CHAPTER 2: DEFINING THE PROBLEM SPACE ............................................................ 11 EXTRACTING THE OBJECTS TO BE MIGRATED .................................................................................. 11 Working With Oracle Dump Files ............................................................................................ 11 CHAPTER 3: DATA DEFINITION LANGUAGE ................................................................. 15 IDENTIFIERS .................................................................................................................................. 15 Delimited identifiers ................................................................................................................. 15 Explicit versus implicit schema/owner names ......................................................................... 17 Reserved words or keywords .................................................................................................. 18 Summary ................................................................................................................................. 19 DATABASES .................................................................................................................................. 19 Databases, database names and instances ........................................................................... 19 ANSI vs. non-ANSI .................................................................................................................. 20 TABLESPACES AND DBSPACES ....................................................................................................... 20 TABLES......................................................................................................................................... 21 Attributes and general syntax .................................................................................................. 21 Lock mode ............................................................................................................................... 21 Storage .................................................................................................................................... 21 Constraints .............................................................................................................................. 22 ALTER TABLE......................................................................................................................... 22 Data Partitioning ...................................................................................................................... 23 DATA TYPES.................................................................................................................................. 25 Numeric ................................................................................................................................... 25 CHAR, VARCHAR and LVARCHAR ....................................................................................... 26 Date and Time ......................................................................................................................... 27 Interval ..................................................................................................................................... 27 Raw data and Large Objects ................................................................................................... 28 ROWID .................................................................................................................................... 28 User-defined types .................................................................................................................. 29 Complex data types ................................................................................................................. 30 SEQUENCE OBJECTS ..................................................................................................................... 30 Oracle sequences.................................................................................................................... 30 Informix sequences ................................................................................................................. 30 INDEXES ....................................................................................................................................... 30 Composite indexes .................................................................................................................. 30 Maximum key size ................................................................................................................... 31 Index fragmentation ................................................................................................................. 31 General index information ....................................................................................................... 31 VIEWS .......................................................................................................................................... 32 STORED PROCEDURES AND TRIGGERS ........................................................................................... 32 ORACLE EXTENSIONS .................................................................................................................... 33 CHAPTER 4: DATA MANIPULATION LANGUAGE .......................................................... 34 SQL ............................................................................................................................................. 34 Labels using keywords ............................................................................................................ 34 Inequality operations ............................................................................................................... 34 Selects ..................................................................................................................................... 34 Optimizer directives ................................................................................................................. 34 Version 1.6 4 IBM Software Group IBM Information Management External optimizer directives .................................................................................................... 35 Inserts ...................................................................................................................................... 36 Temporary tables..................................................................................................................... 36 Outer joins ............................................................................................................................... 36 Sorts ........................................................................................................................................ 36 Exceptions ............................................................................................................................... 37 Correlation names ................................................................................................................... 37 Aliases ..................................................................................................................................... 37 Hierarchical queries ................................................................................................................. 37 Truncate .................................................................................................................................. 39 HOST VARIABLES........................................................................................................................... 40 DATE AND TIME FUNCTIONS ........................................................................................................... 41 SYSDATE ................................................................................................................................ 41 Number formats ....................................................................................................................... 41 Date formats ............................................................................................................................ 42 The RR date format element ................................................................................................... 43 USER-DEFINED ROUTINES .............................................................................................................. 43 COMPATIBLE SQL FUNCTIONS ....................................................................................................... 43 AGGREGATE FUNCTIONS ................................................................................................................ 43 MATHEMATICAL FUNCTIONS ........................................................................................................... 44 STRING FUNCTIONS ....................................................................................................................... 44 LENGTH .................................................................................................................................. 44 LTRIM and RTRIM .................................................................................................................. 44 Oracle Extensions ................................................................................................................... 44 OTHER ORACLE FUNCTIONS .......................................................................................................... 44 MACROS ....................................................................................................................................... 45 %ISOPEN and %NOT FOUND ............................................................................................... 45 %ROWTYPE ........................................................................................................................... 45 %TYPE .................................................................................................................................... 45 PSEUDO-COLUMNS ........................................................................................................................ 45 LEVEL ..................................................................................................................................... 46 ROWID .................................................................................................................................... 46 ROWNUM................................................................................................................................ 46 UPDATE USING CURSORS............................................................................................................... 49 SYSTEM TABLES ............................................................................................................................ 50 STORED PROCEDURES .................................................................................................................. 50 Size limit .................................................................................................................................. 50 Parameter limit ........................................................................................................................ 50 Packages ................................................................................................................................. 51 Routines .................................................................................................................................. 51 Exceptions ............................................................................................................................... 51 Error handling .......................................................................................................................... 52 Cursors .................................................................................................................................... 52 Variable declaration and assignment ...................................................................................... 53 Boolean ................................................................................................................................... 53 Binary data types ..................................................................................................................... 53 Dynamic SQL .......................................................................................................................... 53 Compiler .................................................................................................................................. 53 TRIGGERS..................................................................................................................................... 54 CONSTRAINTS ............................................................................................................................... 55 DUAL TABLE................................................................................................................................. 55 CHAPTER 5: EMBEDDED SQL......................................................................................... 57 HOST VARIABLES ........................................................................................................................... 57 Declaration override ................................................................................................................ 57 Arrays ...................................................................................................................................... 57 Formatting ............................................................................................................................... 58 SQL STRUCTURES ........................................................................................................................ 59 Version 1.6 5 IBM Software Group IBM Information Management SQLCA .................................................................................................................................... 59 SQLDA .................................................................................................................................... 60 ORACA .................................................................................................................................... 61 PRE-COMPILER OPTIONS................................................................................................................ 61 EMBEDDED PL/SQL ...................................................................................................................... 61 ORACLE CALL INTERFACE .............................................................................................................. 61 Database library calls .............................................................................................................. 62 Global area processing............................................................................................................ 62 Fetch cycle .............................................................................................................................. 62 RAW binary data type processing ........................................................................................... 62 Parameter bindings ................................................................................................................. 62 EMBEDDED SQL FOR C ................................................................................................................. 63 VARCHAR ............................................................................................................................... 63 Host variables .......................................................................................................................... 63 EMBEDDED SQL FOR COBOL ....................................................................................................... 63 VARCHAR ............................................................................................................................... 63 Level 88 ................................................................................................................................... 64 SQLDA .................................................................................................................................... 64 REDEFINES ............................................................................................................................ 64 Tips .......................................................................................................................................... 64 ODBC .......................................................................................................................................... 64 CHAPTER 6: APPLICATION ARCHITECTURE ................................................................ 65 TRANSACTION PROCESSING ........................................................................................................... 65 Autonomous Transaction......................................................................................................... 66 Savepoints ............................................................................................................................... 67 CONCURRENCY ............................................................................................................................. 67 Row Level Locking .................................................................................................................. 67 Transaction Isolation Level ...................................................................................................... 68 CHAPTER 7: SECURITY ................................................................................................... 70 USER AUTHENTICATION ................................................................................................................. 70 Operating system security ....................................................................................................... 70 Non-operating system security ................................................................................................ 70 ROLE BASED AUTHORITY ............................................................................................................... 71 COLUMN-LEVEL ENCRYPTION ......................................................................................................... 71 Passwords and hints ............................................................................................................... 71 Encrypting a column ................................................................................................................ 72 Querying an encrypted column................................................................................................ 72 Performance impact of encryption ........................................................................................... 73 Storage considerations ............................................................................................................ 73 Compliance regulations ........................................................................................................... 73 CHAPTER 8: ENVIRONMENT ........................................................................................... 74 SCRIPTS ....................................................................................................................................... 74 SQL script substitution variables ............................................................................................. 74 UTILITIES ...................................................................................................................................... 75 SYSTEM CATALOG ......................................................................................................................... 75 CHAPTER 9: USING BLADELETS .................................................................................... 76 W HAT IS A BLADELET? .................................................................................................................. 76 INFORMIX EXEC BLADELET FOR DYNAMIC SQL ............................................................................... 77 SQLLIB DATABLADE MODULE (SQLLIBC.1.1 / SQLLIBJ.1.1) ............................................................ 78 A P P E N D I X A: A P P E N D I X B: PLANNING GUIDE ..................................................................................... 79 SYNTAX COMPARISON ............................................................................ 82 Version 1.6 6 IBM Software Group IBM Information Management A P P E N D I X C: A P P E N D I X D: DATABASE CONCEPTS ........................................................................... 84 SAMPLE CODE .......................................................................................... 85 DATE ARITHMETIC ......................................................................................................................... 88 UNITS Keyword ....................................................................................................................... 88 Using EXTEND function .......................................................................................................... 88 STRING REPRESENTATION OF DATE AND TIME ................................................................................. 88 Day and date ........................................................................................................................... 88 Abbreviation of day of week .................................................................................................... 88 USING AN INFORMIX COLLECTION DATA TYPE .................................................................................. 89 A collection data type as a VARRAY ....................................................................................... 89 Using MULTISET and ROW to replace PL/SQL TABLE and RECORD types ........................ 91 A P P E N D I X E: COUNTING SYSTEM OBJECTS ............................................................... 93 USING THE SYSTEM CATALOG ........................................................................................................ 93 System catalog ........................................................................................................................ 93 USING SQL CODE ......................................................................................................................... 94 Built-in function references ...................................................................................................... 94 Object types and counts .......................................................................................................... 95 A P P E N D I X F: DATA TYPE MAPPING .............................................................................. 97 DOUBLE ................................................................................................................................ 101 VARCHAR ............................................................................................................................. 101 NUMBER ............................................................................................................................... 101 National language types ........................................................................................................ 102 ROWID .................................................................................................................................. 102 %TYPE .................................................................................................................................. 102 %ROWTYPE ......................................................................................................................... 102 FLOAT ................................................................................................................................... 102 XMLType ............................................................................................................................... 103 Version 1.6 7 IBM Software Group IBM Information Management CHAPTER 1: Introduction This document is a technical document describing the differences between Oracle and Informix® Dynamic Server (IDS) functionality and syntax. . This porting guide details differences between Oracle’s 10g release 1 (10.1) and 10g release 2 (10.2) and Informix Dynamic Server (version 11.10). In addition, it can be a useful reference for all migrations between the two database servers regardless of version. In addition to DDL, DML and overall SQL syntax, this document explores the differences between Oracle and Informix with regards to object technology through the use of large objects, user defined types, and user defined routines. It also looks at the differences between Oracle’s table partitioning and Informix’s table fragmentation. Part of the explanation is a discussion on what needs to be changed to make an application running on an Oracle database run on an Informix database. If there is more than one way to port some piece of functionality, alternatives and recommendations are also discussed. This document is intended to be a living document. Missing items, better ways of implementing Oracle functionality in Informix other than what is documented here, examples to better illustrate a point, and other suggestions should be sent to Chris Golledge (
[email protected]) so they may be included with future revisions. Purpose This document should be used as a guide to assist in porting applications that run on an Oracle database server to run on an Informix database server. Regardless of whether a porting effort will be completely manual or if tools will be utilized, this document can provide value. Obviously, if the port is performed manually, this guide will provide the most value. If the port will utilize tools, then analysis can be performed using this guide to determine how much of the application the tools will convert, and how much must be converted manually. In any case, this guide is intended to be used to both visualize the scope of a porting effort and to aid in the porting effort by addressing the technical issues. This document will be refined based on users’ experiences to identify as many of the porting issues as possible at the beginning of a project. Product overview IBM Informix Server provides high performance and scalability with legendary reliability and nearly hands-free administration of databases of all size. IDS is industry proven on UNIX, Linux, or Windows platforms with online transaction processing (OLTP) systems, data marts, data warehouses, and e-business applications. The IDS 11 release features significant additions over prior versions. It supports the servicesoriented architecture (SOA) model for application integration and offers many new features for data replication, continuous availability and “hands free” database administration. Organization This document is broken into sections to logically discuss the different database concepts. Some of the sections may contain the same sub-sections; for example, stored procedures and triggers Version 1.6 8 IBM Software Group IBM Information Management are discussed in the DDL and DML sections. This allows separate discussions on the issues facing the same item in each area. For example, the issues surrounding creating a stored procedure are discussed in the DDL section, and the issues surrounding the stored procedure language are discussed in the DML section. Among the other references, the “Planning Guide” appendix contains a quick reference of most of the differences between Oracle and Informix along with the degree of difficulty to port each difference. This reference is intended for use in the planning process to illustrate the scope and the effort required to perform the porting project. Some of the stored procedure logic used to convert Oracle functions is included in the appendix ”Sample code”. Conventions The conventions used in this document include: Courier Text Distinguishes a logic example from regular text. Example Illustrates a point. In most cases an Oracle example will be followed by an Informix example. Italicized Text Signifies a substitution; in other words, substitutes for the italicized word, the actual object the word is describing, for example: table_name, column_name, and so on. Signifies database vendor reserved words or statements such as INTEGER, CREATE TABLE, and so on highlights the item that is being explained. UPPER CASE TEXT Bold example text Version 1.6 9 IBM Software Group IBM Information Management Version 1.6 10 IBM Software Group IBM Information Management CHAPTER 2: Defining the Problem Space It is important to define the set of objects to be migrated, and it is better if effort is put into this prior to beginning the actual migration. This is true if you are the responsible for both the database in general and its migration from one supporting DBMS to another, but it is particularly true that there be a clear agreement on the scope of what is to be migrated if the migration is to be performed by a party other than the one with long term responsibility for the database. Defining the scope will involve extracting a representation of the objects to be migrated out of the Oracle system catalogs and representing them in some format that can be manipulated into constructs that can be used in Informix. Extracting the objects to be migrated It is possible to represent the objects in more than one language. Using the SQL language is the most common method, and it will be the only one discussed at length in this document (in its current revision). However, it is also possible to express database objects in XML, SQL has the advantage that the standard, common grammars are widely understood in the context of defining tables, indexes, etc. XML would have an advantage of not requiring parsing that is sensitive to the minor differences in ways of expressing essentially the same object. Working With Oracle Dump Files There exist 3 -party tools for reading system catalogs, but it is doubtful that any can read the Oracle catalogs more reliably than Oracle’s own tools. There are two, incompatible, export and import tool sets used by Oracle. The older tools are imp and exp and the newer ones are impdp and expdp, where '..dp' stands for Data Pump. Oracle has encouraged the use of the newer tools instead of the older ones since the newer ones were introduced at version 10. Nonetheless, there are many Oracle users still using imp and exp. In the discussions below, only the metadata will be extracted into a dump file. That is because any data in the dump file will be in a proprietary format, and very likely some of the data will have to go through a type conversion process before being loaded into an Informix system. Oracle exported data generally cannot be used to load a database in Informix. Export and Import If someone has given you a dump file created with the older export tool, there exist tools such as The DDL Wizard, by Net 2000 Limited, which can be used to extract viable DDL. The contents of the dump file contain the SQL for re-creating the database, but they are not in a readily usable format. The older export and import tools are also more sensitive to the layout of the tablespaces than impdb. The ability to extract DDL from the dump file in a usable form, segmented by whatever grouping you choose, is limited compared to what can be achieved through the Data Pump versions.. rd Version 1.6 11 IBM Software Group IBM Information Management It may be tempting to feed the dump file into the IBM Migration Toolkit (MTK) or other tool that works on SQL flat files. This will not work. MTK works on flat text SQL files, and the dump file is binary. If you open it up, you can see what may be a large number of SQL statements, but there are also binary codes within the same file. That binary data will prevent MTK from being able to process it. Getting SQL statements If you want to get the SQL statements out of the dump file, as they would be executed on the server, there is an option available from the import tool itself, 'show=y'. imp 'myID/myPWD as sysdba' file=myFile.dmp full=yes show=y > dmp_show.txt This will produce a file which contains the statements, but they are wrapped in quotes. The contents will look something like: "ALTER SESSION SET CURRENT_SCHEMA= "SYSTEM"" "CREATE UNDO TABLESPACE "TBSPC1" BLOCKSIZE 8192 DATAFILE 'D:\ORACLE\ORADA" "TA\SCHEMA1\CHUNK.DBF' SIZE 387973120 AUTOEXTEND ON NEXT 5242880 MAXS" "IZE 32767M EXTENT MANAGEMENT LOCAL " which makes them strings instead of SQL statements. Fixing this is a two-step process -- removes quotes from the start of lines sed 's/^ "//g' dmp_show.txt > step1.txt -- removes quotes from the end of lines sed 's/"$//g' step1.txt > dmp_step2.sql The statements are still not correctly formatted because they have been truncated to a fixed length and line-endings inserted. However, at this point it is possible to get a count of how many of each object type there is using the script in the appendix “Getting an initial count of database objects”. Data Pump Export and Import These newer tools provide better performance and more capabilities than their predecessors. Please refer to documentation on Oracle for a complete description; searches for “Data Pump Import” and “Data Pump Export” should return relevant results quickly. Below is enough information to get started on defining and extracting the objects to be migrated. Metadata can be filtered through the use of the export mode clause, and INCLUDE and EXCLUDE parameters. However, any dependent object of an included or excluded object is likewise included or excluded. For example, if you include an index, then the table associated with the index will also be included, and if you exclude a table, then any associated indexes will also be excluded. The SQL that will generate all the objects in the dump file can be created by the impdp command with the SQLFILE parameter. If the output file is very large, you might want to break it into smaller pieces. There are a couple of ways to do this, and the one that is best for you somewhat depends Version 1.6 12 IBM Software Group IBM Information Management on how you intend to proceed with the conversion. Data definition language (DDL) SQL for all the objects can be put into one file, or the INCLUDE or EXCLUDE parameters can be added to direct Data Pump to filter on the types of objects. If you put all the objects into one file, then you can break that into smaller files by object type fairly easily because the objects are sorted by type in the SQL file. So, it is just a matter of cutting the file into smaller pieces. This method may be preferred because it allows for you to do things like, create all tables, load the data, and then create the indexes and triggers. If the data set is large, creating the indexes after loading the data can yield substantial performance improvements. However, this is less useful if you want to convert sets of objects according to dependencies; in which case the filtering option is likely to be more useful. Just to illustrate a difference between these two methods, in one customer case, the metadata dump file was about 18MB, and putting all objects in one SQL file resulted in a file that was about 15MB. The table definitions occupied a little under 300KB of the 15MB. The output file generated by filtering on objects of type TABLE was just over 1MB. The addition content was mostly GRANT, INDEX, and TRIGGER creation statements. There were no differences in the CREATE TABLE statements themselves. Examples Prerequisite The Data Pump tools require the use of an Oracle DIRECTORY object. This is basically a mapping between a name that exists within the DBMS and a system path. It can be a tripping point, but it provides an abstraction layer that, for instance, allows the same commands to work across systems with different disk layouts or different operating systems. Creating a DIRECTORY on a Windows system from SQLPlus: SQL>connect / as sysdba; SQL>CREATE OR REPLACE DIRECTORY dumpDir AS 'c:\temp'; Note: Make sure this directory is not read-only. There also could be problems if the directory is a network drive on Windows; these can be related to there being different users between the Oracle executable and the current user, and those users having different permissions on the network drive. SQL>GRANT read, write ON DIRECTORY dumpDir TO userID; Creating a dump file of the entire database: expdp 'userName/userPwd as sysdba' FULL=Y dumpfile=DB_full.dmp LOGFILE=LG_expdp.log CONTENT=METADATA_ONLY DIRECTORY=dumpDir Creating a dump file of just one schema: expdp 'userName/userPwd as sysdba' SCHEMAS=hr DIRECTORY=dumpDir DUMPFILE=hr .dmp LOGFILE=hr .log CONTENT=METADATA_ONLY Creating a dump of just the tables and their dependents: expdp 'userName/userPwd as sysdba' SCHEMAS=hr INCLUDE=TABLE DIRECTORY=dumpDir DUMPFILE=hr.dmp LOGFILE=hr.log CONTENT=METADATA_ONLY Creating a dump of just one table: expdp 'userName/userPwd as sysdba' SCHEMAS=hr INCLUDE=TABLE:\"LIKE \'COUNTRIES\'\" DIRECTORY=dumpDir DUMPFILE=hr.dmp LOGFILE=hr.log CONTENT=METADATA_ONLY Version 1.6 13 IBM Software Group IBM Information Management Creatinge an SQL file from a dump file: impdp 'userName/userPwd as sysdba' DIRECTORY=dumpDir DUMPFILE=hr.dmp SQLFILE=dumpDir:hr.sql The INCLUDE and EXCLUDE options are also valid when used with impdb; so, it may be easier to get a complete dump of everything to be migrated, then use these options to create subsets of objects it smaller SQL files, than it would be to create separate dump files. It is easier to divide objects than to merge them together if they are not divided initially as wanted. There is a caveat when using this impdp to generate an SQL file; the object names will all be delimited identifiers. Example: CREATE TABLE "HR"."COUNTRIES"... Because of differences between how Informix handles these and how Oracle handles these, case-sensitivity issues could occur. See the chapter on “Delimited Identifiers”, and, in particular, the description of default shift problems. If it can be determined that there are no name collisions if regular identifiers are used, removing the quotation marks as part of the conversion might avoid many problems. In other words, if there are no objects whose names only differ in case, like foo and FOO, then changing every occurrence of “FOO” to FOO in the DDL could eliminate case sensitivity issues in the application DML. It depends on if the application code uses the delimited form of the name predominantly or not. Version 1.6 14 IBM Software Group IBM Information Management Data Definition Language CHAPTER 3: Identifiers An identifier specifies the simple name of a database object such as a table name, column name, index name, view name, stored procedure name, and so on. The maximum length of an Oracle identifier is 30 characters and can contain letters, numbers, “_”, “$”, and “#”, although Oracle highly recommends that “$” and “#” should not be used. In contrast, Informix identifiers have a maximum length of 128 characters and may contain letters, numbers, “_” and “$” (as long as “$” is not the first character). See the Delimited Identifiers section below for information. This means that all Oracle identifiers containing a “#” anywhere in the identifier or a “$” as the first character must be replaced or modified to remove those characters from the identifier. Delimited identifiers Delimited identifiers are also known as quoted identifiers and sometimes as escaped identifiers. They have been part of the SQL standard since SQL/92, but existed in various database management systems (DBMS) prior to that time. In a nutshell, they are a way to preserve case sensitivity in object names as well as to allow objects to have names outside of the conventions of SQL regular identifiers. Prior to the behavior of identifiers in general being standardized, different DBMS vendors implemented different means of achieving case-insensitivity for object names; some converted all regular identifiers to uppercase, and some converted regular identifiers to lowercase. By SQL/99 the concept of case normal form was standardized to mean, simply stated, that if there exists an uppercase equivalent of any character in a name, then the uppercase version is stored in the system catalogs and used for comparison purposes. See the SQL/99 standard, chapter 5, section 2, and in particular, items 21 and 22, for a formal description of case normal form and how it applies to identifier comparisons. Delimited identifiers can be used to specify names for database objects that are otherwise identical to SQL reserved keywords, such as TABLE, WHERE, DECLARE, and so on. The only database object for which delimited identifiers cannot be used is a database name. Database administrators and application programmers do not always communicate to each other with 100% efficiency, and there is no guarantee that within either of these pools, every member codes by the standard practice of the other members. After describing how delimited identifiers work in Informix, there will be a description of how any inconsistency of usage and the differences between Oracle and Informix can introduce some migration hurdles. DELIMIDENT Environment Variable To use delimited identifiers with Informix, you must set the DELIMIDENT environment variable. While DELIMIDENT is set , strings enclosed in quotation marks ( “ ) are treated as identifiers of database objects, and strings enclosed in apostrophes ( ’ ) are treated as literal strings. If the Version 1.6 15 IBM Software Group IBM Information Management DELIMIDENT environment variable is not set, strings enclosed in quotation marks are also treated as literal strings. When you set the DELIMIDENT environment variable, you cannot use quotation marks ( ” ) to delimit literal strings. If DELIMIDENT is set, the database server interprets all strings enclosed in quotation marks as SQL identifiers, not string literals. Commonly within Informix systems, DELIMIDENT is set or not on the client side, according to the design of the application. However, if you are converting a database and all applications accessing it from a system where every occurrence of quotation marks (“objectName”) indicates an identifier and not a string literal, you may want to consider setting it in the server environment in order to avoid any inconsistency between clients where it is set and where it might not be. Using Quotes in Strings The apostrophe ( ’ ) has no special significance in string literals delimited by quotation marks. Conversely, double quote ( ″ ) has no special significance in strings delimited by apostrophes. For example, these strings are valid: "Nancy’s puppy jumped the fence" ’Billy told his kitten, "No!" ’ A string delimited by quotation marks can include a double quote character by preceding it with another double quote, as the following string shows: "Enter ""y"" to select this row." When the DELIMIDENT environment variable is set, quotation marks can only delimit identifiers, not strings. Use of Uppercase Characters You can specify the name of a database object with uppercase characters, but the Informix server shifts these to lowercase characters unless the DELIMIDENT environment variable is set and the name of the database object is enclosed in quotation marks. In this case, the database server treats the name of the database object as a delimited identifier and preserves the uppercase characters in the name. Delimited Identifiers By default, the character set of a valid SQL identifier is restricted to letters, digits, underscore, and dollar-sign symbols. If you set the DELIMIDENT environment variable, however, SQL identifiers can also include additional characters from the codeset implied by the setting of the DB_LOCALE environment variable. See the Identifier section of the Informix Guide to SQL: Syntax for more information. Storage Object Names In Informix, delimited identifiers can be used to specify nonalphanumeric characters in the names of database objects. However, delimited identifiers can not be used to specify non-alpha characters in the names of storage objects such as dbspaces and blobspaces. To use delimited identifiers, DELIMIDENT must be set both at compile and run times. For example, to set DELIMIDENT environment variable with sh or ksh, issue the following at the command line prompt: export DELIMIDENT= Note: You do not need to assign a value to the environment variable, and counter-intuitively, setting DELIMIDENT=n has the same effect as DELIMIDENT=y. Also, in a Windows environment: set DELIMIDENT= effectively undefines the variable. Default shift problems Any direct access of system catalog information involving names will cause problems when migrating from a system, like Oracle, where system catalog names are stored in uppercase to a system, like Informix, where the catalog names are stored in lowercase. For example: CREATE TABLE foo (c1 int); Version 1.6 16 IBM Software Group IBM Information Management Within Oracle, the following will return information about the table: select * from sys.all_objects where object_name = 'FOO'; and the following will not: select * from sys.all_objects where object_name = 'foo'; Conversely, in Informix: select * from systables where tabname = 'foo'; will be successful, and the same with 'FOO' will not be. A similar, if not more common, problem can occur when the table has been created without the name being delimited, but is sometimes delimited when accessed by the application. Per the standard, foo == FOO == “FOO”, but in Informix, foo == FOO == “foo”. In Oracle, the following SELECT statement will be successful: SELECT * FROM “FOO”; but within Informix, this will result in an error, “206: The specified table (FOO) is not in the database.” Likewise, if the name is delimited in its definition, such as CREATE TABLE "SCHEMA1"."TABLE1"... this creates a table that can be referenced in Oracle without the name being delimited. The statements SELECT * FROM schema1.table1; and SELECT * FROM SCHEMA1.TABLE1; will be successful in the Oracle system, but will fail in the Informix one. In theory, whether on object name is delimited or not should be the same in the database and at every occurrence within the application, but this is not always the case. It may be beneficial to examine whether delimited identifiers or regular identifiers are most commonly used in application code, and use whichever is most common as a naming convention when translating the DDL. Explicit versus implicit schema/owner names Another complication to keep in mind is that when the log mode is ANSI, implicit and explicit owner names are treated differently between the Oracle and Informix servers. In Oracle, there is no difference in the case handling of an implicit owner name and an explicit one. For example, if connected to Oracle as scott the following CREATE TABLE statements create table foo1(c1 int); create table scott.foo2(c1 int); create table SCOTT.foo3(c1 int); all result in tables owned by SCOTT. The following select statements, in various forms that may be in application code, will all run without error. select * from foo1; select * from scott.foo1; Version 1.6 17 IBM Software Group IBM Information Management select * from SCOTT.foo1; select * from foo2; select * from scott.foo2; select * from SCOTT.foo2; select * from foo3; select * from scott.foo3; select * from SCOTT.foo3; These statements will also run without error within a non-ANSI mode Informix database, but not all of them will run successfully within an ANSI mode database. This is because Informix does not uniformly fold the case of owner names, and handles implicit names differently from explicit names; so, there will always be some combination of DDL and DML usage patterns that work within an Oracle system that do not work in an Informix one. You can set ANSIOWNER before starting your Informix server, but that will merely change which combinations do not work. You may want to examine usage patterns within the applications and choose a naming convention when translating the DDL that matches the most common naming convention used in the applications. Note: This behavior of Informix forces a tradeoff between using an ANSI mode database, which most closely matches the transaction behavior of Oracle, and a non-ANSI mode database, which has the least mismatch in identifier case sensitivity. Reserved words or keywords Oracle and Informix both allow identifiers to be reserved words as long as they are delimited. However, Oracle and Informix reserved words differ. In addition, both systems have keywords, like FROM, that are not always reserved in the sense that using them as an identifier will always generate a syntax error. There should be few problems with reserved words or keywords if identifiers that are delimited in the original database remain delimited in the destination database. The appendix Keywords of SQL for IBM Informix in the Guide to SQL: Syntax contains more information about Informix keywords. Also, one word, non-delimited identifiers should be verified against the Informix reserved words list to ensure that they do not need to be delimited or renamed. Although almost any word can be used as an identifier in Informix, syntactic ambiguities can result from using reserved words as identifiers in SQL statements. Delimited identifiers provide a way to use a reserved word as an identifier without causing syntactic ambiguities. However, doing this changes a case-insensitive name into a case-sensitive one, and that means that every reference to the object has to be checked for issues related to case-sensitivity. Essentially, this is not much less work than changing the name of the object because, in essence, it is changing the name of the object. Examples The following example shows how to create an Informix table with a case sensitive table name: CREATE TABLE "My_Customers" (…) The following example shows how to create a table whose name includes a space character. If the table name were not in quotation marks ("), a space character or any other non-alpha character, except an underscore ( _ ) or dollar sign ($) (see special conditions above), could be used in the name. CREATE TABLE "My Customers" (…) Version 1.6 18 IBM Software Group IBM Information Management The following example shows how to create a table that uses a keyword as the table name: CREATE TABLE "TABLE" (…) To include a double-quote (") within a delimited identifier, the double-quote (") must be preceded with another double-quote ("), as shown in the following example: CREATE TABLE "My""Good""Data" (…) Summary The main points to keep in mind when deciding about any questions on how identifiers should be translated are: Oracle always has delimited identifiers enabled, and Informix does not. Oracle folds regular identifiers to uppercase, and Informix folds to lowercase. Informix has behavioral differences between an ANSI mode database and a non-ANSI mode one. It is beneficial to consider the most common usage patterns within the existing application code prior to deciding default identifier translation patterns. Databases Databases, database names and instances Oracle and Informix implement databases and instances differently. Oracle considers a database to be a set of data files, control file(s) and log files, while an instance is a set of background processes and memory structures accessing database data. Oracle instances are also known as servers. Each Oracle instance or server runs against one database, but through the use of Oracle Parallel Server, a database may have multiple instances accessing it. An Oracle database is named at initialization and the name has a limit of 8 ASCII characters. Informix databases are defined as “a collection of information contained in tables that are useful to a particular organization or used for a specific purpose”. Informix database names may be up to 128 characters in length. An Informix instance is a set of processes, memory and disk allocations that manage data and will contain at least two and in most cases many databases. Informix instances are also referred to as servers. Informix databases may share disk allocations (dbspaces) with other databases within an Informix instance. Oracle databases cannot share disk allocations (tablespaces). Informix databases should be created in a dbspace other than the root dbspace. The root dbspace holds data for the system, configuration information for the engine, and so on. If userdefined objects are created in this dbspace, then the dbspace may become unmanageably large, and during backup, the system information may also get backed up unnecessarily. The CREATE DATABASE statements of Informix and Oracle have differences which may require modification to routines responsible for database creation. Informix will accept the same minimal syntax required by Oracle (e.g. CREATE DATABASE myDB) however issuing the command will implicitly accept the creation of a database without transaction logging enabled and establish the default location for all database objects as the instance’s default storage location. The instance’s default storage location, or “root” dbspace, should not contain user-defined objects. The root dbspace holds data for the system and some configuration information for the engine. If user- Version 1.6 19 IBM Software Group IBM Information Management defined objects are created in this dbspace it may become unmanageable and result in the system contending with user objects for the available space. ANSI vs. non-ANSI Informix supports both ANSI and non-ANSI databases. Informix non-ANSI databases are most common, but there are some advantages of ANSI databases when porting from Oracle: Oracle’s transaction behavior is more or less like that of an ANSI database. Applications ported on Oracle are always in a transaction. In Oracle and ANSI databases, transactions begin implicitly with the first of a series of SQL statements and will end immediately after a commit or rollback statement. Therefore, unlike a non-ANSI database, a COMMIT WORK statement does not need to be preceded with a BEGIN WORK statement. In ANSI databases, database object references must be prefixed by the owner’s name, unless the owner is the one referencing the object. If Oracle applications reference database objects with a both parts of the object name, prefixing the object name with schema, using an ANSI database may ease modifications necessary when porting to Informix. The “‘grant to PUBLIC”’ concept provide by Informix database security does not apply to ANSI databases. Every database object is private and privileges must be explicitly granted to each user. If an Informix ESQL/C or 4GL application was developed to run on a non-ANSI Informix database (including BEGIN WORK statements), the application could run on an ANSI Informix database in one of two ways: By placing the BEGIN WORK statement in a pre-processor directive, such as IFDEF object_name, and omitting the proper preprocessor compile options, such as -D object_name By compiling the application normally and ignoring the resulting warning messages Additionally, the LOG MODE ANSI clause must be added to the CREATE DATABASE statements. ANSI mode databases cannot have buffered logging. Applications written to use an open API, like ODBC or JDBC, are compiled without awareness of the database and these APIs are written with the intent to abstract specific database behavior. For instance, in one of these, the application developer would call a function to begin or end a transaction rather than execute a BEGIN and COMMIT or ROLLBACK. The API would execute these statements internally, or not, as necessary for the database connection. See the Transaction Processing section in Application Architecture for more information on how an application running on a non-ANSI Informix database can simulate Oracle transaction behavior. Tablespaces and dbspaces Oracle tablespace names need to be replaced with Informix dbspace names. Unlike the Oracle tablespace, the Informix dbspace cannot be created using DDL (SQL) scripts. It must be created using the Informix onspaces utility before any DDL (SQL) scripts are executed. Therefore, Oracle’s CREATE TABLESPACE statements should be removed from the DDL (SQL) scripts. The user interface for accomplishing these tasks is onmonitor. The command line utility is onspaces. Therefore, the onspaces commands can be placed in shell or batch scripts. After successfully executing the onspaces commands, the DDL (SQL) scripts may be called through the same shell or batch scripts. For example: Version 1.6 20 IBM Software Group IBM Information Management onspaces … onspaces … … dbaccess database_name DDL_script_file_name dbaccess database_name DDL_script_file_name … Tables Attributes and general syntax When creating tables, the Oracle CREATE TABLE statement must be converted to Informix, taking advantage of the Informix options IN, EXTENT, NEXT, and LOCK MODE. IN specifies the dbspace in which the table will reside. EXTENT specifies the amount of space that will initially be allocated to the table (16K default on most platforms). NEXT specifies the amount of space that will be allocated when additional space is needed (16K default on most platforms. Lock mode LOCK MODE specifies whether to use row or page locks for the table. In Oracle, the default is row level locking; in Informix the default setting is page level locking. However it can be changed by using one of the following methods: (resolved in the following order of precedence) 1. LOCK MODE specified using an attribute of the CREATE TABLE or ALTER TABLE command syntax 2. IFX_DEF_TABLE_LOCKMODE environment variable setting 3. DEF_TABLE_LOCKMODE parameter setting in the ONCONFIG file NOTE: If the DEF_TABLE_LOCKMODE parameter cannot be found in the ONCONFIG file it can be added to make the specification for every database within the instance. The Informix instance must be restarted for this parameter to take effect. The new “default” LOCK MODE will apply to all tables created after the parameter has been changed. To change the LOCK MODE of existing objects use the ALTER TABLE statement. Storage The Oracle STORAGE INITIAL and STORAGE NEXT clauses must be changed to the Informix EXTENT SIZE and NEXT SIZE clauses, respectively. In Oracle the STORAGE clause options MINEXTENTS, MAXEXTENTS and PCTINCREASE should be removed. For example: Oracle: CREATE TABLE dept( deptno NUMBER(2), dname VARCHAR2(14), loc VARCHAR2(13) ) STORAGE (INITIAL 100K NEXT 50K MINEXTENTS 1 MAXEXTENTS 50 PCTINCREASE 5); Informix: CREATE TABLE dept ( deptno SMALLINT, dname VARCHAR(14), loc VARCHAR(13) ); Version 1.6 21 IBM Software Group EXTENT SIZE 200 NEXT SIZE 50; IBM Information Management When an Oracle CREATE TABLE statement does not include a STORAGE clause, the table will be created using the tablespace STORAGE clause by default. If an Oracle tablespace is created specifying a STORAGE INITIAL of 100KB and a STORAGE NEXT of 50KB, then all tables created within that tablespace will have a default value of STORAGE INITIAL 100KB and STORAGE NEXT 50KB. Oracle tables cannot be created with smaller STORAGE clause values than the tablespace default they are created in. Informix dbspaces do not have storage clauses attached to them. As stated earlier, the default Informix EXTENT and NEXT sizes are 16KB on most platforms. The minimum EXTENT and NEXT sizes are 4 times the page size. Constraints The Oracle constraint syntax must be changed to the Informix syntax for primary keys, foreign keys, unique, and so on. For example: Oracle: CONSTRAINT PK_NUMBER PRIMARY KEY(CUST_NUM) CONSTRAINT CK_EMP_CODE CHECK (EMP_CODE > 100) Informix: PRIMARY KEY(CUST_NUM) CONSTRAINT PK_NUMBER CHECK (EMP_CODE > 100) CONSTRAINT CK_EMP_CODE An Oracle check constraint statement may contain functions that are not available in Informix. In this circumstance it may be necessary to code the combination of a trigger and a stored procedure to emulate the same functionality. A trigger based on an insert or update event can execute a stored procedure which tests the constraint condition and if necessary raise an exception to reject the row data which violates the constraint. The Oracle PARALLEL clause, which specifies parallel execution of an operation, must be removed from any SQL statements (such as CREATE TABLE and ALTER TABLE). In Informix there are ways to accomplish this parallel execution depending on the context in which these SQL statements are called (shell script file, batch file, and so on.). ALTER TABLE Informix offers a rich set of ALTER TABLE features: adding and dropping columns (rename by using the RENAME statement), changing column data types, altering the next size, and changing the lock mode. Informix will choose one of three algorithms when executing an ALTER TABLE statement depending upon the operation to be performed. For certain conditions, the database server uses either a “slow”, “in-place" or “fast” alter algorithm to modify the table. Informix provides “in-place” alter table support for adding or dropping a column anywhere in the table, changing the column length, and changing the column data type without an exclusive lock on the table. An “in-place” alter will modify the table’s definition without changing the existing rows or unloading and reloading the data. After the ALTER TABLE operation, the database server inserts rows using the latest definition. Informix will not change the row structure until a data row is touched by a user operation. Once a query accesses a row that is not yet converted, there can be a slight degradation in the performance because the database server reformats each row in memory before it is returned. Informix uses the “slow” alter algorithm when the ALTER TABLE statement makes column changes that it cannot perform in place. This type of alter would be: Adding or drop a column created with the ROWIDS keyword Dropping a column of the TEXT or BYTE data type Version 1.6 22 IBM Software Group IBM Information Management Converting an INT or a SMALLINT column to SERIAL or SERIAL8 Modifying the data type of a column so that some possible values of the old data type cannot be converted to the new data type For example, if you modify a column of data type INTEGER to CHAR(n), the database server uses the slow ALTER algorithm if the value of n is less than 11. An INTEGER requires 10 characters plus one for the minus sign for the lowest possible negative values. Modifying the data type of a fragmentation column in a way that value conversion might cause rows to move to another fragment Adding, dropping or modifying any column when the table contains user-defined data types or smart large objects When the database server uses the slow alter algorithm to process an ALTER TABLE statement, the table can be unavailable to other users for a long period of time because the database server will lock the table exclusively for the duration of the operation, make a copy of the table to convert the table to the new definition and convert the data rows. This type of ALTER TABLE processing can encounter the scenario of a statement being a long transaction and abort it if the Long Transaction High Water Mark (LTXHWM) threshold is exceeded. Informix will use the “fast” alter algorithm when the ALTER TABLE statement changes attributes of the table but does not affect the data. The database server uses the fast alter algorithm when you use the ALTER TABLE statement to make the following changes: • • • • Change the next-extent size Add or drop a constraint Change the lock mode of the table Change the unique index attribute without modifying the column type With the “fast” alter algorithm, the database server holds a lock on the table for a very brief period. In some cases, the database server will lock the system catalog tables but only long enough to change the attribute. In either case, the table is unavailable for queries for only a short time. Data Partitioning Informix Table Fragmentation is the counterpart to Oracle’s Table Partitioning and allows the user to control where data is physically placed at the table level. Table Fragmentation or Data Partitioning is typically done to large tables, but Informix and Oracle implement fragmentation and partitioning for different reasons and with different results. Informix recommends using table fragmentation to improve single-user response time, concurrency, data availability, backup / restore characteristics, and data-load performance. While Oracle does state that table partitioning may improve the performance of queries, and that the Oracle optimizer takes partitioning into account, the primary goal of partitioning in Oracle is improving database maintenance. Partitioning strategies in Oracle include range partitioning as well as hash and composite partitioning. Composite partitioning combines range and hash partitions through the use of subpartitions. Tables incorporating Oracle LOB’s (large objects) cannot be partitioned. Informix fragmentation strategies include expression-based and round robin. Basic expressionbased fragmentation can replace Oracle range partitioning. Unlike Oracle LOBs, Informix’s Smart Large Objects can be fragmented. Examples of creating tables with Oracle range partitioning and Informix fragmentation by expression are shown below: Oracle range partitioning: CREATE TABLE dept Version 1.6 23 IBM Software Group ( deptno NUMBER(2), dname VARCHAR2(14), loc VARCHAR2(13) ) partition by range (deptno) (partition PART1 values less than tablespace PART1_TS, partition PART2 values less than tablespace PART2_TS, partition PART3 values less than tablespace PART3_TS, partition PART4 values less than tablespace PART4_TS); Informix expression based fragmentation: IBM Information Management (11) (21) (31) (MAXVALUE) CREATE TABLE dept( deptno SMALLINT, dname VARCHAR(14), loc VARCHAR(13) ) FRAGMENT BY EXPRESSION deptno < 11 IN dbspace1, deptno >= 11 AND deptno < 21 IN dbspace2, deptno >= 21 AND deptno < 31 IN dbspace3, REMAINDER IN dbspace4; Oracle uses MAXVALUE for values not found in the specified range while Informix uses the REMAINDER keyword for values that fall outside the specified expression or expressions. Prior to IDS version 10.00, each table fragment had to go into a separate dbspace. Now tables can be fragmented within a single dbspace. You can create partitions within a dbspace that can each support a table fragment. It is better to have one chunk per disk drive, and now it is not necessary to create a dbspace just to facilitate fragmentation. This feature can simplify the management of dbspaces. The benefits gained through this new development include: It reduces the total number of dbspaces needed for a fragmented table. Storing multiple table fragments in a single dbspace improves query performance over storing each fragmented expression in a different dbspace. For example, Informix partition fragmention in a single dbspace: CREATE TABLE dept( deptno SMALLINT, dname VARCHAR(14), loc VARCHAR(13) ) FRAGMENT BY EXPRESSION PARTITION part1 PARTITION part2 PARTITION part3 PARTITION part4 (deptno < (deptno < (deptno < REMAINDER 11) IN dbspace1, 21) IN dbspace1, 31) IN dbspace1, IN dbspace1; Partitions of an Informix fragmented table can be manipulated using the ALTER FRAGMENT statement which supports attaching, detaching, adding, modifying, and dropping, as well as initializing the distribution strategy. Version 1.6 24 IBM Software Group IBM Information Management For example, the following SQL removes a partition fragment and places the contents into a new table: ALTER FRAGMENT ON TABLE dept DETACH PARTITION part3 dept_part3; Making use of ALTER FRAGMENT to detach fragments can be an effective way of deleting large quantities of rows from a table, quickly and easily. After the above statement completes, the table will no longer contain rows with a deptno greater than 21 and less than 31. Refer to the Informix Guide to SQL: Syntax for a complete description on how to use the ALTER FRAGMENT statement to change the distribution strategy or the storage location of an existing table or index. Data types Numeric Oracle’s NUMBER data type needs to be replaced with one of the following Informix equivalent data types, depending on the size of the column. The Informix numeric data types are listed below. SMALLINT (-32,767 to 32,767) INTEGER (-2,147,483,647 to 2,147,483,647) BIGINT (-9,223,372,036,854,775,807 to 9,223,372,036,854,775,807) INT8 (-9,223,372,036,854,775,807 to 9,223,372,036,854,775,807) The BIGINT type is more efficient in terms of space and speed; there are few reasons to use INT8 now that BIGINT is available. DECIMAL (floating point up to 32 significant digits -10 -130 to 10 124 ) In an ANSI database, there is an implied scale of 0 when no scale is given; so, this is not strictly speaking a floating point type within an ANSI database. NUMERIC (floating point up to 32 significant digits -10 to 10 FLOAT (double precision, like double data type of C) SMALLFLOAT (single precision, like float data type of C) REAL (same as SMALLFLOAT) DOUBLE (same as FLOAT) LONG (same as long data type of C) -130 124 ) Oracle’s NUMBER data type is used to store zero, positive and negative fixed size, and floating -130 125 point numbers with magnitudes between 1.0 x 10 and 9.9...9 x 10 (38 9s followed by 88 0s) with 38 digits of precision. A fixed-point number is specified by NUMBER(p, s) where p is the precision, or the total number of digits, and s is the scale, or the number of digits to the right of the decimal point. For example, a column defined as NUMBER(10,4) would contain numbers in the format 999999.9999. In Oracle, the scale can range from -84 to 127. If the scale is 0, or not mentioned, then the data type can be converted into Informix’s SMALLINT, INTEGER, or BIGINT depending on the precision, or FLOAT, SMALLFLOAT, DOUBLE or REAL, depending on usage in the application. When the scale is positive, the data type should be converted to Informix’s DECIMAL or NUMERIC. A floating-point data type in Oracle is declared using the type NUMBER without mentioning scale and precision. In Informix this can be replaced with FLOAT, SMALLFLOAT, REAL, DOUBLE PRECISION, or DECIMAL(p) in an non-ANSI database. Version 1.6 25 IBM Software Group IBM Information Management Oracle allows a negative scale. If the scale is negative, the actual data is rounded to the specified number of places to the left of the decimal point. For example, a specification of NUMBER (10, -2) means to round to the hundred. If the general form for any number is: mantissa X 10 exponent then precision is the number of digits in the mantissa, and scale is the negative exponent. Example: select cast( '987654321' AS NUMBER(10,-2) ) from dual; returns 987654300 Informix does not support a negative scale. If precision - scale is less than the maximum precision of an integer type, then that is the most efficient type to use. Failing that, and if the database is non-ANSI, a floating point DECIMAL can be used. If that is not sufficient, then it may be necessary to define a user type and associated functions. Oracle also allows the scale to be greater than the precision. Precision is just the number of digits that will be preserved. A NUMBER(3, 5) might have the value 0.00123. If the Oracle scale is less than the Informix maximum precision, then the Oracle scale can be used for both the Informix precision and scale. A NUMERIC(5,5) column could also store the value 0.00123. You may need to take into consideration not only the minimum and maximum values but the default size for data types. For example, when the size of the NUMBER data type is absent, Oracle will default to NUMBER(38), while Informix defaults to NUMERIC(16). Oracle and Informix support the ANSI DECIMAL data type. Therefore, in the case of an ANSI data type, the Oracle data type can be converted to the same Informix data type. There are differences between conversions based on theory versus those based on practice. For instance, it is very common to see an undecorated NUMBER declaration where the customer database and applications are using integer data. When converting NUMBER declarations, it may be necessary to consider how the data is being used in order to determine the best type mapping. CHAR, VARCHAR and LVARCHAR In Oracle, the default for a CHAR column is one character, and the maximum allowed is 255 characters. In Informix, the maximum length of CHAR and LVARCHAR columns is 32,767. Therefore, Oracle’s CHAR columns can always be converted to an Informix CHAR column. The CHAR data type columns can only contain printable characters, tabs, and spaces. The CHAR columns store leading and trailing spaces. In Oracle, a zero-length string can be inserted into a CHAR column, but the column is padded with one blank character when it is used in comparisons. Also, in Informix, a zero-length string can be inserted into a CHAR column; however, the column is padded with enough spaces to fill the column. In the case of inserting NULL into an Informix CHAR column, no padding occurs. Oracle’s VARCHAR and VARCHAR2 are currently synonymous and have a maximum data length of 4,000 bytes while the maximum length for the Informix VARCHAR data type is 255. Informix’s CHAR and LVARCHAR data types have a maximum length of 32,767 and 32,739, respectively, measured in bytes. These should be used to replace Oracle’s VARCHAR, VARCHAR2, LONG and LONG VARCHAR if the data length is between 256 and the maximum. If the data length exceeds the maximum, use Informix’s TEXT, BYTE, BLOB or CLOB data types. See the Raw Data section below for more information on TEXT, BYTE, BLOB and CLOB data types. Version 1.6 26 IBM Software Group IBM Information Management Empty Oracle VARCHAR2 strings are considered NULL. Empty Informix VARCHAR and LVARCHAR strings are not considered NULL; rather, they contain at least one space. Oracle compares VARCHAR2 values using non-padded comparison semantics. Therefore, “BOB” “BOB “. Informix ignores trailing blanks. Therefore, “BOB” = “BOB “ or “” = “ ”. Hence, checks for NULL within the application must be replaced with checks for an empty string (such as “” or “ ”) whenever an empty string is inserted into the column. If NULL is truly inserted into the column, checking for NULL is appropriate (for example, SELECT * FROM … WHERE … IS NULL). In Oracle, LONG columns store variable length character strings containing up to 2 gigabytes, or 31 2 -1 bytes. Informix LONG columns store numeric data. Oracle’s LONG columns have many of the characteristics of VARCHAR2 columns. They can be used in a SELECT list, in the SET clause of an UPDATE statement, and in an INSERT statement, but they cannot be used in a WHERE clause, a GROUP BY clause, or an ORDER BY clause, and they cannot be indexed. Oracle’s LONG columns should be converted to an Informix Smart Large Object or “SLOB” (BLOB, CLOB) column. Date and Time Oracle’s DATE data type contains date and time components, from year down to second. It can be to be replaced most directly with Informix’s DATETIME YEAR TO SECOND. Some analysis may be necessary to determine whether the time is really necessary. In other words, does the application reference the date and time portions of the values? If the application only references the date portion, then replace Oracle’s DATE with Informix’s DATE and change the host variable to X(10) or char(10), accordingly. The Informix DATE default display format is MM-DD-YYYY, although the DATE data type stores the calendar date. The Informix DATE data type requires 4 bytes, which is stored internally as an integer value equal to the number of days since December 31, 1899. The default display format can be changed with the environment variable DBDATE. If the application uses different date formats, then using DBDATE is not a solution. Sometimes it might be necessary to write small subroutines to emulate Oracle’s DATE representations. See the Informix Guide to SQL, Reference manual for details on DBDATE and also, DBCENTURY. If the application only references the time portion of the values, replace Oracle’s DATE with Informix’s DATETIME using the EXTEND function to specify precision. In addition, Informix provides the data type INTERVAL for use while performing DATE and DATETIME calculations. In Oracle, the DATE data type is used to store date and time information. Although date and time information can be represented in both CHAR and NUMBER data types, the DATE data type has special associated properties. For each DATE value the following information is stored: century year month day hour minute second Also regarding the Oracle DATE data type, if a date value without a time component is specified, the default time is 12:00:00a.m. If a DATE value without a date is specified, the default date is the first day of the current month. Interval Within Informix there are two classes of interval types, YEAR-MONTH and DAY-FRACTION. There are different numbers if days in different months; so, the MONTH-DAY boundary cannot be crossed without creating ill-defined results. Within each of these classes, Informix allows you to declare columns or variables as any subset of the start and end fields. Oracle has one INTERVAL Version 1.6 27 IBM Software Group IBM Information Management type for each of the two classes, YEAR(p) TO MONTH and DAY(p) TO SECOND(s). The Oracle scale on the SECOND field is logically equivalent to the Informix scale on the FRACTION field. Raw data and Large Objects Oracle’s support of large blocks of raw, unstructured data (such as graphic images, video clips, and sound waveforms) in binary or character format is provided by the large object (LOB) data types BLOB, CLOB, NCLOB, and BFILE. Traditional Oracle data types MLSLABEL (used with Trusted Oracle), RAW and LONG RAW are also used for data that is not to be interpreted by the engine and converted when moving data between different systems. Informix supports two types of large objects (LOBs; Simple Large Objects and Smart Large 31 Objects. Simple LOBs consist of the TEXT and BYTE data types and can be 2 bytes or 2 Gigabytes in size. Smart LOBs include both Character Large Object (CLOB) and Binary Large 40 Object (BLOB) data types and may be up to 4*2 bytes or 4 Terabytes in size. Simple LOBs can be stored either in-line with table data or in blobspaces while Smart LOB’s are stored in sbspaces. Oracle’s CLOB, BLOB, MLSLABEL, RAW and LONG RAW data types should be replaced with Informix’s BYTE, TEXT, CLOB or BLOB, depending on the column’s size and usage. Informix’s Smart BLOB data types, CLOB and BLOB, should be used under the following conditions and considerations: The large object needs to be referenced by multiple sources. The object size exceeds 2 GB. Fragmentation is required. Byte level locking is available, making it possible to lock only a portion of the object. Logging can be specified at the object level. Updates are done in place or moved as needed. Be aware of the following restrictions for Simple Large Objects: Data can only be inserted into a BYTE or TEXT column with the dbload or onload utilities, the DBACCESS load statement, BYTE or TEXT host variables, respectively, in ESQL/C, or declaring a file in ESQL/COBOL. BYTE and TEXT columns cannot be inserted or updated with a literal, neither quoted text string nor number. BYTE or TEXT columns cannot be used in string operations, with aggregate functions, with the GROUP BY, ORDER BY, IN, LIKE, or MATCHES clauses. ROWID The implementation of ROWID differs between Oracle and Informix. Oracle’s ROWID is both a pseudo-column and a data type. See the ROWID subsection in the Pseudo-Columns section in CHAPTER 3: Data Manipulation Language for more information. Informix implements ROWID as a pseudo-column only. Oracle stores the ROWID column and ROWID data type column values in hexadecimal format. Informix stores the ROWID column values in INTEGER format. The Oracle and Informix engines maintain the values of the ROWID pseudo-column. Oracle’s engine does not maintain the values of other columns of type ROWID. In Informix, the term rowid refers to an integer that defines the physical location of a row. Informix assigns rows in a non-fragmented table a unique rowid, which allows applications access to a particular row in a table. Rows in fragmented tables, in contrast, are not assigned a rowid. To access data by rowid in a fragmented table, a rowid column must be explicitly created as is described in the Informix Guide to SQL: Reference manual Creating a Rowid Column. If applications attempt to reference a rowid in a fragmented table that does not contain a rowid that was explicitly created, Informix displays an appropriate error message and execution of the application is halted. Version 1.6 28 IBM Software Group IBM Information Management When used as a data type for a column, Oracle does not guarantee that the values are valid ROWIDs. For example, if the ROWID is used as a data type on a column, such as a column in a child table named parent_rowid containing the ROWID of the parent, then the values are maintained by the application, outside of the engine. The application must read the ROWID of the parent and insert it into the parent’s corresponding child table rows. This is usually done so the two tables can be joined on the parent’s ROWID and the child’s parent_rowid column. This functionality can be duplicated in Informix by using primary and foreign keys. Informix recommends that primary keys are used as a method of access in applications rather than rowids, because primary keys are defined in the ANSI specification of SQL, using them to access data makes applications more portable. Refer to the Informix Guide to SQL: Reference and the Informix Guide to SQL: Syntax for a complete description on how to define and use primary keys to access data. User-defined types Both Oracle and Informix allow the user to create and define data types. Oracle refers to these as abstract data types while Informix refers to these as user-defined types. In Oracle, all userdefined types are referred to as abstract data types while Informix may refer to user-defined types as COLLECTION types, ROW types, DISTINCT types and OPAQUE types. Currently, the Informix ROW type is closest in form and function to the Oracle abstract data type. An example of syntax for creating both an Oracle abstract data type and an Informix ROW type is shown below: Oracle abstract data type: CREATE TYPE name_type AS OBJECT (first_name VARCHAR2(25), middle_initial CHAR(1), last_name VARCHAR2(30)); Informix row type: CREATE ROW TYPE name_type (first_name VARCHAR(25), middle_initial CHAR(1), last_name VARCHAR(30)); Both Oracle and Informix support the use of user-defined types for table definition or column definition within a table. Building on the example above, the following create table example show a table definition and column definition within a table based on user-defined types: Oracle table creation: CREATE TABLE name OF name_type; Informix table creation: CREATE TABLE name OF type name_type; Oracle column definition: CREATE TABLE employee (empno name Informix column definition: CREATE TABLE employee NUMBER(9), name_type); Version 1.6 29 IBM Software Group (empno name INTEGER, name_type); IBM Information Management For more information on User Defined Types, see the Informix Guide to SQL: Syntax. Complex data types Informix offers users the ability to create data types from existing built-in types, OPAQUE types, DISTINCT types and other complex types. Complex data types may either be a ROW type or a COLLECTION type. Oracle treats complex data types as abstract data types. For more information on Oracle abstract data types see the User Defined Types section above. For more information on Complex Data Types, see the Informix Guide to SQL: Syntax. For recommended type mappings and additional information, see the appendix “Data type mappings”. Sequence objects Oracle sequences An Oracle sequence is a database object from which multiple users, or transactions, may generate unique integers. For example, sequences can be used to automatically generate primary key values. When a sequence object is queried, the sequence number is incremented and passed to the query, independent of the transaction committing or rolling back. If two applications increment the same sequence, the sequence numbers each application acquires may not be sequential since sequence numbers are being generated by the other application. One user, or transaction, can never acquire the sequence number generated by another user, or transaction. Once a user or transaction generates a sequence value, that value will never be generated and passed to another user or transaction. Informix sequences Informix ’s implementation of sequence objects is identical to that of Oracle. Issues regarding syntax compatibility should be negligible with the exception of some keywords which have no effect on the behavior of the sequence. Informix supports DML statements (CREATE SEQUENCE, ALTER SEQUENCE, RENAME SEQUENCE, DROP SEQUENCE) for sequence objects that multiple users can access concurrently to generate unique integers in the 8-byte integer range. GRANT and REVOKE statements support access privileges on sequence objects, and the CREATE SYNONYM and DROP SYNONYM statements can be used to reference synonyms for sequence objects in the local database. The operators, CURRVAL and NEXTVAL, can read or increment the value of an existing synonym with behavior the same as Oracle. Indexes Composite indexes A composite index can have up to 16 key parts that are columns, or up to 341 key parts that are values returned by a UDR. This limit is language-dependent, and applies to UDRs written in SPL or Java™; functional indexes based on C language UDRs can have up to 102 key parts. A composite index can have any of the following items as an index key: One or more columns Version 1.6 30 IBM Software Group IBM Information Management One or move values that is returned by a user-defined function (referred to as a functional index) A combination of columns and user-defined functions Maximum key size The total widths of all indexed columns in a single CREATE INDEX statement have a limitation based upon the page size of the dbspace in which the index resides. This limitation does not apply to functional indexes. An enhancement introduced with Informix Dynamic Server Version 11.10 is configurable page sizes for dbspaces. Prior to this release, the page size of dbspaces were restricted to that of the operating system and could not be changed thus Informix was limited to 390 byte indexes. With page sizes configurable from 2K through 16K wider indexes are supported. Refer to the table below, 16K page sizes raises the limit on index width to over 3000 bytes. Lifting the 390 byte limit also permits LVARCHAR columns larger than 387 bytes to be included in an index definition. Page Size 2 kilobytes 4 kilobytes 8 kilobytes 12 kilobytes 16 kilobytes Maximum Key Size 387 bytes 796 bytes 1615 bytes 2435 bytes 3245 bytes In addition to aiding the porting process, wider indexes have satisfied requirements for Unicode data as well as provide performance gains by reducing B-Tree index traversal costs since indexing can be built with more items on an index page and produce fewer levels. Index fragmentation Just as Oracle and Informix support table fragmentation, or partitioning in Oracle, both support index fragmentation as well. See the Table fragmentation section above for more information regarding Informix table fragmentation and Oracle table partitioning. Oracle indexes that are partitioned in the same manner as the table they are indexing are referred to as local indexes. Informix refers to these as attached indexes. Oracle indexes that span partitions are known as global indexes. Informix refers to these as detached indexes. Informix detached indexes may also refer to indexes that do not follow the same fragmentation scheme as the table they are indexing. Starting with Informix version 11.10, indexes can be fragmented similarly to tables, on a partition level. General index information The Informix CLUSTER option should be used when appropriate. This option orders the data within the table in the order the index requires. It is important to note that the CLUSTER index is not maintained over time. The primary factor determining the frequency of CLUSTER index rebuilds is the amount of DML performed against the table. Frequent INSERTS, UPDATES and DELETES make it necessary to closely monitor CLUSTER indexes. There is only one clustered index per table since a table’s data can only be stored in one order. Oracle’s INITRANS and PCTFREE functionality is similar to the Informix index FILLFACTOR option of the CREATE INDEX statement or the FILLFACTOR configuration parameter in the ONCONFIG file. In either case, the functionality is handled by Informix’s engine once it is set, and can be removed from the application. FILLFACTOR specifies the degree of index-page Version 1.6 31 IBM Software Group IBM Information Management compactness. A low value provides room for growth in the index. A high value compacts the index. If an index is fully compacted (100 percent), any new inserts result in splitting nodes. The setting on the CREATE INDEX statement overrides the ONCONFIG file value. The FILLFACTOR default value for both the CREATE INDEX statement as well as the ONCONFIG is 90. Informix has no support for bitmap indexes. Views Oracle’s CREATE VIEW statement contains three options; FORCE, NO FORCE and WITH READ ONLY. FORCE creates the view regardless of whether the view's base tables exist or the owner of the schema containing the view has privileges on them. NO FORCE creates the view only if the base tables exist and the owner of the schema containing the view has privileges on them. WITH READ ONLY specifies that no deletes, inserts, or updates can be performed through the view. These options must be removed from the Informix CREATE VIEW statements. Because a view is not really a table, it cannot be indexed, and it cannot be the object of such statements as ALTER TABLE and RENAME TABLE. The columns of a view cannot be renamed with RENAME COLUMN. To change anything about the definition of a view, you must drop the view and re-create it. Because it must be merged with the user’s query, the SELECT statement on which a view is based cannot contain any of the following clauses: INTO TEMP The user’s query might contain INTO TEMP; if the view also contains it, the data would not know where to go. The user’s query might contain UNION. No meaning has been defined for nested UNION clauses. The user’s query might contain ORDER BY. If the view also contains it, the choice of columns or sort directions could be in conflict. UNION ORDER BY Stored procedures and triggers The ways stored procedures and triggers are created differ between Oracle and Informix. Oracle allows a REPLACE clause in the CREATE statement to handle situations when the object already exists. Informix does not support the REPLACE option. Instead, a DROP PROCEDURE or DROP TRIGGER statement must precede the CREATE PROCEDURE or CREATE TRIGGER statement, respectively, to handle the situation when the object already exists. It should be noted a DROP statement will generate an error when the object does not exist. If it is run interactively within the DBACCESS user interface, the DROP statement error will terminate processing. If it is run using script execution using DBACCESS at the command line processing will continue. To run DBACCESS from the command line type: dbaccess db_name To run the file containing the create statements from the command line type: dbaccess db_name sql_file_name. Informix introduced an IF EXISTS clause to most of the CREATE and DROP statements at 11.7, but the logic is different from Oracle’s OR REPLACE clause. For example, these statements: Version 1.6 32 IBM Software Group DROP PROCEDURE IF EXISTS … CREATE PROCEDURE … are logically the same as this Oracle statement: CREATE OR REPLACE PROCEDURE … IBM Information Management So, it is possible to achieve the same result, but not necessarily the same number of statements. Oracle extensions The Oracle CLUSTER option in CREATE statements should be removed. These are not to be confused with Informix CLUSTER index statements (See Indexes section above). An Oracle cluster is a database schema object that contains one or more tables that all have one or more columns in common. Rows of one or more tables that share the same value in these common columns are physically stored together within the database. Removing this Oracle feature will not affect the application. Other non-Informix DDL statements like CREATE DATAFILE, CREATE CONTROLFILE, CREATE ROLLBACK SEGMENT, CREATE SNAPSHOT, CREATE DATABASE and CREATE DATABASE LINK must be removed or changed (in the case of the CREATE DATABASE statement). The Oracle CREATE PROFILE statement must be removed. An Oracle profile is a set of limits on database resources. Profiles can be used to limit the database resources available to a user for a single SQL statement or a single session. Version 1.6 33 IBM Software Group IBM Information Management Data Manipulation Language CHAPTER 4: SQL Labels using keywords Oracle displays labels in SQL statements that are Informix reserved words, such as UNITS, YEAR, MONTH, DAY, HOUR, and so on, must be converted by using the Informix AS clause between the column name and the display label. Otherwise, syntactic ambiguities will cause errors. Inequality operations In addition to the Informix supported “not equal to” symbols “<>” and “!=”, Oracle supports the symbol “^=”. All occurrences of ^= must be replaced with one of the Informix supported symbols. Selects Queries are executed based on how the optimizer determines the best path to the data. Oracle and Informix developed their own query optimizers, with their proprietary methods and rules. A query that looks exactly the same in both environments may run differently in each, following a different path and executing faster or slower. Therefore, every query in the ported application should be tested for performance, regardless of whether a query had to be converted. Furthermore, Oracle supports optimizer hints within a SELECT statement. Prior to IDS 7.3 and Informix Dynamic Server 9.2, Informix did not allow optimizer hints. See the Optimizer Directives subsection below for more information on porting Oracle’s optimizer hints to Informix’s optimizer directives. Optimizer directives Informix offers optimizer directives similar to Oracle’s optimizer hints. This feature provides the query developer the flexibility to direct the optimizer to follow specific paths, rather than choosing a plan through its analysis. A query is processed in two steps. First it is optimized and compiled to product a query plan. Secondly, it is executed to produce a result. Optimizer directives are a method for influencing the choice of the optimizer in the creation of the plan. Version 1.6 34 IBM Software Group IBM Information Management Optimizer directives address the following key issues: Reduced time for correcting performance problems. Rewriting queries can be very time consuming, and this allows you to have a quick way to alter a plan. Competitive pressure. Users accustomed to this function were looking for it in Informix. Product development tuning. This allows product development to alter plans dynamically rather than through code changes to evaluate the effect of different optimization techniques. The syntax and behavior of Informix’s implementation is compatible with Oracle’s optimizer hints, which eases the porting of applications from Oracle to Informix. Directives are written as a comment whose first character is a ‘+’ (plus sign) . An example is: select {+ ORDERED AVOID_FULL(e)} empname, deptno from employee e, department d where e.dept_no = d.dept_no; This above example specifies that the tables should be joined in the order specified in the query and that the employee table (e) should NOT be accessed with a full table scan. The Informix implementation differentiates itself significantly from Oracle’s in one way. Informix supports the notion of negative directives whereas Oracle only supports direct hints. A directive that tells the optimizer what to avoid, rather than what to choose, is unique to Informix. The query result can be realized by writing a directive to avoid certain actions known to cause performance issues, but allow any new indexes or table attributes to be explored by the optimizer as they are added over the life of the table and query. This allows a DBA to continue to add indexes to tables and not have to rewrite directives. Additionally, Informix supports full recognition of directives with SET EXPLAIN output. Informix will highlight semantic and syntactic errors, which Oracle does not. Optimizer directives support control in the following areas of the optimization process: Access methods: Index versus scans Join Methods: Forcing hash joins, nested loop joins Join Order: Specify which order the tables are joined Goal: Specify first rows or all rows (response time versus throughput) See the Informix Guide to SQL: Syntax or Reference for more information. External optimizer directives External optimizer directives give the DBA the ability to specify query directives and save them in the database. These directives are applied automatically to subsequent instances of the same query. The SAVE EXTERNAL DIRECTIVES statement associates one or more optimizer directives with a query, and stores a record of this association in the sysdirectives system catalog table, for subsequent use with queries that match the specified query string. The query string must be an exact match that includes case and white space positioning. This statement establishes an association between the list of optimizer directives and the text of a query, but it does not execute the specified query. Only the DBA or user informix can execute SAVE EXTERNAL DIRECTIVES. This associates AVOID_INDEX and FULL directives with the specified query: SAVE EXTERNAL DIRECTIVES /*+ AVOID_INDEX (table1 index1)*/, /*+ FULL(table1) */ ACTIVE FOR SELECT col1, col2 FROM table1, table2 Version 1.6 35 IBM Software Group IBM Information Management WHERE table1.col1 = table2.col1 These directives are applied automatically to subsequent instances of the same query. You must include one of the ACTIVE, INACTIVE, or TEST ONLY keyword options to enable, disable, or restrict the scope of external directives. When external directives are enabled and the sysdirectives system catalog table is not empty, the database server compares every query with the query text of every ACTIVE external directive, and for queries executed by the DBA or user informix, with every TEST ONLY external directive. External directives are ignored if the EXT_DIRECTIVES parameter is set to 0 in the ONCONFIG file. In addition, the client system can disable this feature for its current session by setting the IFX_EXTDIRECTIVES environment variable to 0. If an external directive has been applied to a query, output from the SET EXPLAIN statement indicates “EXTERNAL DIRECTIVES IN EFFECT” for that query. Any inline directive is ignored by the optimizer when the external directives are applied to a query that matches the SELECT statement. Like inline optimizer directives that are embedded within a query, external directives can improve performance in some queries for which the default behavior of the query optimizer is not satisfactory. Unlike inline directives, external directives can be applied without revising or recompiling existing applications. Inserts Informix does not support Oracle’s INSERT with SELECT statement utilizing an UNION clause (although a view with an UNION clause can be referenced, resulting in an UNION operation). Additionally, Informix does not support an INSERT statement with a VALUE clause with SELECT statements. The SELECT statement within a VALUE clause can be rewritten by omitting the VALUE clause and performing an INSERT INTO table_name SELECT … FROM other_table_name. Temporary tables Informix and Oracle implemented the concept of temporary tables differently. Oracle implements temporary tables as a work area during batch data loads. Informix implements temporary tables just like regular database tables. These temporary tables can be created, manipulated, and dropped only within a session, such as stored procedures and ESQL programs. Once the session ends, any remaining temporary tables are automatically dropped. Temporary tables can give an application extra functionality; however, their use should be limited if reducing database extensions to increase database independence within an application is a priority. Outer joins Both Oracle (starting with Oracle 9i) and Informix support ANSI standard syntax for outer join specification. The syntax for left and right outer join using the plus sign (+) in the WHERE clause is only supported for backward compatibility however it is still in existence. The use of ANSI outer join syntax is recommended. The Oracle outer join syntax + (placed after the column which has values not matching the corresponding column in the joined table) can be replaced with the Informix equivalent, OUTER. The OUTER keyword is placed within the SELECT statement before the name of the subservient table. Unlike Oracle, in Informix multiple outer joins per SELECT statement are allowed, grouped with parentheses. Sorts In Oracle sorts, NULL values are considered the highest value and are therefore ordered last in ascending sorts. Informix sorts with NULLS as the lowest value, ordering them first in ascending sorts. First, it must be determined whether this sorting difference will affect the outcome enough to warrant a work around. In most cases it will not. If it does, then a stored procedure can be Version 1.6 36 IBM Software Group IBM Information Management written to evaluate whether a column is NULL and if so, replace it with a high value so that it will sort last. Once the application receives the results, it must replace the high values with NULL if the column is going to be displayed, or manipulated. Exceptions Error handling using hard coded Oracle SQL codes must be replaced with the corresponding Informix SQL codes. Correlation names Informix does not support correlation names with aliases on the main table of an UPDATE or DELETE statement. Such cases must be converted. For example: Oracle: UPDATE customer C SET zip_code = ‘92612’ WHERE zip_code IN (SELECT zip_code FROM zip_table Z WHERE C.create_date <= Z.effective_date) Informix: UPDATE customer SET zip_code = ‘92612’ WHERE zip_code IN (SELECT zip_code FROM zip_table Z WHERE customer.create_date <= Z.effective_date) -andOracle: DELETE customer C WHERE order_date <= (SELECT control_value FROM control_table T WHERE T.control_field = ‘archive_date’ AND C.state = T.state ) Informix: DELETE customer WHERE order_date <= (SELECT control_value FROM control_table T WHERE T.control_field = ‘archive_date’ AND customer.state = T.state ) Note that Informix allows correlation names with aliases on tables other than the updated or deleted table. Aliases Unlike Oracle, in Informix, if an alias is used in the FROM clause of a SELECT statement, it must also be used in the SELECT list and WHERE clauses. The original table name should be replaced with the alias wherever aliases and original names are used together within a SELECT statement in an Oracle application. Hierarchical queries Informix does not allow query results to be organized hierarchically in a B-tree fashion. Oracle does allow it by using the clauses START WITH… and CONNECT BY PRIOR in the SQL Version 1.6 37 IBM Software Group IBM Information Management statement. In Oracle, if a table contains hierarchical data, a bill of materials for example, rows can be selected in a hierarchical order using the following clauses. START WITH The START WITH clause identifies the row(s) to be used as the root(s) of a hierarchical query. This clause specifies a condition that the root(s) must satisfy. If this clause is omitted, Oracle uses all rows in the table as root rows. A START WITH condition can contain a subquery. CONNECT BY The CONNECT BY clause specifies the relationship between parent and child rows in a hierarchical query. This clause contains a condition that defines this relationship. The part of the condition containing the PRIOR operator must have one of the following forms: PRIOR expression comparison_operator expression -orexpression comparison_operator PRIOR expression To find the children of a parent row, Oracle first evaluates the PRIOR expression to identify the parent row(s) and the other expression for each row in the table. Rows for which the other expression is true are the children of the parent. The CONNECT BY clause can contain other conditions to further filter the rows selected by the query. WHERE The WHERE clause can restrict the rows returned by the query without affecting other rows of the hierarchy. Oracle uses the information from each of the clauses mentioned above to form the hierarchy using the following steps. 1. Oracle selects the root row(s) of the hierarchy. These are the rows that satisfy the condition of the START WITH clause. 2. Oracle selects the child rows of each root row. Each child row must satisfy the condition of the CONNECT BY clause with respect to one of the root rows. 3. Oracle selects successive generations of child rows. Oracle first selects the children of the rows returned in step 2, and then the children of those children, and so on. Oracle always selects children by evaluating the CONNECT BY condition with respect to a current parent row. 4. If the query contains a WHERE clause, Oracle removes all rows from the hierarchy that do not satisfy the condition of the WHERE clause. Oracle evaluates the WHERE condition for each row individually, after the row has been fetched, rather than only fetching rows that satisfy the WHERE condition. 5. Oracle returns the rows in the order of an in-order, or left-root-right, binary tree traversal. Version 1.6 38 IBM Software Group IBM Information Management For example, the following SQL statement returns the data in the adjacent table in hierarchical order. The root row is defined to be the employee whose job is ‘PRESIDENT’. The child rows of a parent row are defined to be those rows whose manager number is the employee number of the parent row. SELECT ename, empno, mgr, job FROM emp START WITH job = ‘PRESIDENT’ CONNECT BY PRIOR empno = mgr ENAME KING JONES SCOTT ADAMS FORD SMITH BLAKE ALLEN WARD MARTIN TURNER JAMES CLARK MILLER EMPNO 7839 7566 7788 7876 7902 7369 7698 7499 7521 7654 7844 7900 7782 7934 MGR 7839 7566 7788 7566 7902 7839 7698 7698 7698 7698 7698 7839 7782 JOB PRESIDENT MANAGER ANALYST CLERK ANALYST CLERK MANAGER SALESMAN SALESMAN SALESMAN SALESMAN CLERK MANAGER CLERK To duplicate this hierarchical functionality in Informix, stored procedures or functions can be written requiring intensive use of cursors and program arrays. Informix offers the choice of creating and using a User Defined Routine or “UDR”. See User Defined Routines section below. Use of Informix global arrays will help considerably. To maximize read processing at the expense of insert processing, another method is to break the table into two tables, one containing the data and the other containing the relationship (foreign keys) between each parent and child. A parent row would have an entry in the relationship table for each of its children, grandchildren, great grandchildren, and so on. The opposite is true, where a child row would have an entry in the relationship table for its parent, grandparent, great grandparent, and so on. Truncate Informix supports the TRUNCATE statement for deleting all active data rows from a table and associated indexes. When using Informix’s TRUNCATE, you also have the option of releasing the storage space that was occupied by the rows and index extents or keeping the space allocated to be used as the table is repopulated with new data. TRUNCATE [TABLE] table [ [ DROP | REUSE ] STORAGE ]; You may use the command’s simplest usage: TRUNCATE my_table; In the example above, the TRUNCATE statement drops all the data for the table and keeps only the initial extents. There is a minor difference from Oracle syntax as Informix does not require the keyword TABLE. Another example: TRUNCATE my_table REUSE STORAGE; Version 1.6 39 IBM Software Group IBM Information Management This example drops all the data for the table but retains all the space allocated. The permissions needed for TRUNCATE TABLE depend on the existence of DELETE triggers on the table. For tables with DELETE triggers, ALTER permission on the table is required and RESOURCE or DBA privileges because the DELETE triggers will be bypassed. For tables without DELETE triggers, you need DELETE permission on the table and CONNECT privilege to the database. While Oracle does not provide rollback, Informix does permit TRUNCATE TABLE to execute within a transaction. When you rollback a TRUNCATE statement, no rows are removed from the table, and the storage extents that hold the data rows and index pages continue to be allocated to the table. In a database that supports transaction logging, only the COMMIT WORK or ROLLBACK WORK statement is valid after the TRUNCATE statement within a transaction. Any other statement will generate an error. The TRUNCATE statement does not reset the serial value of SERIAL or SERIAL8 columns. To reset the counter of a serial column, you must do so explicitly by using the MODIFY clause of the ALTER TABLE statement, either before or after you execute the TRUNCATE statement. After the TRUNCATE statement successfully executes, Informix automatically updates the statistics and distributions for the table and for its indexes in the system catalog to show no rows in the table nor in its dbspace partitions. It is not necessary to run the UPDATE STATISTICS statement immediately after you commit the TRUNCATE statement. Host variables Host variable formats should not change during the porting process. Instead, data types should be converted in a manner to preserve the column and host variable’s formats. However, there are times when the database schema changes in a way where the host variable format must change. Any column converted to a different format in Informix than that of Oracle requires that its corresponding host variable format be changed to be consistent. Alternatively, if an Oracle format, such as date and numeric variables, is not directly supported in Informix, then the conversion may require some format processing before or after the SQL statement. For example, the Oracle function TO_CHAR is used to format both date and numerical values. A similar function, or at least two functions, one to handle date data types and another to handle numeric data types, can be declared and used in the Informix application. Informix provides two built-in functions: TO_DATE which converts a character string to a DATETIME variable and TO_CHAR which converts a DATETIME variable to a character string. . When converting variables, an optional formatting string is allowed which determines how the string is displayed (full day names, partial day names, full months, month number, and so on). There is also a function, ifx_to_gl_datetime(), which takes an Oracle date format and returns the corresponding Informix date format. This function is supplied in genlib and is for use in ESQL programs to ease migration efforts from Oracle. The syntax is as follows: TO_CHAR (source_date, fmt) Returns a character string, containing the date specified in source_date, in the format specified by fmt if one is provided. If fmt is missing, the function will use the default date format (as specified by the client environment variables). Note, fmt must be an Informix format and does not necessarily include all possible Oracle date formats. TO_DATE (source_string, fmt) Evaluates source_string as a date according to the format fmt. If fmt is missing, the function will use the Version 1.6 40 IBM Software Group IBM Information Management default format (as specified by the client environment variables). Note, fmt must be an Informix format and does not necessarily include all possible Oracle date formats. These functions are mainly useful for simplifying migration from Oracle to Informix, but are also useful for performing advanced searches on DATETIME data types and for better output presentation. Date and time functions Informix version now has built-in support for most of Oracle’s date functions so it will not be necessary to modify or replace Oracle’s SYSDATE or ADD_MONTHS(date,months), LAST_DAY(date), MONTHS_BETWEEN(date1, date2), NEXT_DAY(date,char), ROUND(date,format), SYSDATE, and TRUNC(date,format). SYSDATE IDS 11.10 will support the SYSDATE operator which returns a single value representing the current date and time from the operating system clock. SYSDATE is identical to the Informix’s CURRENT operator, except that the default precision of SYSDATE is YEAR TO FRACTION(5), while the default precision of CURRENT is YEAR TO FRACTION(3). On Windows platforms that do not support a seconds scale greater than FRACTION(3), SYSDATE is effectively a synonym for the CURRENT operator, In addition to SYSDATE, Informix version 11.10 has support for Oracle’s date functions ADD_MONTHS(date,months), LAST_DAY(date), MONTHS_BETWEEN(date1, date2), NEXT_DAY(date,char), ROUND(date,format), SYSDATE, and TRUNC(date,format). Depending on the whether or not the Oracle date_format argument is supported by Informix, TO_CHAR(date, char_format) and TO_DATE(character_string, date_format) may have to be converted to Informix date processing. Date processing may be required because the Oracle application may format the date in many different ways, unsupported by Informix. The formats used in the Oracle functions TO_CHAR, TO_NUMBER and TO_DATE are illustrated below. These formats should be replaced with Informix formats wherever possible, format processing functions, or stored procedures written to duplicate the Oracle functionality. Refer to the appropriate Informix API Programmer’s Manual (Informix-ESQL/C Programmer’s Manual or Informix-ESQL/COBOL Programmer’s Manual) for more information. Number formats Element 9 Example 9999 Description Return value with the specified number of digits with a leading space if positive. Return value with the specified number of digits with a leading minus if negative. Leading zeros are blank, except for a zero value, which returns a zero for the integer part of the fixed-point number. Return leading zeros. Return trailing zeros. Return value with a leading dollar sign. Return blanks for the integer part of a fixed-point number when the integer part is zero (suppress leading zeros regardless of whether zeros are used in the format model i.e. B00999 ). Return negative value with a trailing minus sign “-“. Return positive value with a trailing blank. Return negative value with a leading minus sign “-“. Return 0 $ B 09999990 $9999 B9999 MI S 9999MI S9999999 Version 1.6 41 IBM Software Group IBM Information Management Element Example S 9999PR 99D99 9G999 C999 L999 9,999 99.99 999V99 9.9EEEE RN FM90.9 PR D G C L , . V EEEE RNrn FM Description positive value with a leading plus sign “+”. Return negative value with a trailing minus sign “-“. Return positive value with a trailing plus sign “+”. Return negative value in <angle brackets>. Return positive value with a leading and trailing blank. Return a decimal point “.” In the specified position. Return a group separator in the specified position. Return the ISO currency symbol in the specified position. Return the local currency symbol in the specified position. Return a comma in the specified position. Return a decimal point “.” In the specified position. n Return a value multiplied by 10 (and if necessary, round it up), where n is the number of “9”s after the “V”. Return a value in scientific notation. Return a value as Roman numerals in uppercase. Return a value as Roman numerals in lowercase. Value can be an integer between 1 and 3999. Return a value with no leading or trailing blanks. Date formats Element -/,.;:”text” AD or A.D. AM or A.M. BC or B.C. CC or SCC D DAY DD DDD DY IW IYY or IY or I IYYY HH or HH12 HH24 J MI MM MONTH MON RM Q RR WW W PM or P.M. SS SSSSS Y/YYY YEAR or SYEAR YYYY or SYYYY Meaning Punctuation and quoted text is reproduced in the result. AD indicator with or without periods. Meridian indicator with or without periods. BC indicator with or without periods. Century; “S” prefixes BC dates with “-“. Day of week (1-7). Name of day, padded with blanks to length of 9 characters. Day of month (1-31). Day of year (1-366). Abbreviated name of day. Week of year (1-52 or 1-53) based on the ISO standard. Last 3, 2, or 1 digit(s) of ISO year. 4-digit year based on the ISO standard. Hour of day (1-12). Hour of day (0-23). Julian day; the number of days since January 1, 4712 BC. Number specified with ‘J’ must be integers. Minute (0-59). Month (01-12; JAN = 01) Name of month, padded with blanks to length of 9 characters. Abbreviated name of month. Roman numeral month (I-XII; JAN = I). Quarter of year (1, 2, 3, 4; JAN-MAR = 1) Last 2 digits of year; see below for years in other centuries. Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year. Week of month (1-5) where week 1 starts on the first day of the month and ends on the seventh. Meridian indicator with and without periods. Second (0-59). Seconds past midnight (0-86399). Year with comma in the “/” position. Year spelled out; “S” prefixes BC dates with “-“. 4-digit year; “S” prefixes BC dates with “-“. Version 1.6 42 IBM Software Group IBM Information Management YYY or YY or Y Last 3, 2, or 1 digit(s) of year. The RR date format element The RR date format element is similar to the YY date format element, but it provides additional flexibility for storing date values in other centuries. The RR date format element allows storing the century even though only two digits are entered for the year. The table below illustrates how the year is stored based on only two digits. If the year is entered as: 0 – 49 50 - 99 The return date is in the The return date is in the century If the last two 0 - 49 current century. before the current one. digits of the current year 50 - 99 The return date is in the The return date is in the current century after the current one. century. are: User-defined routines User-defined data types, user defined behaviors and user defined access methods add rich functionality to Informix. A user-defined routine (UDR) can be developed in C, Java, or Informix SPL language, to vastly extend the capabilities of the database. Porting applications to Informix gives the option to create UDRs for aggregate and mathematical functions or to emulate Oracle built-in functions and reduce the impact of replacing all references throughout the application. Or, you can rename an Informix function to the corresponding Oracle function name. Compatible SQL Functions Informix Dynamic Server version 11.10 has added support for a number of built-in SQL functions to ease a migration effort. These new functions, which perform common operations and data manipulation, provide compatibility with Oracle routines thus simplifying the migration of applications that have been developed for an Oracle database. Date and time functions; ROUND, TRUNC, ADD_MONTHS, LAST_DAY, NEXT_DAY, MONTHS_BETWEEN, SYSDATE Conversion functions; ASCII, TO_CHAR, TO_NUMBER String manipulation; LTRIM, RTRIM Number manipulation; ROUND, TRUNC, CEIL, FLOOR, POWER Bit manipulation; BITAND, BITOR, BITXOR, BITNOT, BITANDNOT Others; NULLIF, FORMAT_UNITS These functions are documented with examples in the IBM Informix Guide to SQL: Syntax. Aggregate functions Like Oracle, the result returned by an Informix aggregate function is NULL if all of the column values are NULL, with the exception of COUNT which returns 0 (zero). Like Oracle, Informix aggregate functions cannot be used in a WHERE clause unless it is on a corresponding column originating from a parent query and the WHERE clause is within a subquery that is within a HAVING clause, or the aggregate function is contained within a sub-query. Unlike Oracle, in Informix the argument of an aggregate function cannot be another aggregate function. Informix supports the SELECT COUNT (column_name) … statement, where the number of rows containing non-null values in the specified column is returned. Version 1.6 43 IBM Software Group IBM Information Management The Oracle aggregate functions STDDEV and VARIANCE are supported, however the Informix standard deviation routine is named STDEV thus all references to Oracle’s STDDEV must be changed to match Informix’s STDEV. Informix does not support Oracle’s greatest lower boundary (GLB ) or least upper boundary (LUB). Therefore, UDRs must be written to provide equivalent functionality. Informix allows users to define their own aggregate functions known as user-defined aggregates (UDA). This extension of existing aggregates can be used for new data types as well as the definition of new aggregates. In Informix, TEXT and BYTE columns cannot be used in aggregate functions such as COUNT, AVG, MAX, MIN and SUM. Mathematical functions Informix does not support all of Oracle’s mathematical functions such as SIGN, COSH, SINH, and TANH. Therefore, functions or UDRs must be written in the Informix application to provide equivalent functionality. In Informix, the rounding factor within the ROUND function must be a value from -32 to 32. This limitation has seldom been encountered as a migration issue. String functions LENGTH Oracle’s LENGTH function will return different values than Informix for strings stored in CHAR data types. The numeric return value representing the string length returned by Oracle will include all trailing spaces. Whereas Informix’s LENGTH function will ignore trailing blanks regardless of data type. For example, for the two CHAR strings “BOB” and “BOB “ Oracle’s LENGTH function will return different values for each string, 3 and 5 respectively. Whereas Informix’s LENGTH function will ignore trailing blanks and return the value 3 for both. When the input string is NULL, both database functions will return a NULL. LTRIM and RTRIM The SQL Compatibility features of Informx has support for LTRIM and RTRIM and will return identical values to that of Oracle. No modifications will be necessary to SQL statements utilizing these functions. Oracle Extensions Informix does not support all of Oracle’s string functions such as CHR, INSTR, SOUNDEX, and TRANSLATE. Therefore, corresponding user-defined routines or UDRs (i.e. functions or stored procedures) must be written in the Informix application to provide equivalent functionality. Please note that the VERITY and EXCALIBUR Datablades included with Informix will work with Oracle’s string function SOUNDEX. Other Oracle Functions Informix does not support the following Oracle functions. Therefore, corresponding functions must be written in the Informix application to provide equivalent functionality. Alternatives to some of the more common Oracle functions follow this list. CHARTOROWID CONVERT DUMP GREATEST GREATEST_LB Version 1.6 44 IBM Software Group IBM Information Management HEXTORAW LEAST LEAST_UB RAWTOHEX ROWIDTOCHAR TO_LABEL TO_MULTI_BYTE TO_SINGLE_BYTE UID USERENV(OSDBA/ LABEL/ LANGUAGE/ TERMINAL/ SESSIONID/ ENTRYID) VSIZE Macros Oracle contains macros, sometimes referred to as language constructs, which can determine the status of an object. Informix does not support Oracle’s macros. These macros must be removed from the code, and the associated logic should be implemented with equivalent Informix logic, where applicable. %ISOPEN and %NOT FOUND The macro cursor_name%ISOPEN returns a Boolean value to indicate whether the cursor is open, and cursor_name%NOTFOUND indicates whether there are any more rows yet to be fetched from the cursor cursor_name. These macros, used in condition statements, are followed by some logic to handle the true and/or false conditions. In the case of ISOPEN, this macro and its associated logic can just be omitted since the FOREACH loop processing does not explicitly open a cursor and therefore, the condition cannot be checked. In the case of NOTFOUND, this macro should be omitted and its associated logic converted to Informix logic within an Informix FOREACH loop. %ROWTYPE The Oracle macro prefix%ROWTYPE is used in the declarative section as a data type to declare variables, similar to the structure construct of C or RECORD of INFORMIX. The prefix can be either a table name or another structure name. Structure names ultimately refer back to a table name. This macro may be replaced with the Informix statement LIKE table_name.*. The construct LIKE table_name.* cannot be used in SPL. Instead, the construct DEFINE column1 LIKE table_name.column1; DEFINE column2 LIKE table_name.column2; and so on, should be used to declare variables corresponding to all the columns of the table. Then operations on the whole or part of the structure should be replaced with operations on single SPL variables. %TYPE The Oracle macro prefix%TYPE is used in the declarative section as a data type to declare single variables. The prefix is either a table_name.column_name or a structure_name.variable_name. A structure_name.variable_name ultimately refers back to a table_name.column_name. This macro can be replaced with the Informix statement LIKE table_name.column_name. Pseudo-columns Pseudo-columns are columns that exist in tables but are not displayed from a SELECT * FROM table_name query. Informix pseudo-columns are generally reserved for the engine’s use. Eliminating the use of such columns increases an application’s portability and eliminates the need for future modifications if the engine is ever modified to process the column differently or not at all. Version 1.6 45 IBM Software Group IBM Information Management LEVEL In Oracle, for each row returned by a hierarchical query, the pseudo-column LEVEL returns 1 for a root node, 2 for a child of a root, 3 for a child whose parent is at level 2, and so on. A root node is the highest node within an inverted tree, and therefore has no parent. A child node is any non-root node. A parent node is any node that has children. A leaf node is any node without children. To define a hierarchical relationship in a query, you must use the START WITH and CONNECT BY clauses. Informix does not support the LEVEL clause. See the Hierarchical Queries subsection in the SQL section in the “Data Manipulation Language” chapter for more information on how to process these queries. ROWID Oracle’s ROWID pseudo-column returns the row's address for each row in the database. ROWID values contain the following information necessary to locate a row: Which data block in the data file Which row in the data block (first row is 0) Which data file (first file is 1) Usually, a ROWID value uniquely identifies a row in the database. However, rows in different tables that are stored together in the same cluster can have the same ROWID. Values of the ROWID pseudo-column have the data type ROWID. ROWID values have several important uses: They are the fastest way to access a single row. They can show you how a table's rows are stored. They are unique identifiers for rows in a table. A ROWID does not change during the lifetime of its row. However, you should not use ROWID as a table's primary key. If a row is deleted and re-inserted, its ROWID may change, even when using the export and import utilities. If a row is deleted, Oracle may re-assign its ROWID to a new row inserted later. Although you can use the ROWID pseudo-column in the SELECT and WHERE clauses of a query, these pseudo-column values are not actually stored in the database. The value of a ROWID pseudo-column cannot be inserted, updated, or deleted. For example, the following Oracle statement selects the address of all rows that contain data for employees in department 20: ROWID 0000000F.0000.0002 0000000F.0003.0002 0000000F.0007.0002 0000000F.000A.0002 0000000F.000C.0002 ENAME SMITH JONES SCOTT ADAMS FORD SELECT ROWID, ename FROM emp WHERE deptno = 20 The equivalent pseudo-column in Informix is also known as ROWID, and although the behavior is similar, the data type is different from Oracle’s. See the ROWID subsection in the Data Types section in the chapter “Data definition language” for more information. ROWNUM In Oracle, for each row returned by a query, the ROWNUM pseudo-column returns a number indicating the order in which Oracle selected the row from a table or set of joined rows. The first row selected has a ROWNUM of 1, the second has 2, and so on. ROWNUM can be used to limit the number of rows returned by a query, as in this example to read 9 rows: SELECT * FROM emp WHERE ROWNUM < 10 Version 1.6 46 IBM Software Group IBM Information Management You can also use ROWNUM to assign unique values to each row of a table, as in this example: UPDATE tabx SET col1 = ROWNUM Oracle assigns a ROWNUM value to each row as it is retrieved, before the rows are sorted by an ORDER BY clause. Therefore, the final order of the rows is in ORDER BY order not in ROWNUM order. An ORDER BY clause does not affect the ROWNUM values except in the case where the ORDER BY clause causes Oracle to use an index. In that case, the ROWNUM values are set in the order the rows are retrieved using the index and hence in ORDER BY order. This order is different than if the rows were retrieved without the index. So in this case the ORDER BY can affect the ROWNUM values. Conditions which test for ROWNUM values greater than zero are always false. For example, this query returns no rows: SELECT * FROM employee WHERE ROWNUM > 1 The first row fetched is assigned a ROWNUM of 1, thus making the condition false. The second row to be fetched is now the first row and is also assigned a ROWNUM of 1 making the condition false. All rows subsequently fail to satisfy the condition, so no rows are returned. Although there is no ROWNUM equivalent in Informix, there are solutions. Beginning with IDS 7.3 and in Informix Dynamic Server 2000, Informix provides a feature which allows the user to limit the results of a query to the first N rows. This was implemented for TPC-D compliance and to support “rank” queries. Rank queries can be assembled by using FIRST N with the ORDER BY clause. Informix will return the top N rows according to some ordering criteria. Therefore the following Oracle SELECT utilizing ROWNUM: SELECT * FROM emp WHERE ROWNUM < 10 can be ported to Informix as follows: SELECT FIRST 9 * FROM emp Informix does not support SELECT FIRST N in complex cases as the following: Sub-queries (SELECT within a SELECT) View definitions (Cannot be used by a SELECT statement which references a view) UNION queries The behavior of SELECT FIRST N depends on the presence of an ORDER BY clause. If no order by clause is specified, the first N rows returned may be in any order. Examples are as follows: /* find 10 highest paid employees */ SELECT FIRST 10 name, salary FROM emp ORDER BY salary; /* find 10 highest salary values SELECT FIRST 10 DISTINCT salary FROM emp ORDER BY salary; */ Version 1.6 47 IBM Software Group IBM Information Management Informix supports column names of “first”. Thus, without a positive integer following the word “first”, the token is parsed as a column name rather than a keyword. The value of N may be 31 between 1 and (2 -1). When ROWNUM is used in a WHERE clause, the same results can be obtained through the use of the FIRST N and SKIP clauses, but if ROWNUM is used in the select list, the solution is a little more complicated. Simply stated, you can construct a query where the ROWNUM column in any row is the count of the rows that came before. This involves creating a correlated subquery where the subquery contains a self-join. This can be done for any table, or set of tables, which has a unique key defined on it. Here is an example using the customer table of the stores7 database. select counter.rownum, base.customer_num, base.fname, base.lname from -- There can be more than one table here, and the result -- set should thought of as a table (just not a base table). -- The result table has to be joined to itself in the -- subquery; so, if there are multiple tables in this FROM -- clause, the expansion can get messy. You have to have a -- set of columns that uniquely identify each row in the -- result table, and join across them. ROWID would -- likely work if the table is not fragmented, but if it is, -- there is no guarantee the final results would be accurate -- because ROWID is not guaranteed to be unique across -- fragments. customer base, (select count(*) as rownum, a.customer_num -- any unique key, possibly multiple -- columns from customer a, customer b where -- This is where the rownum is determined the relationship -- here has to match the outer order-by, where the rows -- are sorted, and has to create a unique sort order --- Any columns to be ordered by go here. -- Also, the unique key has to be used to keep rows -- that do not have a unique sort order from being merged (a.lname > b.lname) -- example, sorting by last name or (a.lname = b.lname and a.fname > b.fname) -- sorting by fname where lname is equal or (a.lname = b.lname and a.fname = b.fname and a.customer_num >= b.customer_num) -- sorting by unique key where all the -- other sort columns are equal -- The expansion above is logically the same as -a.lname || a.fname || to_char(a.customer_num) >= -b.lname || b.fname || to_char(b.customer_num) -- but it allows the server to utilize any indexes that -- may exist. group by -- unique key columns go here Version 1.6 48 IBM Software Group IBM Information Management a.customer_num ) counter where -- key columns in the base tables go here base.customer_num = counter.customer_num -- if you want to limit on rownum, it goes here -- and rownum <= 10 order by counter.rownum If using ROWNUM as an assignment, as in: UPDATE tabx SET col1 = ROWNUM The following Informix-ESQL/C pseudo-code example would have to be used: int counter = 0; EXEC SQL declare c1 cursor for SELECT col1, col2 INTO :col1, :col2 FROM table 1 WHERE col1 = "XX"; EXEC SQL open c1; while (SQLCODE == 0) { EXEC SQL fetch c1; counter++; fetched */ if (counter >= 5) { break; /* logic... */ } } /* End while */ /* End if */ /* Increment counter for each row Update using cursors There are slight differences between the ways Informix and Oracle process cursors. Unlike Oracle, in Informix, the WHERE CURRENT OF clause cannot be used to update tables in a SELECT statement joining multiple tables since the WHERE CURRENT OF clause requires a FOR UPDATE cursor and Informix does not support the FOR UPDATE clause in SELECT statements that join multiple tables. Therefore, in the multi-table join statements, the Oracle options FOR UPDATE and WHERE CURRENT OF should be removed and the update of the rows should be done using a separate UPDATE statement in the same cursor loop. Version 1.6 49 IBM Software Group IBM Information Management In Informix, a commit or rollback statement closes all cursors open in a given transaction boundary. See the Transaction Processing section in CHAPTER 5: Application Architecture for more information. In situations where the intention is to commit or rollback without closing the cursor, in other words, where the commit or rollback is executed within a cursor fetch loop, the cursor can be declared as a HOLD cursor. This requires that a BEGIN WORK is issued before the cursor loop starts. A HOLD cursor is not closed when a commit or rollback statement is issued. In Informix, the FOR UPDATE clause cannot be used when declaring a HOLD cursor. Thus, where an Oracle application updates a table from which it fetches data in the fetch loop or issues a commit work in the fetch loop, it must be converted to Informix. The cursor can be declared with HOLD and then an UPDATE statement can be issued without the WHERE CURRENT OF clause. In order to uniquely identify the row to be updated in the table, in other words, in order to simulate the WHERE CURRENT OF clause, the ROWID or unique primary key columns of the table(s) to be updated must also be fetched and the WHERE CURRENT OF clause can be replaced with WHERE ROWID = fetched rowid or WHERE unique primary key column(s) = fetched unique primary key column(s). A BEGIN WORK must follow immediately after a COMMIT WORK or ROLLBACK WORK in the fetch loops to simulate Oracle implied transaction boundaries. These transaction control statements work for either Informix logged databases or Informix ANSI mode databases, however, ANSI mode database processing with these statements will set certain warning flags after executing the BEGIN WORK statement and continue processing without failure. See the ANSI vs. Non-ANSI subsection in the Databases section in the chapter “Data Manipulation Language” for more information. System tables References to Oracle system tables should be replaced with corresponding references to Informix system tables. The information schema views provided by Informix can be used. These views are created after the database is installed. They are populated by data in the system catalog tables and are used for easier porting. Stored procedures Informix procedures and functions are considered to be user-defined routines (UDR). A UDR can be written in Informix’s Stored Procedure Language (SPL) or an external language, such as C or Java. A procedure is a routine written that does not return a value. A function is a routine written that returns a single value, a value with a complex data type, or multiple values. Informix SPL is an extension to Informix SQL and provides procedural flow control such as looping and branching. A SPL routine is a generic term that includes both SPL procedures and SPL functions. You use SQL and SPL statements to write an SPL routine. SPL statements can be used only inside the CREATE PROCEDURE, CREATE PROCEDURE FROM, CREATE FUNCTION, and CREATE FUNCTION FROM statements. SPL routines are parsed, optimized, and stored in the system catalog tables in executable format. Like Oracle, Informix stored procedures support input, output and input/output parameters and can be used in SQL statements wherever expressions are allowed. Size limit Informix stored procedures have a size limit of approximately 64K. Oracle stored procedures greater than 64K in size can be broken into smaller Informix stored procedures communicating with each other as though they were one by passing and returning the necessary values. Parameter limit Version 1.6 50 IBM Software Group IBM Information Management Informix has a limit of 341 parameters for each stored procedure. This limit also applies to UDRs written in the Java language. UDRs written in the C language can have no more than 102 parameters. You can define any number of SPL routine parameters, but the total length of all parameters passed to an SPL routine must be less than 64 kilobytes. No more than nine arguments to a UDR written in the Java language can be DECIMAL data types of SQL that the UDR declares as BigDecimal data types of the Java language. Any C language UDR that returns an opaque data type must specify opaque_type in the var binary declaration of the C host variable. Packages An Oracle package is a collection of functions, procedures, and variables that can be enabled and accessed as a unit. There is no direct equivalent within Informix, but there are ways of achieving similar capabilities. Within Oracle, a procedure within a package can be accessed through grammar such as package_name.procedure_name. If you create an owner or schema name within the Informix system the same as the package name, then it will be easier to migrate calls to that procedure because the grammar is effectively the same, although the meaning changes somewhat. Using this practice, a call to a migrated procedure would be owner.procedure_name, where the owner name is the same as the package name. Translating package names as owner names is a way to maintain the scoping of the objects within the package. Maintaining the aspect of being able to enable use of or upgrade the collection of package objects can be done by creating a datablade that contains all the objects that used to be part of the package. Datablade routines can be procedures or functions written in Stored Procedure Language (SPL), C, and Java. In addition, routines can be written in C++ on Windows platforms. Routines Oracle’s stored functions and stored procedures are similar in concept to Informix’s stored procedures. Oracle’s user defined database level functions such as stored functions, stored procedures all need to be converted to Informix stored procedures using the Informix SPL, C, or Java. Routines written in SPL have the advantage of being maintained and backed up with database backups. Routines written in C are fast, but are not automatically backed up when the database is and need to be compiled specifically for the operating system on which it will run. Routines written in Java also not backed up by the DBMS itself, but they are platform independent. However, probably as a result of having to convert between system memory where the objects have C structures and Java virtual machine memory where the objects have Java structures, the performance of Java routines can be slow compared with the C equivalent. All procedures written in an Oracle package body must be rewritten as separate Informix procedures. When rewriting package bodies into separate procedures, Oracle’s variable declarations can be replaced at the package level (global) with Informix’s global variables. The Informix global variables must be defined in the first stored procedure of the logical group. Exceptions The method of handling exceptions is different between Informix and Oracle. Oracle has many predefined and user defined exception labels such as CURSOR_ALREADY_OPEN, NO_DATA_FOUND, ZERO_DIVIDE, and so on. The labels can be used in the logic to identify errors. Only one exception check is allowed per BEGIN and END block using the EXCEPTION construct, and it is normally placed just before the END statement. Informix also supports predefined and user defined exceptions, however, they are represented numerically, not with labels. In Informix, all the exceptions checked in the stored procedure control blocks, delimited with BEGIN and END statements, must be declared explicitly at the top of each Version 1.6 51 IBM Software Group IBM Information Management control block with the Informix EXCEPTION construct. Therefore, Oracle procedure code must be restructured to manipulate these exceptions. In Oracle, an exception can be raised globally and acted upon globally. In Informix, the scope of an exception mechanism is always restricted to the block in which it is located. This leads to the addition of multiple Informix exception handling mechanisms for each Oracle exception raised as well as a redesign of the overall stored procedure to a more blocked format. Error handling In Oracle, errors are processed as part of a function call return value. In Informix, errors are retrieved as a separate function call that has multiple structures that have to be created in order to retrieve error and status. The SQLCA structure is not fully available in Informix stored procedures. It is not possible to check the SQLCODE variable in the stored procedure body. Instead, the function DBINFO can be used to extract the value of two members of the SQLCA structure: sqlca.sqlerrd1 and sqlca.sqlerrd2. This function should be used inside the FOREACH construct while the cursor is open. Once the cursor is closed, the DBINFO function cannot return proper values. The sqlca.sqlerrd1 option returns different values depending on the type of SQL statement. For INSERT statements, if the table contains a serial column, then the sqlca.sqlerrd1 option will return the serial value of the last row inserted into the table. For SELECT, DELETE, and UPDATE statements the sqlca.sqlerrd1 option will return the number of rows processed by the query. In these cases, upon each pass through the FOREACH loop, sqlca.sqlerrd1 is the same value: the number of rows the FOREACH loop will process. The sqlca.sqlerrd1 value can still be interrogated during each pass through the FOREACH loop. The sqlca.sqlerrd2 option returns the number of rows processed by a SELECT, INSERT, UPDATE, DELETE, or EXECUTE PROCEDURE statement. It should not be interrogated until after the cursor has finished processing. In other words, within the FOREACH loop, the sqlca.sqlerrd2 value can be moved to a variable which is then interrogated outside of the FOREACH loop. Cursors Unlike Informix, in Oracle, cursors can be global to a package. In Informix, Oracle’s global cursors can be replaced with temporary tables. A temporary table can be created and populated in place of where the cursor is opened. The temporary table can then be processed the same way the cursor is processed. Finally, the temporary table should be dropped in place of where the cursor is closed. Cursors are explicitly declared, opened and fetched in Oracle stored procedures. In Informix, cursors do not need to be explicitly declared, opened and fetched in stored procedures. Instead, Oracle stored procedure cursors can be replaced with Informix’s FOREACH construct. The Oracle cursor name should be used in the FOREACH statement, for example: FOREACH cursor_name SELECT … END FOREACH. In order to update a cursor row in Oracle, the cursor must be declared with the FOR UPDATE clause. In Informix, if the SELECT statement in the FOREACH construct is not a multi-table join, then each fetched row can be updated using the UPDATE <table_name> WHERE CURRENT OF <cursor_name> statement. See the Update Using Cursors section in “Data Manipulation Language” for more information. Informix does not support the SELECT FOR UPDATE statement in a stored procedure. Therefore, the SELECT FOR UPDATE clause cannot be used in the FOREACH construct. Version 1.6 52 IBM Software Group IBM Information Management Variable declaration and assignment Oracle allows implicit variable definitions within a FOR loop. The variables are defined using an assignment operator and a column name. For example: out_customer_name := customer.name; The variable out_customer_name is defined as the same type as the column name in the customer table. Informix requires that all variables be defined at the beginning of each stored procedure control block. For example: DEFINE out_customer_name LIKE customer.name; DEFINE counter INTEGER; Therefore, Oracle’s implicit variable declaration must be replaced with Informix’s explicit variable declaration before the control block. Additionally, assignment operations will need to be ported. Oracle uses a colon and an equal sign (:=) for assignment while Informix utilizes the LET keyword in combination with an equal sign (=) (i.e. LET var = 1). Therefore, assignment statements must be modified with the LET keyword and an equal sign. Boolean Oracle’s BOOLEAN variable type can be replaced with Informix’s BOOLEAN data type. Binary data types Binary data types are very different between Oracle and Informix. In Oracle, the binary type is defined as a type RAW and is explicitly defined with a maximum length as part of the declaration. In Informix, the binary type is defined as REFERENCES BYTE, which deals only with pointers to BLOBs. It implicitly defines a maximum length as part of the function call in which the pointer to the BLOB and maximum length (as used in the function call) was passed as values. As a result the binary type cannot be assigned a binary value in a stored procedure nor can it be inserted into a table via the SQL INSERT statement in a stored procedure. These operations that initialize a binary type must be performed outside of stored procedures. Dynamic SQL Unlike Oracle, Informix does not support dynamic SQL within the Stored Procedure Language. In other words, a statement such as SELECT variable_name FROM variable_name WHERE variable_name is not supported. This kind of processing can be achieved in Informix within an embedded SQL program. This requires that either the Oracle stored procedure be converted to an embedded SQL program, or just the Dynamic SQL portion be placed in an embedded SQL program and then called from within the stored procedure. The latter option will have performance implications since calling an embedded SQL program from a stored procedure requires an operating system call. Another option is to use the Informix EXEC Bladelet. The EXEC Bladelet consists of user-defined functions that take a SQL query as an argument, execute it, and return a result (the format of which varies depending on the function and the kind of query). The EXEC Bladelet functions can handle most Data Definition Language (DDL) statements, and all Data Manipulation Language (DML) queries. Further details can be found in Chapter 7: Using Bladelets. Compiler Unlike Oracle, the Informix compiler was developed with the concept that developers can create stored procedures independently of the database. Therefore, Informix’s stored procedure Version 1.6 53 IBM Software Group IBM Information Management compiler has a limited set of errors that it checks compared to Oracle’s . For example, the Informix stored procedure compiler does not validate database object names such as table, column, and view names. Therefore, database object naming errors will occur at runtime, not compile time. Additionally, the error messages generated from the compiler are not very specific. Identifying a problem in a stored procedure may require breaking it apart and recompiling the pieces until the problem is found. This may impact the time necessary to unit test the stored procedures. Triggers Oracle supports the use of multiple trigger events, for example, insert, update, and delete within the same trigger. Informix only supports one trigger event per trigger. In Informix, the Oracle trigger body in this example must be copied into three different triggers each with only one event. Informix triggers have some constraints; Oracle’s do not. In Informix, if a trigger is fired due to inserting a row into a table, a trigger can only act on any rows or columns within that table using the EXECUTE PROCEDURE … INTO syntax. Similarly, if a trigger is fired due to updating a row, then the trigger can only act on the column that caused the trigger to fire using the EXECUTE PROCEDURE … INTO syntax. Like Oracle, if a trigger is fired due to deleting a row, there are no restrictions. Prior to IDS 7.3 when a trigger was fired on an INSERT/UPDATE action, no modification could be done to the columns (in the row) which just fired the trigger. The reentrant UPDATE/INSERT triggers feature introduced with IDS 11.10 enables users to update the triggering columns by using the EXECUTE PROCEDURE ... INTO syntax. Additionally, the feature enforce that this action will not cause any additional triggers to be fired (same or different trigger) causing a cascading of triggers and potentially an infinite loop. The following example demonstrates how the reentrant UPDATE/INSERT trigger feature is used: create table foo ( x int default NULL, y int default NULL, z int default NULL ); create procedure reentrant() returning int; return 56; end procedure; create trigger reentrant_trigger insert on foo for each row ( execute procedure reentrant() into z ); insert into foo values ( 1, 2, 3 ); The following SELECT will result in the following returned data: select * from foo; x 1 y 2 z 56 Version 1.6 54 IBM Software Group IBM Information Management Oracle allows SQL statements, logical statements, and stored procedure calls within a trigger body. Informix allows only SQL statements and stored procedure calls within a trigger body. Oracle triggers containing logical statements must be converted to Informix by moving their contents into a stored procedure. The stored procedure should then be executed from the trigger. Constraints Constraints in both Oracle and Informix can be enabled and disabled within an application transaction, however, there are behavioral differences. In Oracle, the application is always executing within a transaction. In Informix, transaction boundaries are explicitly declared and care should be taken to ensure that this functionality is performed within the boundaries of a transaction. See the Transaction Processing section in CHAPTER 5: Application Architecture for more information. The constraint syntax available in Informix is: SET {CONSTRAINTS/INDEXES/TRIGGERS} FOR table_name {ENABLED|DISABLED} SET CONSTRAINTS comma_separated_constraint_names {ENABLED|DISABLED} SET INDEXES comma_separated_index_names {ENABLED|DISABLED} SET TRIGGERS comma_triggers_constraint_names {ENABLED|DISABLED} SET CONSTRAINTS {ALL/constraint_name} {IMMEDIATE|DEFERRED} The first four statements must be executed with DBA privileges if placed in the application. Therefore, care should be taken when using them. Note that the last statement contains a DEFERRED clause. This is the statement used if the users of the application do not have DBA privileges. When the constraint is set with the DEFERRED clause, the specified constraints are not checked until the transaction is committed. If a constraint violation occurs while the transaction is being committed, the transaction is rolled back. DUAL table Oracle’s DUAL table is a system table with only one row containing one column named DUMMY. This table is used to complete the syntax of an SQL statement that does not involve an existing table such as: A SELECT statement which assigns some constant value (which can only be derived in an SQL statement) to a host variable: SELECT TO_CHAR(SYSDATE) INTO :date_var FROM DUAL; A SELECT statement which assigns a value of another variable to a variable: SELECT :source_var INTO :dest_var FROM DUAL; A SELECT statement which unions application host variable values with another SELECT statement to return a result containing a row of application variable values: SELECT col1, col2, col3 FROM table_name UNION SELECT :host_var1, :host_var2, :host_var3 FROM DUAL; Converting Oracle’s DUAL table functionality to Informix depends on the way it is used. Converting statements that just assign constants is as simple as replacing the DUAL statement with an Informix assignment statement. The solutions below apply to Informix versions prior to 11. At 11 and above, the concept of a DUAL table, as well as sysdate, is supported: Version 1.6 55 IBM Software Group IBM Information Management SELECT sysdate FROM sysmaster:sysdual; If you want a dual table in your local database, then create the following synonym. The sysmaster:sysdual table is much faster than a physical table in your database because it is an inmemory pseudo table. CREATE SYNONYM dual FOR sysmaster:sysdual; If you are restricted to using a version of Informix prior to 11, then the following solutions can be used. A dummy table can be created to mimic the behavior of simple SELECT INTO statements used to assign values to variables. CREATE TABLE dual (dummy VARCHAR(1)); INSERT INTO dual VALUES(‘X’); An alternate solution to widespread use of the DUAL table could be creating a permanent view that is generally accessible. CREATE VIEW dual (d) as SELECT 'X' FROM systables WHERE tabid = 1; The exact definition could be used or modified as needed, but this would solve the problem of having DUAL be permanent, usable by anyone, and not modifiable. Version 1.6 56 IBM Software Group IBM Information Management CHAPTER 5: Embedded SQL Oracle’s embedded SQL products are Pro*C and Pro*COBOL. Informix’s corresponding embedded SQL products are ESQL/C and ESQL/COBOL. Host variables Declaration override The Oracle EXEC SQL VAR declarative statement (host variable equivalency of Oracle external data types by overriding the default assignment) can be converted to an Informix explicit declaration statement. For example: Oracle: char char_var[20]; EXEC SQL VAR char_var IS STRING(11); Informix: char char_var[11]; Note that the size and type used in the EXEC SQL VAR section is the final information required to declare the host variable in Informix. Arrays Oracle arrays are not subscripted in SQL statements since it is not required to process the host arrays in a loop in the SQL statement. In Informix, subscripting is mandatory. In Oracle, if an array is defined of size 200 then an SQL statement would be: EXEC SQL FETCH CURSOR-NAME INTO :VAR-A, :VAR-B, :VAR-C END-EXEC -orEXEC SQL SELECT COL1, COL2, COL3 INTO :VAR-A, :VAR-B, :VAR-C FROM TABLE1 WHERE COL2 > 100 END-EXEC where VAR-A, VAR-B and VAR-C are each an array of say 200. Version 1.6 57 IBM Software Group IBM Information Management In Informix, the first example would be converted to: PERFORM VARYING LOOP-CTR FROM 1 BY 1 UNTIL LOOP-CTR IS GREATER THAN 200 EXEC SQL FETCH CURSOR-NAME INTO :VAR-A(LOOP-CTR), :VAR-B(LOOP-CTR), :VAR-C(LOOP-CTR) END-EXEC IF SQLCODE = 100 GO TO EXIT-PERFORM END-IF END-PERFORM EXIT-PERFORM. For the second Oracle example given above, a cursor must be declared in Informix, opened, and fetched for 200 rows or less (less, because there may be less than 200 qualifying rows) into the host array and then should be closed. If the number of qualifying rows is more than 200, then the extra rows must be ignored to duplicate the Oracle functionality. But for the first example, each fetch in Oracle will return the next 200 or less number of rows. Therefore, each Oracle fetch needs to be replaced by another call to the Informix fetch loop. Oracle also uses this method for inserts, deletes and updates. These array techniques are mainly used in batch SQL processing. Formatting In Informix, when a DECIMAL, MONEY, DATE, or DATETIME value is selected into a host variable of the same data type, the value must be formatted using the available Informix formatting routines before they can be printed or displayed. For example: … dec_t dec_host_var; char formatted_dec[40]; EXEC SQL SELECT DEC_COLUMN INTO :dec_host_var FROM TABLE_NAME; rfmtdec(&dec_host_var, “&&&&&&&.&&&”, formatted_dec); printf(“The Decimal Value is %s\n”, formatted_dec); … Version 1.6 58 IBM Software Group IBM Information Management Informix supplies many formatting functions and formatting strings. The names of these functions are different in Informix ESQL/C and ESQL/COBOL, but the functionality is the same. Refer to the appropriate Informix API Programmer’s Manual (Informix-ESQL/C Programmer’s Manual or Informix-ESQL/COBOL Programmer’s Manual) for more information. SQL structures SQLCA Oracle’s SQLCA structure is different from that of Informix., However, the components of both structures are logically the same. Oracle’s SQLCA components can be replaced with the corresponding Informix components. Oracle’s SQLCA structure: SQLCAID SQLCABC SQLCODE SQLERRM SQLERRML SQLERRMC SQLERRP SQLERRD SQLERRD(0) SQLERRD(1) SQLERRD(2) SQLERRD(3) SQLERRD(4) SQLERRD(5) SQLWARN SQLWARN(0) SQLWARN(1) SQLWARN(2) SQLWARN(3) SQLWARN(4) SQLWARN(5) SQLWARN(6) SQLWARN(7) SQLTEXT string containing “SQLCA” length of SQLCA data structure Oracle error message code sub-record for error message length of error message text of error message reserved for future use array of six-integer status codes reserved for future use reserved for future use number of rows processed reserved for future use parse error offset reserved for future use array of eight warning flags another warning flag set character string truncated no longer in use SELECT list not equal to INTO list DELETE or UPDATE without WHERE clause reserved for future use no longer in use no longer in use reserved for future use Version 1.6 59 IBM Software Group IBM Information Management Informix’s SQLCA structure: SQLCODE SQLERRM SQLERRP SQLERRD SQLERRD(0) SQLERRD(1) SQLERRD(2) SQLERRD(3) SQLERRD(4) SQLERRD(5) SQLWARN SQLWARN0 SQLWARN1 revoked SQLWARN2 SQLWARN3 end SQLWARN4 SQLWARN5 SQLWARN6 SQLWARN7 Informix error code error message parameter reserved for future use array of six-integer status codes estimated number of rows returned serial value of INSERT or ISAM error code number of rows processed estimated cost parse error offset ROWID after INSERT structure of eight warning flags another warning flag set character string truncated or no privileges NULL value returned or ANSI database SELECT list not equal to INTO list or turbo backDELETE or UPDATE without WHERE clause if non-ANSI statement if server is in data replication secondary mode reserved In order to get Oracle error messages, an application normally uses the SQLCA element SQLERRMC. The Informix equivalent is SQLERRM. On the HP/UX and Solaris platforms, the Informix function rgetmsg should be used instead of the element SQLERRM. At the time of this writing, using SQLERRM would cause an error on the HP/UX and Solaris platforms. SQLDA The implementation of Oracle’s SQLDA structure is very different from that of Informix. Significant changes are necessary to convert Oracle’s functionality to Informix . Oracle’s sqlald function can be replaced with Informix’s ALLOCATE DESCRIPTOR statement. Since Oracle’s and Informix’s SQLDA structures are so different, Oracle’s SQLDA logic must be rewritten with the more powerful Informix SQLDA logic or alternatively, the Informix system-descriptor area logic. Oracle’s SQLDA Structure: N maximum number of SELECT list items or placeholders V pointer to array of address of SELECT list items or bind variables L pointer to array of lengths of SELECT list items or bind variables T pointer to array of data types of SELECT list items or bind variables I pointer to array of addresses of indicator variable values F actual number of SELECT list items or placeholders S pointer to array of addresses of SELECT list items or placeholder names M pointer to array of max lengths of SELECT list items or placeholder names C pointer to array of current lengths of SELECT list items or placeholder names X pointer to array of addresses of indicator variable names Y pointer to array of maximum lengths of indicator variable names Z pointer to array of current lengths of indicator variable names Version 1.6 60 IBM Software Group IBM Information Management Informix’s SQLDA Structure: SQLD SQLVAR SQLDATA SQLTYPE SQLLEN SQLIND SQLNAME SQLFORMAT SQLIDATA SQLITYPE SQLILEN count of the following SQLVAR members pointer to array of structures pointer to actual data data type of placeholder or SELECT item length of the placeholder or SELECT item pointer to the indicator pointer to placeholder or SELECT item name reserved indicator data pointer indicator variable type length of indicator variable If an Oracle application uses the simple dynamic SQL method using macros only, not the SQLDA structure directly, the value indicators/placeholders can be replaced with a ‘?’ in Informix . For example: Oracle: sprintf(sqlstring, “INSERT INTO TABLE1 VALUES (:v1, :v2)”); Informix: sprintf(sqlstring, “INSERT INTO TABLE1 VALUES (?, ?)”); ORACA Oracle’s ORACA structure is not supported by Informix. This structure is used to handle Oracle specific communication and more runtime information than SQLCA. Oracle statements containing references to this structure should be removed from the application. Pre-compiler options The Oracle options beginning with the EXEC ORACLE OPTION statement can be removed from the application. The options set after this statement do not affect the application logic. They may affect some application performance only. Embedded PL/SQL Oracle allows procedure language (PL/SQL) blocks inside Pro*C and Pro*COBOL code delimited by the EXEC SQL EXECUTE and END-EXEC statements. In Informix, these blocks must be replaced with the equivalent ESQL/C or ESQL/COBOL code. Oracle Call Interface Oracle call interface (OCI) is the base layer for a variety of other application programming interfaces such as JDBC-OCI, Oracle Precompilers, and Oracle ODBC. It is also used directly by customers who are willing to develop their code using a lower level, proprietary API. From a high level, it can be thought of as if IBM Informix had standardized and published the function calls in the ESQL/C library. Although some customers have reverse engineered these calls and developed applications using them, this is generally discouraged because there is no guarantee that the library of calls will not change. There is no direct equivalent to this Oracle API in Informix. Migrating applications written using Oracle Call Interface (OCI) will require rewriting the code from scratch. OCI is native to the C language and so it should be less effort to migrate the application to an Informix API that is also native to C. This could be ESQL/C or CLI. The following information explains differences between Oracle OCI and Informix ODBC. Version 1.6 61 IBM Software Group IBM Information Management The following table is an approximate mapping of the differences between the Informix CLI and Oracle OCI function calls in which there is not a direct one-to-one mapping: Oracle HAD, LDA, CDA OLOG, OOPEN OPARSE, OBNDRV, OEXEC ODEFIN, OFETCH OCOM, OROL OLOGOF, OCLOSE Informix HENV, HDBC, HSTMT SQLConnect, SQLAllocEnv, SQLAllocConnect, SQLAllocStmt SQLPrepare, SQLBindParameter, SQLExecute SQLBindCol, SQLFetch SQLTransact SQLDisconnect, SQLFreeStmt, SQLFreeConnect, SQLFreeEnv Buffer Areas Connect and allocate resources Update, delete, insert, select Select Commit, Rollback Disconnect Database library calls One of the first main differences between Informix CLI and Oracle OCI calls is the overall data structures and mechanisms used to process database library calls. Oracle uses direct connections to the database server. Informix uses an ODBC connection to the database server. As a result, the Informix calls will be implemented as ODBC SQL calls utilizing three tiers of increasing SQL levels of complexity. Global area processing Another main difference between Informix CLI and Oracle OCI is Informix’s use of pointers to three global areas versus Oracle’s use of pointers to three different, but similar, global areas. Hence, the Oracle OCI calls that utilize HAD (Handle Data Area), LDA (Logon Data Area) and CDA (Cursor Data Area) pointers must be translated over to Informix CLI calls that utilize HENV (Environment Area), HDBC (Database Connection Area) and HSTMT (Statement Area) pointers, respectively, in conjunction with an ODBC interface. Fetch cycle Another difference between Informix CLI and Oracle OCI calls is that the overall fetch cycle is different for each and must be modified accordingly. In Oracle, the fetch cycle for selecting multiple rows utilizing a cursor area involves parsing, binding, defining, fetching, and then executing into a cursor data area. These must be translated to a cycle that involves preparing, binding, executing, binding, and then fetching via the “For Each” cycle. RAW binary data type processing RAW binary data types in Oracle will need to be translated to a pointer-to-BLOB REFERENCES BYTE type in Informix. See the Binary Data Types subsection of the Stored Procedures section in CHAPTER 3: Parameter bindings In Oracle, multiple output bindings of parameters are declared as type “OUTPUT” in the ODEFIN() calls. In Informix, convert the calls to Informix SQLBindCol() calls and bind the output parameters as columns. Differences also exist between Informix CLI and Oracle OCI in the use of input/output bindings. In Oracle, these bindings are declared as type “INPUT/OUTPUT” in the ODEFIN() calls . In Informix, a “round robin” approach is used using three variables in the following manner: A=B, B=C, C=A. A variable (A) is declared before the SQL bindings as an input variable. The function is called Version 1.6 62 IBM Software Group IBM Information Management using a second input variable (B) that is set to the first input variable (A). The function returns a third variable (C) that is bound as an output column. Finally, the first variable (A) is now set to the returned third variable (C) that made the first variable (A) appear as an “input/output” variable to the rest of the program. Embedded SQL for C VARCHAR Oracle’s Pro*C VARCHAR data type is expanded after pre-compiling into a structure with two elements, one is unsigned short len and the other is unsigned char arr[size]. The name of the expanded structure is the same name as the VARCHAR variable and size is replaced with the size of the VARCHAR variable. In Informix, the VARCHAR data type is just a synonym for the C language’s char data type. Therefore, statements that use variable_name.len can be removed from the application and all occurrences of variable_name.arr can be replaced with variable_name. For example: Oracle: VARCHAR vName[40]; strcpy(vName.arr, “Company Name”); vName.len = strlen(vName.arr); Informix: VARCHAR vName[40]; strcpy(vName, “Company Name”); Host variables The Oracle statement EXEC SQL TYPE is synonymous with the C language’s typedef declarative instruction. It can be easily replaced with the Informix declarative instruction typedef. Embedded SQL for COBOL VARCHAR Oracle’s Pro*COBOL character host variable declaration statement’s VARYING clause is expanded after pre-compiling into a group with two elements, one is variable-name-LEN PIC S9(4) COMP and the other is variable-name-ARR PIC X(size). The group name is the same as the host variable name and size is replaced with the size of the VARYING variable. Informix does not support this concept. Therefore, statements that use variable-name-LEN must be removed from the application and all occurrences of variable-name-ARR must be replaced with variable-name. Also, the VARYING clause must be removed from the declarative statement. For example: Oracle: 05 vName PIC X(40) VARYING. … MOVE “Company Name” TO vName-ARR. MOVE 12 TO vName-LEN. Informix: 05 vName PIC X(40). … MOVE “Company Name” TO vName. Version 1.6 63 IBM Software Group IBM Information Management Level 88 COBOL’s level 88 functionality is not supported within an Informix ESQL/COBOL host variable declaration section in versions prior to 7.2. Level 88 functionality is supported in Informix ESQL/COBOL 7.2. SQLDA In Informix ESQL/COBOL, the SQLDA structure is not available. The Informix macros, such as ALLOCATE DESCRIPTOR, DESCRIBE, GET DESCRIPTOR, and SET DESCRIPTOR must be used to retrieve and manipulate the SQLDA data. REDEFINES Oracle supports the use of the REDEFINES clause at the elementary level in the host variable declaration section. In other words, the redefined item cannot be a group item but the redefining item may be a group item. Informix does not support the use of the REDEFINES clause in the host variable declaration section in versions prior to 7.2. The REDEFINES clause is supported in Informix ESQL/COBOL 7.2. Tips If the Informix pre-compiler finds an SQL statement within an IF [ELSE IF … ] END-IF or EVALUATE END-EVALUATE or PERFORM END-PERFORM block, then the pre-compiler adds a period after the generated COBOL statements regardless of whether there is a period at the end of the SQL statement. This is a violation of COBOL rules for such blocks and the code may cause syntax errors during compilation, or may behave unexpectedly. To avoid these problems, each SQL statement group should be moved to a new paragraph and these paragraphs should be performed from the block from which they were moved. ODBC Although the Open Database Connectivity (ODBC) API is designed to make it possible to write and compile an application once and use it with any ODBC driver on the same platform, there are frequently differences between the underlying DBMSs that can expose problems to the applications. For instance, there can be differences between how unspecified parts of a date-time value are filled. Most of these kinds of differences that get exposed through ODBC have their origins in the differences between the DBMSs themselves. There are also cases where there are gaps in the ODBC standard; for example, there is a standard ODBC call that allows the application to ask the driver if it can support scrollable cursors, and there is another call to ask if it can support updateable cursors. There is no call to ask if it can support both at the same time. With Informix, you can have a scrollable cursor and you can have an updateable cursor, but you can not have one cursor that is both scrollable and updateable. A more complete description of ODBC application migration issues is beyond the scope of this article. Version 1.6 64 IBM Software Group IBM Information Management CHAPTER 6: Application Architecture Transaction processing Transaction boundaries are defined differently in Oracle and Informix. Oracle’s transactions begin implicitly in one of two ways, either by issuing a CONNECT statement or issuing a COMMIT or ROLLBACK statement. A CONNECT statement marks the beginning of a transaction and a COMMIT or ROLLBACK statement commits or rolls back the previous transaction, respectively, and then implicitly begins a new transaction. CONNECT statements are usually issued once at the beginning of a program; however, they may be nested or issued more than once serially to connect to the same or a different database. When using more than one CONNECT statement within a program, each CONNECT statement must be named. This marks the beginning of named transactions. Every SQL statement within a named transaction must also be named with the same transaction name as the CONNECT statement at where the SQL is to be executed. SQL is named using the EXEC SQL statement’s AT clause. Informix’s transaction boundaries are explicitly marked with the reserved words BEGIN WORK and COMMIT WORK or ROLLBACK WORK. Note that once a transaction has begun for a given connection, it stays open until a COMMIT or ROLLBACK is executed. In other words, if an ESQL program begins a transaction and then calls a stored procedure, the stored procedure executes within the same transaction. If the stored procedure issues a COMMIT, then the whole transaction is committed and the calling ESQL program should not issue a COMMIT or ROLLBACK. Informix transactions can be written to behave the same way as Oracle’s. To simulate Oracle’s CONNECT statement behavior, an Informix CONNECT should also be issued followed by a BEGIN WORK statement. If more than one CONNECT statement is used in a program, Informix also requires that the CONNECT and SQL statements be named. To simulate Oracle’s COMMIT and ROLLBACK statement behavior, an Informix COMMIT WORK or ROLLBACK WORK statement should be issued followed by a BEGIN WORK statement. Nested transactions can be simulated in Informix with the CONNECT statement and naming the transactions. Then the Informix SET CONNECTION connection_name statement can be used to switch between transactions. Committing or rolling back each named transaction does not commit or roll back other active transactions. The Oracle statement EXEC SQL {COMMIT/ROLLBACK} RELEASE should be replaced with the Informix statements EXEC SQL {COMMIT/ROLLBACK} [WORK] followed by EXEC SQL DISCONNECT CURRENT. In the following examples, ws-dbenviron is the database name, ws-connect-nm is the user name and ws-identity-nm is the password set in the program, enp and connection-name are the connection names. For Informix, the user name and password information should correspond to the entries in the /etc/passwd file on the UNIX system. See the User Authentication section later in this chapter for more information. The default server name is automatically obtained from the environment variable INFORMIXSERVER. The following examples illustrate how Oracle and Informix implement the CONNECT functionality. Version 1.6 65 IBM Software Group IBM Information Management Simple Connect Oracle: CONNECT :ws-connect-nm IDENTIFIED BY :ws-identity-nm Informix: CONNECT :ws-dbenviron USER :ws-connect-nm USING :ws-identity-nm Named Connect with Literal Oracle: DECLARE enp DATABASE … CONNECT :ws-connect-nm IDENTIFIED BY :ws-identity-nm AT enp USING :ws-dbenviron … AT enp SELECT col1, col2 INTO :var1, :var2 FROM table_name Informix: CONNECT TO :ws-dbenviron AS “enp” USER :ws-connect-nm USING :ws-identity-nm WITH CONCURRENT TRANSACTION … SET CONNECTION “enp” … SELECT col1, col2 INTO :var1, :var2 FROM table_name Named Connect with Variable Oracle: CONNECT :ws-connect-nm IDENTIFIED BY :ws-identity-nm AT :connection-name USING :ws-dbenviron … AT :connection-name SELECT col1, col2 INTO :var1, :var2 FROM table_name Informix: CONNECT TO :ws-dbenviron AS :connection-name USER :ws-connect-nm USING :ws-identity-nm WITH CONCURRENT TRANSACTION … SET CONNECTION :connection-name … SELECT col1, col2 INTO :var1, :var2 FROM table_name Autonomous Transaction An autonomous transaction is an independent transaction that is initiated by another transaction (the parent transaction). An autonomous transaction can modify data and commit or rollback independent of the state of the parent transaction. The autonomous transaction must commit or roll back before the autonomous transaction is ended and the parent transaction continues. Version 1.6 66 IBM Software Group IBM Information Management An autonomous transactions has been available since the release of Oracle 8i. An autonomous transaction is defined in the declaration of a PL/SQL block. This can be an anonymous block, function, procedure, object method or trigger.This is done by adding the statement 'PRAGMA AUTONOMOUS_TRANSACTION;' anywhere in the declaration block. There isn't much involved in defining a PL/SQL block as an autonomous transaction. You simply include the following statement in your declaration section: PRAGMA AUTONOMOUS_TRANSACTION; Sample code: PROCEDURE test_autonomous IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN insert .... commit; END test_autonomous; Autonomous transactions can be used for logging in the database independent of the rollback/commit of the parent transaction. Savepoints Oracle allows savepoints within its PL/SQL. For example, a PL/SQL block can be structured so that SAVEPOINT A is issued upon entry and SAVEPOINT B is issued after some processing. Then, after some more processing, a COMMIT or ROLLBACK can be issued against all the logic since the last COMMIT point, which is before the PL/SQL block, or up to any of the savepoints, by naming the COMMIT or ROLLBACK with the savepoint name. In other words, a savepoint can be issued at the beginning of a stored procedure and then only the stored procedure logic can be committed without committing the changes pending before the stored procedure was executed. Those changes can be committed or rolled back once processing is returned from the stored procedure. Informix does not support savepoint processing within a stored procedure. Concurrency Database concurrency can be managed through the combination of two configurable settings; one specifies the lock granularity on database objects while the other influences how the application behaves in regards to the presence and placement of locks using a transaction isolation level. Row Level Locking Oracle and Informix implement locking differently on database objects. Oracle defaults to row level locking whereas Informix defaults to page level. In most cases, row level locking is preferred to improve concurrency. Informix supports row level locking however it must be either explicitly requested, specified by an environment variable or permanently changed in the instance’s configuration. Having three methods for establishing the lock level there is an order of precedence: 1. To be explicitly requested the CREATE TABLE statement must include the specification of the LOCK MODE attribute: CREATE TABLE emp ( name CHAR(40), salary MONEY, ssn LVARCHAR(64) ) LOCK MODE ROW; Version 1.6 67 IBM Software Group IBM Information Management 2. The environment variable IFX_DEF_TABLE_LOCKMODE will establish the default for CREATE TABLE statements issued without the LOCK MODE attribute and will override the Instance’s configuration setting. The variable setting on the client will override the server environment setting. set IFX_DEF_TABLE_LOCKMODE=ROW -orexport IFX_DEF_TABLE_LOCKMODE=ROW 3. Informix allows the instance’s lock level default to be changed using a configuration parameter. Row level locking can be as the default by adding the parameter DEF_TABLE_LOCKMODE to the instance’s ONCONFIG file. This parameter may not be found in the existing file and if not present can be added using an editor and establish a new default setting of ROW: DEF_TABLE_LOCKMODE ROW None of the steps will apply a change to existing tables only establish the lock level for the CREATE TABLE statement. Transaction Isolation Level Oracle databases incorporate a feature known as “read consistency” which allows a query return a result based on the state of the data when the query started, regardless of other processes updating or deleting the same data while the query is running. Informix facilitates this type of behavior differently, by providing control of the application’s placement of locks as well as how it reacts to those placed by other processes through the use of the transaction’s isolation level. If Oracle’s technique of “readers not blocking writers” is a requirement, then in many circumstance Informix’s DIRTY READ isolation level can be used. The readers will not block the writers with dirty read processing; however, the query result may contain updates performed while the query was running which may be uncommitted. If the emulation of Oracle’s “read consistency” is desired, the LAST COMMITTED option of Informix’s COMMITTED READ isolation level can reduce the risk of locking conflicts when two or more sessions attempt to access the same row in a table whose locking granularity is row-level. When an application attempts to read a row on which another session holds an exclusive lock, these keywords instruct the database server to return the most recently committed version of the row, rather than wait for the lock to be released. This isolation level attribute could be preferable to executing queries using DIRTY READ since it will return only the most recently committed versions of the data. The LAST COMMITTED option can be enabled in the following ways: 1. Setting the USELASTCOMMITTED parameter in the Instance’s configuration (ONCONFIG) will apply this feature as the default isolation level to all database transactions. The parameter‘s values will designate the scope of applying the LAST COMMITED option to either ALL, NONE or only COMMITED READ and DIRTY READ isolation levels. USELASTCOMMITTED [ALL | NONE | COMMITTED READ | DIRTY READ] 2. The SET ENVIRONMENT statement can override the USELASTCOMMITTED configuration parameter setting for the duration of the current session. The same semantics apply as the parameter within the ONCONFIG file with the setting value enclosed in quotes (‘). SET ENVIRONMENT USELASTCOMMITTED ‘ALL’; Version 1.6 68 IBM Software Group IBM Information Management 3. The SET ISOLATION statement can be used to override the instance configuration and session environment setting. The SET ISOLATION statement is an Informix extension to the ANSI SQL-92 standard and provides the same functionality as the ISO/ANSIcompliant SET TRANSACTION statement. There is no support for the LAST COMMITTED option with the SET TRANSACTION statement. SET TRANSACTION complies with ANSI SQL-92 and only ANSI supported isolation levels are supported. SET ISOLATION TO COMMITTED READ LAST COMMITTED; 4. PREPARE statements in ODBC/JDBC applications This feature is primarily documented in the Guide to SQL: Syntax, Guide to SQL: Reference, Administrator's Reference, and Administrator's Guide. Version 1.6 69 IBM Software Group IBM Information Management CHAPTER 7: Security User authentication In Oracle security information is stored in the database. Up until recently Informix has used only the operating system security subsystem to check security. For security purposes Oracle connects to the server and passes the user ID and password information to the database for verification. Operating system security Informix does not store user ID and password information in the database. Instead they are validated against the operating system’s security mechanism on the server, for example the /etc/passwd file in UNIX. Therefore, either every database user must have an entry in the server’s password file, or the application must handle a level of security where user IDs are converted into a group id based on database permissions and only those group IDs are entered into the password file. Note that if the second alternative is chosen, then auditing capabilities based on user ID are lost. Also, if the application performs security functions, then those functions are not applicable to third party applications, such as report writers, application development tools, and so on, when they access the database. The home directory of all the users should point to the same physical directory where the application executable resides. The Informix method of using the operating system for security checking allows external programs to be called from within stored procedures. This capability is not allowed in Oracle. In Oracle, the OPS$User_name is sometimes used to connect to the database where OPS$User_name is a valid Oracle user and User_name is a valid operating system level user. In this case, the connection is made to the database using just the ‘/’ character without the user id and password. Informix’s technique of using the /etc/passwd file for security works fine for this type of connection. Non-operating system security In previous releases, each user who needed to access the database server also needed an operating system account on the host computer. Now you can configure Informix so that users who are authenticated by an external authentication service (such as Kerberos or Microsoft Active Directory) can connect to Informix. The new USERMAPPING configuration parameter specifies whether or not such users can access the database server, and whether any of those users can have administrative privileges. When Informix is configured to allow user mapping, you can still control which externally authenticated users are allowed to connect to Informix and their privileges. Please refer to the IBM Informix documentation on connection security for more information. Version 1.6 70 IBM Software Group IBM Information Management Informix supports for the pluggable authentication module (PAM) framework, which lets an administrator customize authentication for individual applications, and permits column-level encryption. Role based authority Role based authority can help you manage your database permissions. As of version 11.10, you can create a role and assign that as a default role for individual users (or to PUBLIC). A role is a work-task classification, such as “payroll” or “manager” . Each defined role has privileges on the database object granted to the role. You use the CREATE ROLE statement to define a role and GRANT DEFAULT ROLE to establish the user’s initial setting when connecting to the database. A user’s role can be changed after connecting by using the SET ROLE statement. Column-level encryption You can use column-level encryption to improve the confidentiality of your data. IDS Version 11.10 has new built-in SQL scalar functions provide methods for data in columns to be stored in an encrypted format. Use the ENCRYPT_AES or the ENCRYPT_TDES function to define encrypted data. Use the DECRYPT_BINARY() and DECRYPT_CHAR() functions to query encrypted data. When using the routines the encryption of data is under application control and the DBMS is not aware that data is encrypted. Informix’s support of column level encryption is analogous to Oracle’s DBMS Obfuscation Tool Kit. Built-in ENCRYPT functions provide methods for encrypting and decrypting the following character data types or smart large object data types: CHAR NCHAR VARCHAR NVARCHAR LVARCHAR BLOB CLOB Passwords and hints Passwords are necessary when using the encryption functions. Only users who can provide a secret password can view, copy, or modify encrypted data. The password must be a minimum of 6 bytes and can be a maximum of 128 bytes. When you set an encryption password, you have the option of specifying a password hint. Hints can be up to 32 bytes of text. If you specify a hint, you can store the hint with the encrypted password or in another location. Passwords (and hints) to be used by default can be set for a session using the SET ENCRYPTION PASSWORD statement. SET ENCRYPTION PASSWORD ‘password’ [ WITH HINT ‘hint string’ ]; Hence, the password and hint parameters can be optional when calling the encryption function. Explicit values override the session default values. The password or hint can be a single word or several words. The hint should be a word or phrase that helps you to remember the password, but does not include the password. You can subsequently execute the built-in GETHINT function (with an encrypted value as its argument) to return the plain text of hint. Calling the GETHINT function with an argument of encrypted data will return the hint (if any) from the encrypted data or an empty string (NULL) is no hint was provided. There is no privilege needed, anybody can get any hint at any time. When you set a password, Informix transfers the password and any hint to 128-bit key that is used to encrypt the password and hint. Passwords and hints are not stored as plain text any table of the system catalog. The key is a time-based random value per instance. The database server Version 1.6 71 IBM Software Group IBM Information Management initializes the key when the server starts; the key is destroyed when the database server shuts down. Encrypting a column Column data can be encrypted through the use of two functions, ENCRYPT_TDES and ENCRYPT_AES, corresponding to the two data encryption standards; Triple Data Encryption Standard (Triple-DES) and Advanced Encryption Standard (AES) which describes the cipher algorithm used to protect data from unauthorized viewing. AES encryption (also known as Rijndael) uses a 128-bit key size and Triple-DES encryption uses two 56-bit keys for 112-bits overall. Both functions are variant functions and will return a different result every time it is used. The following example demonstrates how to use the encryption functions with a column that contains a social security number: CREATE TABLE emp (name CHAR (40), salary MONEY, ssn LVARCHAR(64)); INSERT INTO emp VALUES ('Alice', 50000, ENCRYPT_AES('123-456-7890','one two three 123')); -orCREATE TABLE emp (name CHAR(40), salary MONEY, ssn LVARCHAR(64)); SET ENCRYPTION PASSWORD "one two three 123"; INSERT INTO emp VALUES ('Alice', 50000, ENCRYPT_AES('123-456-7890')); Querying an encrypted column Encrypted data can be translated using Informix’s DECRYPT_CHAR and DECRYPT_BINARY scalar functions. Encrypted data contains information about the encryption method and all the other data needed to decrypt it except the password. The encrypted data value is passed as the first argument and a password as the second, unless the SET ENCRYPTION statement has specified for this session with the same session password by which the first argument was encrypted. If the data is not encrypted, the function call will return an error. The DECRYPT_CHAR function is used to invoke a decryption routine on character data types (i.e. CHAR, LVARCHAR, NCHAR, NVARCHAR and VARCHAR) while the DECRYPT_BINARY function accepts an encrypted large object of type BLOB or CLOB. If the first argument to DECRYPT_CHAR or DECRYPT_BINARY is not an encrypted value, or if the second argument (or the default password specified by SET ENCRYPTION) is not the password that was used when the first argument was encrypted, Informix will issue an error and the call fails. If the call to the decryption function is successful, it returns the plain text version of the encrypted data argument. The following example demonstrates how to use the decrypt function to query encrypted data: SELECT name, salary, DECRYPT_CHAR(ssn, ‘one two three 123’) FROM emp WHERE DECRYPT_CHAR(ssn) = ‘123-456-7890’; -orSET ENCRYPTION PASSWORD ‘one two three 123’; SELECT name, salary, DECRYPT_CHAR(ssn) FROM emp WHERE DECRYPT_CHAR(ssn) = ‘123-456-7890’; The first argument to DECRYPT_BINARY is expected to be an encrypted value of a large object data type however if it is called with a character data type Informix invokes the DECRYPT_CHAR function and attempts to decrypt the specified value. Version 1.6 72 IBM Software Group IBM Information Management Do not use either decryption function to create a functional index on an encrypted column. This would store the decrypted values as plain text data in the database, defeating the purpose of encryption. Performance impact of encryption The performance impact of encryption is significant. Encryption, by its nature, will slow down most SQL statements. If some care and discretion are used, the amount of extra overhead should be minimal. Also, encrypted data will have a significant impact on your database design. In general, you want to encrypt a few very sensitive data elements in a schema, like social security numbers, credit card numbers, patient names, and so on. The effect on performance depends on direction: encrypting is slower than decrypting. It does not depend measurably on which algorithm is used; AES performs at the same speed as Triple-DES. It does depend on data size; the relative overhead is less when there is more data to encrypt. Storage considerations An encrypted value uses more storage space than the corresponding plain text value. Data encrypted using AES can be bigger than Triple-DES, but not by a significant amount. This occurs because all of the information needed to decrypt the value, except the encryption key, is stored with the value. Therefore, before you set the encryption password and encrypt the data, you must be sure the encrypted data can fit in the column. A chart of the expected sizes can be found in the IBM Informix Administrator Guide. Compliance regulations Informix’s encryption routines comply with the United States’ regulations of Health Insurance Portability and Accountability Act (HIPAA), Sarbanes-Oxley, Gramm-Leach-Bliley, and California Personal Information: Privacy (SB 1386) as well as the international requirements of Basel II. Also, they comply with the latest cryptographic standards (OpenSSL 0.9.7c). Version 1.6 73 IBM Software Group IBM Information Management CHAPTER 8: Environment Scripts Oracle make files used to build the application should be modified to invoke the appropriate Informix ESQL/COBOL, ESQL/C, MF COBOL, and other application language commands. The shell scripts, shell startup scripts, and command files should be modified for Informix compliant statements. SQL script substitution variables In Oracle, a substitution variable is a user variable name preceded by one or two ampersands (&). When Oracle (SQL*Plus) encounters a substitution variable in a command, Oracle executes the command as though it contained the value of the substitution variable, rather than the variable itself. In Oracle, the substitution variables can be used anywhere in SQL and SQL*Plus commands, except as the first word entered at the command prompt. When SQL*Plus encounters an undefined substitution variable in a command, SQL*Plus prompts the user for the value. SQL*Plus reads the responses from the keyboard, even if terminal input or output has been redirected to a file. If a terminal is not available (if, for example, the command file is run in batch mode), SQL*Plus uses the redirected file. Informix’s dbaccess does not provide this functionality, however, a work -around for the redirected file type of substitution is a follows (no user prompting). Create the following shell script (named subvals.ksh in this case). #!/bin/kshif [[ $# -ne 2 ]] then echo "\nUsage: $0 <dfile> <cfile>\n" echo "Desc: This script accepts two files - dfile, the decode file," echo " and cfile, the change file. It modifies the change file" echo " using the values listed in the decode file." echo " The decode file should contain two columns - old value" echo " and new value." echo " Ex:" echo " changethis tothis" echo echo " In this case, every occurance of 'changethis' would be" echo " replaced with 'tothis'.\n" exit 1 fi Version 1.6 74 IBM Software Group IBM Information Management #----------------------# Declare variables #----------------------dfile=$1 cfile=$2 awk '{print "s/"$1"/"$2"/"}' $dfile > $dfile"_sed" sed -f $dfile"_sed" $cfile The preceding script is used by creating a “decode” file (named decodefile in this case), which holds two columns in the form, which may contain multiple rows: oldval newval Executing the following command: subvals.ksh decodefile chgfile will replace every occurrence of oldval with newval in the file chgfile. The shell script (subvals.ksh) writes to standard output, therefore, the output should be redirected. The redirected file will then be able to run with dbaccess. Utilities To improve performance, the Informix EXPLAIN utility should be used by executing the SET EXPLAIN ON command to see how an SQL statement accesses the database. From that point forward the access path for every SQL statement is written in the SQEXPLAIN.OUT file in the current working directory, or the home directory of the process that initiated the request on the host machine. The Informix SET EXPLAIN OFF should be executed to turn off this feature when finished. The overall system performance should be monitored using the Informix ONSTAT utility. System catalog The following items discuss how Informix implements the system catalog differently than Oracle. Each item refers to the Informix implementation only. Object names are stored in lower case. The system catalog tables are owned by ‘informix’. To query the systables table use: SELECT * from ‘informix’.systables. Each table in systables is assigned a tabid by the system. It is used as the primary key and therefore, the foreign key for the other catalog tables. The identifier information is stored in sysindexes. Columns part1 - part16 each contain a column component of a composite index. They are integers, positive for ascending order, negative for descending order. In syscolumns, collength and coltype have special meanings depending on the data type of the column. See the Informix Guide to SQL, Reference Version for details. Version 1.6 75 IBM Software Group IBM Information Management CHAPTER 9: Using Bladelets What is a Bladelet? A "Bladelet" is a small, informal Informix Datablade module. It's meant to be useful "out of the box," and is offered complete with source code at no cost -- and no support or warranty. In the context of this document, the term "Datablade" refers to an extension to the standard IBM Informix database engine. These generally take the form of new data types (for example, timeseries or video) and/or new routines that execute inside the server. The new routines might operate on new data types, or on existing ones. A Datablade module is usually a formal product, available for sale. In addition a Datablade is: Developed either by IBM Informix or by a third-party developer Rigorously tested Packaged and sold for profit May or may not be formally certified by IBM Informix as having been developed according to approved Datablade coding practices In contrast, a Bladelet is: Not rigorously tested Not sold for profit Not certified by IBM Informix A Bladelet is an extension to the database engine that is narrow in scope and thought to be generally useful being offered at no cost, complete with source. Just remember before you use any of this code in mission-critical applications, it's up to you to thoroughly test it and fix any problems you find. Please review the disclaimer which applies to any sample code you find on this site: http://www.ibm.com/developerworks/db2/zones/informix/library/samples/db_downloads.html Note: The bladelets at this link have not been updated for some time (at the time of this writing); some of the capabilities, especially simple function call that increase compatibility, may have been introduced into the Informix server itself since they were written. Useful Bladelets Bladelet Description Defines two user-defined functions that provide dynamic SQL functionality within a SPL procedure. A complete access method that lets you build virtual tables based on operating system files. Implements a collection of "other database vendor" functions for Informix Exec Flat File Access Method IUtil Version 1.6 76 IBM Software Group IBM Information Management JPEG Image Bladelet Provides a user-defined type for JPEG images that allows you to manipulate images and to extract and search on image properties Multirepresentational lvarchar Opaque Type Creates the idn_mrLvarchar opaque type, which stores character data up to 2 gigabytes. Creates the node opaque type, which is intended to solve one of the hardest problems in relational databases: transitive closure. Creates routines that let you manipulate character and clob data using regular expressions. Creates several opaque types for managing spatial data, including R-tree index support. Node Datablade Module Regexp Shapes DataBlade Module Smart Blob Information DataBlade Module Includes a variety of useful routines for working with smart blobs. SqlLib C SqlLib Java Versioning Table Access Method SqlLib is a Bladelet that adds several "other database compatibility" functions to Informix . There are two functionally identical implementations: SqlLib for Java and SqlLib for C. SqlLib is a Bladelet that adds several "other database compatibility" functions to Informix . There are two functionally identical implementations: SqlLib for Java and SqlLib for C. Permits users to create and manage "versioned" tables. We will look closer at two bladelets which could assist with migration; the Exec datablade and SQLLib. Informix Exec bladelet for Dynamic SQL Often, it is desirable to execute a SQL query that is generated at run-time within the RDBMS. For example, a developer may not know the name of the temporary table they wish to run the query against, or they might want to append predicates to a query. In external programs, this can be accomplished using the ESQL/C SQLCA and DESCRIPTOR facilities. Unfortunately the INFORMIX Stored Procedure Language (SPL) does not support Dynamic SQL. Queries must be hard-coded into the SPL logic. The objective of the Exec bladelet is to remedy this situation. Exec consists of user-defined functions that take an SQL query as an argument, execute it, and return the result (the format of which varies depending on which function is called and the kind of query). The Exec functions can handle most Data Definition Language (DDL) statements and all Data Manipulation Language (DML) queries. For more details and download refer to: http://www.ibm.com/developerworks/db2/zones/informix/library/demo/ids_exec.html There are three User Defined Routines (UDRs) in the Exec bladelet. Two of them are external 'C' functions that use the Server API (SAPI). These must be compiled into shared libraries on the target machine, and you need to declare them to the server using CREATE FUNCTION statements. The third UDR is a routine written in Informix Stored Procedure Language (SPL) that uses the first two UDRs to return a collection of items. The Exec bladelet module consists of the following: 1. EXEC The EXEC() function takes an LVARCHAR that it treats as a SQL query, executes the query and returns a single, LVARCHAR result string. Depending on what kind of SQL statement is submitted, Exec() returns a different result format. Version 1.6 77 IBM Software Group IBM Information Management 2. EXEC_FOR_ROWS The EXEC_FOR_ROWS() UDR is an iterator function, which means it can return more than one result row. Of course, it only does so when it is asked to execute a SELECT. Otherwise, it behaves exactly as the EXEC() UDR. Being an iterator function limits the ways in which such a UDR can be used. It cannot be used in another SQL query. The only place it can be used effectively is inside a SPL routine. 3. EXEC_FOR_MSET EXEC_FOR_MSET() is a SPL routine that uses the EXEC_FOR_ROWS() routine introduced above. Instead of returning a set of rows as an iterator, or a single row as the EXEC() UDF does, this UDR collects the results of the SQL query together into a single object: a multi-set. Refer to Appendix E: Sample Code for examples on using the EXEC bladelet functions. SqlLib DataBlade module (SqlLibC.1.1 / SqlLibJ.1.1) The SqlLib DataBlade module implements SQL routines that the Informix does not support natively but that are supported by some other database vendors. Source code is freely available for download and includes two implementations, one in C (SqlLibC) and the other in Java (SqlLibJ). The following routines are included: ascii - takes a single character as input and returns the ASCII value, in decimal, that corresponds to that character ceil - takes a single numeric value as input and returns the smallest integer that is either equal to or greater than the input value chr - takes an integer value and returns the character that is represented by the ASCII value (in decimal) of that integer instr - searches for a value in a string, and returns the position where it was found. It returns 0 if the search value was not found. instrb - provides the exact same functionality as instr() but for single-byte character sets sign - takes a numeric argument and returns an integer that indicates if the input value is positive (1), negative (-1), or zero (0) to_decimal - converts the input character argument into a decimal type to_float - converts the input character argument into a float type to_integer - converts the input character argument into an integer type For more details and download refer to: http://www.ibm.com/developerworks/db2/zones/informix/library/techarticle/db_sqllib.html For more details and download refer to: http://www.ibm.com/developerworks/db2/zones/informix/library/techarticle/db_sqllib.html Version 1.6 78 IBM Software Group IBM Information Management A P P E N D I X A: Planning guide The checklist below will help an analyst determine which Oracle features which need to be converted to Informix and give some guidance on the effort involved. Each porting task is listed. They are followed by a quantity field used to determine how prevalent the item, the difficulty or amount of effort involved, 1 (low) to 5 (high), is used to estimate the amount of time needed to perform the task when the associated item exists in the application a moderate number of times. What is moderate depends on the size of the application. If there are an unusually large number of occurrences of an item, then the effort should be increased. Conversely, if there are a small number of occurrences of an item, then the effort should be decreased. Each of these items is discussed in detail within this document. Each heading corresponds to a section heading within the document. Tasks Quantity Effort DDL Identifiers Delimit identifiers named with Informix reserved words Databases Determine database names and create databases Replace Oracle tablespace names with Informix dbspace names ANSI vs. Non-ANSI considerations Tables Convert create table statements Convert DDL syntax Reduce primary key length to 390 bytes Data Types Convert Oracle’s NUMBER data type Convert VARCHAR columns Convert Oracle’s DATE data type Convert raw data types Convert Rowids Indexes Convert Indexes Views Remove CREATE VIEW statement options Stored Procedures and Triggers Convert CREATE and REPLACE Stored Procedure and Trigger syntax Oracle Extensions Remove non-supported Oracle extensions from DDL 2 3 1 3 2 3 5 3 2 5 2 2 2 1 1 1 Tasks Quantity Effort DML SQL Add the Informix AS clause to display labels Replace non-supported DML syntax: ^=, /* … */, :=, ELSE IF Convert Optimizer Directive/Hints Convert Insert/Select/Union and Insert/Value/Select statements 1 2 2 3 Version 1.6 79 IBM Software Group IBM Information Management Convert Delete statement syntax Convert temporary table processing Convert Join statements: +, MINUS, INTERSECT, different data types Convert sorts Convert hard coded SQL error messages Remove correlation names on the main table of Update and Delete statements Replace original table names in SELECT statements using aliases Convert hierarchical implementation (CONNECT BY PRIOR) Host Variables Convert host variable format or add conversion processing Functions Convert date and time functions Convert aggregate functions Convert mathematical functions Convert string function syntax Convert other unsupported Oracle functions Macros Convert ISOPEN, NOT FOUND, ROWTYPE, and TYPE macros Pseudo-Columns Convert Level processing Convert Rownum processing Update Using Cursors Convert WHERE CURRENT OF processing for multiple table joins Convert FOR UPDATE with a HOLD cursor processing Convert concurrency processing from Oracle’s implicit to Informix’s explicit System Tables Convert system table references 1 2 2 4 3 2 1 5 5 5 3 5 1 5 3 3 2 5 2 4 1 Tasks Stored procedures Break stored procedures greater than 64K in size into smaller ones Convert Oracle’s stored procedure packages Convert exception processing Convert error handling (SQLCA) Convert Oracle’s global cursor processing Convert Oracle’s explicit cursor processing Convert flow control processing Convert Oracle’s implicit variable declaration and assignment Convert Oracle’s BOOLEAN variable type Convert binary data type processing Convert Oracle’s dynamic SQL processing Triggers Convert multi-functional triggers Convert Insert and Update triggers which are not supported in Informix Place logical statements in a stored procedure Constraints Convert constraint enabling and disabling processing DUAL table Convert Dual table processing Quantity Effort 2 3 5 2 5 2 5 3 1 5 3 5 5 5 4 Embedded SQL Host variables Version 1.6 80 IBM Software Group IBM Information Management Convert declaration overrides Convert array processing Convert Oracle’s formatting functions SQL structures Convert SQLDA processing Convert ORACA processing Pre-compiler options Convert Oracle pre-compiler options Embedded PL/SQL Convert Oracle’s Embedded PL/SQL ODBC Convert Oracle’s OCI Convert database library calls Convert global area processing Convert fetch cycle Convert RAW data type processing Convert parameter bindings Embedded SQL for C Convert VARCHAR processing Embedded SQL for COBOL Convert VARCHAR processing Convert 88 Level processing in COBOL Convert Redefine processing in COBOL 1 5 4 5 1 1 5 5 2 2 2 4 Tasks Quantity Effort Application architecture Transaction processing Convert application to Informix transaction processing Convert multiple connect processing Convert savepoints Concurrency Address Informix locking processing User authentication Address Informix security processing 5 5 n/a n/a 2 4 Environment Convert make files Convert shell scripts, shell startup scripts, and command files Convert script substitution variables 2 2 Version 1.6 81 IBM Software Group IBM Information Management A P P E N D I X B: Syntax comparison Oracle Informix AND NOT OR >= <= <> or != letters, numbers, and “_” CHAR CHAR or TEXT SMALLINT INTEGER DECIMAL BYTE, TEXT, CLOB, BLOB --, { … } LET var1 = expression ELSE or ELIF IF AVG(expression), AVG(ALL expression), AVG(DISTINCT column_name), AVG(UNIQUE column_name) COUNT(*), COUNT(DISTINCT column_name), COUNT (UNIQUE column_name) MAX(expression), MAX(ALL expression), MAX(DISTINCT column_name), MAX(UNIQUE column_name) MIN(expression), MIN(ALL expression), MIN(DISTINCT column_name), MIN(UNIQUE column_name) SUM(expression), SUM(ALL expression), SUM(DISTINCT column_name), SUM(UNIQUE column_name) ROUND(expression,rounding factor) STDDEV(expression) VARIANCE(expression) RANGE(expression) See the Informix Guide to SQL: Syntax for more information on each item. Boolean AND NOT OR Greater Than or Equal To Less Than or Equal TO Not Equal Identifiers Data types Character Long Character Number (2 bytes) Number (4 bytes) Number (> 4 bytes) Image DML Comments Assignment Condition Aggregate functions Average AND NOT OR >= <= <>, !=, or ^= letters, numbers,”_”,”$”,”#” CHAR VARCHAR (2000) if <= 2000 LONG VARCHAR if > 2000 NUMBER NUMBER NUMBER LONG RAW, CLOB, BLOB --, /* … */ var1 := expression ELSE IF AVG(expression), AVG(ALL expression), AVG(DISTINCT expression) COUNT(*), COUNT (expression), COUNT(ALL expression), COUNT (DISTINCT expression) MAX(expression), MAX(ALL expression), MAX(DISTINCT expression) MIN(expression), MIN(ALL expression), MIN(DISTINCT expression) SUM(expression), SUM(ALL expression), SUM(DISTINCT expression) ROUND(expression,rounding factor) STDEV(expression) VARIANCE(expression) ? Count Maximum Minimum Sum Algebraic functions Round Statistical functions Standard Deviation Variance Range Version 1.6 82 IBM Software Group IBM Information Management String functions Trim Length Like Like (single character) Like (wildcard) String Delimiter Escape Character Stored procedures Create LTRIM(expression) RTRIM(expression) LENGTH(string) LIKE(string) _ % ‘ or “ set within LIKE clause using ESCAPE “?” to make “?” the escape character CREATE OR REPLACE PROCEDURE procedure_name (var1 IN CHAR(1), var2 OUT DATE, var3 IN OUT NUMBER); EXECUTE PROCEDURE procedure_name (var1 IN, var2 OUT, var3 IN OUT); LTRIM(expression) RTRIM(expression) LENGTH(string) LIKE(string) _ % ‘ or “ \ or set within LIKE clause using ESCAPE “?” to make “?” the escape character DROP PROCEDURE procedure_name; CREATE PROCEDURE procedure_name (var1 CHAR(1), var3 INTEGER) RETURNING DATE, INTEGER; EXECUTE PROCEDURE procedure_name (var1, var3) RETURNING var2, var3; Arguments Version 1.6 83 IBM Software Group IBM Information Management A P P E N D I X C: Database concepts Informix Specific database name Chunk Dbspace Extent Physical Log Logical Log Root dbspace Temporary dbspaces, dbspacetemp environment variable, or dbspacetemp onconfig parameter Blobspace, Sbspace table fragmentation Not supported Not supported finderr utility onstat and oncheck utilities dbaccess utility ESQL/C ESQL/COBOL INet Like the ESQL function library after precompiling, if they were publically documented. COMMITTED READ LAST COMMITTED The table below illustrates the relationship between each database vendor’s database objects. Oracle Database name attached to a group of files Data file Tablespace (logical partition) or Segments (Index Segment, Table Segment and so on) Extent Rollback Segment Redo Log System tablespace Temporary tablespace Pointer Table partitioning Database link Snapshot oerr utility sqldba and svrmgrl - monitor utilities SQLPlus utility Pro*C Pro*COBOL SQL*NET OCI Read Consistency Version 1.6 84 IBM Software Group IBM Information Management A P P E N D I X D: Sample code User defined functions User Defined Routines (UDR) can be developed to extend the capabilities of the database and provides the option to create UDRs to emulate Oracle built-in functions and reduce the impact of replacing all references throughout the application. Or rename an Informix function to the corresponding Oracle function name. CONCAT CREATE PROCEDURE concat (str1 VARCHAR(255), str2 VARCHAR(255)) RETURNING VARCHAR(255); RETURN str1 || str2; END PROCEDURE; INSTR CREATE PROCEDURE instr( str VARCHAR(255), mask VARCHAR(255), strt SMALLINT DEFAULT 1, occur SMALLINT DEFAULT 1 ) RETURNING INT; DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE LET LET LET LET LET LET LET LET LET slen SMALLINT; mlen SMALLINT; tempstr VARCHAR(255); tempmask VARCHAR(255); nomatch SMALLINT; count SMALLINT; pos SMALLINT; retpos SMALLINT; i SMALLINT; j SMALLINT; srchstrt SMALLINT; = = = = = = = = = LENGTH(str); LENGTH(mask); 0; 0; 1; 1; ''; ''; strt; slen mlen count nomatch pos retpos tempstr tempmask srchstrt IF (strt < 0) THEN - reverse the str FOR i = 1 TO slen LET tempstr = str[1,1] || tempstr; LET str = str[2,255]; END FOR; -- reverse the mask FOR i = 1 TO mlen LET tempmask = mask[1,1] || tempmask; LET mask = mask[2,255]; END FOR; Version 1.6 85 IBM Software Group IBM Information Management LET srchstrt = strt * -1; LET str = tempstr; LET mask = tempmask; END IF; FOR j = 1 TO slen LET tempstr = str; LET tempmask = mask; FOR i = 1 TO mlen IF (tempmask[1,1] != tempstr[1,1]) THEN LET nomatch = 1; EXIT FOR; END IF; LET tempmask = tempmask[2,255]; LET tempstr = tempstr[2,255]; END FOR; IF (nomatch = 0) THEN IF (pos >= srchstrt) THEN LET count = count + 1; END IF; IF (count = occur) THEN IF (strt < 0) THEN RETURN slen - pos + 1; ELSE RETURN pos; END IF; END IF; END IF; LET str = str[2,255]; LET nomatch = 0; LET pos = pos + 1; END FOR; RETURN 0; END PROCEDURE; YYYYMMDD CREATE PROCEDURE yyyymmdd (str VARCHAR(10)) RETURNING varchar(8); DEFINE retstr VARCHAR(8); IF str IS NULL THEN RETURN NULL; ELSE LET retstr = str[7,10] || str[1,2] || str[4,5]; RETURN retstr; END IF; END PROCEDURE; Using EXEC bladelet functions Version 1.6 86 IBM Software Group IBM Information Management Using EXEC Note the ROW data type variable can be referenced using table and column syntax. CREATE PROCEDURE example1() RETURNING INTEGER DEFINE sql_stmt LVARCHAR(255); DEFINE rcnt ROW(val INTEGER); LET sql_stmt = "select count(*) from items where manu_code = ‘ANZ’"; EXECUTE FUNCTION EXEC(sql_stmt) INTO rcnt; RETURN rcnt.val; END PROCEDURE; Using EXEC_FOR_ROWS CREATE PROCEDURE example2() RETURNING INTEGER, CHAR(1) DEFINE rs_rowdata ROW(customer_num INTEGER, call_code CHAR(1)); DEFINE sql_stmt LVARCHAR(255); LET sql_stmt = "select customer_num, call_code from cust_calls where customer_num = 106"; FOREACH EXECUTE FUNCTION EXEC_FOR_ROWS(sql_stmt) INTO rs_rowdata RETURN rs_rowdata.customer_num, rs_rowdata.call_code WITH RESUME; END FOREACH; END PROCEDURE; Using EXEC_FOR_MSET Note the DEFINE statement for the variable rs_data. Collection data types used in a SPL routine cannot be defined using the LIKE attribute in the DEFINE statement. CREATE PROCEDURE example3() RETURNING INTEGER, VARCHAR(8) DEFINE v_customer_num LIKE accounts.customer_num; DEFINE v_account_cd LIKE accounts.account_cd; DEFINE rs_data MULTISET(ROW( r_customer_num INTEGER, r_account_cd VARCHAR(8)) NOT NULL); DEFINE sql_stmt LVARCHAR; LET sql_stmt = "select customer_num, order_num from orders where company_id = 51155"; EXECUTE FUNCTION EXEC_FOR_MSET(sql_stmt) INTO rs_data; FOREACH SELECT r_customer_num, r_account_cd INTO v_customer_num, v_account_cd FROM TABLE(rs_data) RETURN v_customer_num, v_account_cd WITH RESUME; END FOREACH; END PROCEDURE; Version 1.6 87 IBM Software Group IBM Information Management Date arithmetic Informix has extensive capabilities for manipulating dates and times. You can add or subtract DATE and DATETIME variables from each other. You can add or subtract an INTERVAL to a DATE or DATETIME. UNITS Keyword When working with INTERVAL values, sometimes it is necessary to specify the precision with which you are dealing. For example, suppose you have the following field defined: To add 10 days to the lead time you could use a SQL statement like this: SELECT lead_time + INTERVAL(10) DAY to DAY FROM orders -orSELECT lead_time + 10 UNITS DAY FROM orders Using EXTEND function The EXTEND function allows operations between data types of different precisions. SELECT EXTEND(myvar, YEAR to MINUTE) – INTERVAL(5) MINUTE to MINUTE FROM member Calculate Week Of The Year SELECT ROUND((TODAY - DATE('01/01/'||YEAR(TODAY))) /7 + .5) FROM DUAL; Number of days difference SELECT DATE('01/06/'||YEAR(TODAY)) - DATE('01/06/2005') FROM DUAL; Number of weeks difference SELECT ROUND((DATE('01/06/'||YEAR(TODAY)) - DATE('01/06/2005')) / 7 + .5) FROM DUAL; String representation of date and time Day and date SELECT TO_CHAR(TODAY, '%A %B %d, %Y') FROM dual; -returns month, day and 4 digit yearTuesday January 03, 2006 Abbreviation of day of week SELECT SUBSTR(TO_CHAR(TODAY, '%A %B %d, %Y %R'),1,3) FROM dual; -orSELECT TO_CHAR(TODAY, '%a') FROM dual; -returns MON, TUE, WED, THU, FRI, SAT, SUN- Returning the number of rows affected Version 1.6 88 IBM Software Group IBM Information Management Within SPL routines, you can use the DBINFO function to find out the number of rows that have been processed in SELECT, INSERT, UPDATE, DELETE, EXECUTE PROCEDURE, and EXECUTE FUNCTION statements. CREATE FUNCTION del_rows(pnumb INT) RETURNING INT; DEFINE nrows INT; DELETE FROM sec_tab WHERE part_num = pnumb; LET nrows = DBINFO('sqlca.sqlerrd2'); RETURN nrows; END FUNCTION; To ensure valid results, use this option after SELECT and EXECUTE PROCEDURE or EXECUTE FUNCTION statements have completed executing. If you use the 'sqlca.sqlerrd2' option within cursors, make sure that all rows are fetched before the cursors are closed. Using an Informix collection data type A collection data type as a VARRAY In the following examples the collection data types of MULTISET and SET are used in a function to hold a collection of values. You cannot define a collection variable as global (with the GLOBAL keyword) or with a default value. Informix SPL allows you to declare a cursor for a collection variable using the FOREACH statement called a collection cursor. Also demonstrated is how to utilize a variable declared with the keyword COLLECTION. A variable declared as type SET, MULTISET, or LIST is a typed collection variable. It can hold a collection of its specified data type only. However a COLLECTION is an un-typed (or generic) collection variable that can hold a collection of any data type. This function will build a MULTISET list of values: CREATE FUNCTION func1() RETURNING MULTISET(INTEGER NOT NULL) DEFINE v_array_of_ints MULTISET(INTEGER NOT NULL); INSERT INTO TABLE (v_array_of_ints) VALUES(1); INSERT INTO TABLE (v_array_of_ints) VALUES(2); INSERT INTO TABLE (v_array_of_ints) VALUES(5); INSERT INTO TABLE (v_array_of_ints) VALUES(4); RETURN v_array_of_ints; END FUNCTION; This function will receive the COLLECTION list of values from func1: CREATE FUNCTION func2() RETURNING INTEGER --- DEFINE lmset MULTISET(INTEGER NOT NULL); -DEFINE lmset COLLECTION; DEFINE v_int INTEGER; --- call func1 to populate local multiset variable -LET lmset = func1(); --- tally a count the number of elements -FOREACH SELECT COUNT(*) INTO v_int FROM TABLE(lmset) Version 1.6 89 IBM Software Group RETURN v_int WITH RESUME; END FOREACH; --- return the values -FOREACH SELECT * INTO v_int FROM TABLE(lmset) RETURN v_int WITH RESUME; END FOREACH; END FUNCTION; IBM Information Management NOTE: In the example func2, a duplicate declaration for the “lmset” variable as a MULTISET data type has been commented out, by declaring the variable as a COLLECTION it can accept any of the 3 types (MULTISET, SET and LIST). When executing the function, the resulting expression will be: EXECUTE FUNCTION func2(); (expression) 4 (row count) 1 2 5 4 By using a SET data type, inserts will not permit duplicates: CREATE FUNCTION func1() RETURNING SET(INTEGER NOT NULL) DEFINE v_array_of_ints SET(INTEGER NOT NULL); INSERT INTO TABLE (v_array_of_ints) VALUES(1); INSERT INTO TABLE (v_array_of_ints) VALUES(2); INSERT INTO TABLE (v_array_of_ints) VALUES(2); INSERT INTO TABLE (v_array_of_ints) VALUES(5); INSERT INTO TABLE (v_array_of_ints) VALUES(5); INSERT INTO TABLE (v_array_of_ints) VALUES(5); INSERT INTO TABLE (v_array_of_ints) VALUES(5); INSERT INTO TABLE (v_array_of_ints) VALUES(5); RETURN v_array_of_ints; END FUNCTION; NOTE: Because func2 is expecting a un-typed COLLECTION variable func1 does not have to be dropped and recreated. It can receive a value from any Collection Data Type (MULITSET, SET and LIST). The function’s return values would change to: (with duplicates suppressed) EXECUTE FUNCTION func2(); (expression) 3 (row count) 1 2 5 Using a LIST data type, support for assigning values based on ordinal position: Version 1.6 90 IBM Software Group IBM Information Management CREATE FUNCTION func1() RETURNING LIST(INTEGER NOT NULL) DEFINE v_array_of_ints LIST(INTEGER NOT NULL); --- first insert no ordinal position can be established, By default, the -- database server inserts LIST elements at the end of the list. -INSERT AT 5 INTO TABLE (v_array_of_ints) VALUES(5); INSERT AT 1 INTO TABLE (v_array_of_ints) VALUES(1); INSERT AT 2 INTO TABLE (v_array_of_ints) VALUES(2); --- Only 3 LIST elements exist, position 4 has not been instantiated -- this element will insert at the end of the list -INSERT AT 4 INTO TABLE (v_array_of_ints) VALUES(4); INSERT AT 3 INTO TABLE (v_array_of_ints) VALUES(3); RETURN v_array_of_ints; END FUNCTION; NOTE: The elements of a LIST have ordinal positions; with a first, second, and third element in a LIST. To support the ordinal position of a LIST, the INSERT statement provides the AT clause. This clause allows you to specify the position at which you want to insert a list-element value. Note the behavior of the ordinal positioning above preceded by a commented explanation. When executing the function, the resulting expression will be: EXECUTE FUNCTION func2(); (expression) 5 (row count) 1 2 3 5 4 Using MULTISET and ROW to replace PL/SQL TABLE and RECORD types Creating variables using Informix Collection data types SET, MULTISET and LIST in combination with ROW data types can be useful when migrating applications which use Oracle REF CURSORS as arguments to functions as well as Oracle collection data types TABLE and RECORD. Typical Oracle Package specification: CREATE OR REPLACE PACKAGE MyPackage IS TYPE recOrders IS RECORD ( order_num orders.order_num%TYPE, order_date orders.order_date%TYPE, customer_num orders.customer_num%TYPE, po_num orders.po_num%TYPE ); TYPE tabOrders IS TABLE OF recOrders INDEX BY BINARY_INTEGER; TYPE ref_curtype IS REF CURSOR; Both tabOrders or ref_curtype can be converted to the following Informix SPL definition: DEFINE tabOrders MULTISET(ROW( Version 1.6 91 IBM Software Group IBM Information Management order_num INTEGER, order_date DATETIME YEAR TO FRACTION(3), customer_num INTEGER, po_num CHAR(10)) NOT NULL); DEFINE ref_curtype MULTISET(ROW( { define columns based on query result set} ) NOT NULL); Version 1.6 92 IBM Software Group IBM Information Management A P P E N D I X E: Counting system objects Using the system catalog System catalog Below is a list of SQL statements that will extract most, if not all, of the counts of system objects owned by user ‘XXX’. select count(table_name) from all_tables where owner=’xxx’; select count(*) from dba_indexes where owner=’xxx’; select count(synonym_name) from dba_synonyms where owner=’xxx’; select count(*) from dba_views where owner=’xxx’; select count(*) from dba_tab_columns where length(column_name)>18 and owner=’xxx’; select count(*) from dba_tab_columns and owner=’xxx’; select count distinct(*) from dba_tab_columns and owner=’xxx’; select count(*) from dba_tables where length(table_name)>18 and owner=’xxx’; select count(*) from dba_synonyms where length(synonym_name)>18 and owner=’xxx’; select count(*) from dba_views where length(view_name)>18 and owner=’xxx’; select count(*) from dba_indexes where length(index_name)>18 and owner=’xxx’; select count(*) from dba_tab_columns where data_type=’varchar2’ and owner=’xxx’; Version 1.6 93 IBM Software Group IBM Information Management select data_type, count(data_type) from dba_tab_columns where owner=’xxx’ group by data_type; select count(*) from dba_tab_columns where data_type=’varchar2’ and length(data_type)>254 and owner=’xxx’; select count(*) from dba_tab_columns where data_type=’number’ and data_scale=0 and owner=’xxx’; Using SQL code Built-in function references The following instructions can be used to find references to built-in functions. On the UNIX machine or the NT box where your code resides, please create the following file as /tmp/functionlist: convert( hextoint( inttohex( getdate( datename( datepart( datediff( dateadd( months_between( char( char_length( charindex( compute( difference( length( lower( patindex( replicate( reverse( right( soundex( space( str( stuff( substring( upper( abs( atn2( log( ceiling( Version 1.6 94 IBM Software Group cot( degrees( floor( radians( rand( sign( to_char( to_date( to_number( exception( pi( raise_application_error( raiserror( SQLCODE rollback savepoint /\* declare decode( nvl( print( rowid waitfor recompile bcp %isopen( %notfound( %rowtype( %type( IBM Information Management Then, please run the following program with the proper subdirectories: #! /bin/ksh rm –f /somehome/*.outfile cd /somesource/database export sourcebase=”somepath” for functor in `cat /tmp/function_list` do echo before find $functor find /$sourcebase/database -exec grep -i $functor {} \; >> /somehome/$functor.outfile find /$sourcebase/intfc -exec grep -i $functor {} \; >> /somehome/$functor.outfile echo after find $functor done Object types and counts This script attempts to count the objects of each type by grepping for CREATE statements containing the object type, and counts procedure definitions that are inside package bodies, which are not proceeded by a CREATE keyword. It is not a sophisticated parser; so, the results may not be perfect. #!/bin/bash if [ "$1" = "" ]; then Version 1.6 95 IBM Software Group IBM Information Management echo "Usage: objCounts.sh \"<FilePattern>\"" return 1; fi declare -a objectTypes=('CLUSTER' 'CONTEXT' 'CONTROLFILE' 'DATABASE' 'DATABASE +LINK' 'DIMENSION' \ 'DIRECTORY' 'DISKGROUP' 'FUNCTION' 'INDEX' 'INDEXTYPE' 'JAVA' 'LIBRARY' 'MATERIALIZED +VIEW' \ 'MATERIALIZED +VIEW +LOG' 'OPERATOR' 'OUTLINE' 'PACKAGE' 'PACKAGE +BODY' 'PFILE' 'PROCEDURE' 'PROFILE' \ 'RESTORE +POINT' 'ROLE' 'ROLLBACK +SEGMENT' 'SCHEMA' 'SEQUENCE' 'SPFILE' 'SYNONYM' 'TABLE' 'TABLESPACE' \ 'TRIGGER' 'TYPE' 'TYPE +BODY' 'USER' 'VIEW'); echo for type in "${objectTypes[@]}" do if [ "$type" = "PROCEDURE" ]; then echo $type " object count: " `cat $1 | grep -icE "^ *(|CREATE +|OR REPLACE +)\b$type\b"` else echo $type " object count: " `cat $1 | grep -icE "^ *(CREATE +|OR +REPLACE +)\b$type\b"` fi done Version 1.6 96 IBM Software Group IBM Information Management A P P E N D I X F: Data type mapping Oracle built-in data types (including ANSI types) can be converted to similar data types in the Informix database, according to the mapping described here. This table provides default mappings based on most common usage patterns; in some cases another mapping may work better. In the following table n is used to indicate length, p is used to indicate precision, and s is used to indicate scale. Oracle data type BFILE LONG RAW BLOB BINARY_DOUBLE BINARY_FLOAT BINARY_INTEGER PLS_INTEGER NATURAL NATURALN POSITIVE POSITIVEN 32-bit integers; only used in PL/SQL. NATURAL and POSITIVE are unsigned integers. BOOLEAN (PL/SQL only) CHAR(n) or CHARACTER(n) DATE BOOLEAN CHAR(n) DATETIME YEAR TO SECOND DOUBLE PRECISION BLOB Informix® Server data type DOUBLE PRECISION DOUBLE PRECISION INTEGER DOUBLE PRECISION Version 1.6 97 IBM Software Group IBM Information Management FLOAT See note on FLOAT below. INTEGER INT A 38-digit NUMBER sub-type. May want to flag with a warning on possible overflow. INTERVAL DAY(p) TO SECOND(s) Note: Oracle fractional seconds have 9 digits. INTERVAL YEAR(p) TO MONTH LONG CLOB LONG VARCHAR (xxx) Oracle recommends not using this type. NCHAR(n) NATIONAL CHAR(n), or NATIONAL CHARACTER(n) NCLOB NUMBER NUMBER(*,0) NUMBER(p) NUMBER(p, s) Where scale (s) is less than 0 (that is, s < 0), and p-s < 10. NUMBER(3,-2): 12300 FLOAT INTEGER INTERVAL DAY(p) TO FRACTION(min(5, s)) INTERVAL YEAR(p) TO MONTH CLOB LVARCHAR NCHAR(n) See National Language Types below. CLOB INTEGER Version 1.6 98 IBM Software Group IBM Information Management NUMBER(p) NUMBER (p,0) Where precision (p) is greater than or equal to 10 and <= 18. NUMBER(p, s) Where scale (s) is less than 0 (that is, s < 0), and 10 <= p-s < 19. NUMBER(9,-2): 12345678900 NUMBER(p, s) Where scale (s) is greater than 0 and precision (p) is greater than or equal to s (that is, s > 0 and p >= s). NUMBER(p, s) Where scale (s) is greater than 0 and precision (p) is less than s (that is, s > 0 and p < s). NUMBER(3,5): 0.00123 NUMBER(p, s) Where scale (s) is less than 0 (that is, s < 0), and p-s > 18 and p-s <= 32. BIGINT NUMERIC (min(p,32), min(s,32)) NUMERIC (min(s,32),min(s,32)) NUMERIC (min(p-s,32),0) NUMBER(p, s) NUMERIC (32) Scale cannot be greater than precision. Where scale (s) is greater than 32, or pInformix-s is greater NUMERIC(32,32) would guarantee that than 32. digits to the left of the decimal would be lost. Using a floating point decimal provides the simplest, best way to capture as many digits in a value as possible. Version 1.6 99 IBM Software Group IBM Information Management DEC DECIMAL NUMERIC DEC(p) DECIMAL(p), NUMERIC(p), DEC(p, s) DECIMAL(p, s) NUMERIC(p, s) NVARCHAR2(n) NCHAR VARYING(n) NATIONAL CHAR VARYING(n) NATIONAL CHARACTER VARYING(n) RAW(n) REAL Record There is no difference within Oracle between these types and each other and these types and NUMBER. The Oracle system catalogs contain the NUMBER type id for all declarations of these types, and the integer types as well. Declarations of these types can be mapped using the same rules as those for NUMBER. LVARCHAR(min(n, 32767)) BLOB DOUBLE PRECISION ROWTYPE Using a CREATE ROWTYPE statement. ROWID SMALLINT TIMESTAMP(p) If p is not specified, precision defaults to 6. UROWID(n) INTEGER SMALLINT DATETIME YEAR TO FRACTION(min(5, p)) INTEGER Version 1.6 100 IBM Software Group IBM Information Management VARCHAR2(n) VARCHAR(n) CHAR VARYING(n) CHARACTER VARYING(n)2 Where number (n) is less than or equal to 255 (that is, n <= 255). VARCHAR2(n) VARCHAR(n) CHAR VARYING(n) CHARACTER VARYING(n)2 Where number (n) is greater than 255 (that is, n > 255). XMLType Notes on specific types are below. VARCHAR(n) LVARCHAR(n) CLOB DOUBLE The range of values for the DOUBLE PRECISION data type is the same as the range of the C double data type on your computer. VARCHAR While Oracle VARCHAR data types have a limit of 4000 bytes, an Informix Server VARCHAR data types has a limit of 255 bytes. An Informix Server LVARCHAR has a limit of 32,739 Bytes. NUMBER Oracle abstracts its numeric types from the hardware and operating system through the use of its NUMBER type. Most numeric types, like INTEGER, are actually NUMBERs internally. When porting to a database server like Informix which retains more of a relationship between the operating system types and the database types, it is essentially a one-to-many mapping, and heuristics have to be used to determine the best-fit target type. No single set of type mapping rules will fit all use scenarios. The converter has default rules which fit the more common user patterns. When considering a number represented as mantissa and exponent, mantissa x 10 exponent precision is the number of digits in the mantissa, and scale is the negative exponent, how many places to the right of the decimal point the least significant digit is located. Oracle NUMBERs have a maximum precision of 38 and the maximum precision available in Informix native types is 32. The first step in a type mapping is to truncate the target type precision to min(precision, 32). The scale of a type can be considered its anchor point with respect to the decimal point. The most Version 1.6 101 IBM Software Group IBM Information Management significant digit of a numeric type is located precision digits to the left of the decimal point and precision-scale digits to the right of the decimal point. The start and end locations of the digits are used to determine the closest fitting Informix type. The order of preference is INTEGER, BIGINT, NUMERIC(p, s), and lastly, NUMERIC(p). NUMERIC(p) in a non-ANSI mode database is a decimal type with a floating point type. In an ANSI mode database, the scale is implicitly 0. All Oracle integer declaration types, such as INT, INTEGER, SMALLINT become NUMBER(38) type in the Oracle metadata. In Oracle, when precision is given, but scale is not, scale becomes 0 implicitly, which makes the type in the set of integral types. One exception to the general mapping rules is the NUMBER type specified without precision or scale. Strictly speaking, this has a floating point scale with 38 digits of precision, but most commonly it is used where an integer is more appropriate. Even if the user does specify INTEGER in a type definition, when it comes out of the Oracle catalogs, the type is NUMBER. This is one case in particular where one type mapping will not fit well with all usage scenarios. National language types Oracle N*char types are allocated by number of characters, where Informix is allocated by the number of bytes. For Oracle, the storage size of the characters varies with the encoding used, and is always subject to the standard 32,767 limit on maximum bytes, but during declarations, n always means the number of characters. Oracle PL/SQL supports explicit and implicit data type conversions. These explicit and implicit data types are converted to syntax by using Informix Dynamic Server's explicit and implicit data type conversion mechanism. ROWID Oracle databases support a pseudo-column named ROWID, such that every row in every table is assigned an auto-generated ID. Oracle also allows you to define a column of type ROWID, which can be used as a foreign key to a ROWID pseudo-column. Oracle ROWID columns contain data that if converted to a string are 18 characters long, for example ‘AAAQ+jAAEAAAAAeAAN’. It is probably not advisable to migrate this data to an Informix database, and instead use the Informix type, which is an integer. If there is a dependency on the values themselves, and therefore need migrate the data, then an integer type would not hold the values. %TYPE In PL/SQL, you can refer to the type of an existing variable, field, or column by using the %TYPE attribute. The converter replaces this reference directly by the type it refers to. %ROWTYPE In PL/SQL, you can refer to the record type that represents a row of a table, a cursor, or a cursor variable. PL/SQL scripts containing the %ROWTYPE attribute are handled in the same way as a record type that has been previously declared. FLOAT Oracle FLOAT type is a floating point number capable of storing up to 38 decimal digits, or 126 binary digits. It has a floating point scale that can exist outside the boundaries of what an Informix DECIMAL can represent. A C language double, or Informix float type is capable of only 16 digits; so, there is the chance that some data will be lost. Using an Informix FLOAT type makes it so that less significant digits are lost in preference to more significant digits. Version 1.6 102 IBM Software Group IBM Information Management References to subtypes declared in packages and PL/SQL blocks are converted to the Informix Server equivalent base types. Variables of type RECORD are not translated by the DDL Converter at the time of this writing. They will be handled by the stored procedure converter. XMLType Informix does not have a built-in type for representing XML. The nearest approximation is to store it in a CLOB. Another character type, like LVARCHAR, can be used if it can be determined that the actual data will fit in it. Version 1.6 103