git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33428 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			1230 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
			
		
		
	
	
			1230 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
| \section{Database classes overview}\label{odbcoverview}
 | |
| 
 | |
| Following is a detailed overview of how to use the wxWidgets ODBC classes - \helpref{wxDb}{wxdb} 
 | |
| and \helpref{wxDbTable}{wxdbtable} and their associated functions. These are 
 | |
| the ODBC classes donated by Remstar International, and are collectively 
 | |
| referred to herein as the wxODBC classes.
 | |
| 
 | |
| \subsection{wxDb/wxDbTable wxODBC Overview}\label{wxodbcoverview}
 | |
| 
 | |
| Classes: \helpref{wxDb}{wxdb}, \helpref{wxDbTable}{wxdbtable}
 | |
| 
 | |
| The wxODBC classes were designed for database independence. Although SQL and 
 | |
| ODBC both have standards which define the minimum requirements they must 
 | |
| support to be in compliance with specifications, different database vendors 
 | |
| may implement things slightly differently. One example of this is that Oracle 
 | |
| requires all user names for the datasources to be supplied in uppercase 
 | |
| characters. In situations like this, the wxODBC classes have been written 
 | |
| to make this transparent to the programmer when using functions that require 
 | |
| database-specific syntax.
 | |
| 
 | |
| Currently several major databases, along with other widely used databases, 
 | |
| have been tested and supported through the wxODBC classes. The list of 
 | |
| supported databases is certain to grow as more users start implementing 
 | |
| software with these classes, but at the time of the writing of this document, 
 | |
| users have successfully used the classes with the following datasources:
 | |
| 
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item DB2
 | |
| \item DBase (IV, V)**
 | |
| \item Firebird
 | |
| \item INFORMIX
 | |
| \item Interbase
 | |
| \item MS SQL Server (v7 - minimal testing)
 | |
| \item MS Access (97, 2000, 2002, and 2003)
 | |
| \item MySQL (2.x and 3.5 - use the 2.5x drivers though)
 | |
| \item Oracle (v7, v8, v8i)
 | |
| \item Pervasive SQL
 | |
| \item PostgreSQL
 | |
| \item Sybase (ASA and ASE)
 | |
| \item XBase Sequiter
 | |
| \item VIRTUOSO
 | |
| \end{itemize}
 | |
| 
 | |
| An up-to-date list can be obtained by looking in the comments of the function 
 | |
| \helpref{wxDb::Dbms}{wxdbdbms} in db.cpp, or in the enumerated type 
 | |
| \helpref{wxDBMS}{wxdbenumeratedtypes} in db.h.
 | |
| 
 | |
| **dBase is not truly an ODBC datasource, but there are drivers which can 
 | |
| emulate much of the functionality of an ODBC connection to a dBase table. 
 | |
| See the \helpref{wxODBC Known Issues}{wxodbcknownissues} section of this 
 | |
| overview for details.
 | |
| 
 | |
| 
 | |
| \subsection{wxODBC Where To Start}\label{wxodbcwheretostart}
 | |
| 
 | |
| First, if you are not familiar with SQL and ODBC, go to your local bookstore 
 | |
| and pick up a good book on each. This documentation is not meant to teach 
 | |
| you many details about SQL or ODBC, though you may learn some just from 
 | |
| immersion in the subject.
 | |
| 
 | |
| If you have worked with non-SQL/ODBC datasources before, there are some 
 | |
| things you will need to un-learn. First some terminology as these phrases will 
 | |
| be used heavily in this section of the manual.
 | |
| 
 | |
| \begin{twocollist}\itemsep=0pt
 | |
| \twocolitem{Datasource}{(usually a database) that contains the data that will be 
 | |
| accessed by the wxODBC classes.}
 | |
| \twocolitem{Data table}{The section of the datasource that contains the rows and 
 | |
| columns of data.}
 | |
| \twocolitem{ODBC driver}{The middle-ware software that interprets the ODBC 
 | |
| commands sent by your application and converts them to the SQL format expected 
 | |
| by the target datasource.}
 | |
| \twocolitem{Datasource connection}{An open pipe between your application and 
 | |
| the ODBC driver which in turn has a connection to the target datasource. 
 | |
| Datasource connections can have a virtually unlimited number of wxDbTable 
 | |
| instances using the same connect (dependent on the ODBC driver). A separate 
 | |
| connection is not needed for each table (the exception is for isolating 
 | |
| commits/rollbacks on different tables from affecting more than the desired 
 | |
| table. See the class documentation on 
 | |
| \helpref{wxDb::CommitTrans}{wxdbcommittrans} and 
 | |
| \helpref{wxDb::RollbackTrans}{wxdbrollbacktrans}.)}
 | |
| \twocolitem{Rows}{Similar to records in old relational databases, a row is a 
 | |
| collection of one instance of each column of the data table that are all 
 | |
| associated with each other.}
 | |
| \twocolitem{Columns}{Individual fields associated with each row of a data 
 | |
| table.}
 | |
| \twocolitem{Query}{Request from the client to the datasource asking for 
 | |
| the data that matches the requirements specified in the users request. When 
 | |
| a query is performed, the datasource performs the lookup of the rows with 
 | |
| satisfy the query, and creates a result set.}
 | |
| \twocolitem{Result set}{The data which matches the requirements specified 
 | |
| in a query sent to the datasource. Dependent on drivers, a result set 
 | |
| typically remains at the datasource (no data is transmitted to the ODBC driver) 
 | |
| until the client actually instructs the ODBC driver to retrieve it.}
 | |
| \twocolitem{Cursor}{A logical pointer into the result set that a query 
 | |
| generates, indicating the next record that will be returned to the client 
 | |
| when a request for the next record is made.}
 | |
| \twocolitem{Scrolling cursors}{Scrolling refers to the movement of cursors 
 | |
| through the result set. Cursors can always scroll forward sequentially in 
 | |
| the result set (FORWARD ONLY scrolling cursors). With Forward only scrolling 
 | |
| cursors, once a row in the result set has been returned to the ODBC driver 
 | |
| and on to the client, there is no way to have the cursor move backward in 
 | |
| the result set to look at the row that is previous to the current row in 
 | |
| the result set. If BACKWARD scrolling cursors are supported by both the 
 | |
| ODBC driver and the datasource that are being used, then backward 
 | |
| scrolling cursor functions may be used (
 | |
| \helpref{wxDbTable::GetPrev}{wxdbtablegetprev}, 
 | |
| \helpref{wxDbTable::GetFirst}{wxdbtablegetfirst}, and 
 | |
| \helpref{wxDbTable::GetLast}{wxdbtablegetlast}). If the datasource or the 
 | |
| ODBC driver only support forward scrolling cursors, your program and logic 
 | |
| must take this in to account.}
 | |
| \twocolitem{Commit/Rollback}{Commit will physically save 
 | |
| insertions/deletions/updates, while rollback basically does an undo of 
 | |
| everything done against the datasource connection that has not been 
 | |
| previously committed. Note that Commit and Rollbacks are done on a 
 | |
| connection, not on individual tables. All tables which use a shared 
 | |
| connection to the datasource are all committed/rolled back at the same 
 | |
| time when a call to 
 | |
| \helpref{wxDb::CommitTrans}{wxdbcommittrans} or 
 | |
| \helpref{wxDb::RollbackTrans}{wxdbrollbacktrans} is made.}
 | |
| \twocolitem{Index}{Indexes are datasource-maintained lookup structures 
 | |
| that allow the datasource to quickly locate data rows based on the values 
 | |
| of certain columns. Without indexes, the datasource would need to do a 
 | |
| sequential search of a table every time a query request is made. Proper 
 | |
| unique key index construction can make datasource queries nearly instantaneous.}
 | |
| \end{twocollist}
 | |
| 
 | |
| Before you are able to read data from a data table in a datasource, you must 
 | |
| have a connection to the datasource. Each datasource connection may be used 
 | |
| to open multiple tables all on the same connection (number of tables open are 
 | |
| dependent on the driver, datasource configuration and the amount of memory on 
 | |
| the client workstation). Multiple connections can be opened to the same 
 | |
| datasource by the same client (number of concurrent connections is dependent 
 | |
| on the driver and datasource configuration).
 | |
| 
 | |
| When a query is performed, the client passes the query to the ODBC driver, 
 | |
| and the driver then translates it and passes it along to the datasource. The 
 | |
| database engine (in most cases - exceptions are text and dBase files) running 
 | |
| on the machine hosting the database does all the work of performing the search 
 | |
| for the requested data. The client simply waits for a status to come back 
 | |
| through the ODBC driver from the datasource. 
 | |
| 
 | |
| Depending on the ODBC driver, the result set either remains "queued" on the 
 | |
| database server side, or is transferred to the machine that the driver is 
 | |
| queued on. The client does not receive this data. The client must request 
 | |
| some or all of the result set to be returned before any data rows are 
 | |
| returned to the client application.
 | |
| 
 | |
| Result sets do not need to include all columns of every row matching the 
 | |
| query. In fact, result sets can actually be joinings of columns from two 
 | |
| or more data tables, may have derived column values, or calculated values 
 | |
| returned.
 | |
| 
 | |
| For each result set, a cursor is maintained (typically by the database) 
 | |
| which keeps track of where in the result set the user currently is. 
 | |
| Depending on the database, ODBC driver, and how you configured the 
 | |
| wxWidgets ODBC settings in setup.h (see \helpref{wxODBC - Compiling}{wxodbccompiling}), cursors can be 
 | |
| either forward or backward scrolling. At a minimum, cursors must scroll 
 | |
| forward. For example, if a query resulted in a result set with 100 rows, 
 | |
| as the data is read by the client application, it will read row 1, then 2, 
 | |
| then 3, etc. With forward only cursors, once the cursor has moved to 
 | |
| the next row, the previous row cannot be accessed again without re-querying 
 | |
| the datasource for the result set over again. Backward scrolling cursors 
 | |
| allow you to request the previous row from the result set, actually 
 | |
| scrolling the cursor backward.
 | |
| 
 | |
| Backward scrolling cursors are not supported on all database/driver 
 | |
| combinations. For this reason, forward-only cursors are the default in 
 | |
| the wxODBC classes. If your datasource does support backward scrolling 
 | |
| cursors and you wish to use them, make the appropriate changes in setup.h 
 | |
| to enable them (see \helpref{wxODBC - Compiling}{wxodbccompiling}). For greatest portability between 
 | |
| datasources, writing your program in such a way that it only requires 
 | |
| forward scrolling cursors is your best bet. On the other hand, if you are 
 | |
| focusing on using only datasources that support backward scrolling cursors, 
 | |
| potentially large performance benefits can be gained from using them.
 | |
| 
 | |
| There is a limit to the number of cursors that can be open on each connection 
 | |
| to the datasource, and usually a maximum number of cursors for the datasource 
 | |
| itself. This is all dependent on the database. Each connection that is 
 | |
| opened (each instance of a wxDb) opens a minimum of 5 cursors on creation 
 | |
| that are required for things such as updates/deletions/rollbacks/queries. 
 | |
| Cursors are a limited resource, so use care in creating large numbers of 
 | |
| cursors.
 | |
| 
 | |
| Additional cursors can be created if necessary with the 
 | |
| \helpref{wxDbTable::GetNewCursor}{wxdbtablegetnewcursor} function. One example 
 | |
| use for additional cursors is to track multiple scroll points in result 
 | |
| sets. By creating a new cursor, a program could request a second result set 
 | |
| from the datasource while still maintaining the original cursor position in 
 | |
| the first result set.
 | |
| 
 | |
| Different than non-SQL/ODBC datasources, when a program performs an 
 | |
| insertion, deletion, or update (or other SQL functions like altering 
 | |
| tables, etc) through ODBC, the program must issue a "commit" to the 
 | |
| datasource to tell the datasource that the action(s) it has been told to 
 | |
| perform are to be recorded as permanent. Until a commit is performed, 
 | |
| any other programs that query the datasource will not see the changes that 
 | |
| have been made (although there are databases that can be configured to 
 | |
| auto-commit). NOTE: With most datasources, until the commit is 
 | |
| performed, any cursor that is open on that same datasource connection 
 | |
| will be able to see the changes that are uncommitted. Check your 
 | |
| database's documentation/configuration to verify this before relying on it 
 | |
| though.
 | |
| 
 | |
| A rollback is basically an UNDO command on the datasource connection. When 
 | |
| a rollback is issued, the datasource will flush all commands it has been told 
 | |
| to do since the last commit that was performed.
 | |
| 
 | |
| NOTE: Commits/Rollbacks are done on datasource connections (wxDb instances) 
 | |
| not on the wxDbTable instances. This means that if more than one table 
 | |
| shares the same connection, and a commit or rollback is done on that 
 | |
| connection, all pending changes for ALL tables using that connection are 
 | |
| committed/rolled back.
 | |
| 
 | |
| \subsection{wxODBC - Configuring your system for ODBC use}\label{wxodbcconfiguringyoursystem}
 | |
| 
 | |
| Before you are able to access a datasource, you must have installed and 
 | |
| configured an ODBC driver. Doing this is system specific, so it will not be 
 | |
| covered in detail here. But here are a few details to get you started.
 | |
| 
 | |
| Most database vendors provide at least a minimal ODBC driver with their 
 | |
| database product. In practice, many of these drivers have proven to be slow 
 | |
| and/or incomplete. Rumour has it that this is because the vendors do not want 
 | |
| you using the ODBC interface to their products; they want you to use their 
 | |
| applications to access the data. 
 | |
| 
 | |
| Whatever the reason, for database-intensive applications, you may want to 
 | |
| consider using a third-party ODBC driver for your needs. One example of a 
 | |
| third-party set of ODBC drivers that has been heavily tested and used is 
 | |
| Rogue Wave's drivers. Rogue Wave has drivers available for many different 
 | |
| platforms and databases.
 | |
|  
 | |
| Under Microsoft Windows, install the ODBC driver you are planning to use. You 
 | |
| will then use the ODBC Administrator in the Control Panel to configure an 
 | |
| instance of the driver for your intended datasource. Note that with all 
 | |
| flavors of NT, this configuration can be set up as a System or User DSN 
 | |
| (datasource name). Configuring it as a system resource will make it 
 | |
| available to all users (if you are logged in as 'administrator'), otherwise 
 | |
| the datasource will only be available to the user who configured the DSN.
 | |
| 
 | |
| Under Unix, iODBC is used for implementation of the ODBC API. To compile the 
 | |
| wxODBC classes, you must first obtain iODBC from \urlref{http://www.iodbc.org}{www.iodbc.org} and install it. 
 | |
| (Note: wxWidgets currently includes a version of iODBC.) Then you must create the file "~/.odbc.ini" (or optionally create 
 | |
| "/etc/odbc.ini" for access for all users on the system). This file contains 
 | |
| the settings for your system/datasource. Below is an example section of a 
 | |
| odbc.ini file for use with the "samples/db" sample program using MySQL:
 | |
| 
 | |
| \begin{verbatim}
 | |
|         [contacts]
 | |
|         Trace    = Off
 | |
|         TraceFile= stderr
 | |
|         Driver   = /usr/local/lib/libmyodbc.so
 | |
|         DSN      = contacts
 | |
|         SERVER   = 192.168.1.13
 | |
|         USER     = qet
 | |
|         PASSWORD = 
 | |
|         PORT     = 3306
 | |
| \end{verbatim}
 | |
| 
 | |
| \subsection{wxODBC - Compiling}\label{wxodbccompiling}
 | |
| 
 | |
| The wxWidgets setup.h file has several settings in it pertaining to compiling 
 | |
| the wxODBC classes.
 | |
| 
 | |
| \begin{twocollist}\itemsep=0pt
 | |
| \twocolitem{wxUSE\_ODBC}{This must be set to 1 in order for the compiler to 
 | |
| compile the wxODBC classes. Without setting this to 1, there will be no 
 | |
| access to any of the wxODBC classes. The default is 0.}
 | |
| \twocolitem{wxODBC\_FWD\_ONLY\_CURSORS}{When a new database connection is 
 | |
| requested, this setting controls the default of whether the connection allows 
 | |
| only forward scrolling cursors, or forward and backward scrolling cursors 
 | |
| (see the section in "WHERE TO START" on cursors for more information on 
 | |
| cursors). This default can be overridden by passing a second parameter to 
 | |
| either the \helpref{wxDbGetConnection}{wxdbfunctions} or 
 | |
| \helpref{wxDb constructor}{wxdbctor}. The default is 1.}
 | |
| \twocolitem{wxODBC\_BACKWARD\_COMPATABILITY}{Between v2.0 and 2.2, massive 
 | |
| renaming efforts were done to the ODBC classes to get naming conventions 
 | |
| similar to those used throughout wxWidgets, as well as to preface all wxODBC 
 | |
| classes names and functions with a wxDb preface. Because this renaming would 
 | |
| affect applications written using the v2.0 names, this compile-time directive 
 | |
| was added to allow those programs written for v2.0 to still compile using the 
 | |
| old naming conventions.  These deprecated names are all {\tt\#}define'd to their 
 | |
| corresponding new function names at the end of the db.cpp/dbtable.cpp source 
 | |
| files. These deprecated class/function names should not be used in future 
 | |
| development, as at some point in the future they will be removed. The default 
 | |
| is 0.}
 | |
| \end{twocollist}
 | |
| 
 | |
| {\it Under MS Windows}
 | |
| 
 | |
| You are required to include the "odbc32.lib" provided by your compiler vendor 
 | |
| in the list of external libraries to be linked in. If using the makefiles 
 | |
| supplied with wxWidgets, this library should already be included for use with 
 | |
| makefile.b32, makefile.vc, and makefile.g95. 
 | |
| 
 | |
| \normalbox{MORE TO COME}
 | |
| 
 | |
| {\it Under Unix}
 | |
| --with-odbc flag for configure
 | |
| 
 | |
| \normalbox{MORE TO COME}
 | |
| 
 | |
| \subsection{wxODBC - Basic Step-By-Step Guide}\label{wxodbcstepbystep}
 | |
| 
 | |
| To use the classes in an application, there are eight basic steps:
 | |
| 
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item Define datasource connection information
 | |
| \item Get a datasource connection
 | |
| \item Create table definition
 | |
| \item Open the table
 | |
| \item Use the table
 | |
| \item Close the table
 | |
| \item Close the datasource connection
 | |
| \item Release the ODBC environment handle
 | |
| \end{itemize}
 | |
| 
 | |
| Following each of these steps is detailed to explain the step, and to 
 | |
| hopefully mention as many of the pitfalls that beginning users fall in 
 | |
| to when first starting to use the classes. Throughout the steps, small 
 | |
| snippets of code are provided to show the syntax of performing the step. A 
 | |
| complete code snippet is provided at the end of this overview that shows a 
 | |
| complete working flow of all these steps (see 
 | |
| \helpref{wxODBC - Sample Code}{wxodbcsamplecode1}).
 | |
| 
 | |
| {\bf Define datasource connection information}
 | |
| 
 | |
| To be able to connect to a datasource through the ODBC driver, a program must 
 | |
| supply a minimum of three pieces of information: Datasource name, User ID, and 
 | |
| Authorization string (password). A fourth piece of information, a default 
 | |
| directory indicating where the data file is stored, is required for Text and 
 | |
| dBase drivers for ODBC.
 | |
| 
 | |
| The wxWidgets data class wxDbConnectInf exists for holding all of these 
 | |
| values, plus some others that may be desired.
 | |
| 
 | |
| The 'Henv' member is the environment handle used to access memory for use by the 
 | |
| ODBC driver. Use of this member is described below in the "Getting a Connection 
 | |
| to the Datasource" section.
 | |
| 
 | |
| The 'Dsn' must exactly match the datasource name used to configure the ODBC 
 | |
| datasource (in the ODBC Administrator (MSW only) or in the .odbc.ini file).
 | |
| 
 | |
| The 'Uid' is the User ID that is to be used to log in to the datasource. This 
 | |
| User ID must already have been created and assigned rights within the 
 | |
| datasource to which you are connecting. The user that the connection is 
 | |
| establish by will determine what rights and privileges the datasource 
 | |
| connection will allow the program to have when using the connection that 
 | |
| this connection information was used to establish. Some datasources are 
 | |
| case sensitive for User IDs, and though the wxODBC classes attempt to hide 
 | |
| this from you by manipulating whatever data you pass in to match the 
 | |
| datasource's needs, it is always best to pass the 'Uid' in the case that 
 | |
| the datasource requires.
 | |
| 
 | |
| The 'AuthStr' is the password for the User ID specified in the 'Uid' member. 
 | |
| As with the 'Uid', some datasources are case sensitive (in fact most are). 
 | |
| The wxODBC classes do NOT try to manage the case of the 'AuthStr' at all. 
 | |
| It is passed verbatim to the datasource, so you must use the case that the 
 | |
| datasource is expecting.
 | |
| 
 | |
| The 'defaultDir' member is used with file based datasources (i.e. dBase, 
 | |
| FoxPro, text files). It contains a full path to the location where the 
 | |
| data table or file is located. When setting this value, use forward 
 | |
| slashes '/' rather than backslashes '\' to avoid compatibility differences 
 | |
| between ODBC drivers.
 | |
| 
 | |
| The other fields are currently unused. The intent of these fields are that 
 | |
| they will be used to write our own ODBC Administrator type program that will 
 | |
| work on both MSW and Un*x systems, regardless of the datasource. Very little 
 | |
| work has been done on this to date.
 | |
| 
 | |
| {\bf Get a Datasource Connection}
 | |
| 
 | |
| There are two methods of establishing a connection to a datasource. You 
 | |
| may either manually create your own wxDb instance and open the connection, 
 | |
| or you may use the caching functions provided with the wxODBC classes to 
 | |
| create/maintain/delete the connections.
 | |
| 
 | |
| Regardless of which method you use, you must first have a fully populated 
 | |
| wxDbConnectInf object. In the wxDbConnectInf instance, provide a valid 
 | |
| Dns, Uid, and AuthStr (along with a 'defaultDir' if necessary). Before 
 | |
| using this though, you must allocate an environment handle to the 'Henv' 
 | |
| member.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     wxDbConnectInf DbConnectInf;
 | |
|     DbConnectInf.SetDsn("MyDSN");
 | |
|     DbConnectInf.SetUserID("MyUserName");
 | |
|     DbConnectInf.SetPassword("MyPassword");
 | |
|     DbConnectInf.SetDefaultDir("");
 | |
| \end{verbatim}
 | |
| 
 | |
| To allocate an environment handle for the ODBC connection to use, the 
 | |
| wxDbConnectInf class has a datasource independent method for creating 
 | |
| the necessary handle:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     if (DbConnectInf.AllocHenv())
 | |
|     {
 | |
|         wxMessageBox("Unable to allocate an ODBC environment handle",
 | |
|                      "DB CONNECTION ERROR", wxOK | wxICON_EXCLAMATION);
 | |
|         return;
 | |
|     } 
 | |
| \end{verbatim}
 | |
| 
 | |
| When the wxDbConnectInf::AllocHenv() function is called successfully, a 
 | |
| value of true will be returned. A value of false means allocation failed, 
 | |
| and the handle will be undefined.
 | |
| 
 | |
| A shorter form of doing the above steps is encapsulated into the 
 | |
| long form of the constructor for wxDbConnectInf.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     wxDbConnectInf *DbConnectInf;
 | |
| 
 | |
| 	 DbConnectInf = new wxDbConnectInf(NULL, "MyDSN", "MyUserName",
 | |
| 	                                   "MyPassword", "");
 | |
| \end{verbatim}
 | |
| 
 | |
| This shorthand form of initializing the constructor passes a NULL for the SQL 
 | |
| environment handle, telling the constructor to allocate a handle during 
 | |
| construction. This handle is also managed for the life of wxDbConnectInf 
 | |
| instance, and is freed automatically upon destruction of the instance.
 | |
| 
 | |
| Once the wxDbConnectInf instance is initialized, you are ready to 
 | |
| connect to the datasource.
 | |
| 
 | |
| To manually create datasource connections, you must create a wxDb 
 | |
| instance, and then open it.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     wxDb *db = new wxDb(DbConnectInf->GetHenv());
 | |
| 
 | |
|     opened = db->Open(DbConnectInf);
 | |
| \end{verbatim}
 | |
| 
 | |
| The first line does the house keeping needed to initialize all 
 | |
| the members of the wxDb class. The second line actually sends the request 
 | |
| to the ODBC driver to open a connection to its associated datasource using 
 | |
| the parameters supplied in the call to \helpref{wxDb::Open}{wxdbopen}.
 | |
| 
 | |
| A more advanced form of opening a connection is to use the connection 
 | |
| caching functions that are included with the wxODBC classes. The caching 
 | |
| mechanisms perform the same functions as the manual approach to opening a 
 | |
| connection, but they also manage each connection they have created, 
 | |
| re-using them and cleaning them up when they are closed, without you 
 | |
| needing to do the coding.
 | |
| 
 | |
| To use the caching function \helpref{wxDbGetConnection}{wxdbfunctions} to get 
 | |
| a connection to a datasource, simply call it with a single parameter of the 
 | |
| type wxDbConnectInf:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     db = wxDbGetConnection(DbConnectInf);
 | |
| \end{verbatim}
 | |
| 
 | |
| The wxDb pointer that is returned is both initialized and opened. If 
 | |
| something failed in creating or opening the connection, the return value 
 | |
| from \helpref{wxDbGetConnection}{wxdbfunctions} will be NULL.
 | |
| 
 | |
| The connection that is returned is either a new connection, or it is a 
 | |
| "free" connection from the cache of connections that the class maintains 
 | |
| that was no longer in use. Any wxDb instance created with a call to 
 | |
| \helpref{wxDbGetConnection}{wxdbfunctions} is recorded in a linked list of established 
 | |
| connections. When a program is finished with a connection, a call to 
 | |
| \helpref{wxDbFreeConnection}{wxdbfunctions} is made, and the datasource 
 | |
| connection will then be tagged as FREE, making it available for the next 
 | |
| call to \helpref{wxDbGetConnection}{wxdbfunctions} that needs a connection 
 | |
| using the same connection information (Dsn, Uid, AuthStr). The cached 
 | |
| connections remain cached until a call to \helpref{wxDbCloseConnections}{wxdbfunctions} is made, 
 | |
| at which time all cached connections are closed and deleted.
 | |
| 
 | |
| Besides the obvious advantage of using the single command caching routine to 
 | |
| obtain a datasource connection, using cached connections can be quite a 
 | |
| performance boost as well. Each time that a new connection is created 
 | |
| (not retrieved from the cache of free connections), the wxODBC classes 
 | |
| perform many queries against the datasource to determine the datasource's 
 | |
| datatypes and other fundamental behaviours. Depending on the hardware, 
 | |
| network bandwidth, and datasource speed, this can in some cases take a 
 | |
| few seconds to establish the new connection (with well-balanced systems, 
 | |
| it should only be a fraction of a second). Re-using already established 
 | |
| datasource connections rather than creating/deleting, creating/deleting 
 | |
| connections can be quite a time-saver.
 | |
| 
 | |
| Another time-saver is the "copy connection" features of both 
 | |
| \helpref{wxDb::Open}{wxdbopen} and \helpref{wxDbGetConnection}{wxdbfunctions}. 
 | |
| If manually creating a wxDb instance and opening it, you must pass an existing 
 | |
| connection to the \helpref{wxDb::Open}{wxdbopen} function yourself to gain the performance 
 | |
| benefit of copying existing connection settings. The 
 | |
| \helpref{wxDbGetConnection}{wxdbfunctions} function automatically does this 
 | |
| for you, checking the Dsn, Uid, and AuthStr parameters when you request 
 | |
| a connection for any existing connections that use those same settings. 
 | |
| If one is found, \helpref{wxDbGetConnection}{wxdbfunctions} copies the datasource settings for 
 | |
| datatypes and other datasource specific information that was previously 
 | |
| queried, rather than re-querying the datasource for all those same settings.
 | |
| 
 | |
| One final note on creating a connection. When a connection is created, it 
 | |
| will default to only allowing cursor scrolling to be either forward only, 
 | |
| or both backward and forward scrolling. The default behavior is 
 | |
| determined by the setting {\tt wxODBC\_FWD\_ONLY\_CURSORS} in setup.h when you 
 | |
| compile the wxWidgets library. The library default is to only support 
 | |
| forward scrolling cursors only, though this can be overridden by parameters 
 | |
| for wxDb() constructor or the \helpref{wxDbGetConnection}{wxdbfunctions} 
 | |
| function. All datasources and ODBC drivers must support forward scrolling 
 | |
| cursors. Many datasources support backward scrolling cursors, and many 
 | |
| ODBC drivers support backward scrolling cursors. Before planning on using 
 | |
| backward scrolling cursors, you must be certain that both your datasource 
 | |
| and ODBC driver fully support backward scrolling cursors. See the small 
 | |
| blurb about "Scrolling cursors" in the definitions at the beginning of 
 | |
| this overview, or other details of setting the cursor behavior in the wxDb 
 | |
| class documentation.
 | |
| 
 | |
| {\bf Create Table Definition}
 | |
| 
 | |
| Data can be accessed in a datasource's tables directly through various 
 | |
| functions of the wxDb class (see \helpref{wxDb::GetData}{wxdbgetdata}). But to make life much 
 | |
| simpler, the wxDbTable class encapsulates all of the SQL specific API calls 
 | |
| that would be necessary to do this, wrapping it in an intuitive class of APIs.
 | |
| 
 | |
| The first step in accessing data in a datasource's tables via the wxDbTable 
 | |
| class is to create a wxDbTable instance.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     table = new wxDbTable(db, tableName, numTableColumns, "", 
 | |
|                           !wxDB_QUERY_ONLY, "");
 | |
| \end{verbatim}
 | |
| 
 | |
| When you create the instance, you indicate the previously established 
 | |
| datasource connection to be used to access the table, the name of the 
 | |
| primary table that is to be accessed with the datasource's tables, how many 
 | |
| columns of each row are going to be returned, the name of the view of the 
 | |
| table that will actually be used to query against (works with Oracle only 
 | |
| at this time), whether the data returned is for query purposes only, and 
 | |
| finally the path to the table, if different than the path specified when 
 | |
| connecting to the datasource.
 | |
| 
 | |
| Each of the above parameters are described in detail in the wxDbTable 
 | |
| class' description, but one special note here about the fifth 
 | |
| parameter - the queryOnly setting. If a wxDbTable instance is created as 
 | |
| {\tt wxDB\_QUERY\_ONLY}, then no inserts/deletes/updates can be performed 
 | |
| using this instance of the wxDbTable. Any calls to \helpref{wxDb::CommitTrans}{wxdbcommittrans} 
 | |
| or \helpref{wxDb::RollbackTrans}{wxdbrollbacktrans} against the datasource 
 | |
| connection used by this wxDbTable instance are ignored by this instance. If 
 | |
| the wxDbTable instance is created with {\tt !wxDB\_QUERY\_ONLY} as shown above, 
 | |
| then all the cursors and other overhead associated with being able to 
 | |
| insert/update/delete data in the table are created, and thereby those 
 | |
| operations can then be performed against the associated table with this 
 | |
| wxDbTable instance.
 | |
| 
 | |
| If a table is to be accessed via a wxDbTable instance, and the table will 
 | |
| only be read from, not written to, there is a performance benefit (not as 
 | |
| many cursors need to be maintained/updated, hence speeding up access times), 
 | |
| as well as a resource savings due to fewer cursors being created for the 
 | |
| wxDbTable instance. Also, with some datasources, the number of 
 | |
| simultaneous cursors is limited. 
 | |
| 
 | |
| When defining the columns to be retrievable by the wxDbTable instance, you 
 | |
| can specify anywhere from one column up to all columns in the table. 
 | |
| 
 | |
| \begin{verbatim}
 | |
|     table->SetColDefs(0, "FIRST_NAME", DB_DATA_TYPE_VARCHAR, FirstName,
 | |
|                       SQL_C_WXCHAR, sizeof(FirstName), true, true);
 | |
|     table->SetColDefs(1, "LAST_NAME", DB_DATA_TYPE_VARCHAR, LastName,
 | |
|                       SQL_C_WXCHAR, sizeof(LastName), true, true);
 | |
| \end{verbatim}
 | |
| 
 | |
| Notice that column definitions start at index 0 and go up to one less than 
 | |
| the number of columns specified when the wxDbTable instance was created 
 | |
| (in this example, two columns - one with index 0, one with index 1).
 | |
| 
 | |
| The above lines of code "bind" the datasource columns specified to the 
 | |
| memory variables in the client application. So when the application 
 | |
| makes a call to \helpref{wxDbTable::GetNext}{wxdbtablegetnext} (or any other function that retrieves 
 | |
| data from the result set), the variables that are bound to the columns will 
 | |
| have the column value stored into them. See the 
 | |
| \helpref{wxDbTable::SetColDefs}{wxdbtablesetcoldefs} 
 | |
| class documentation for more details on all the parameters for this function.
 | |
| 
 | |
| The bound memory variables have undefined data in them until a call to a 
 | |
| function that retrieves data from a result set is made 
 | |
| (e.g. \helpref{wxDbTable::GetNext}{wxdbtablegetnext},
 | |
| \helpref{wxDbTable::GetPrev}{wxdbtablegetprev}, etc). The variables are not 
 | |
| initialized to any data by the wxODBC classes, and they still contain 
 | |
| undefined data after a call to \helpref{wxDbTable::Query}{wxdbtablequery}. Only 
 | |
| after a successful call to one of the ::GetXxxx() functions is made do the 
 | |
| variables contain valid data.
 | |
| 
 | |
| It is not necessary to define column definitions for columns whose data is 
 | |
| not going to be returned to the client. For example, if you want to query 
 | |
| the datasource for all users with a first name of 'GEORGE', but you only want 
 | |
| the list of last names associated with those rows (why return the FIRST\_NAME 
 | |
| column every time when you already know it is 'GEORGE'), you would only have 
 | |
| needed to define one column above.
 | |
| 
 | |
| You may have as many wxDbTable instances accessing the same table using the 
 | |
| same wxDb instance as you desire. There is no limit imposed by the classes 
 | |
| on this. All datasources supported (so far) also have no limitations on this.
 | |
| 
 | |
| {\bf Open the table}
 | |
| 
 | |
| Opening the table is not technically doing anything with the datasource 
 | |
| itself. Calling \helpref{wxDbTable::Open}{wxdbtableopen} simply does all the 
 | |
| housekeeping of checking that the specified table exists, that the current 
 | |
| connected user has at least SELECT privileges for accessing the table, 
 | |
| setting up the requisite cursors, binding columns and cursors, and 
 | |
| constructing the default INSERT statement that is used when a new row is 
 | |
| inserted into the table (non-wxDB\_QUERY\_ONLY tables only).
 | |
| 
 | |
| \begin{verbatim}
 | |
|     if (!table->Open())
 | |
|     {
 | |
|         // An error occurred opening (setting up) the table
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| The only reason that a call to \helpref{wxDbTable::Open}{wxdbtableopen} is likely to fail is if the 
 | |
| user has insufficient privileges to even SELECT the table. Other problems 
 | |
| could occur, such as being unable to bind columns, but these other reason 
 | |
| point to some lack of resource (like memory). Any errors generated 
 | |
| internally in the \helpref{wxDbTable::Open}{wxdbtableopen} function are logged to the error log 
 | |
| if SQL logging is turned on for the classes.
 | |
| 
 | |
| {\bf Use the table}
 | |
| 
 | |
| To use the table and the definitions that are now set up, we must first 
 | |
| define what data we want the datasource to collect in to a result set, tell 
 | |
| it where to get the data from, and in which sequence we want the data returned.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     // the WHERE clause limits/specifies which rows in the table
 | |
|     // are to be returned in the result set
 | |
|     table->SetWhereClause("FIRST_NAME = 'GEORGE'");
 | |
| 
 | |
|     // Result set will be sorted in ascending alphabetical 
 | |
|     // order on the data in the 'LAST_NAME' column of each row
 | |
|     // If the same last name is in the table for two rows, 
 | |
|     // sub-sort on the 'AGE' column
 | |
|     table->SetOrderByClause("LAST_NAME, AGE");
 | |
| 
 | |
|     // No other tables (joins) are used for this query
 | |
|     table->SetFromClause("");
 | |
| \end{verbatim}
 | |
| 
 | |
| The above lines will be used to tell the datasource to return in the result 
 | |
| all the rows in the table whose column "FIRST\_NAME" contains the name 
 | |
| 'GEORGE' (note the required use of the single quote around the string 
 | |
| literal) and that the result set will return the rows sorted by ascending 
 | |
| last names (ascending is the default, and can be overridden with the 
 | |
| "DESC" keyword for datasources that support it - "LAST\_NAME DESC").
 | |
| 
 | |
| Specifying a blank WHERE clause will result in the result set containing 
 | |
| all rows in the datasource.
 | |
| 
 | |
| Specifying a blank ORDERBY clause means that the datasource will return 
 | |
| the result set in whatever sequence it encounters rows which match the 
 | |
| selection criteria. What this sequence is can be hard to determine. 
 | |
| Typically it depends on the index that the datasource used to find the 
 | |
| rows which match the WHERE criteria. BEWARE - relying on the datasource 
 | |
| to return data in a certain sequence when you have not provided an ORDERBY 
 | |
| clause will eventually cause a problem for your program. Databases can be 
 | |
| tuned to be COST-based, SPEED-based, or some other basis for how it gets 
 | |
| your result set. In short, if you need your result set returned in a 
 | |
| specific sequence, ask for it that way by providing an ORDERBY clause.
 | |
| 
 | |
| Using an ORDERBY clause can be a performance hit, as the database must 
 | |
| sort the items before making the result set available to the client. 
 | |
| Creating efficient indexes that cause the data to be "found" in the correct 
 | |
| ORDERBY sequence can be a big performance benefit. Also, in the large 
 | |
| majority of cases, the database will be able to sort the records faster 
 | |
| than your application can read all the records in (unsorted) and then sort 
 | |
| them. Let the database do the work for you!
 | |
| 
 | |
| Notice in the example above, a column that is not included in the bound 
 | |
| data columns ('AGE') will be used to sub-sort the result set. 
 | |
| 
 | |
| The FROM clause in this example is blanked, as we are not going to be 
 | |
| performing any table joins with this simple query. When the FROM clause 
 | |
| is blank, it is assumed that all columns referenced are coming from 
 | |
| the default table for the wxDbTable instance.
 | |
| 
 | |
| After the selection criteria have been specified, the program can now 
 | |
| ask the datasource to perform the search and create a result set that 
 | |
| can be retrieved:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     // Instruct the datasource to perform a query based on the 
 | |
|     // criteria specified above in the where/orderBy/from clauses.
 | |
|     if (!table->Query())
 | |
|     {
 | |
|         // An error occurred performing the query
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| Typically, when an error occurs when calling \helpref{wxDbTable::Query}{wxdbtablequery}, it is a 
 | |
| syntax problem in the WHERE clause that was specified. The exact SQL 
 | |
| (datasource-specific) reason for what caused the failure of \helpref{wxDbTable::Query}{wxdbtablequery} 
 | |
| (and all other operations against the datasource can be found by 
 | |
| parsing the table's database connection's "errorList[]" array member for 
 | |
| the stored text of the error.
 | |
| 
 | |
| When the \helpref{wxDbTable::Query}{wxdbtablequery} returns true, the 
 | |
| database was able to successfully complete the requested query using the 
 | |
| provided criteria. This does not mean that there are any rows in the 
 | |
| result set, it just mean that the query was successful.
 | |
| 
 | |
| \normalbox{IMPORTANT: The result created by the call to 
 | |
| \helpref{wxDbTable::Query}{wxdbtablequery} can take one of two forms. It is 
 | |
| either a snapshot of the data at the exact moment that the database 
 | |
| determined the record matched the search criteria, or it is a pointer to 
 | |
| the row that matched the selection criteria. Which form of behavior is 
 | |
| datasource dependent. If it is a snapshot, the data may have changed 
 | |
| since the result set was constructed, so beware if your datasource 
 | |
| uses snapshots and call \helpref{wxDbTable::Refresh}{wxdbtablerefresh}. Most larger brand databases 
 | |
| do not use snapshots, but it is important to mention so that your application 
 | |
| can handle it properly if your datasource does.}
 | |
| 
 | |
| To retrieve the data, one of the data fetching routines must be used to 
 | |
| request a row from the result set, and to store the data from the result 
 | |
| set into the bound memory variables. After \helpref{wxDbTable::Query}{wxdbtablequery} 
 | |
| has completed successfully, the default/current cursor is placed so it 
 | |
| is pointing just before the first record in the result set. If the 
 | |
| result set is empty (no rows matched the criteria), then any calls to 
 | |
| retrieve data from the result set will return false.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     wxString msg;
 | |
| 
 | |
|     while (table->GetNext())
 | |
|     {
 | |
|         msg.Printf("Row #%lu -- First Name : %s  Last Name is %s",
 | |
|       	           table->GetRowNum(), FirstName, LastName);
 | |
|         wxMessageBox(msg, "Data", wxOK | wxICON_INFORMATION, NULL);
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| The sample code above will read the next record in the result set repeatedly 
 | |
| until the end of the result set has been reached. The first time that 
 | |
| \helpref{wxDbTable::GetNext}{wxdbtablegetnext} is called right after the successful 
 | |
| call to \helpref{wxDbTable::Query}{wxdbtablequery}, it actually returns the first record 
 | |
| in the result set. 
 | |
| 
 | |
| When \helpref{wxDbTable::GetNext}{wxdbtablegetnext} is called and there are 
 | |
| no rows remaining in the result set after the current cursor position, 
 | |
| \helpref{wxDbTable::GetNext}{wxdbtablegetnext} (as well as all the other 
 | |
| wxDbTable::GetXxxxx() functions) will return false.
 | |
| 
 | |
| {\bf Close the table}
 | |
| 
 | |
| When the program is done using a wxDbTable instance, it is as simple as 
 | |
| deleting the table pointer (or if declared statically, letting the 
 | |
| variable go out of scope). Typically the default destructor will take 
 | |
| care of all that is required for cleaning up the wxDbTable instance.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     if (table)
 | |
|     {
 | |
|         delete table;
 | |
|         table = NULL;
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| Deleting a wxDbTable instance releases all of its cursors, deletes the 
 | |
| column definitions and frees the SQL environment handles used by the 
 | |
| table (but not the environment handle used by the datasource connection 
 | |
| that the wxDbTable instance was using).
 | |
| 
 | |
| {\bf Close the datasource connection}
 | |
| 
 | |
| After all tables that have been using a datasource connection have been 
 | |
| closed (this can be verified by calling \helpref{wxDb::GetTableCount}{wxdbgettablecount} 
 | |
| and checking that it returns 0), then you may close the datasource 
 | |
| connection. The method of doing this is dependent on whether the 
 | |
| non-caching or caching method was used to obtain the datasource connection.
 | |
| 
 | |
| If the datasource connection was created manually (non-cached), closing the 
 | |
| connection is done like this:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     if (db)
 | |
|     {
 | |
|         db->Close();
 | |
|         delete db;
 | |
|         db = NULL;
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| If the program used the \helpref{wxDbGetConnection}{wxdbfunctions} function to get a datasource 
 | |
| connection, the following is the code that should be used to free the 
 | |
| connection(s):
 | |
| 
 | |
| \begin{verbatim}
 | |
|     if (db)
 | |
|     {
 | |
|         wxDbFreeConnection(db);
 | |
|         db = NULL;
 | |
|     }
 | |
| \end{verbatim}
 | |
| 
 | |
| Note that the above code just frees the connection so that it can be 
 | |
| re-used on the next call the \helpref{wxDbGetConnection}{wxdbfunctions}. To actually dispose 
 | |
| of the connection, releasing all of its resources (other than the 
 | |
| environment handle), do the following:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     wxDbCloseConnections();
 | |
| \end{verbatim}
 | |
| 
 | |
| {\bf Release the ODBC environment handle}
 | |
| 
 | |
| Once all of the connections that used the ODBC environment handle (in 
 | |
| this example it was stored in "DbConnectInf.Henv") have been closed, then 
 | |
| it is safe to release the environment handle:
 | |
| 
 | |
| \begin{verbatim}
 | |
|     DbConnectInf->FreeHenv();
 | |
| \end{verbatim}
 | |
| 
 | |
| Or, if the long form of the constructor was used and the constructor was allowed 
 | |
| to allocate its own SQL environment handle, leaving scope or destruction of the 
 | |
| wxDbConnectInf will free the handle automatically.
 | |
| 
 | |
| \begin{verbatim}
 | |
|     delete DbConnectInf;
 | |
| \end{verbatim}
 | |
| 
 | |
| \normalbox{Remember to never release this environment handle if there are any 
 | |
| connections still using the handle.}
 | |
| 
 | |
| \subsection{wxODBC - Known Issues}\label{wxodbcknownissues}
 | |
| 
 | |
| As with creating wxWidgets, writing the wxODBC classes was not the simple 
 | |
| task of writing an application to run on a single type of computer system. 
 | |
| The classes need to be cross-platform for different operating systems, and 
 | |
| they also needed to take in to account different database manufacturers and 
 | |
| different ODBC driver manufacturers. Because of all the possible combinations 
 | |
| of OS/database/drivers, it is impossible to say that these classes will work 
 | |
| perfectly with datasource ABC, ODBC driver XYZ, on platform LMN. You may run 
 | |
| into some incompatibilities or unsupported features when moving your 
 | |
| application from one environment to another. But that is what makes 
 | |
| cross-platform programming fun. It also pinpoints one of the great 
 | |
| things about open source software. It can evolve!
 | |
| 
 | |
| The most common difference between different database/ODBC driver 
 | |
| manufacturers in regards to these wxODBC classes is the lack of 
 | |
| standard error codes being returned to the calling program. Sometimes 
 | |
| manufacturers have even changed the error codes between versions of 
 | |
| their databases/drivers. 
 | |
| 
 | |
| In all the tested databases, every effort has been made to determine 
 | |
| the correct error codes and handle them in the class members that need 
 | |
| to check for specific error codes (such as TABLE DOES NOT EXIST when 
 | |
| you try to open a table that has not been created yet). Adding support 
 | |
| for additional databases in the future requires adding an entry for the 
 | |
| database in the \helpref{wxDb::Dbms}{wxdbdbms} function, and then handling any error codes 
 | |
| returned by the datasource that do not match the expected values.
 | |
| 
 | |
| {\bf Databases}
 | |
| 
 | |
| Following is a list of known issues and incompatibilities that the 
 | |
| wxODBC classes have between different datasources. An up to date 
 | |
| listing of known issues can be seen in the comments of the source 
 | |
| for \helpref{wxDb::Dbms}{wxdbdbms}.
 | |
| 
 | |
| {\it ORACLE}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item Currently the only database supported by the wxODBC classes to support VIEWS
 | |
| \end{itemize}
 | |
| 
 | |
| {\it DBASE}
 | |
| 
 | |
| NOTE: dBase is not a true ODBC datasource. You only have access to as much 
 | |
| functionality as the driver can emulate.
 | |
| 
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item Does not support the SQL\_TIMESTAMP structure
 | |
| \item Supports only one cursor and one connect (apparently? with Microsoft driver only?)
 | |
| \item Does not automatically create the primary index if the 'keyField' param of SetColDef is true. The user must create ALL indexes from their program with calls to \helpref{wxDbTable::CreateIndex}{wxdbtablecreateindex}
 | |
| \item Table names can only be 8 characters long
 | |
| \item Column names can only be 10 characters long
 | |
| \item Currently cannot CREATE a dBase table - bug or limitation of the drivers used??
 | |
| \item Currently cannot insert rows that have integer columns - bug??
 | |
| \end{itemize}
 | |
| 
 | |
| {\it SYBASE (all)}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added after every table name involved in the query/join if that table's matching record(s) are to be locked
 | |
| \item Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
 | |
| \end{itemize}
 | |
| 
 | |
| {\it SYBASE (Enterprise)}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item If a column is part of the Primary Key, the column cannot be NULL
 | |
| \item Maximum row size is somewhere in the neighborhood of 1920 bytes
 | |
| \end{itemize}
 | |
| 
 | |
| {\it mySQL}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item If a column is part of the Primary Key, the column cannot be NULL.
 | |
| \item Cannot support selecting for update [\helpref{wxDbTable::CanSelectForUpdate}{wxdbtablecanselectforupdate}]. Always returns false.
 | |
| \item Columns that are part of primary or secondary keys must be defined as being NOT NULL when they are created. Some code is added in \helpref{wxDbTable::CreateIndex}{wxdbtablecreateindex} to try to adjust the column definition if it is not defined correctly, but it is experimental (as of wxWidgets v2.2.1)
 | |
| \item Does not support sub-queries in SQL statements
 | |
| \end{itemize}
 | |
| 
 | |
| {\it POSTGRES}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
 | |
| \item Does not support sub-queries in SQL statements
 | |
| \end{itemize}
 | |
| 
 | |
| {\it DB2}
 | |
| \begin{itemize}\itemsep=0pt
 | |
| \item Columns which are part of a primary key must be declared as NOT NULL
 | |
| \end{itemize}
 | |
| 
 | |
| {\bf UNICODE with wxODBC classes}
 | |
| 
 | |
| As of v2.6 of wxWidgets, the wxODBC classes now fully support the compilation 
 | |
| and use of the classes in a Unicode build of wxWidgets, assuming the compiler 
 | |
| and OS on which the program will be compiled/run is Unicode capable.
 | |
| 
 | |
| The one major difference in writing code that can be compiled in either 
 | |
| unicode or non-unicode builds that is specific to the wxODBC classes is to 
 | |
| use the SQL\_C\_WXCHAR datatype for string columns rather than SQL\_C\_CHAR or 
 | |
| SQL\_C\_WCHAR.
 | |
| 
 | |
| \subsection{wxODBC - Sample Code}\label{wxodbcsamplecode1}
 | |
| 
 | |
| Simplest example of establishing/opening a connection to an ODBC datasource, 
 | |
| binding variables to the columns for read/write usage, opening an 
 | |
| existing table in the datasource, inserting a record, setting query parameters 
 | |
| (where/orderBy/from), querying the datasource, reading each row of the 
 | |
| result set, deleting a record, releasing the connection, then cleaning up.
 | |
| 
 | |
| NOTE: Very basic error handling is shown here, to reduce the size of the 
 | |
| code and to make it more easily readable.  The HandleError() function uses the wxDbLogExtendedErrorMsg() function for retrieving database error messages.
 | |
| 
 | |
| \begin{verbatim}
 | |
| // ----------------------------------------------------------------------------
 | |
| // HEADERS
 | |
| // ----------------------------------------------------------------------------
 | |
| #include "wx/log.h"         // #included to enable output of messages only
 | |
| #include "wx/dbtable.h"
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| // FUNCTION USED FOR HANDLING/DISPLAYING ERRORS
 | |
| // ----------------------------------------------------------------------------
 | |
| // Very generic error handling function.  
 | |
| // If a connection to the database is passed in, then we retrieve all the 
 | |
| // database errors for the connection and add them to the displayed message
 | |
| int HandleError(wxString errmsg, wxDb *pDb=NULL)
 | |
| {
 | |
|     // Retrieve all the error message for the errors that occurred
 | |
|     wxString allErrors;
 | |
|     if (!pDb == NULL)
 | |
|         // Get the database errors and append them to the error message
 | |
|         allErrors = wxDbLogExtendedErrorMsg(errmsg.c_str(), pDb, 0, 0);
 | |
|     else
 | |
|         allErrors = errmsg;
 | |
| 
 | |
|     // Do whatever you wish with the error message here
 | |
|     // wxLogDebug() is called inside wxDbLogExtendedErrorMsg() so this
 | |
|     // console program will show the errors in the console window,
 | |
|     // but these lines will show the errors in RELEASE builds also
 | |
|     wxFprintf(stderr, wxT("\n%s\n"), allErrors.c_str());
 | |
|     fflush(stderr);
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| // entry point
 | |
| // ----------------------------------------------------------------------------
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| wxDbConnectInf  *DbConnectInf    = NULL;    // DB connection information
 | |
| 
 | |
| wxDb            *db              = NULL;    // Database connection
 | |
| 
 | |
| wxDbTable       *table           = NULL;    // Data table to access
 | |
| const wxChar     tableName[]     = wxT("USERS"); // Name of database table
 | |
| const UWORD      numTableColumns = 2;       // Number table columns
 | |
| wxChar           FirstName[50+1];           // column data: "FIRST_NAME"
 | |
| wxChar           LastName[50+1];            // column data: "LAST_NAME"
 | |
| 
 | |
| wxString         msg;                       // Used for display messages
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // DEFINE THE CONNECTION HANDLE FOR THE DATABASE
 | |
| // -----------------------------------------------------------------------
 | |
| DbConnectInf = new wxDbConnectInf(NULL, 
 | |
|                                     wxT("CONTACTS-SqlServer"),
 | |
|                                     wxT("sa"),
 | |
|                                     wxT("abk"));
 | |
| 
 | |
| // Error checking....
 | |
| if (!DbConnectInf || !DbConnectInf->GetHenv())
 | |
| {
 | |
|     return HandleError(wxT("DB ENV ERROR: Cannot allocate ODBC env handle"));
 | |
| }
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // GET A DATABASE CONNECTION
 | |
| // -----------------------------------------------------------------------
 | |
| db = wxDbGetConnection(DbConnectInf);
 | |
| 
 | |
| if (!db)
 | |
| {
 | |
|     return HandleError(wxT("CONNECTION ERROR - Cannot get DB connection"));
 | |
| }
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // DEFINE THE TABLE, AND THE COLUMNS THAT WILL BE ACCESSED
 | |
| // -----------------------------------------------------------------------
 | |
| table = new wxDbTable(db, tableName, numTableColumns, wxT(""), 
 | |
|                     !wxDB_QUERY_ONLY, wxT(""));
 | |
| //
 | |
| // Bind the columns that you wish to retrieve. Note that there must be
 | |
| // 'numTableColumns' calls to SetColDefs(), to match the wxDbTable def
 | |
| //
 | |
| // Not all columns need to be bound, only columns whose values are to be 
 | |
| // returned back to the client.
 | |
| //
 | |
| table->SetColDefs(0, wxT("FIRST_NAME"), DB_DATA_TYPE_VARCHAR, FirstName,
 | |
|                 SQL_C_WXCHAR, sizeof(FirstName), true, true);
 | |
| table->SetColDefs(1, wxT("LAST_NAME"), DB_DATA_TYPE_VARCHAR, LastName,
 | |
|                 SQL_C_WXCHAR, sizeof(LastName), true, true);
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // CREATE (or RECREATE) THE TABLE IN THE DATABASE
 | |
| // -----------------------------------------------------------------------
 | |
| if (!table->CreateTable(true))  //NOTE: No CommitTrans is required
 | |
| {
 | |
|     return HandleError(wxT("TABLE CREATION ERROR: "), table->GetDb());
 | |
| }
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // OPEN THE TABLE FOR ACCESS
 | |
| // -----------------------------------------------------------------------
 | |
| if (!table->Open())
 | |
| {
 | |
|     return HandleError(wxT("TABLE OPEN ERROR: "), table->GetDb());
 | |
| }
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // INSERT A NEW ROW INTO THE TABLE
 | |
| // -----------------------------------------------------------------------
 | |
| wxStrcpy(FirstName, wxT("JULIAN"));
 | |
| wxStrcpy(LastName, wxT("SMART"));
 | |
| if (!table->Insert())
 | |
| {
 | |
|     return HandleError(wxT("INSERTION ERROR: "), table->GetDb());
 | |
| }
 | |
| 
 | |
| // Must commit the insert to write the data to the DB
 | |
| table->GetDb()->CommitTrans();
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // RETRIEVE ROWS FROM THE TABLE BASED ON SUPPLIED CRITERIA
 | |
| // -----------------------------------------------------------------------
 | |
| // Set the WHERE clause to limit the result set to return
 | |
| // all rows that have a value of 'JULIAN' in the FIRST_NAME
 | |
| // column of the table.
 | |
| table->SetWhereClause(wxT("FIRST_NAME = 'JULIAN'"));
 | |
| 
 | |
| // Result set will be sorted in ascending alphabetical 
 | |
| // order on the data in the 'LAST_NAME' column of each row
 | |
| table->SetOrderByClause(wxT("LAST_NAME"));
 | |
| 
 | |
| // No other tables (joins) are used for this query
 | |
| table->SetFromClause(wxT(""));
 | |
| 
 | |
| // Instruct the datasource to perform a query based on the 
 | |
| // criteria specified above in the where/orderBy/from clauses.
 | |
| if (!table->Query())
 | |
| {
 | |
|     return HandleError(wxT("QUERY ERROR: "), table->GetDb());
 | |
| }
 | |
| 
 | |
| // Loop through all rows matching the query criteria until
 | |
| // there are no more records to read
 | |
| while (table->GetNext())
 | |
| {
 | |
|     msg.Printf(wxT("Row #%lu -- First Name : %s  Last Name is %s"),
 | |
|             table->GetRowNum(), FirstName, LastName);
 | |
| 
 | |
|     // Code to display 'msg' here
 | |
|     wxLogMessage(wxT("\n%s\n"), msg.c_str());
 | |
| }
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // DELETE A ROW FROM THE TABLE
 | |
| // -----------------------------------------------------------------------
 | |
| // Select the row which has FIRST_NAME of 'JULIAN' and LAST_NAME
 | |
| // of 'SMART', then delete the retrieved row
 | |
| //
 | |
| if (!table->DeleteWhere(wxT("FIRST_NAME = 'JULIAN' and LAST_NAME = 'SMART'")))
 | |
| {
 | |
|     return HandleError(wxT("DELETION ERROR: "), table->GetDb());
 | |
| }
 | |
|     
 | |
| // Must commit the deletion to the database
 | |
| table->GetDb()->CommitTrans();
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // TAKE CARE OF THE ODBC CLASS INSTANCES THAT WERE BEING USED
 | |
| // -----------------------------------------------------------------------
 | |
| // If the wxDbTable instance was successfully created
 | |
| // then delete it as we are done with it now.
 | |
| wxDELETE(table);
 | |
| 
 | |
| // Free the cached connection
 | |
| // (meaning release it back in to the cache of datasource
 | |
| // connections) for the next time a call to wxDbGetConnection()
 | |
| // is made.
 | |
| wxDbFreeConnection(db);
 | |
| db = NULL;
 | |
| 
 | |
| 
 | |
| // -----------------------------------------------------------------------
 | |
| // CLEANUP BEFORE EXITING APP
 | |
| // -----------------------------------------------------------------------
 | |
| // The program is now ending, so we need to close
 | |
| // any cached connections that are still being 
 | |
| // maintained.
 | |
| wxDbCloseConnections();
 | |
| 
 | |
| // Release the environment handle that was created
 | |
| // for use with the ODBC datasource connections
 | |
| wxDELETE(DbConnectInf);
 | |
| 
 | |
| wxUnusedVar(argc);  // Here just to prevent compiler warnings
 | |
| wxUnusedVar(argv);  // Here just to prevent compiler warnings
 | |
| 
 | |
| return 0;
 | |
| }
 | |
| \end{verbatim}
 | |
| 
 | |
| \subsection{A selection of SQL commands}\label{sqlcommands}
 | |
| 
 | |
| The following is a very brief description of some common SQL commands, with
 | |
| examples.
 | |
| 
 | |
| \wxheading{See also}
 | |
| 
 | |
| \helpref{Database classes overview}{odbcoverview}
 | |
| 
 | |
| \subsubsection{Create}\label{odbccreateexample}
 | |
| 
 | |
| Creates a table.
 | |
| 
 | |
| Example:
 | |
| 
 | |
| \begin{verbatim}
 | |
| CREATE TABLE Book
 | |
|  (BookNumber     INTEGER     PRIMARY KEY
 | |
|  , CategoryCode  CHAR(2)     DEFAULT 'RO' NOT NULL
 | |
|  , Title         VARCHAR(100) UNIQUE
 | |
|  , NumberOfPages SMALLINT
 | |
|  , RetailPriceAmount NUMERIC(5,2)
 | |
|  )
 | |
| \end{verbatim}
 | |
| 
 | |
| \subsubsection{Insert}\label{odbcinsertexample}
 | |
| 
 | |
| Inserts records into a table.
 | |
| 
 | |
| Example:
 | |
| 
 | |
| \begin{verbatim}
 | |
| INSERT INTO Book
 | |
|   (BookNumber, CategoryCode, Title)
 | |
|   VALUES(5, 'HR', 'The Lark Ascending')
 | |
| \end{verbatim}
 | |
| 
 | |
| \subsubsection{Select}\label{odbcselectexample}
 | |
| 
 | |
| The Select operation retrieves rows and columns from a table. The criteria
 | |
| for selection and the columns returned may be specified.
 | |
| 
 | |
| Examples:
 | |
| 
 | |
| {\tt SELECT * FROM Book}
 | |
| 
 | |
| Selects all rows and columns from table Book.
 | |
| 
 | |
| {\tt SELECT Title, RetailPriceAmount FROM Book WHERE RetailPriceAmount > 20.0}
 | |
| 
 | |
| Selects columns Title and RetailPriceAmount from table Book, returning only
 | |
| the rows that match the WHERE clause.
 | |
| 
 | |
| {\tt SELECT * FROM Book WHERE CatCode = 'LL' OR CatCode = 'RR'}
 | |
| 
 | |
| Selects all columns from table Book, returning only
 | |
| the rows that match the WHERE clause.
 | |
| 
 | |
| {\tt SELECT * FROM Book WHERE CatCode IS NULL}
 | |
| 
 | |
| Selects all columns from table Book, returning only rows where the CatCode column
 | |
| is NULL.
 | |
| 
 | |
| {\tt SELECT * FROM Book ORDER BY Title}
 | |
| 
 | |
| Selects all columns from table Book, ordering by Title, in ascending order. To specify
 | |
| descending order, add DESC after the ORDER BY Title clause.
 | |
| 
 | |
| {\tt SELECT Title FROM Book WHERE RetailPriceAmount >= 20.0 AND RetailPriceAmount <= 35.0}
 | |
| 
 | |
| Selects records where RetailPriceAmount conforms to the WHERE expression.
 | |
| 
 | |
| \subsubsection{Update}\label{odbcupdateexample}
 | |
| 
 | |
| Updates records in a table.
 | |
| 
 | |
| Example:
 | |
| 
 | |
| {\tt UPDATE Incident SET X = 123 WHERE ASSET = 'BD34'}
 | |
| 
 | |
| This example sets a field in column `X' to the number 123, for the record
 | |
| where the column ASSET has the value `BD34'.
 | |
| 
 |