| Click here to support our advertisers

SOFTWARE FOR SALE
BOOKS FOR SALE
SEARCH CENTRAL
JOB BANK
CLASSIFIEDS
DIRECTORIES
REFERENCE Online Library
LEARNING CENTER
JOURNAL
NEWS CENTRAL
DOWNLOADS
COMMUNITY
CALENDAR
ABOUT US
Journal:
Get the weekly email highlights from the most popular journal for developers! Current issue
> developer.com developerdirect.com htmlgoodies.com javagoodies.com jars.com intranetjournal.com javascripts.com | All Categories : C/C++ vcg07.htm Understanding the Structure of the ODBC API Single-Tier and Multiple-Tier ODBC Drivers ODBC API Conformance Levels ODBC SQL Conformance Levels ODBC Data Types and the Access Database Engine Built-In ODBC Scalar Functions String Manipulation Functions Numeric Scalar Functions Date, Time, and Timestamp Functions ODBC Initialization Files ODBCINST.INI ODBC.INI Using Desktop ODBC Datasources with Visual C++ Adding STDREG32.MDB as an ODBC Datasource Using the Student Registration Datasource in a Visual C++ Program Tracing the Execution of ODBC Function Calls and SQL Statements Using Microsoft Query and the Desktop Database Drivers Creating an ODBC Datasource from dBASE Files Using an ODBC Datasource with Microsoft Query Differences Between Access SQL and ANSI/ODBC SQL Statements Summary # - 7 - Using the Open Database Connectivity API Microsoft's Open Database Connectivity API is one of the first members of the Windows Open Services API (WOSA) to be released as a commercial product. WOSA is a suite of application programming interfaces for Windows applications that are intended to simplify and standardize the programming of a variety of classes of Windows-based procedures. Other components of WOSA include the Open Data Services (ODS) API for connecting to mainframe and minicomputer databases, the Messaging API (MAPI) that is employed by Microsoft Mail, and the Telephony API (TAPI) for call routing and voice mail. (Microsoft had a variety of other WOSA member APIs in the development stage at the time this book was written.) This chapter explains the structure of the ODBC API and how the Access database engine uses the ODBC API, and it gives examples of using the ODBC API with ODBC drivers for desktop databases. Chapter 20, "Creating Front Ends for Client-Server Databases," explains how to use the ODBC API and ODBC drivers for RDBMSs such as Microsoft SQL Server for OS/2 and Windows NT, IBM DB2 and DB2/2, and Watcom SQL. ### Understanding the Structure of the ODBC API The ODBC API consists of Windows DLLs that include sets of functions to provide the following two fundamental database services for all database types for which ODBC drivers are available: Installing, setting up, and removing ODBC datasources. A datasource is a named connection to a database. You need an ODBC driver for each different type of database to which your applications connect. The ODBC API is designed primarily for use with client-server RDBMSs, but you also can use ODBC drivers to connect to desktop database files, worksheets, and flat text files. ODBC uses the ODBCCP32.DLL library to set up and remove datasources. ODBCAD32.EXE is a stand-alone, executable version of ODBCCP32.DLL that is supplied with Access and Visual C++. Managing the communication of queries and other SQL statements from client front ends to database server back ends, and the transfer of query result sets or confirmations of the execution of action queries in the reverse direction. The ODBC driver manager, ODBC32.DLL, opens the ODBC driver for the datasource and then passes SQL statements to the driver. After a client-server RDBMS processes the query, the ODBC driver returns the rows through ODBC32.DLL to your application. The ODBC API implements SQL as a call-level interface (CLI). A call-level interface employs a set of standard functions to perform specific duties, such as translating SQL queries from ANSI SQL to the dialect of SQL used by the RDBMS, representing the RDBMS's field data types by an extended set of SQL-92 field data types, and handling error conditions. The ODBC API conforms to the CLI standard (SQL CAE draft specification—1991) developed by the SQL Access Group (SAG), a consortium of client-server RDBMS software publishers and users who have a large stake in the success of client-server database technology. Visual C++ database applications that use the ODBC API have a multitiered structure similar to the structure of the desktop databases supported by the Access database engine, shown in Figure 6.1 in the preceding chapter. The full structure of a Visual C++ database application that uses all the features of the ODBC API, including the Open Database Services (ODS) API to access mainframe and minicomputer databases, is shown in Figure 7.1. Figure 7.1. The full structure of the ODBC API as it is employed by Visual C++ database applications. Microsoft supplies ODBC drivers for its versions of SQL Server for OS/2 and Windows NT, and Oracle databases with Visual C++. The Microsoft Query applet included with Excel and Word for Windows, as well as the Microsoft ODBC Desktop Database Drivers kit, incorporate each of the single-tier drivers shown in Figure 7.1 below the ISAM drivers path. Independent software vendors (ISVs) such as Q+E Software (Intersolv) supply suites of ODBC drivers for a variety of desktop DBMs and client-server RDBMSs. Sources of commercial ODBC drivers and mainframe/minicomputer gateways are listed in the section "Open Database Connectivity (ODBC) Drivers" in Appendix A, "Resources for Developing Visual C++ Database Applications." NOTE A group of Microsoft's competitors, including Borland International, Lotus Development Corporation, and IBM, have joined to promulgate an alternative database connectivity standard called Integrated Database Application Programming Interface (IDAPI). References to the IDAPI group in the computer trade press attribute its formation to a desire by these competitors to prevent Microsoft from creating a de facto industry standard database connectivity API. Being first in the market gives Microsoft's ODBC API the momentum to qualify as today's industry "standard" for database connectivity. It's likely that any competing standard, if such a product appears, will be compatible with or directly comparable to the ODBC API. The following sections describe the basic features of the ODBC API and show how you can use the ODBC API with desktop DBMs and unconventional datasources such as worksheets and text files. NOTE Most of the information in the following sections is derived from Microsoft's Programmer's Reference for the ODBC SDK, and from a white paper called "Jet Database Engine ODBC Connectivity," written by Neil Black of Microsoft's Jet Program Management and Stephen Hecht of the Jet Development group. You can download the complete text of this white paper in Word for Windows .DOC format from Section 11 (ODBC Connectivity) of the MSACCESS forum on CompuServe as RJETWP.ZIP. The paper for Access 2 is in RJETV2.ZIP. This paper can also be found on the MSDN Level I CD. An additional document called The Jet Database Engine 2.0: A User's Overview by Paul Litwin can also be found on MSDN Level I. #### Single-Tier and Multiple-Tier ODBC Drivers ODBC drivers are classified as members of one of the two following categories: Single-tier ODBC drivers are designed for use with DBMs that don't have the capability to process ANSI SQL statements. Single-tier drivers translate ANSI SQL statements into a long series of low-level instructions that operate directly on the files that constitute the database. At present, Microsoft and Q+E single-tier ODBC drivers connect to dBASE, FoxPro, Paradox, Btrieve, Excel, and text files. Microsoft is said to be currently working on a Lotus 1-2-3 spreadsheet driver, scheduled to be released in early 1996. Multiple-tier ODBC drivers rely on the client-server RDBMS to process SQL statements. Using the back-end server to process queries is a more efficient process than the single-tier approach in a multiuser or networked environment. Network traffic is minimized because the server returns only the rows specified by the criteria of your SQL statement. This chapter concentrates on the use of single-tier ODBC drivers. #### ODBC API Conformance Levels Three levels of conformance to the ODBC API are defined: core level, level 1, and level 2. Here are the general definitions for each of the three levels: Core-level conformance provides for connecting to the database, preparing and executing SQL statements, receiving query result datasets, committing or rolling back transactions, and retrieving error information. Level 1 conformance is similar to core-level conformance. It adds connectivity to datasources with dialogs designed specifically for the ODBC driver, get- and set-connection options, and obtain-catalog and driver/datasource capabilities. Level 2 conformance is similar to level 1. It adds the capability to list and browse the connections to datasources available to the client, retrieve query result sets in array format, use scrollable cursors, and obtain additional catalog information, such as privileges, keys, and lists of stored procedures. Core-level conformance meets the requirements of the SAG CLI specification. The majority of commercial ODBC drivers provide level 1 conformance, plus the scrollable cursor feature of level 2. Future versions of ODBC drivers probably will provide full level 2 conformance if the RDBMS that the driver is designed for supports level 2 features. #### ODBC SQL Conformance Levels The ODBC API specifies three levels of conformance to SQL grammar: minimum, core, and extended. The SQL conformance levels define the ANSI SQL reserved words that ODBC drivers and datasources must be able to process. Table 7.1 lists the Data Manipulation Language (DML), Data Definition Language (DDL), and Data Control Language (DCL) SQL reserved words required for each level of conformance. SQL data types and expressions also are listed. Successively higher levels of grammar include the grammar of the lower levels. The core SQL grammar conforms to the requirements of the SQL Access Group CAE SQL draft specification—1991; almost all commercial ODBC drivers conform to at least the core-level SQL grammar. Table 7.1. SQL grammar, data type, and expression support for ODBC grammar conformance levels.
> Level | DML Statements | DDL/DCL Statements | Data Types | Expressions
> Minimum | SELECT, | CREATE | SQL_CHAR | Simple
> | INSERT, | TABLE, |  | arithmetic
> | UPDATED | DROP TABLE |  |
> | SEARCHED, |  |  |
> | DELETE |  |  |
> | SEARCHED, |  |  |
> | COMMIT |  |  |
> | TRANSACTION, |  |  |
> | ROLLBACK |  |  |
> | TRANSACTION |  |  |
> Core | Full SELECT, | ALTER | SQL_VARCHAR, | Subqueries
> | positioned | TABLE, | SQL_DECIMAL, | and aggregate
> | UPDATE, and | CREATE | SQL_NUMERIC, | functions
> | positioned | INDEX, | SQL_SMALLINT, |
> | DELETE | DROP | SQL_INTEGER, |
> |  | INDEX, | SQL_REAL, |
> |  | CREATE | SQL_FLOAT, |
> |  | VIEW, | SQL |
> |  | DROP | DOUBLEPRECISION |
> |  | VIEW, |  |
> GRANT, |  |  |  |
> |  | REVOKE |  |
> Extended | LEFT OUTER | Batch SQL | SQL_LONGVARCHAR, | SUBSTRING, ABS
> | JOIN, RIGHT | statements, | SQL_BIT, |
> | OUTER JOIN | stored | SQL_TINYINT, |
> |  | procedures | SQL_BIGINT, |
> |  |  | SQL_BINARY, |
> |  |  | SQL_VARBINARY, |
> |  |  | SQL_LONGVARBINARY, |
> |  |  | SQL_DATE, |
> |  |  | SQL_TIME, |
> |  |  | SQL_TIMESTAMP |
> The extended-level SQL grammar category is a catch-all for extensions to ANSI SQL that were standardized in SQL-92, such as reserved words to enforce referential integrity and nonstandard reserved words that are found in many RDBMS implementations of SQL. NOTE At the time this book was written, no commercial ODBC drivers fully supported the equivalent of Access SQL's PIVOT and TRANSFORM keywords used to create crosstab queries. Crosstab queries, a subject of the next chapter, are one of the most useful forms of summary queries. In most cases, the Access database engine sends GROUP BY statements to the server and then transforms the results into crosstab form. It's likely that future versions of RDBMS and the ODBC drivers that accompany them will include PIVOT and TRANSFORM (or their equivalents) as SQL reserved words. #### ODBC Data Types and the Access Database Engine Table 5.8 in Chapter 5, "Learning Structured Query Language," provides a list of the field data types of SQL-92 and their Access SQL equivalents (where exact equivalents are supported). The names assigned to some ODBC data types that appear in Table 7.1 differ slightly from the formal SQL-92 naming conventions. You use the ODBC data type keywords in SQL statements unless you've specified the SQL pass-through option. Access converts most unsupported numeric data types to double-precision numbers. In most cases, a double-precision number is adequate to represent accurately any numeric value commonly found in database tables. There is no provision in Access field data types to specify the precision and scale of a numeric data type. Precision is the number of digits that compose the number (including digits in the fractional portion of the number). Scale is the number of digits following the decimal point. In xBase, you specify the precision and scale of a field by specifying the width of the field (including a position for the decimal point) followed by the number of decimal places. Access converts numeric values to either single-precision or double-precision numbers based on the values of precision and scale of the ODBC numeric data type. CAUTION It's an uncommon practice to use a numeric field with decimal fraction values as the primary key field of a table. Doing so can cause the Access database engine to lose the bookmark values that specify the location of a record by its primary key value. If the precision or scale of a numeric value used as a primary key field on which a unique index is created exceeds the representational capability of Access's double-precision field, the message #Deleted appears in a text box bound to the field. Access has a repertoire of field data types that is broader than that of any desktop database presently supported by commercial ODBC drivers. #### Built-In ODBC Scalar Functions The ODBC API contains a variety of built-in functions that you can use to make the ANSI SQL code that you send to the RDBMS's ODBC driver with the SQL pass-through option independent of the RDBMS in use. To implement the ODBC scalar functions, you use the ODBC escape shorthand syntax. Escape shorthand syntax, called simply escape syntax in this book, encloses the shorthand syntax in French braces ({}). The escape syntax for all ODBC functions is
>     {fn ODBCFunction([Argument(s)])}
> Thus, if you want to return a person's first name and last name from values in the first_name and last_name fields of a table, you use the following statement:
>     {fn CONCAT(first_name, CONCAT(' ', last_name))}
> You need an embedded CONCAT() function, because CONCAT() supports concatenation of only two strings. (This is unlike concatenation with Visual C++'s & symbol, which lets you concatenate any number of strings.) Note that the standard literal string identifier character is the single quote (') in ANSI SQL. The following sections compare the ODBC scalar functions for string, numeric, and date/time values to the equivalent functions of Visual C++ (where equivalents exist). This information is derived from the Programmer's Reference for the Microsoft Open Database Connectivity Software Development Kit. NOTE In most cases, you can use Visual C++'s built-in functions in your application code to perform the same operations that are offered by the ODBC scalar functions. However, you might need to use the ODBC scalar functions to create joins between table fields of different data types. ##### String Manipulation Functions Table 7.2 lists the ODBC string functions and their equivalent functions, where available, in Visual C++. Table 7.2. The scalar string functions of ODBC version 1.0.
> ODBC String Function | Purpose
> ASCII(string_exp) | Returns the ASCII code value of the leftmost character of a string.
> CHAR(integer_exp) | Returns the ASCII character whose code is integer_exp.
> CONCAT(string1, string2) | Concatenates string1 and string2.
> INSERT(string1, start, | Replaces the length characters of
> length, string2) | string1 beginning at start with string2.
> LEFT(string_exp, count) | Returns the leftmost count characters.
> LENGTH(string_exp) | Returns an integer representing the length of the string.
> LOCATE(string1, | Returns an integer representing the
> string2[, start]) | position of string2 in string1.
> LCASE(string_exp) | Returns an all-lowercase string.
> REPEAT(string_exp, count) | Returns a string consisting of string_exp repeated count times.
> RIGHT(string_exp, count) | Returns the rightmost count characters.
> RTRIM(string_exp) | Removes trailing blank spaces.
> SUBSTRING(string_exp, | Returns length characters beginning at
> start, length) | start.
> UCASE(string_exp) | Returns an all-uppercase string.
> ##### Numeric Scalar Functions Table 7.3 lists the ODBC numeric functions, except for trigonometric functions, and their equivalent functions, where available, in Visual C++. Table 7.3. The numeric scalar functions available in ODBC version 1.0.
> ODBC Numeric Function | Purpose
> ABS(numeric_exp) | Returns the absolute value of the expression.
> CEILING(numeric_exp) | Returns the next largest integer greater than the expression.
> EXP(numeric_exp) | Returns the exponential value of the expression.
> FLOOR(numeric_exp) | Returns the largest integer less than or equal to the expression.
> LOG(float_exp) | Returns the natural (naperian) logarithm of the expression.
> MOD(integer_exp) | Returns the remainder of integer division as an integer.
> PI() | Returns the value of pi as a floating-point number.
> RAND([integer_exp]) | Returns a random floating-point number with an optional seed value.
> SIGN(numeric_exp) | Returns –1 for values less than 0, 0 for 0 values, and 1 for values greater than 0.
> SQRT(float_exp) | Returns the square root of a floating-point value.
> ##### Date, Time, and Timestamp Functions Table 7.4 lists the ODBC date, time, and timestamp functions and their equivalent functions, where available, in Visual C++. Table 7.4. The date/time scalar functions of ODBC version 1.0.
> ODBC Date/Time Function | Purpose
> NOW() | Returns the date and time in TIMESTAMP format.
> CURDATE() | Returns the current date.
> CURTIME() | Returns the current time.
> DAYOFMONTH(date_exp) | Returns the day of the month.
> DAYOFWEEK(date_exp) | Returns the day of the week (Sunday = 1).
> DAYOFYEAR(date_exp) | Returns the Julian date.
> HOUR(time_exp) | Returns the hour (0 to 23).
> MINUTE(time_exp) | Returns the minute (0 to 59).
> MONTH(date_exp) | Returns the number of the month.
> QUARTER(date_exp) | Returns the number of the calendar quarter.
> SECOND(time_exp) | Returns the second (0 to 60).
> WEEK(date_exp) | Returns the week number (1 to 52).
> YEAR(date_exp) | Returns the four-digit year.
> #### ODBC Initialization Files The ODBC administrator applications, ODBCCP32.DLL and ODBCAD32.EXE, create or delete entries in the two ODBC initialization files in your \WINDOWS directory. The purpose of these two files and the relevance of the entries they contain are explained in the following sections. ##### ODBCINST.INI After installing the ODBC driver manager and administrator files, the first step in providing SQL connectivity is to add ODBC drivers for the types of databases to which your applications connect. When you install an ODBC driver, the driver setup application adds an entry to the [ODBC Drivers] section of the installed drivers initialization file, ODBCINST.INI, and marks the driver as available with an Installed value. For the 32-bit drivers, the same technique is used, with the section named as [ODBC 32 bit Drivers]. It appears that 32-bit drivers use the ODBCINST.INI file for backwards compatibility. The contents of the ODBCINST.INI file of the workstation computer used to write this book are as follows:
>     [ODBC Drivers]

>     Microsoft Excel Driver (*.xls)=Installed

>     Microsoft Text Driver (*.txt; *.csv)=Installed

>     SQL Server=Installed

>     Microsoft Access Driver (*.mdb)=Installed

>     Microsoft Dbase Driver (*.dbf)=Installed

>     Microsoft FoxPro Driver (*.dbf)=Installed

>     Access Data (*.mdb)=Installed

>     [ODBC Translators]

>     MS Code Page Translator=Installed

>     [Microsoft Access Driver (*.mdb) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     32Bit=1

>     [ODBC 32 bit Drivers]

>     Microsoft Access Driver (*.mdb) (32 bit)=Installed

>     Microsoft Dbase Driver (*.dbf) (32 bit)=Installed

>     Microsoft Excel Driver (*.xls) (32 bit)=Installed

>     Microsoft FoxPro Driver (*.dbf) (32 bit)=Installed

>     Microsoft Paradox Driver (*.db ) (32 bit)=Installed

>     Microsoft Text Driver (*.txt; *.csv) (32 bit)=Installed

>     SQL Server (32 bit)=Installed

>     Oracle (32 bit)=Installed

>     CR SQLBase (32 bit)=Installed

>     CR Oracle7 (32 bit)=Installed

>     CR Sybase System 10 (32 bit)=Installed

>     [Microsoft Dbase Driver (*.dbf) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\oddbse32.dll

>     32Bit=1

>     [Microsoft Excel Driver (*.xls) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\odexl32.dll

>     32Bit=1

>     [Microsoft FoxPro Driver (*.dbf) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\odfox32.dll

>     32Bit=1

>     [Microsoft Paradox Driver (*.db ) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\odpdx32.dll

>     32Bit=1

>     [Microsoft Text Driver (*.txt; *.csv) (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     Setup=C:\WINDOWS\SYSTEM\odtext32.dll

>     32Bit=1

>     [Microsoft Excel Driver (*.xls)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     Setup=C:\WINDOWS\SYSTEM\odexl16.dll

>     SQLLevel=0

>     FileExtns=*.xls

>     FileUsage=1

>     DriverODBCVer=02.01

>     ConnectFunctions=YYN

>     APILevel=1

>     [Microsoft Text Driver (*.txt; *.csv)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     Setup=C:\WINDOWS\SYSTEM\odtext16.dll

>     SQLLevel=0

>     FileExtns=*.,*.asc,*.csv,*.tab,*.txt

>     FileUsage=1

>     DriverODBCVer=02.01

>     ConnectFunctions=YYN

>     APILevel=1

>     [SQL Server]

>     Driver=C:\WINDOWS\SYSTEM\sqlsrvr.dll

>     Setup=C:\WINDOWS\SYSTEM\sqlsrvr.dll

>     APILevel=1

>     ConnectFunctions=YYY

>     DriverODBCVer=02.01

>     FileUsage=0

>     SQLLevel=1

>     [SQL Server (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\sqlsrv32.dll

>     Setup=C:\WINDOWS\SYSTEM\sqlsrv32.dll

>     32Bit=1

>     [Microsoft Access Driver (*.mdb)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     Setup=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     SQLLevel=0

>     FileExtns=*.mdb

>     FileUsage=2

>     DriverODBCVer=02.01

>     ConnectFunctions=YYN

>     APILevel=1

>     [Microsoft Dbase Driver (*.dbf)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     Setup=C:\WINDOWS\SYSTEM\oddbse16.dll

>     SQLLevel=0

>     FileExtns=*.dbf,*.ndx,*.mdx

>     FileUsage=1

>     DriverODBCVer=02.01

>     ConnectFunctions=YYN

>     APILevel=1

>     [Microsoft FoxPro Driver (*.dbf)]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     Setup=C:\WINDOWS\SYSTEM\odfox16.dll

>     SQLLevel=0

>     FileExtns=*.dbf,*.cdx,*.idx,*.ftp

>     FileUsage=1

>     DriverODBCVer=02.01

>     ConnectFunctions=YYN

>     APILevel=1

>     [MS Code Page Translator]

>     Translator=C:\WINDOWS\SYSTEM\mscpxlt.dll

>     Setup=C:\WINDOWS\SYSTEM\mscpxlt.dll

>     [Access Data (*.mdb)]

>     Driver=C:\WINDOWS\SYSTEM\simba.dll

>     Setup=C:\WINDOWS\SYSTEM\simadmin.dll

>     "FileUsage"=2

>     "FileExtns"=*.mdb

>     "DirectConnect"=0

>     [Oracle (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\vsorac32.dll

>     Setup=C:\WINDOWS\SYSTEM\orstub32.dll

>     32Bit=1

>     [CR SQLBase (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\crgup07.dll

>     Setup=C:\WINDOWS\SYSTEM\crgup07.dll

>     32Bit=1

>     [CR Oracle7 (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\cror707.dll

>     Setup=C:\WINDOWS\SYSTEM\cror707.dll

>     32Bit=1

>     [CR Sybase System 10 (32 bit)]

>     Driver=C:\WINDOWS\SYSTEM\crsyb07.dll

>     Setup=C:\WINDOWS\SYSTEM\crsyb07.dll

>     32Bit=1
> The preceding ODBCINST.INI file includes entries for the following 16-bit datasources, which can be used with legacy 16-bit applications: Six ODBC datasource types are provided by the Microsoft ODBC Desktop Database Drivers kit. They're included with 16-bit applications that come supplied with Microsoft ODBC: Microsoft Excel Driver (*.XLS) Microsoft Text Driver (*.TXT, *.CSV) SQL Server Microsoft Dbase Driver (*.DBF) Microsoft FoxPro Driver (*.DBF) Access Data (Simba version comes with ODBC driver pack) The Access 2.0 driver that is supplied with the Microsoft Office product. Microsoft Access Driver. (The Jet driver, which comes with Microsoft Office 2, can't be used with MFC applications.) The preceding ODBCINST.INI file includes entries for the following 32-bit datasources, which can be used with newer 32-bit applications: Six ODBC datasource types are provided by the Microsoft ODBC Desktop Database Drivers kit. They're included with 32-bit applications that come supplied with Microsoft ODBC: Microsoft Access Driver (*.MDB) (32-bit) Microsoft Dbase Driver (*.DBF) (32-bit) Microsoft Excel Driver (*.XLS) (32-bit) Microsoft FoxPro Driver (*.DBF) (32-bit) Microsoft Paradox Driver (*.DB) (32-bit) Microsoft Text Driver (*.TXT, *.CSV) (32-bit) The ODBC SQL server provided by the Microsoft ODBC Desktop Database Drivers kit and included with 32-bit applications that come supplied with Microsoft ODBC: SQL Server (32-bit) The ODBC Oracle driver provided by the Microsoft ODBC Desktop Database Drivers kit and included with 32-bit applications that come supplied with Microsoft ODBC: Oracle (32-bit) installed Three ODBC drivers installed with the Crystal Reports generator, which is supplied with Crystal Reports. These are installed automatically when you install the Crystal Reports module, which is included with Visual C++ 4.0: CR SQLBase (32-bit) CR Oracle7 (32-bit) CR Sybase System 10 (32-bit) The datasources appear in the Add Data Source dialog box, which appears when you launch the ODBC Administrator application (either from Control Panel or by executing ODBCAD32.EXE) and then click the Drivers button in the initial Data Sources dialog box. Figure 7.2 shows the drivers listed in the [ODBC Drivers] section of the system registry. Figure 7.2. The Add Data Source dialog box of the ODBC Administrator application. With the Windows 3.1x 16-bit ODBC drivers, SIMBA.DLL is the master ODBC driver for all Microsoft single-tier database drivers. ("Simba" is the Swahili word for "lion.") With the exception of Excel worksheet and ASCII text files, the database file types supported by SIMBA.DLL are the same as those supported by the Access database engine: Access, dBASE, FoxPro, Paradox, and Btrieve. Windows 95 (and Windows NT 3.5x) uses 32-bit ODBC drivers. ODBC no longer uses the original Simba driver model. Instead, all the ODBC drivers work at the same level, called directly by either ODBCJT32.DLL (for ODBC) or MSJT3032.DLL (for DAO). Examples of the use of Microsoft Query and the Access ODBC Desktop Database Drivers appear in the following sections. ##### ODBC.INI Entries in the ODBC.INI initialization file designate the ODBC datasources that appear in the Data Sources dialog box of the ODBC Administrator application. As is the case for ODBCINST.INI, ODBC.INI has a two-tiered structure. A list of datasources appears in ODBC.INI's [ODBC Data Sources] section, followed by a section such as dbCMOLSC that provides additional information required by the ODBC driver for the datasource. NOTE Windows 95 (and Windows NT 3.5x) use the system registry rather than .INI files to store information used by applications and subsystems. Most of the ODBC information is contained in the registry keys (Windows 95) [HKEY_LOCAL_MACHINE\SOFTWARE\ODBC] and (usually) [HKEY_USERS\.Default\Software\ODBC]. The subkeys found under these keys are arranged in a manner similar to the 16-bit .INI files. For 32-bit ODBC drivers, this information is contained in the system registration database. The 16-bit ODBC drivers save information in the ODBC.INI file. The ODBC.INI file that is used in conjunction with the ODBCINST.INI file described in the preceding section appears as follows:
>     [ODBC Data Sources]

>     MS Access 2.0 Databases=Microsoft Access Driver (*.mdb)

>     Text CSV=Microsoft Text Driver (*.txt; *.csv)

>     STARmanager=Microsoft dBASE Driver (*.dbf)

>     Student Registration=Microsoft Access Driver (*.mdb)

>     Nafta=Access Data (*.mdb)

>     NorthWind V2=Access Data (*.mdb)

>     [MS Access 2.0 Databases]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     FIL=MS Access;

>     DefaultDir=C:\ACCESS

>     JetIniPath=MSACC20.INI

>     UID=Admin

>     DriverID=25

>     [Text CSV]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     DefaultDir=E:\STARBETA

>     Description=Text CSV files

>     DriverId=27

>     FIL=text;

>     JetIniPath=odbcddp.ini

>     [STARmanager]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     DefaultDir=E:\STARBETA

>     Description=STARmanger

>     DriverId=21

>     FIL=dBASE III;

>     JetIniPath=odbcddp.ini

>     [Student Registration]

>     Driver=C:\WINDOWS\SYSTEM\odbcjt16.dll

>     DriverId=25

>     JetIniPath=odbcddp.ini

>     UID=admin

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     [ODBC 32 bit Data Sources]

>     dBASE Files=Microsoft dBASE Driver (*.dbf) (32 bit)

>     Excel Files=Microsoft Excel Driver (*.xls) (32 bit)

>     FoxPro Files=Microsoft FoxPro Driver (*.dbf) (32 bit)

>     Paradox Files=Microsoft Paradox Driver (*.db ) (32 bit)

>     CRSS=SQL Server (32 bit)

>     CRGUP=CR SQLBase (32 bit)

>     CROR7=CR Oracle7 (32 bit)

>     CRSYB=CR Sybase System 10 (32 bit)

>     Text Files=Microsoft Text Driver (*.txt; *.csv) (32 bit)

>     Student Registration=Microsoft Access Driver (*.mdb) (32 bit)

>     [dBASE Files]

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     [Excel Files]

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     [FoxPro Files]

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     [Paradox Files]

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll

>     [Nafta]

>     Driver=C:\WINDOWS\SYSTEM\simba.dll

>     FileType=RedISAM

>     DataDirectory=T:\nafta.mdb

>     SingleUser=True

>     UseSystemDB=True

>     SystemDB=C:\MAPNGO\system.mda

>     [CRSS]

>     Driver32=C:\WINDOWS\SYSTEM\sqlsrv32.dll

>     [CRGUP]

>     Driver32=C:\WINDOWS\SYSTEM\crgup07.dll

>     [CROR7]

>     Driver32=C:\WINDOWS\SYSTEM\cror707.dll

>     [CRSYB]

>     Driver32=C:\WINDOWS\SYSTEM\crsyb07.dll

>     [NorthWind V2]

>     Driver=C:\WINDOWS\SYSTEM\simba.dll

>     Description=MS NorthWind Sample Database

>     FileType=RedISAM

>     DataDirectory=c:\access\sampapps\nwind.mdb

>     SingleUser=False

>     UseSystemDB=False

>     [Text Files]

>     Driver32=C:\WINDOWS\SYSTEM\odbcjt32.dll
> Figure 7.3 shows the entries in the Data Sources dialog box of the ODBC Administrator that correspond to the entries in the preceding ODBC.INI file. Figure 7.3. ODBC datasources for dBASE datasources. Figure 7.4 shows the registration database entries for the ODBC SQL Server 32-bit driver. The information in the registry typically matches the ODBC.INI file if both the 16-bit ODBC (which uses ODBC.INI) and the 32-bit ODBC (which is in the registry) are in sync. Figure 7.4. ODBC entries in the system registration database. ### Using Desktop ODBC Datasources with Visual C++ If you have RED110.DLL, the Access ODBC driver that is supplied with the Microsoft ODBC Desktop Database Drivers kit and with Microsoft Query, you can compare the performance of queries executed against STDREG32.MDB directly by the Access database engine or by the single-tier ODBC driver for Access databases by adding an ODBC datasource created from STDREG32.MDB. The RED110.DLL file is a 16-bit driver. You could also compare the system's performance to a 32-bit driver/application combination. The following sections show you how to create an ODBC datasource named Student Registration from the STDREG32.MDB Access database, run queries against the Student Registration datasource, and trace SQL statements and ODBC function calls. #### Adding STDREG32.MDB as an ODBC Datasource To create the Student Registration ODBC datasource, follow these steps: Launch the ODBC Administrator application (called either "ODBC" or "32bit ODBC") from the Windows Control Panel. If you're using Windows 95, be sure to use the 32-bit version of ODBC. Click the Add button in the Data Sources dialog box, shown in Figure 7.5, to display the Add Data Source dialog box. Figure 7.5. Choosing the Access database class for the ODBC datasource. Double-click MS Access 7.0 Database (Microsoft Access Driver (*.mdb) in the Installed ODBC Drivers list box (see Figure 7.2) to display the ODBC Microsoft Access 7.0 Setup dialog box, shown in Figure 7.6. Click the Options button to display the additional choices that are available. Figure 7.6. The opening dialog box that is used to create a new Access ODBC datasource. NOTE If you don't have MS Access 7.0 Database (Microsoft Access Driver (*.mdb)) displayed in your list of datasources, click the Add button and add the driver from there. Enter Student Registration in the Data Source Name text box and a description of the STDREG32.MDB database in the Description text box (use Student Registration if you can't think of anything else). Click the Select button to open the Select Database dialog box. If you're using Visual C++ 4.0, the STDREG32.MDB database is found in the directory V:\MSDEV\SAMPLES\MFC\DATABASE\STDREG, where V: is either the drive where you installed the sample applications or the Visual C++ 4.0 distribution CD. Users of other versions of Visual C++ should use the command dir /s for stdreg*.mdb to find the file. The non-Access 7 version of STDREG is usually called STDREG.MDB. Select the STDREG32.MDB database file as the datasource and close the Select Database dialog box. Make sure that the None option button is selected, unless you have the correct version of Access installed and you want to use your SYSTEM.MDA library to demonstrate password-protected ODBC logon procedures. Your ODBC Microsoft Access 7.0 Setup dialog box appears as shown in Figure 7.7. Figure 7.7. The ODBC Microsoft Access 7.0 Setup dialog box that is used to create the Student Registration datasource. Click the OK button to close the dialog box, and then click the Close button of the Data Sources dialog box to exit the ODBC Administrator application. You now have an ODBC datasource, Student Registration, that you can use with any application that supports connections to ODBC datasources, such as Visual C++, Access, and Excel with Microsoft Query. #### Using the Student Registration Datasource in a Visual C++ Program To open the Student Registration datasource, follow these steps: Open the Visual C++ sample application \MSDEV\SAMPLES\MFC\TUTORIAL\ENROLL\STEP4 and build the program if necessary. Start the program to automatically open the Student Registration datasource. NOTE When you're building the examples supplied with Visual C++ 4, they must be located on a drive that you have the ability to write to. If you haven't copied the example from the CD to a hard drive, you must do so before you try to build it. You can't write to the Visual C++ 4 distribution CD. Try using each of the functions available from the ENROLL program. If you have MS Query or Access, you can compare these two applications with the ENROLL program. Because MS Query uses ODBC to access datasources, MS Query won't exhibit much better performance than ENROLL. #### Tracing the Execution of ODBC Function Calls and SQL Statements To trace the behind-the-scenes activity that occurs when you execute a query from any application that uses ODBC to access a datasource, you need to enable ODBC's data logging option. You set this option using the ODBC Administrator (found in Control Panel). You must enable ODBC event logging by clicking the Options button in the ODBC Administrator Data Sources dialog box. The ODBC Options dialog box, shown in Figure 7.8, appears. Figure 7.8. The ODBC Administrator's ODBC Options dialog box. In this dialog box, you can specify that all ODBC transactions be traced (that is, logged) to a file. You also can select the Stop Tracing Automatically check box to make the logging active for the current session. WARNING When ODBC tracing is enabled, ODBC's performance will be substantially affected. Enable tracing only when needed, and turn tracing off as soon as you're done using it. If you don't turn logging off, the resulting tracing files ultimately grow to mammoth proportions. Therefore, you must be careful not to fill all your disk's free space with the ODBC trace file, because any application that interacts with ODBC can write thousands of records to the file. ### Using Microsoft Query and the Desktop Database Drivers One of the many advantages of upgrading to (or acquiring) Excel 7, Word for Windows 7, or Microsoft Office 95 is that Microsoft Query and the suite of Microsoft Desktop Database Drivers are bundled with these three mega-apps. Microsoft Query is very useful for learning how to write ANSI SQL statements and how to use the Desktop Database drivers that accompany MS Query. MS Query uses ANSI SQL, with modifications to accommodate ODBC data types, not Access SQL to execute SELECT queries. (MS Query doesn't use the Access database engine to process queries.) Thus, you can use MS Query to validate SQL SELECT query statements you write in the form of string variables in your Visual C++ database applications that use the SQL pass-through option. Microsoft Query also is part of Visual C++ 1.5x. Since Visual C++ 2.x includes Visual C++ 1.5x, you should always be able to find a copy of Microsoft Query there. TIP If nothing else, using MS Query is a way to avoid typing a lengthy query string. Do this by designing your query interactively, and then use the Clipboard to cut and paste the resulting SQL string. The following sections describe how to create an ODBC datasource, MSQueryDBF, and how to use Microsoft Query as a stand-alone application to execute queries against the datasource. #### Creating an ODBC Datasource from dBASE Files Microsoft Query comes with a sample dBASE III database whose .DBF files are located, along with the Microsoft Query executable files, in your \WINDOWS\MSAPPS\MSQUERY directory. To create a dBASE III ODBC datasource called MSQueryDBF from these files, follow these steps: Launch Microsoft Query (you probably will have an icon in either your Excel or Word folder). Choose File | New Query or click the New Query button to open the Select Data Source dialog box. An empty Select Data Source dialog box appears, as shown in Figure 7.9, if you haven't previously added an MS Query datasource. NOTE There are two versions of MS Query: a 16-bit version in MSQUERY.EXE and a 32-bit version in MSQRY32.EXE. You should use the 32-bit version if possible because it's compatible with the 32-bit ODBC drivers used with Windows NT and Windows 95. Figure 7.9. Microsoft Query's Select Data Source dialog box. Click the Other button to open the ODBC Data Sources dialog box. ODBC datasources you have created previously, listed in ODBC.INI, appear in the ODBC Data Sources dialog box, shown in Figure 7.10. Figure 7.10. Microsoft Query's ODBC Data Sources dialog box. Click the New button to display the Add Data Source dialog box, shown in Figure 7.11. Figure 7.11. The ODBC Administrator's Add Data Source dialog box. Double-click the dBASE Files (*.dbf) entry to open the ODBC dBASE Setup dialog box, shown in Figure 7.12. Figure 7.12. The ODBC dBASE Setup dialog box. Click the Options button to display the expanded version of the dialog box. Click the dBASE III option button and the Select Directory command button to display the Select Directory dialog box. Choose the \WINDOWS\MSAPPS\MSQUERY directory, where six .DBF files appear, and then click the OK button to close the Select Directory dialog box. Complete the entries in the ODBC dBASE Setup dialog box as shown in Figure 7.13. Click the OK button to close the dialog box and display the ODBC Data Source dialog box. Figure 7.13. The ODBC dBASE Setup dialog box for the Microsoft Query sample database. TIP The collating sequence shown in Figure 7.13 is ASCII, which is the normal collating sequence for text fields of dBASE and FoxPro files created by DOS applications. The sample dBASE files supplied with Microsoft Query are exported from Access's Northwind Traders sample database. These files use the ANSI (Windows) collating sequence. Choose the ANSI collating sequence for files that are created by Windows applications. Double-click the MSQueryDBF entry in the ODBC Data Source dialog box to redisplay MS Query's Select Data Source dialog box with your new MS Query datasource added. Microsoft Query maintains its own list of ODBC datasources in the MSQUERY.INI file that you'll find in your \WINDOWS directory. The typical contents of the MSQUERY.INI file are as follows:
>     [Table Options]

>     Tables=1

>     Views=1

>     Synonyms=1

>     SysTables=0

>     [Microsoft Query]

>     Tutorial=0

>     Maximized=0

>     [Data Sources]

>     MSQueryDBF=
Because MS Query maintains its own list of datasources, you need to create an explicit ODBC datasource for MS Query using the preceding procedure. Alternatively, you can use the ODBC Administrator application to create the ODBC datasource and then add the datasource to MSQUERY.INI's [Data Sources] section with a text editor or by choosing the ODBC datasource that you created in MS Query's ODBC Data Sources dialog box. Like the Student Registration ODBC datasource you created earlier in this chapter, you can use the MSQueryDBF datasource with any ODBC-compliant application. #### Using an ODBC Datasource with Microsoft Query Microsoft Query offers a valuable resource to Visual C++ database programmers. It allows you to manipulate ODBC datasources, create SQL statements graphically, and view the results of a query easily and dynamically. That's the good news. The bad news is that Microsoft Query doesn't always work as well as it should, and it's not a proper Windows 95 application (it doesn't allow long filenames, for example). NOTE Microsoft Query comes in two flavors, a 16-bit version and a 32-bit version. The 16-bit version works only with 16-bit ODBC drivers, and the 32-bit version works only with 32-bit ODBC drivers. The 32-bit version of Microsoft Query can be found on the Microsoft Office 95 distribution CD, in the directory U:\OFFICE95\OS\MSAPPS\MSQUERY, where U: is the drive containing the Microsoft Office 95 CD. The executable filename is MSQRY32.EXE. The 16-bit version of Microsoft Query isn't included with Office 95. In setting up a dBASE data source, we'll assume that you created your Student Registration data source as outlined earlier and that you called it Student Registration. If by chance you didn't install the 32-bit version of the student registration database, you should do so before continuing with this part of the chapter. Load your Student Registration database into Microsoft Query. Start Microsoft Query and click the New Query button (the first button on the left) or choose File | New Query. The Select Data Source dialog box appears. If Student Registration doesn't appear in the list of available databases, add it using the same techniques discussed earlier, when you added the Northwind Traders database to Microsoft Query. Double-click the Student Registration datasource in the Available Data Sources list box. Microsoft Query will then display the Add Tables dialog box, shown in Figure 7.14. Figure 7.14. Microsoft Query's Add Tables dialog box. Add the Course, Enrollment, Instructor, Section, and Student tables in sequence to the query by selecting the table name in the Table list box and then clicking the Add button. (You can ignore the table called Dynabind Section.) Click the Close button when you've added the five tables. You'll see that the tables were added to the query design pane of Microsoft Query's main window. Joins should already exist between each of these tables. There should be a join between CourseID and the Course, Enrollment, and Section tables. There should be a join between StudentID and the Enrollment and Student tables. There should be a join between SectionNo and the Section and Enrollment tables. There should be a join between InstructorID and the Section and Instructor tables. If any of these joins are missing, either restore the original database from the Visual C++ CD or simply re-create the join. Create joins between tables by clicking and dragging the column to be joined in the first table to the column to which it is to be joined in the second table. (Although the column names don't have to be identical, it makes sense to use the same name for columns that contain the same information when you design your database.) For example, to join the StudentID column in the Enrollment table with the StudentID column in the Student table, click and drag the StudentID column in the Enrollment table and drop it on the StudentID column in the Enrollment table. To view your joins, select Table | Joins. You will see the dialog shown in Figure 7.15. Figure 7.15. The Microsoft Query Joins dialog box. Once your database has been set up (it has been loaded, and the necessary joins have been created), you can the perform queries on it. NOTE After you've created your joins, they will continue as part of the database until they're removed. Joins are a feature of the database, not Microsoft Query. An example of a simple query would be to display a group of records from a single table. For instance, you can show the records in the Student table by either selecting all columns (the * at the top of the lists of columns signifies all columns) or by selecting each desired column in the order you want it displayed. Figure 7.16 shows all the columns in the Student table in their default order. Figure 7.16. Student Registration's Student table with all columns displayed. NOTE Up to this point, using MS Query has been similar to the methods you employ in Access's query design window, except that the field names in the table windows are in alphabetical order rather than the left-to-right order of the fields in the tables. Equi-joins are indicated by lines connecting the fields with dot terminators instead of Access's single arrowhead. Access has a query criteria grid that you can display if you want to. MS Query's criteria grid is optional. The following steps demonstrate the difference between using Access's query design mode and that of MS Query. To create a more complex query, add all the fields from the Instructor and Course tables to your query. Reorder the fields so that the tables are displayed in this order: Course, Instructor, and Student. Leave the columns in their default order. See Figure 7.17. Figure 7.17. Adding fields to the Microsoft Query grid. NOTE You can also drag a field from a table into the open column (the rightmost column) to add a field to the query. If the Automatic Query button (the button with the ! and the semicircular arrows) is depressed, the column will be updated with the values for the new column. To add a criterion to the query, select Criteria | Add Criteria. Choose the student.GradYear field in the Field combo box and equals in the Operator combo box, and then enter 95 in the Value text box, as shown in Figure 7.18. Click the Add button to apply your criterion to the records in the grid. Figure 7.18. Microsoft Query's Add Criteria dialog box. Click the Close button to return to MS Query's main window. The Criteria pane appears. MS Query translates the peculiar date range syntax you typed in the Value list box of the Add Criteria dialog box to standard Access SQL date/time syntax. Select the Name column by clicking the field name header cell. Then click the Ascending Sort button (AZ) to sort the students by name. Your data grid appears as shown in Figure 7.19. Figure 7.19. The data grid of MS Query after you apply a criterion and a sort order. You can click the SQL button on the toolbar to display the SQL statement that MS Query sends to the ODBC driver manager, ODBC32.DLL. The SQL statement that results from the preceding steps appears as shown in Figure 7.20. Figure 7.20. The ANSI/ODBC SQL statement for the query shown in Figure 7.19. NOTE Don't be surprised if your SQL window doesn't appear exactly like the one shown in Figure 7.20. Figure 7.20 is a composite image created from three bitmaps of the SQL window—one with the first part of the statement, and the others incorporating the last parts of the statement. This book has several composite images of this type. #### Differences Between Access SQL and ANSI/ODBC SQL Statements SQL statements generated by Microsoft Query, such as the ones shown in Figure 7.20, differ somewhat from Access SQL statements that you would send to the Access database engine in Visual C++ database applications. The following characteristics of SQL statements created by MS Query distinguish the statements from Access SQL: ANSI/ODBC SQL doesn't support the DISTINCTROW qualifier to create updatable query result sets. You can, however, use the ANSI SELECT DISTINCT statement to eliminate duplicate rows. ANSI/ODBC SQL employs the WHERE clause rather than the INNER JOIN reserved words to create equi-joins between tables. You use ODBC's special OUTER JOIN escape syntax {oj table_name {LEFT|RIGHT} OUTER JOIN table_name ON join_condition} to create LEFT or RIGHT OUTER JOINs. The advantage of using ODBC escape syntax is that your SQL statement isn't specific to one RDBMS's dialect of SQL. ANSI/ODBC SQL uses the escape syntax to specify DATE, TIME, and TIMESTAMP data. For instance, a {d '1989-05-01'} entry in is an example of the ODBC DATE escape syntax. Substitute t for d to specify TIME and ts for d to specify TIMESTAMP data. You use single quotation marks (') in ANSI/SQL to enclose string literals, including dates and times, instead of Access SQL's choice of single or double (") quotation-mark string identification characters. ANSI/ODBC SQL doesn't support Access SQL's WITHOWNERACCESS OPTION statement. You need explicit permissions for the tables that underlie your ANSI/ODBC queries. You don't end ANSI/ODBC SQL statements with a semicolon (;), as is done in Access SQL. ### Summary This chapter introduced you to version 2.5 of the Microsoft Open Database Connectivity API and provided the basic information you need to use Microsoft and third-party ODBC drivers with Visual C++ applications. I gave examples of creating ODBC datasources from Access files, from dBASE files, and from the Microsoft Query applet. I also covered the differences between Access SQL and ANSI/ODBC SQL statements and showed you when to implement the SQL pass-through option for your queries. The next chapter deals with advanced SQL techniques that you use to transform time-series and similar data from row-column to column-row format. It also shows you how to write SQL statements in Visual C++ code to append, update, and delete records from database tables. Contact reference@earthweb.com with questions or comments. Copyright 1998 EarthWeb Inc., All rights reserved. PLEASE READ THE ACCEPTABLE USAGE STATEMENT. Copyright 1998 Macmillan Computer Publishing. All rights reserved. |