Configuring an ODBC Data Source The process of configuring an ODBC data source is simply a matter of providing some information to the driver manager and the DBMS-specific driver.. The
Trang 1EXEC IAF PUT GLOBAL.OT_TAXES VALUES ( :taxes );
EXEC IAF PUT GLOBAL.OT_GRAND_TOTAL VALUES ( :grand_total );
Performance Tuning
When developing applications with embedded SQL, performance can become a major issue depending on what type of platform you may be using This section provides easy-to-apply methods for improving the performance of your
applications It looks at what causes poor performance and how performance can be improved
See Chapter 15, "Performance Tuning and Optimizing," for more information about performance tuning
Poor Performance
One of the first causes of poor performance is high Oracle communication overhead Oracle processes each SQL statement one at a time, which results in numerous calls to Oracle If you are operating in a network environment, each call creates additional traffic on the network The more traffic you have, the slower the performance will become
The second cause of poor performance is inefficient SQL statements Just because SQL statements can be written in several different ways and still get the same results, this does not mean that every statement is running efficiently In some cases, full table scans will be occurring (which is time consuming if the table is large); in other cases, using indexes greatly speeds up the search
The third cause of poor performance is managing cursors inefficiently The result of not managing cursors correctly is additional parsing and binding, which adds noticeable processing overhead for Oracle
These problems can be improved by reducing Oracle communication overhead or reducing processing overhead The next section provides methods that will help reduce overhead and improve performance
Improving Performance
Improving performance can make a dramatic difference in the way your application functions under normal or high usage Two areas always should be considered when writing an SQL statement: Oracle communications and processing overhead
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 2Reducing Oracle Communication Overhead
There are two methods that can be used to reduce Oracle communication overhead: host arrays and PL/SQL blocks
Using host arrays can dramatically boost your applications performance You can issue one SQL statement to manipulate numerous rows, instead of issuing a SQL statement for each row For example, if you wanted to update 1200 student grades, you could issue one SQL statement with a host array instead of 1200 with just a host variable For more
information, see the section on host arrays
The second method to reducing Oracle communication overhead is to use embedded PL/SQL If your application is database intensive, you can utilize PL/SQL blocks to group SQL statements together and then send the block to Oracle for processing
After reducing the Oracle communication overhead, your next step should be to reduce processing overhead
Reducing Processing Overhead
In order to reduce processing overhead, your SQL statement should be analyzed to ensure it is using the appropriate indexes, it is using row-locking properly, and it is managing cursors effectively To ensure that indexes are being used properly, Oracle has provided tools that will help to identify problem areas
The trace facility in conjunction with the EXPLAIN PLAN statement will generate statistics enabling you to identify which SQL statements are taking a lot of time to execute This explain plan describes what database operations need to
be carried out by Oracle to complete processing of the SQL statement that you have written One of the most common problems with SQL statements is that full table scans are being done instead of indexes being utilized The explain plan indicates if full table scans are being done; from this you can alter the SQL statements to utilize indexes
Another area that can improve performance is how the database is locking data To increase performance you want to lock only at the row level This will enable many users (instead of just one) to access the table Applications that do online transactions can drastically benefit from row locking verses table locking The default value is different
depending on what version of Oracle you are using In Oracle Version 6, row-locking is the default
Managing cursors can create an enormous amount of processing overhead The easiest way to manage cursors is to declare them explicitly This gives you the flexibility to control them as you need resources Remember that you need to PREPARE, DECLARE, OPEN, and CLOSE explicit cursors in dynamic SQL—especially with methods three and four After a cursor has been PREPAREd (which does the parsing), it can be used multiple times until it is CLOSEd This can drastically reduce the parsing and binding that is done with each cursor
Now that you have stepped through each part of creating an embedded SQL host program, it would be advisable to stay current on what new features the precompilers have Oracle has taken extra effort in improving its tools with each step;
as a programmer, you should capitalize on these features
New Features in Version 1.4
The new features in Version 1.4 precompilers help meet the needs of professional software developers Some of the features are as follows:
● New debugging aid The SQLCA stores additional runtime information about the outcome of SQL operations
● Enhanced WHENEVER statement The improved WHENEVER statement now lets you take actions when an
error or warning is detected With previous versions you only had three choices: GOTO, CONTINUE, or STOP Added to version 1.4 is the DO statement, which allows for procedural functions to be taken
Trang 3● Revised HOST option With previous versions of precompilers, the HOST parameter indicated what host
language was being used Version 1.4 uses separate precompilers executables each designed for a specify language
● In previous versions of Oracle precompilers, options for setting the area size (which is initially set for cursor) had to be specified With the current version of precompilers, resizing is automatically done This feature makes the AREASIZE and REBIND options obsolete
● Previous versions of precompilers generated several database calls per embedded SQL statement In Version 1.4, precompilers generate only one (bundled) database call per embedded SQL statement
Remember to try and keep current on the new features Oracle includes in its precompilers This could make a dramatic difference in the performance and functionality of your program
Summary
ORACLE precompilers provide an excellent tool for programmers to create dynamic applications This chapter provided information on what a precompiler does, the benefits of being able to embed SQL statements, how to use it a
precompiler, and how to create a host program
This concludes the section on precompilers I hope that the information has been beneficial to you and has given you some quick tips to enhance performance.
Previous
Page TOC
Next Page Home
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 4Previous
Page TOC
Next Page Home
● 51
❍ ODBC Applications
■ Components of ODBC
■ Configuring an ODBC Data Source
■ Connecting to an ODBC Data Source using the ODBC API
■ Setting Connection Options
■ Applying SQL Transactions
■ Retrieving Result Sets
■ Handling Errors
■ Calling Stored Procedures and Functions
■ Disconnecting and Freeing Resources
■ Debugging ODBC Applications
complexities of the ODBC API
Components of ODBC
ODBC software is made up of several distinct components The application layer contains embedded SQL and logic for data entry, preparing transactions, and displaying result sets It calls API functions exported by the driver manager to connect to the data source, apply SQL, and retrieve results and error codes The driver manager provides the common ODBC interface, loads database-specific drivers as requested by the application, performs call-level validations, and maps ODBC calls to functions exported by the database-specific driver The database-specific driver processes the ODBC function calls, optionally converting SQL and data types to the native syntax of the DBMS, and formats DBMS error codes into a standard format It also returns result sets and error codes to the driver manager The data source consists of the DBMS itself, in addition to any network or operating system software required to connect to it Figure 51.1 illustrates these layers
Figure 51.1 This is a visual representation of the components of ODBC
For local desktop databases, the data source might simply consist of the name of local server or database file When the
Trang 5DBMS resides on a remote server, however, the data source includes any network software required to access the remote host For example, if you attempt to access Oracle on a remote server, SQL*Net must be installed and properly
configured Although this software is not actually part of ODBC, it is considered part of the data source because it is required by ODBC to connect to the database
Configuring an ODBC Data Source
The process of configuring an ODBC data source is simply a matter of providing some information to the driver
manager and the DBMS-specific driver The driver manager uses entries in ODBC.INI to determine what driver to load for a particular data source name The specific driver may use ODBC.INI to determine the server name and the values of any database-specific parameters required to connect
The ODBC administration program ships with nearly all Windows development tools that support ODBC and is
typically installed as part of the Windows Control Panel applet This program, ODBCADM.EXE, and a DLL,
ODBCINST.DLL, are used to install specific drivers and configure data sources The following instructions on how to use the driver manager are based on version 1.02 of ODBCADM.EXE This application may vary slightly from version
to version, but the functions provided are essentially the same
Always use the ODBC administration program to install drivers and configure data sources ODBC.INI and ODBCINST.INI should not be edited manually, unless it becomes absolutely necessary because of corruption or other extreme problems If this situation arises, the files should be backed up prior to editing
When the administration application is started, a list of defined data sources is presented, as shown in Figure 51.2
Figure 51.2 The Data Sources dialog displays a list of defined data sources
The Close and Help buttons should be self-explanatory, and the Options button will be discussed later, in the section on debugging Add is used to define a new data source for one of the installed drivers Delete is used to delete an existing data source, but does not delete the driver The configuration of the selected data source can be edited by clicking on the Setup button The Drivers button is used to install additional DBMS-specific ODBC drivers
The following examples illustrate the installation of the Oracle ODBC driver and the configuration of an Oracle data source This process begins with the installation of the Oracle ODBC driver First, select Drivers from the Data Sources dialog When this button is clicked, all installed drivers are displayed, as shown in Figure 51.3
Figure 51.3 The Drivers dialog displays a list of installed drivers
To install the Oracle ODBC driver, click the Add button This will display a dialog requesting the location of the drivers Select the drive and directory containing the ODBC.INF file and click OK The dialog box shown in Figure 51.4
indicates that the Oracle ODBC driver was located
Figure 51.4 The Install Drivers dialog displays a list of drivers available for installation
The Advanced button displays a dialog that allows the user to specify installation of the driver manager and code page translators The Versions button in the lower-right corner brings up a second dialog that can be used to view extended version information about each component available to install These two dialogs are shown in Figure 51.5
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 6Figure 51.5 The Advanced Installation Options and Versions dialogs can be used to control the installation of the Oracle ODBC driver
The Install selected driver(s) with version checking should be selected This will cause the installation program to prompt before overwriting an existing driver if it is the same or a newer version of the driver being installed
The version checking options should also be used for the installation of the Driver Manager and translators Newer versions of these DLLs should not be overwritten if they exist
Code page translators are used to translate between different character sets and languages In some cases, they are used for encryption or data type conversion Although a translator is not needed in most cases, you can install them for possible future use After making your selections from these options, click the OK button to return to the Install Drivers dialog
To complete the installation of the Oracle7 driver, make sure that Oracle7 is highlighted in the list box and click the OK button The Drivers dialog should now appear as shown in Figure 51.6
Figure 51.6 The Drivers dialog shows that the Oracle ODBC driver was successfully installed
Click the Close button to complete driver installation
Configuring the data source is simply a matter of specifying a driver, naming the data source, and providing some additional information for the driver to use when connecting to the database Refer to the Data Sources dialog in Figure 51.2 From this dialog, click Add to configure a new Oracle data source The next dialog, Add Data Sources, requires the selection of an installed driver Select Oracle7 from the list and click the OK button Next, the Oracle7 ODBC Setup dialog prompts the user for a data source name, description, and SQL*Net connect string, as illustrated in Figure 51.7
Figure 51.7 This is the Oracle7 ODBC Setup dialog
The connect string is specific to the network transport (if any), the hostname or address of the server, and the system ID
of the database to be accessed (if more than one Oracle database exists on the host) The syntax of the connect string is
transport_code:host_name:database
The transport_code is a single character used to specify the SQL*Net driver to be used (T for TCP/IP, X for IPX/SPX, and so on) The host_name is the name, alias, or network address of the server The database argument is necessary only
if more than one Oracle database exists on the host In this case, the argument should be the system name of the
database, as specified when the database was created with the CREATE DATABASE command Consult the Oracle ODBC driver release notes and the SQL*Net documentation for further information on the SQL*Net Connect String
For the purposes of this example, assume that the database resides on a UNIX host and will be accessed from the
Windows workstation using TCP/IP Enter ORACLE for the data source name, Oracle 7.1 for the description, and T: ORACLE_SERVER for the SQL*Net connect string The dialog should now look like the one shown in Figure 51.8
Figure 51.8 The Oracle7 ODBC Setup dialog requires a name and a SQL*Net Connect String The description is optional
Trang 7The Options button enables the user to select a code page translator, assuming that a translator was installed with the driver In most cases, no translation is necessary Click OK to complete the data source setup The new data source should appear in the Data Sources dialog as shown in Figure 51.9
Figure 51.9 The Data Sources dialog shows that the new Oracle7 Data Source was successfully added
The data source is now fully configured and ready to be accessed by an application Note that the setup routine in Figure 51.7 and Figure 51.8 is specific to the driver (Oracle 7.1 version 1.11.0002, in this case) This setup dialog will vary slightly from driver to driver, but will always require similar information
Connecting to an ODBC Data Source using the ODBC API
Before your application connects to the ODBC data source, some memory allocation and initialization must be
performed First, the application must call SQLAllocEnv, passing a pointer to memory allocated to store an environment handle This handle will be used to establish connections and for transaction processing The application might need to establish more than one environment handle, but a single environment handle is usually sufficient, except in
multithreaded environments
Next, the application should call SQLAllocConnect, passing the previously established environment handle and a pointer
to storage allocated for the connection handle The driver manager allocates storage for connection information and associates the resulting connection handle with the environment handle Multiple connections can be established for a single environment handle, but each connection can only be associated with a single environment The connection handle will be used to allocate statement handles and process embedded SQL transactions
Finally, the application may call either SQLConnect or SQLDriverConnect, passing the connection handle instantiated
by the call to SQLAllocConnect The primary difference between these two functions is that SQLDriverConnect accepts
a full connection string, rather than separate arguments for the data source name, userid, and password This allows for additional database-specific parameters to be passed to the driver as part of the connection string Additionally,
SQLDriverConnect provides an argument used to define the behavior of the driver manager and a window handle to be used as the parent of the data sources dialog (if one will be presented) The arguments to SQLDriverConnect are, in order:
● An allocated connection handle
● The handle of the parent window from the Data Sources dialog (or NULL, if no dialog will be presented)
● A connection string
● The length of the connection string
● A pointer to storage for the connection string actually used by the driver (It may add information to the
connection string it receives.)
● A pointer to storage to hold the length of the completed connection string
● An integer constant used to control the behavior of the driver manager
A typical connection string looks like this:
DSN=ORACLE;UID=scotty;PWD=tiger;
Additional database-specific parameters may be provided, or the connection string may be partial, or empty, in which Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 8case the driver will provide a dialog requesting the information required to connect If SQL_DRIVER_NOPROMPT is passed as the completion constant, the application must provide all required information in the connection string
C applications should include ODBC.H (or SQL.H and SQLEXT.H, depending on the compiler), which contains all function prototypes, data types, and constants available in the ODBC API When using other development tools, the developer must provide prototypes for all ODBC functions used by the application Listings 51.1 and 51.2 demonstrate connecting to an ODBC data source in C and Visual Basic, in a Microsoft Windows application context
Listing 51.1 This C function establishes a single connection to a data source.
int ConnectToDataSource(
HENV *hEnv, /* used to store the environment handle */
HDBC *hDBc) /* used to store the connection handle */
Trang 9' include these prototypes in the module:
Declare Function SQLAllocEnv
Lib "odbc.dll" (hEnv As Long) As Integer
Declare Function SQLAllocConnect
Lib "odbc.dll" (ByVal hEnv As Long,
hDBc As Long) As Integer
Declare Function SQLDriverConnect
Lib "odbc.dll" (ByVal hDBc As Long,
ByVal hWnd As Integer,
ByVal szCSin As String,
ByVal iCSinLen As Integer,
ByVal szCSOut As String,
ByVal iCSOutMaxLen As Integer,
iCSOutLen As Integer,
ByVal iDriverComplete As Integer)
As Integer
' also define these constants:
Global Const SQL_SUCCESS = 0
Global Const SQL_SUCCESS_WITH_INFO = 1
Global Const SQL_STILL_EXECUTING = 2
Global Const SQL_NEED_DATA = 99
Global Const SQL_NO_DATA_FOUND = 100
Global Const SQL_ERROR = 1
Global Const SQL_INVALID_HANDLE = 2
Global Const SQL_NTS = 3
Global Const SQL_DRIVER_NOPROMPT = 0
Global Const SQL_MAX_MESSAGE_LENGTH = 512
Function ConnectToDataSource(hEnv As Long, hDBc As Long) As Integer
Dim szConnect As String
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 10Dim szConnectOut As String
Dim iConnectOutLen As Integer
Dim iError As Integer
If (iError = SQL_SUCCESS) Then
iError = SQLDriverConnect(hDBc, 0, szConnect, SQL_NTS,
Setting Connection Options
After you allocate a connection, options can be set to control the behavior of statements processed by the connection, using SQLSetConnectOption There are numerous parameters available, the most significant of which is the
SQL_AUTOCOMMIT option By default, this option is enabled, which means that transactions are committed as sent, with no possibility of rollback This can be very dangerous if the application uses multiple statements to process one logical transaction For DBMSs that do not support stored procedures and triggers, this situation is nearly unavoidable The function examples in Listing 51.3 demonstrates the ODBC API call to set a connection option
Listing 51.3 This C function disables the AUTOCOMMIT option for a connection.
/* not part of ODBC.H */
enum ConnectOptionValues
{
Trang 11statement handles processed by the connection
Applying SQL Transactions
Transaction control through ODBC is dependent on SQL_AUTOCOMMIT being set to OFF, as described in the
previous section Before applying SQL, the application must call SQLAllocStmt to create a statement handle After allocating a statement handle, the application can then apply SQL using either prepared, or direct execution
Prepared execution should be used when the statement to be processed is complex and will be called repeatedly Under the prepared execution method, the statement is compiled and the access plan is bound before the SQL is executed For each subsequent execution of the statement, the driver sends only an access plan identifier, instead of the entire
statement, to the server To implement prepared execution, the application calls SQLPrepare, passing an allocated statement handle, the SQL statement, and the length of the SQL statement The application can then reuse the statement handle and the associated SQL statement using SQLExecute The SQL statement can be parameterized, using ? as a placeholder for a parameter Parameter values can be set with each execution through calls to SQLSetParam The
arguments to SQLSetParam are as follows:
● The statement handle for which the SQL will be executed
● The number of the parameter to be set (position, starting at 1, within the prepared statement)
● The C data type of the parameter
● The ODBC SQL data type of the parameter
● The precision of the parameter value
● The scale of the parameter value
● A pointer to storage containing the parameter value
● A pointer to storage that contains the length of the parameter value (This might always be NULL, provided that any strings are NULL-terminated.)
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 12For full descriptions of C data types, ODBC SQL data types, and their corresponding precisions and scales, refer
to the Microsoft ODBC SDK documentation
If a statement will be executed only once or is fairly simple, direct execution may be preferable It requires fewer
functions calls, so it is easier to implement It should also be faster than the prepared method for the first execution of a statement Direct execution requires only a single call to SQLExecDirect, passing an allocated statement handle, the SQL string, and the length of the SQL string
Regardless of the type of execution used, the application should call SQLTransact to commit or roll back transactions based on the return code of the call to SQLExecute or SQLExecDirect If SQL_AUTOCOMMIT is set to OFF and the application calls SQLTransact with SQL_ROLLBACK, all statements processed by the connection since the last commit will be rolled back The statement handle can then be freed using SQLFreeStmt If the statement handle is not freed, it is available to be reused or overwritten The second parameter to SQLFreeStmt is an integer constant used to close an open cursor, release buffers for parameters and bound columns, or free all resources associated with the statement
(invalidating the handle)
Listings 51.4 and 51.5 demonstrate how an application can insert values into a table, using either the prepared or the direct execution method
Listing 51.4 This C function inserts records into a table using prepared execution.
/* structure containing company information */
HENV hEnv, /* pre-allocated environment handle */
HDBC hDBc, /* pre-allocated connection handle */
COMPANY *Companies, /* pointer to an array of COMPANYs */
int iNumCompanies) /* number of COMPANYs in the array */
{
RETCODE iError;
int i;
Trang 14Listing 51.5 This Visual Basic function inserts records into a table using direct execution.
' include these prototypes in the module:
Declare Function SQLAllocStmt Lib "odbc.dll" (ByVal hStmt As Long)
As Integer
Declare Function SQLExecDirect Lib "odbc.dll" (ByVal hStmt As Long,
ByVal szSQL As String, ByVal iSQLLen As Long)
As Integer
Declare Function SQLFreeStmt Lib "odbc.dll" (ByVal hStmt As Long
ByVal iOption As Integer) As Integer
Declare Function SQLTransact Lib "odbc.dll" (ByVal hEnv As Long,
ByVal hDBc As Long, ByVal iType As Integer)
As Integer
' also define these constants:
Global Const SQL_CHAR = 1
Global Const SQL_INTEGER = 4
Global Const SQL_C_CHAR = 0
Global Const SQL_C_LONG = 1
Global Const SQL_COMMIT = 0
Global Const SQL_ROLLBACK = 1
Global Const SQL_CLOSE = 0
Global Const SQL_DROP = 1
Global Const SQL_UNBIND = 2
Global Const SQL_RESET_PARAMS = 3
Type COMPANY
Dim ID As Long
Dim Company As String
Dim Notes As String
End Type
Trang 15Function InsertCompanyRecords(ByVal hEnv As Long,
ByVal hDBc As Long, Companies() As COMPANY,
ByVal iNumCompanies As Integer) As Integer
Dim iError As Integer
szSQL = szSQL & Companies(i).Company & Ò' , ' Ó
szSQL = szSQL & Companies(i).Notes & Ò' )Ó
If (iError = SQL_SUCCESS) Then
iError = SQLTransact(hEnv, hDBc, SQL_COMMIT)
Else
iError = SQLTransact(hEnv, hDBc, SQL_ROLLBACK)
End If
If (iError = SQL_SUCCESS) Then
iError = SQLFreeStmt(hStmt, SQL_DROP)
End If
InsertCompanyRecords = iError
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 16End Function
There are several variations on the prepared execution method These include setting multiple values for each parameter, and providing parameter values after the call to SQLExecute For information on these methods, consult the ODBC SDK documentation for the SQLParamData and SQLPutData functions
Retrieving Result Sets
The prepared execution and direct execution methods also apply to SQL SELECT statements The additional methods
available to retrieve results through ODBC, however, are almost too numerous to mention SQLSetStmtOption can be used to enable asynchronous processing, which allows single-threaded environments such as Windows 3.x to process multiple statements simultaneously When used with SQLSetScrollOptions, SQLSetStmtOption can enable multiple rows to be fetched with a single call to SQLExtendedFetch SQLSetScrollOptions and SQLExtendedFetch can also be used to create cursors that scroll in both directions; and when used with SQLSetPos, the record pointer can be placed at a specific row in the result set
Unfortunately, these extended functions are not part of the core ODBC standard and are currently unsupported by the
Oracle ODBC driver Although third-party driver vendors might supply some of these functions, the code examples in
this section will focus on bound and unbound fetches using prepared and direct execution These examples use core functions and level 1 extensions, all of which are supported by the current Oracle ODBC driver available from Oracle Corporation
Although the SQL to retrieve result sets can be executed in exactly the same manner as SQL to-process transactions, the application must take additional steps to bind result set columns to application variables Columns may be prebound
using SQLBindCol, or bound after execution, using SQLGetData
In order to bind columns prior to execution, SQLBindCol must be called once for each column to be bound The
arguments to SQLBindCol are somewhat similar to those for SQLSetParam, as enumerated here:
● The statement handle for the executed SQL
● The number of the column to be bound (position, starting at 1, within the result set)
● The C data type of the variable to be bound
● A pointer to storage allocated for the variable
● The maximum length of the variable, in bytes
● A pointer for storage to receive the full length of the column in the result set This can be used to determine if data was truncated
Alternatively, the application can bind variables to result set columns after execution, using SQLGetData Its arguments are identical to those for SQLBindCol The difference between these two methods is essentially a matter of when the application variables are bound to result set columns
SQLDescribeCol can be called prior to SQLGetData to obtain information about a column, including its name, data type, and length This information can be used to ensure that no data is truncated, among other things For example, when used with SQLNumResultCols, a result set generated by a SQL statement such as
SELECT * FROM view_name
Trang 17can be bound to application variables completely dynamically at runtime
Regardless of when the columns are bound to variables, the application calls SQLFetch, with the statement handle as the only argument This positions the cursor at the next row in the result set If columns are prebound, data is placed in variables at this time Otherwise, the call to SQLFetch simply scrolls the cursor one row forward
An application can use SQLFetch with SQLGetData to locate a specific row or set of rows in the result set, based on the value of one or more columns Although this can be accomplished with bound columns, it should be more efficient to use SQLGetData to do comparisons on a single column when there are many rows and columns in the result set
The code examples in Listings 51.6 and 51.7 demonstrate the fetching of bound and unbound columns, using direct execution
Listing 51.6 This C function retrieves the first record in a result set using column-wise binding.
int GetCompanyInfo(
HENV hEnv, /* preallocated environment handle */
HDBC hDBc, /* preallocated connection handle */
COMPANY *Company, /* pointer to a COMPANY */
char *szName) /* Company Name to find */
"SELECT ID, Company, Notes FROM Company WHERE
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 18Listing 51.7 This Visual Basic function retrieves multiple records with unbound columns.
' include these prototypes in the module:
Declare Function SQLFetch Lib "odbc.dll" (ByVal hStmt As Long)
As Integer
Trang 19Declare Function SQLGetData Lib "odbc.dll" (ByVal hStmt As Long,
ByVal iColNum As Integer,
ByVal iDataType As Integer, ByVal hBuffer As Any,
ByVal iBuffLen As Long, iLenOut As Long)
As Integer
Function GetCompanies (ByVal hDBc As Long, Companies() As Company,
iNumCoOut As Integer) As Integer
Dim iError As Integer
Dim hStmt As Long
Dim szSQL As String
Dim iOut1 As Long
Dim iOut2 As Long
Dim iTemp As Integer
iNumCoOut = 0
ReDim Companies(iNumCoOut)
iError = SQLAllocStmt(hDBc, hStmt)
If (iError = SQL_SUCCESS) Then
szSQL = "SELECT COMPANY, NOTES FROM COMPANY ORDER BY 2"
iError = SQLExecDirect(hStmt, szSQL, SQL_NTS)
If (iError = SQL_SUCCESS) Then
While (iError >= SQL_SUCCESS)
ReDim Preserve Companies(iNumCoOut)
If (iError >= SQL_SUCCESS) Then
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 20iTemp = SQLFreeStmt(hStmt, SQL_CLOSE)
iTemp = SQLFreeStmt(hStmt, SQL_DROP)
standard ODBC error codes, DBMS-specific error codes, and error and informational text from the driver This function,
SQLError, has the following arguments:
● The environment handle in which the error occurred
● The connection handle in which the error occurred
● The statement handle in which the error occurred
● A pointer to storage to receive a null-terminated string containing SQL state information This will inform the application that data was truncated, among other things
● A pointer to storage for a long integer that will receive the DBMS's native error code
● A pointer to storage that will receive error text
● A pointer to storage that will receive the length of error text, in bytes This can be used to determine whether error text was truncated
The error code SQL_INVALID_HANDLE (-2), does not provide additional SQL state or error information It indicates that an environment, connection, or statement handle was invalid These errors are commonly the result of indirection or scope problems within the application
Trang 21The code example in Listing 51.8 displays and retrieves SQL state and/or error text and displays it to the user
Listing 51.8 This Visual Basic function displays ODBC SQL states and error messages.
' include this prototype in the module:
Declare Function SQLError Lib "odbc.dll" (ByVal hEnv As Long,
ByVal hDBc As Long, ByVal hStmt As Long,
ByVal szSQLState As String, iNativeError As Long,
ByVal szBuffer As String,
ByVal iBufLen As Integer, iLenOut As Integer)
As Integer
Sub ODBCError (ByVal hEnv As Long, ByVal hDBc As Long,
ByVal hStmt As Long)
Dim iError As Integer
Dim szSQLState As String * 10
Dim iNativeError As Long
Dim szErrorMsg As String * 511
Dim iMsgLength As Integer
Dim szODBCMsg As String
iError = SQLError(hEnv, hDBc, hStmt, szSQLState, iNativeError,
szErrorMsg, SQL_MAX_MESSAGE_LENGTH 1, iMsgLength)
Trang 22handle, depending on the nature of the error or information The application might supply NULL arguments for two of the three handles to retrieve information specific to the supplied handle This error information is stored until the handle
is reused
Calling Stored Procedures and Functions
Support for stored procedures and functions is highly DBMS-specific Oracle-stored procedures and functions are accessible through ODBC in much the same way as embedded SQL In most cases, it is preferable to use stored
procedures or functions to process inserts, updates, and deletes This simplifies client-side development and allows for greater control over transactions by allowing database objects to handle errors, and to commit or rollback work, as needed
Oracle-stored procedures and functions can be executed using either the prepared or direct execution methods, with the following SQL syntax:
procedures: {call proc_name(' an example of direct execution' , ' param2')}
functions: {?=call proc_name(' an example of prepared execution' , ?, ?)}
Note that the entire statement must be enclosed in curly braces, and that when calling functions, a placeholder must be supplied for the return value (Use prepared execution when calling functions.)
Listings 51.9 and 51.10 demonstrate the prepared and direct execution methods for calling Oracle procedures
Listing 51.9 This C function inserts records into a table using an Oracle stored procedure with prepared execution.
int InsertCompanySP(
HENV hEnv, /* preallocated environment handle */
HDBC hDBc, /* preallocated connection handle */
COMPANY *Companies, /* pointer to an array of COMPANYs */
int iNumCompanies) /* number of COMPANYs in the array */
Trang 23Function InsertCompanySP (ByVal hEnv As Long, ByVal hDBc As Long,
Companies() As Company, ByVal iNumCompanies As Integer)
As Integer
Dim iError As Integer
Dim iTemp As Integer
Trang 24Dim szCompany As String
Dim szNotes As String
iError = SQLAllocStmt(hDBc, hStmt)
If (iError = SQL_SUCCESS) Then
For i = 0 To (iNumCompanies 1)
szSQL = "{CALL sp_insert_company('"
szSQL = szSQL & Companies(i).Company & "', '"
szSQL = szSQL & Companies(i).Notes & "')}"
performance, procedures and functions are typically better suited to this task than embedded SQL when using ODBC
Disconnecting and Freeing Resources
The application should free all ODBC resources and cleanly disconnect before exiting First, all statement handles should be freed by calling SQLFreeStmt
Trang 25Next, the application should call SQLDisconnect, passing the active connection handle Then, the application should call SQLFreeConnect, passing the connection handle as the argument Finally, the application should use SQLFreeEnv to free all resources allocated for the environment handle
The order in which these functions are called is very important All statement handles for a connection should be freed prior to passing the connection handle to SQLDisconnect Each connection handle for an environment should be
disconnected and freed prior to passing the environment handle to SQLFreeEnv Freeing allocated memory and
disconnecting in the proper order ensures that the connection will not remain active on the server Listing 51.11
illustrates these steps
Listing 51.11 This C function disconnects from the data source and frees all resources allocated to the connection and environment handles.
Debugging ODBC Applications
The ODBC API provides the developer with most of the tools needed to debug an ODBC application The SQLError function, for example, is arguably more useful in debugging mode than in production code The application can include debug code to display information from SQLError after every call to the ODBC API that results in a return value other than SQL_SUCESS This is especially helpful in the initial phases of development, because it assists not only in locating programming bugs but also in determining what errors are likely to occur in a production environment Steps should be taken to simplify the task of invalidating or removing debug code when it is no longer needed An easy way to do this is
to define a constant in the application and call it DEBUG_MODE, for example The constant can be used to
conditionally branch to debug code or to ignore it, depending on the value of the constant By simply changing the value
of the constant, the developer can then enable or disable debug code This also simplifies the process of removing this code at a later date, by providing a single value to search for that will exist wherever debug code exists
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.