ODBC Classes

From our examination of the native ODBC API, it is clear that a great deal of boilerplate code can easily be moved off into class member functions. For instance, several types of handles are used by ODBC. To avoid leaking resources, each of these handles needs to be allocated before being used and freed before the class is destroyed . These tasks are ideal candidates for C++ class constructors and destructors.

As a brief review, constructors and destructors are special C++ class member functions that are called when a class is created and destroyed, respectively. The construction commonly takes place in one of the two following ways: a locally defined class calls its constructor when the object comes into scope as it is declared, or a class pointer can be declared and the constructor called when the new operator is called to dynamically create the class instance. Consider the following code fragment:

 CODBCDatabase*Db; Db=newCODBCDatabase(/*constructorarguments*/); 

The destructor is called as a class goes out of scope (in the case of a locally declared class instance) or when the delete operator is called on a class instance. This mechanism is ideal for allocation and deallocation of resources such as handles or memory.

Considering the information previously discussed, what classes should be created to interact with ODBC data? A reasonable class represents a connection to a data source. This would create a connection that could be used to access multiple independent tables, views, or queries within the single data source. In our ODBC database access classes, which will be used throughout the rest of the book, this reasonable class is called CODBCDatabase. It is similar to the MFC CDatabase class, although it is not derived from that class nor does it implement identical functionality.

We also need classes to manage the interaction with the various tables, views, and queries. In the MFC classes, such class management would be provided by something like CRecordset . The CODBCDatabase class won't have all the functionality of the CRecordset class. The CRecordset class contains functionality that is completely appropriate and necessary for a client application, but superfluous for server-based applications. (For example, server-based applications don't need the CRecordset field-exchange mechanism, which allows database fields to be bound to form fields.) What server-based applications do need is a way to bind variables to values returned by queries. The CODBCCursor class will accomplish that.

Note that this chapter will not replace the ODBC SDK documentation. Most of my coverage of the raw ODBC API will describe function use within the CODBCCursor class; the CODBCCursor class is not representative of every possible ODBC application. For instance, the CODBCCursor class will use the SQLFetch function to get data returned by the queries executed by the class. In many cases, the function SQLExtendedFetch would be the better choice, allowing many rows to be returned in a single operation. The SQLExtendedFetch function is beyond the scope of this discussion and will not be essential for the way this class will be used in future chapters.

An Example:ODBC Classes in Action

Listing 8-1 presents a simple console mode application that displays the fields of the tblProblems table of the Troubleshooter database (introduced in Chapter 9). The problem ID, the date entered, the name of the user who entered the problem, and part of the Description field are captured and displayed in the console. This is a simple application ”in the real world, we'd want to scroll through entries or page the output so that all entries could be easily seen. Here the emphasis is simply on getting the data.

Listing 8-1

ODBCTest.cpp

 //ODBCTest.cpp:Definestheentrypointforthe //consoleapplication. #include"windows.h" //DefinitionforoursimpleODBCclass #include"odbclass.h" //ConvertsdatefromYYYY-MM-DDformattoMM/DD/YYYYformat. char*fixDate(char*tdate) { charresult[255]; strcpy(result,"null"); if(tdate[0]!=' 
 //  ODBCTest.cpp  :  Defines  the  entry  point  for  the //    console  application. #include  " windows .h" //  Definition  for  our  simple  ODBC  class #include  "odbclass.h" //  Converts  date  from  YYYY-MM-DD  format  to  MM/DD/YYYY  format. char  *fixDate(char  *tdate) {         char  result[255]; strcpy (result,  "null");         if  (tdate[0]!='\0')         {                 sprintf(result,  "%2.2s/%2.2s/%4.4s",  &tdate[5],                         &tdate[8],  tdate);         } strncpy ((char  *)tdate,  result,                 strlen((const  char  *)result)  +  1);         return(tdate); } int  main(int  argc,  char*  argv[]) {         WORD  rc;         WORD  col  =  1;         //  InitCriticalSection  must  be  called  before  the  first  use         //    of  the  CODBCDatabase  class.         CODBCDatabase::InitCriticalSection();         //  Declare  and  dynamically  create  CODBCDatabase  object.         CODBCDatabase  *Db;         Db  =  new  CODBCDatabase("TroubleshootSQL","sa","SECRET");         //  Declare  and  create  CODBCCursor  object.         CODBCCursor  *curs;         curs  =  new  CODBCCursor(Db);         //  Set  the  table  or  tables.         curs->setTables("tblProblems");         //  Bind  the  fields.         curs->bindColumn(col,  "ProblemID",  255);         curs->bindColumn(col,  "DateEntered",  255);         curs->bindColumn(col,  "EnteredBy",  255);         curs->bindColumn(col,  "Description",  255);         //  Set  an  ORDER  BY  clause.         curs->setOrderBy("Description");         //  Also  set  WHERE  clause  in  order  to  filter  the  data.         curs->setRestrict("WHERE  Description  >  ''");         //  Select  the  records.    No  data  yet...         curs->doSelect();         //  ...NOW  we  have  the  data.         rc=curs->fetch();         //  Print  a  header.         printf("Problem  ID  Date  Entered      Entered  By          Description\n");         printf("===============================================");         printf("===============\n");         do  {                 //  If  the  fetch  was  successful,  print  the  data.                 if  (rc==SQL_SUCCESS)                 {                         printf("%10.10s  %-14.14s  %-14.14s  %-32.32s\n",                                 (char  *)(*curs)[1],                                 fixDate((char  *)(*curs)[2]),                                 (char  *)(*curs)[3],                                 (char  *)(*curs)[4]);                         rc=curs->fetch();                 }         }  while  (rc==SQL_SUCCESS);         delete  curs;         delete  Db;         CODBCDatabase::DelCriticalSection();         return  0; } 
') { sprintf(result,"%2.2s/%2.2s/%4.4s",&tdate[5], &tdate[8],tdate); } strncpy((char*)tdate,result, strlen((constchar*)result)+1); return(tdate); } intmain(intargc,char*argv[]) { WORDrc; WORDcol=1; //InitCriticalSectionmustbecalledbeforethefirstuse //oftheCODBCDatabaseclass. CODBCDatabase::InitCriticalSection(); //DeclareanddynamicallycreateCODBCDatabaseobject. CODBCDatabase*Db; Db=newCODBCDatabase("TroubleshootSQL","sa","SECRET"); //DeclareandcreateCODBCCursorobject. CODBCCursor*curs; curs=newCODBCCursor(Db); //Setthetableortables. curs->setTables("tblProblems"); //Bindthefields. curs->bindColumn(col,"ProblemID",255); curs->bindColumn(col,"DateEntered",255); curs->bindColumn(col,"EnteredBy",255); curs->bindColumn(col,"Description",255); //SetanORDERBYclause. curs->setOrderBy("Description"); //AlsosetWHEREclauseinordertofilterthedata. curs->setRestrict("WHEREDescription>''"); //Selecttherecords.Nodatayet... curs->doSelect(); //...NOWwehavethedata. rc=curs->fetch(); //Printaheader. printf("ProblemIDDateEnteredEnteredByDescription\n"); printf("==============================================="); printf("===============\n"); do{ //Ifthefetchwassuccessful,printthedata. if(rc==SQL_SUCCESS) { printf("%10.10s%-14.14s%-14.14s%-32.32s\n", (char*)(*curs)[1], fixDate((char*)(*curs)[2]), (char*)(*curs)[3], (char*)(*curs)[4]); rc=curs->fetch(); } }while(rc==SQL_SUCCESS); deletecurs; deleteDb; CODBCDatabase::DelCriticalSection(); return0; }

The first function defined in this simple test application, fixDate , converts a non-NULL date from YYYY-MM-DD format to MM/DD/YYYY format. (SQL Server, the server database used, returns dates in the YYYY-MM-DD format.) Of course, a more elegant solution would take the value returned by SQL Server and convert it to the format specified in the regional settings of the user's computer.

Next is the main function. The presence of main rather than WinMain reminds you that this is not a GUI application but a console mode application. Console mode applications were discussed in Chapter 1 as a reasonable way to start debugging a server application. After declaring a couple of simple variables, main calls the InitCriticalSection static method of the CODBCDatabase class. The details of this will be discussed shortly, but recall that a critical section is one of the synchronization objects discussed in Chapter 2. In order to make these classes thread safe, the critical section object that the CODBCDatabase class contains must be initialized. It is not reasonable for the critical section object to be initialized within any of the non-static member functions, so it is the responsibility of the calling application to call the static initialization member function before any objects of the CODBCDatabase class are created.

Next, main declares and dynamically creates a CODBCDatabase object and a CODBCCursor object. Dynamically creating the CODBCDatabase object is a convenience that lets us call the DelCriticalSection member function to destroy the critical section object. When we examine the internals of the classes, we will see why this is important. The constructor for the CODBCDatabase class takes the name of the ODBC data source and optionally the user name and password. In this example, SQL Server required a user name and password. (Other databases might or might not require a password.) The CODBCCursor class constructor takes a pointer to the CODBCDatabase class that was just created.

Internally, the class simply uses its member data members and creates SQL statements to pass on to the ODBC Driver Manager. Most SQL SELECT statements used to retrieve information (such as the statements created by our test application) are similar to the following statement:

 SELECTProblemID,DateEntered,EnteredBy,Description FROMtblProblems ORDERBYDescription WHEREDescription>'' 

Note that SQL keywords are all uppercase letters . The example gets the four fields listed from each record in the tblProblems table, sorts them by the Description field, and filters the records so that only those that contain description information are returned.

Within main , we first set the table, using the setTables method; next we bind the columns we want returned, using the bindColumn method; then setOrderBy and setRestrict methods are called with the appropriate arguments; and doSelect is called to actually pass the SQL statement to the ODBC Driver Manager. Note that at this point no data is returned and accessible to the application. Data will be returned and accessible after the call to the fetch method. (The fetch method causes the data returned to be stuffed into the buffers created during column binding.)

The output of the program is shown in Figure 8-5. After all the records have been fetched , the application destroys the CODBCDatabase object and the CODBCCursor object. After the objects have been destroyed, the critical section object is destroyed with the call to DelCriticalSection .

click to view at full size.

Figure 8-5 Output from the simple ODBC sample program.

Simply displaying some records on a console seems like a lot of work, but what is happening is amazing. The same code could operate on data from a Microsoft Access database (with the exception of the call to fixDate since by default Access does not return dates in the same format as SQL Server). There are virtually no dependencies on the particular DBMS, leaving us with a comfortable level of abstraction. This abstraction is quite useful and a great deal easier than the equivalent ”pure ODBC API calls. Looking at the actual class source will provide us with a better understanding of the native ODBC API and show why wrapping it is such a good idea.

The ODBCDatabase Class

Some of the complications in the ODBCDatabase class are caused by the way ODBC works. Virtually all ODBC operations take one or more handles to do their task. The following are the three types of handles used by ODBC:

  • Environment handles. One per application
  • Connection handles. One per connection, although you can have more if it is convenient for the application
  • Statement handles. Generally more than one per application

A single environment handle needs to be allocated for an application. You can allocate more than one, but the second environment handle that you allocate will hide some of the resources associated with the first handle, causing a resource leak (a very bad thing to do in a server application). The way the CODBCDatabase class manages this allocation is through use of a reference count. Now the critical section comes in handy. Look at the header for the ODBC classes in Listing 8-2. The first three data members of the CODBCDatabase class are the static reference count, critical section, and environment handle: referenceCount , CriticalSection , and henv , respectively. Two static methods also exist: one for initializing the critical section and one for deleting it. Additional private data members hold the data source name, user name, and password, and track both the number of tables in use and the connection status of the object. Most of the methods are simply access methods to get at the member variables. The two exceptions are GetSQLError and ExecuteSQL . Both of these exceptions will be described in detail as we look at the implementations .

Listing 8-2

ODBClass.h

 #ifndefODBCLASS_H #defineODBCLASS_H #include<windows.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include"SQL.H" #include"sqlext.h" #include<string.h> //512columnsshouldbeadequate. #defineMAX_COLUMNS512 classCODBCDatabase{ private: //ThereferenceCountandtheCriticalSection //membervariablesmustbestatic;therecan //beonlyoneofeachforallclassinstances. staticreferenceCount; staticCRITICAL_SECTIONCriticalSection; staticHENVhenv; intnumTables; chardataSourceName[255]; charuid[255]; charpassword[255]; intconnected; RETCODElastRetCode; public: HDBChdbc; CODBCDatabase(char*_dataSourceName, char*_uid=NULL, char*_password=NULL); ~CODBCDatabase(); //Thesestaticfunctionsdealwithstaticvariables. staticvoidInitCriticalSection() { InitializeCriticalSection(&CriticalSection); } staticvoidDelCriticalSection() { DeleteCriticalSection(&CriticalSection); } RETCODEGetSQLError(SQLSMALLINTHandleType,SQLHANDLEhandle); RETCODEExecuteSQL(char*command); operatorHDBC() { return(hdbc); } char*getDataSourceName(char*t) { if(connected==TRUE) { strcpy(t,dataSourceName); return(t); } return(NULL); } intisConnected() { return(connected); } intnumTablesOpen() { return(numTables); } }; structCOLUMN_INFO{ char*name; char*data; longlen; longretLen; SQLSMALLINTtype; }; classCODBCCursor{ private: CODBCDatabase*_database; HSTMThstmt; //Arbitrarilylimitto512columns.(Increaseasneeded.) structCOLUMN_INFO*colInfo[MAX_COLUMNS]; UWORDnumCols; UWORDm_topN; RETCODElastRetCode; charwhereClause[512]; chartableClause[256]; charorderByClause[256]; charsetClause[256]; CRITICAL_SECTIONCriticalSection; public: //DefaulttoNULLforUIDandPassword. CODBCCursor(CODBCDatabase*db); ~CODBCCursor(); //Allowcolumnstobeboundtoreturnedrows. RETCODEbindColumn(UWORD&colNum,char*columnName, SDWORDlen,SWORDtype=SQL_C_CHAR); UWORDgetTopN() { return(m_topN); } voidsetTopN(UWORDvalue) { m_topN=value; } UWORDgetNumCol() { return(numCols); } RETCODEGetSQLError(SQLSMALLINThandleType,SQLHANDLEhandle) { return(_database->GetSQLError(handleType,handle)); } void*getColData(UWORDcolNum) { if(colInfo[colNum]!=0) { return((void*)colInfo[colNum]->data); } return(0); } intclearBindings() { for(intloop=1;colInfo[loop]!=0;loop++) { deletecolInfo[loop]->name; deletecolInfo[loop]->data; deletecolInfo[loop]; colInfo[loop]=0; } return(loop); } voidsetRestrict(char*_whereClause) { strcpy(whereClause,_whereClause); } char*getRestrict(char*_whereClause) { strcpy(_whereClause,whereClause); return(_whereClause); } char*getTables(char*_tableClause) { strcpy(_tableClause,tableClause); return(_tableClause); } voidsetTables(char*_tableClause) { strcpy(tableClause,_tableClause); } voidsetOrderBy(char*_orderByClause) { strcpy(orderByClause,_orderByClause); } char*getOrderBy(char*_orderByClause) { strcpy(_orderByClause,orderByClause); return(_orderByClause); } char*getSet(char*_setClause) { strcpy((char*)_setClause,(char*)setClause); return(_setClause); } voidsetSet(char*_setClause) { strcpy((char*)setClause,(char*)_setClause); } intdoSelect(); intdoDelete(); intdoUpdate(); RETCODEfetch(); UWORDassignColumn(char*fname,SDWORDlen); void*operator[](unsignedlongi) { if(colInfo[i]!=0) { return((void*)colInfo[i]->data); } return(0); } void*operator[](char*colName) { UWORDloop; for(loop=1; colInfo[loop]!=0&& (stricmp((char*)colInfo[loop]->name, (char*)colName));) { loop++; } if(colInfo[loop]!=0) { return((void*)colInfo[loop]->data); } return(0); } }; #endif 

The COLUMN_INFO structure is used by the CODBCCursor class to store information about the fields bound to each object. Most of the members of the structure are self-explanatory, with the exception of the type member. The type member is designed to hold the type of the column. The constants used for this are part of the ODBC standard and represent virtually any data type you might need. See the ODBC online documentation for details about types other than those used in the example.

The CODBCCursor class contains a pointer to a CODBCDatabase object, a statement handle, an array of COLUMN_INFO structures (arbitrarily limited to 512), and several variables that hold the parts of the SQL statements used to construct the final SQL statement. Additionally, the CODBCCursor class has its own critical section object, used to ensure that the object is thread-safe. The CODBCCursor class could have used the critical section of the CODBCDatabase class, but that would have serialized through a single critical section access to CODBCCursor objects. Serialization of access is guaranteed to cause problems as the number of CODBCCursor objects increases within an application.

Many of the inline methods of CODBCCursor are self-explanatory, but two are not: the two functions that overload the [] array operators. These functions are used to allow the application to index into the CODBCCursor object either by the column number (assigned as the column is bound) or by the name. In Listing 8-1, the following lines would return the same value:

 (char*)(*curs)[1]; (char*)(*curs)["ProblemID"]; 

Because ProblemID is the first column bound, and because the column variable is initialized to 1, these lines would both return void pointers to the ProblemID field. Indexing based on the column number is faster, but the ability to index based on the field name can be quite convenient and often worth the extra time required. To improve performance, it is also possible to use variables to cache the column numbers as the columns are bound and then use those variables to index using the column number.

Listing 8-3 shows the implementation of the CODBCDatabase class and the CODBCCursor class. The constructor for the CODBCDatabase class first enters the critical section (thus it is necessary to have this initialized before any CODBCDatabase object is constructed). Once safely within the critical section, the reference count is checked. If the count is 0, indicating that this database object is the first one constructed , the environment handle is allocated using the SQLAllocHandle ODBC API function. Next SQLSetEnvAttr is called. (The SQL_OV_ODBC3 parameter indicates that this will be an ODBC version 3 application.) If all that succeeds, the reference count is incremented.

Listing 8-3

ODBClass.cpp

 #include<windows.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<string.h> #include"SQL.H" #include"sqlext.h" #include"odbclass.h" #ifndefNULL #defineNULL0 #endif //Staticvariablesneedtobedefinedlikethisexactlyonce, //whichiswhytheyaredefinedintheCPPfileratherthan //intheHfilewheretheirclassisdefined. intCODBCDatabase::referenceCount=0; HENVCODBCDatabase::henv=0; CRITICAL_SECTIONCODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char*_dataSourceName,char*_uid, char*_password) { RETCODErc=SQL_SUCCESS; numTables=0; EnterCriticalSection(&CriticalSection); //Allocateenvironment(onlyforfirstinstance). if(referenceCount==0) { SDWORDvalEnv=SQL_OV_ODBC3; RETCODErc2; //Allocateanenvironmenthandle. rc=SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE, &henv); //Settheversioninformation.(Withouttheversion //information,youwillgetsequenceerrors.) rc2=SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER); if(rc2!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_ENV,henv); } } if(rc==SQL_SUCCESSrc==SQL_SUCCESS_WITH_INFO) { referenceCount++; } LeaveCriticalSection(&CriticalSection); //Allocateaconnectionhandle. rc=SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc); if(rc!=SQL_SUCCESS&&rc!=SQL_SUCCESS_WITH_INFO) { GetSQLError(SQL_HANDLE_ENV,henv); connected=FALSE; } else { strcpy(dataSourceName,_dataSourceName); //DefaultvaluesfortheseareNULL.SomeODBC //drivers(notablyOpenM)failonNULLuserorpassword. if(_uid!=NULL) { strcpy(uid,_uid); } else { strcpy(uid,""); } if(_password!=NULL) { strcpy(password,_password); } else { strcpy(password,""); } //Connecttothedatasource.Thedatasourcemustbeset //upusingtheODBCAdministrator(orthe //32-BitAdministrator)program. rc=SQLConnect(hdbc,(unsignedchar*)dataSourceName, SQL_NTS,(unsignedchar*)uid,SQL_NTS, (unsignedchar*)password,SQL_NTS); lastRetCode=rc; if(rc!=SQL_SUCCESS&&rc!=SQL_SUCCESS_WITH_INFO) { connected=FALSE; this->GetSQLError(SQL_HANDLE_DBC,hdbc); } else { connected=TRUE; } } } CODBCDatabase::~CODBCDatabase() { //ThesethreefunctionsarepairedwithSQLConnect, //SQLAllocConnect,andSQLAllocEnv. SQLDisconnect(hdbc); SQLFreeHandle(SQL_HANDLE_DBC,hdbc); EnterCriticalSection(&CriticalSection); referenceCount--; //Isthelastinstancebeingdestroyed? if(referenceCount==0) { SQLFreeHandle(SQL_HANDLE_ENV,henv); } LeaveCriticalSection(&CriticalSection); } RETCODECODBCDatabase::ExecuteSQL(char*command) { HSTMThstmt; RETCODEret; lastRetCode=SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt); if(lastRetCode!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_DBC,hdbc); } else { if((lastRetCode=SQLExecDirect(hstmt, (unsignedchar*)command,SQL_NTS))!=SQL_SUCCESS&& lastRetCode!=SQL_SUCCESS_WITH_INFO) { GetSQLError(SQL_HANDLE_STMT,hstmt); } ret=SQLFreeHandle(SQL_HANDLE_STMT,hstmt); if(ret!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_STMT,hstmt); } return(lastRetCode); } return(SQL_ERROR); } SQLRETURNCODBCDatabase::GetSQLError(SQLSMALLINTHandleType, SQLHANDLEhandle) { SQLRETURNrc; charszSqlState[256]; charszErrorMsg[256]; SQLSMALLINTbufLen=255; SQLSMALLINTtextLen; SDWORDpfNativeError=255; shortrecNum=1; do{ rc=SQLGetDiagRec(HandleType, handle, recNum++,//Getrecord1,try2,andsoon. (unsignedchar*)szSqlState, &pfNativeError, (unsignedchar*)szErrorMsg, bufLen, &textLen); if(rc==SQL_SUCCESS) { printf("\nSqlState=%sszErrorMsg=%src=%d", szSqlState,szErrorMsg,rc); printf("\nSQL_INVALID_HANDLE=%drc=%d", SQL_INVALID_HANDLE,rc); } }while(rc==SQL_SUCCESS); return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase*db) { intloop; InitializeCriticalSection(&CriticalSection); if(db==0db->isConnected()==0) { return; } _database=db; //Allocateastatementhandle;thestatementhandleisused //forothertransactions. lastRetCode=SQLAllocStmt(_database->hdbc,&hstmt); if(lastRetCode!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_DBC,_database->hdbc); } for(loop=0;loop<MAX_COLUMNS;loop++) { colInfo[loop]=0; } tableClause[0]=' 
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
'; orderByClause[0]='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
'; whereClause[0]='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
'; m_topN=0; } CODBCCursor::~CODBCCursor() { intloop; SQLFreeHandle(SQL_HANDLE_STMT,hstmt); for(loop=1;loop<MAX_COLUMNS&&colInfo[loop]!=0;loop++) { deletecolInfo[loop]->name; deletecolInfo[loop]->data; deletecolInfo[loop]; } DeleteCriticalSection(&CriticalSection); } RETCODECODBCCursor::bindColumn(UWORD&col,char*columnName, SDWORDlen,SWORDtype) { SDWORDretLen; EnterCriticalSection(&CriticalSection); col=assignColumn(columnName,len); colInfo[col]->type=type; LeaveCriticalSection(&CriticalSection); return(SQLBindCol(hstmt,col,type,colInfo[col]->data, len,&retLen)); } UWORDCODBCCursor::assignColumn(char*columnName,SDWORDlen) { UWORDloop=1; while(colInfo[loop]!=0) { loop++; } colInfo[loop]=newstructCOLUMN_INFO; numCols=loop; if(colInfo[loop]!=0) { //Allocatethespaceforthenameandthedataelement. colInfo[loop]->name=newchar[(strlen(columnName))+1]; colInfo[loop]->data=newchar[len+1]; colInfo[loop]->len=len; if(colInfo[loop]->name==0colInfo[loop]->data==0) { deletecolInfo[loop]; colInfo[loop]=0; } strcpy(colInfo[loop]->name,columnName); } return(loop); } intCODBCCursor::doSelect() { charselect[1280]; UWORDloop; UWORDcol=1; UWORDrc; memset(select,'
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
',1024); if(tableClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { //ConstructaSELECTstatement. if(getNumCol()) { if(m_topN>0) { sprintf(select,"SELECTTOP%d",m_topN); } else { strcpy(select,"SELECT"); } for(loop=1;colInfo[loop]!=0;loop++) { if(loop>1) { strcat(select,","); } else { strcat(select,""); } strcat(select,colInfo[loop]->name); } strcat(select,"FROM"); strcat(select,tableClause); if(whereClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { strcat(select,""); strcat(select,whereClause); } if(orderByClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { strcat(select,"ORDERBY"); strcat(select,orderByClause); } if((lastRetCode=SQLExecDirect(hstmt, (unsignedchar*)select,SQL_NTS))!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_STMT,hstmt); } return(lastRetCode); } } return(SQL_ERROR); } intCODBCCursor::doDelete() { chardel[1280]; memset(del,'
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
',1024); if(tableClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { //ConstructaDELETEstatement. if(getNumCol()) { strcpy(del,"DELETE*"); strcat(del,"FROM"); strcat(del,tableClause); if(whereClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { strcat(del,""); strcat(del,whereClause); } if((lastRetCode=SQLExecDirect(hstmt, (unsignedchar*)del,SQL_NTS))!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_STMT,hstmt); } return(lastRetCode); } } return(SQL_ERROR); } intCODBCCursor::doUpdate() { charupdate[1280]; memset(update,'
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
',1024); if(tableClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
'setClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { //ConstructanUPDATEstatement. if(getNumCol()) { strcpy(update,"UPDATE"); strcat(update,tableClause); strcat(update,"SET"); strcat(update,setClause); if(whereClause[0]!='
 #include  <windows.h> #include  <stdio.h> #include  <string.h> #include  <stdlib.h> #include  <string.h> #include  "SQL.H" #include  "sqlext.h" #include  "odbclass.h" #ifndef  NULL #define  NULL  0 #endif //  Static  variables  need  to  be  defined  like  this  exactly  once, //    which  is  why  they  are  defined  in  the  CPP  file  rather  than  //    in  the  H  file  where  their  class  is  defined. int  CODBCDatabase::referenceCount=0; HENV  CODBCDatabase::henv=0; CRITICAL_SECTION  CODBCDatabase::CriticalSection; CODBCDatabase::CODBCDatabase(char  *_dataSourceName,char  *_uid,         char  *_password) {         RETCODE  rc  =  SQL_SUCCESS;         numTables  =  0;         EnterCriticalSection(&CriticalSection);         //  Allocate  environment  (only  for  first  instance).         if  (referenceCount==0)         {                 SDWORD  valEnv=SQL_OV_ODBC3;                 RETCODE  rc2;                 //  Allocate  an  environment  handle.                 rc  =  SQLAllocHandle(SQL_HANDLE_ENV,  SQL_NULL_HANDLE,                         &henv);                 //  Set  the  version  information.  (Without  the  version                 //    information,  you  will  get  sequence  errors.)                 rc2  =  SQLSetEnvAttr(henv,  SQL_ATTR_ODBC_VERSION,                         (SQLPOINTER)  SQL_OV_ODBC3,  SQL_IS_INTEGER);                 if  (rc2!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_ENV,henv);                 }         }         if  (rc==SQL_SUCCESS    rc==SQL_SUCCESS_WITH_INFO)         {                 referenceCount++;         }         LeaveCriticalSection(&CriticalSection);         //  Allocate  a  connection  handle.         rc  =  SQLAllocHandle(SQL_HANDLE_DBC,  henv,  &hdbc);         if  (rc!=SQL_SUCCESS  &&  rc!=SQL_SUCCESS_WITH_INFO)         {                 GetSQLError(SQL_HANDLE_ENV,  henv);                 connected=FALSE;         }         else         {                 strcpy(dataSourceName,  _dataSourceName);                 //  Default  values  for  these  are  NULL.  Some  ODBC                 //    drivers  (notably  OpenM)  fail  on  NULL  user  or  password.                 if  (_uid!=NULL)                 {                         strcpy(uid,_uid);                 }                 else                 {                         strcpy(uid,"");                 }                 if  (_password!=NULL)                 {                         strcpy(password,_password);                 }                 else                 {                         strcpy(password,"");                 }                 //  Connect  to  the  data  source.  The  data  source  must  be  set                 //    up  using  the  ODBC  Administrator  (or  the                  //    32-Bit  Administrator)  program.                 rc  =  SQLConnect(hdbc,  (unsigned  char  *)dataSourceName,                         SQL_NTS,  (unsigned  char  *)uid,  SQL_NTS,                         (unsigned  char  *)password,  SQL_NTS);                 lastRetCode  =  rc;                 if  (rc  !=  SQL_SUCCESS  &&  rc  !=  SQL_SUCCESS_WITH_INFO)                 {                         connected=FALSE;                         this->GetSQLError(SQL_HANDLE_DBC,hdbc);                 }                 else                 {                         connected=TRUE;                 }         } } CODBCDatabase::~CODBCDatabase() {         //  These  three  functions  are  paired  with  SQLConnect,         //    SQLAllocConnect,  and  SQLAllocEnv.         SQLDisconnect(hdbc);         SQLFreeHandle(SQL_HANDLE_DBC,hdbc);         EnterCriticalSection(&CriticalSection);         referenceCount--;         //  Is  the  last  instance  being  destroyed?         if  (referenceCount==0)         {                 SQLFreeHandle(SQL_HANDLE_ENV,henv);         }         LeaveCriticalSection(&CriticalSection); } RETCODE  CODBCDatabase::ExecuteSQL(char  *command) {         HSTMT  hstmt;         RETCODE  ret;         lastRetCode  =  SQLAllocHandle(SQL_HANDLE_STMT,  hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  hdbc);         }         else          {                 if  ((lastRetCode=SQLExecDirect(hstmt,                         (unsigned  char  *)command,  SQL_NTS))!=SQL_SUCCESS  &&                         lastRetCode!=SQL_SUCCESS_WITH_INFO)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 ret  =  SQLFreeHandle(SQL_HANDLE_STMT,  hstmt);                 if  (ret!=SQL_SUCCESS)                 {                         GetSQLError(SQL_HANDLE_STMT,  hstmt);                 }                 return(lastRetCode);         }         return(SQL_ERROR); } SQLRETURN  CODBCDatabase::GetSQLError(SQLSMALLINT  HandleType,         SQLHANDLE  handle) {         SQLRETURN  rc;         char  szSqlState[256];         char  szErrorMsg[256];         SQLSMALLINT  bufLen  =  255;         SQLSMALLINT  textLen;         SDWORD  pfNativeError  =  255;         short  recNum  =  1;         do{                 rc  =  SQLGetDiagRec(HandleType,                         handle,                         recNum++,        //  Get  record  1,  try  2,  and  so  on.                         (unsigned  char  *)szSqlState,                         &pfNativeError,                         (unsigned  char  *)szErrorMsg,                         bufLen,                         &textLen);                 if  (rc==SQL_SUCCESS)                 {                         printf("\nSqlState=%s  szErrorMsg=%s  rc=%d",                                 szSqlState,szErrorMsg,rc);                         printf("\nSQL_INVALID_HANDLE=%d  rc=%d  ",                                 SQL_INVALID_HANDLE,  rc);                 }         }  while  (rc==SQL_SUCCESS);         return(rc); } CODBCCursor::CODBCCursor(CODBCDatabase  *db) {         int  loop;         InitializeCriticalSection(&CriticalSection);         if  (db==0    db->isConnected()==0)         {                 return;         }         _database=db;         //  Allocate  a  statement  handle;  the  statement  handle  is  used          //    for  other  transactions.         lastRetCode=SQLAllocStmt(_database->hdbc,  &hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_DBC,  _database->hdbc);         }         for  (loop=0  ;  loop<MAX_COLUMNS  ;  loop++)         {                 colInfo[loop]  =  0;         }         tableClause[0]  =  '\0';         orderByClause[0]  =  '\0';         whereClause[0]  =  '\0';         m_topN=0; } CODBCCursor::~CODBCCursor() {         int  loop;         SQLFreeHandle(SQL_HANDLE_STMT,hstmt);         for  (loop=1  ;  loop<MAX_COLUMNS  &&  colInfo[loop]!=0  ;  loop++)         {                 delete  colInfo[loop]->name;                 delete  colInfo[loop]->data;                 delete  colInfo[loop];         }         DeleteCriticalSection(&CriticalSection); } RETCODE  CODBCCursor::bindColumn(UWORD&  col,  char  *columnName,         SDWORD  len,  SWORD  type) {         SDWORD  retLen;         EnterCriticalSection(&CriticalSection);         col=assignColumn(columnName,  len);         colInfo[col]->type  =  type;         LeaveCriticalSection(&CriticalSection);         return(SQLBindCol(hstmt,  col,  type,  colInfo[col]->data,                 len,  &retLen)); } UWORD  CODBCCursor::assignColumn(char  *columnName,  SDWORD  len) {         UWORD  loop  =  1;         while  (colInfo[loop]!=0)         {                 loop++;         }         colInfo[loop]  =  new  struct  COLUMN_INFO;         numCols  =  loop;         if  (colInfo[loop]!=0)         {                 //  Allocate  the  space  for  the  name  and  the  data  element.                 colInfo[loop]->name  =  new  char[(strlen(columnName))+1];                 colInfo[loop]->data  =  new  char[len+1];                 colInfo[loop]->len  =  len;                 if  (colInfo[loop]->name==0    colInfo[loop]->data==0)                 {                         delete  colInfo[loop];                         colInfo[loop]=0;                 }                 strcpy(colInfo[loop]->name,  columnName);         }         return(loop); } int  CODBCCursor::doSelect() {         char  select[1280];         UWORD  loop;         UWORD  col  =  1;         UWORD  rc;         memset(select,  '\0',  1024);         if  (tableClause[0]!='\0')         {                 //  Construct  a  SELECT  statement.                 if  (getNumCol())                 {                         if  (m_topN>0)                         {                                 sprintf(select,  "SELECT  TOP  %d  ",  m_topN);                         }                         else                         {                                 strcpy(select,  "SELECT  ");                         }                         for  (loop=1  ;  colInfo[loop]!=0  ;  loop++)                         {                                 if  (loop>1)                                 {                                         strcat(select,  ",  ");                                 }                                 else                                 {                                         strcat(select,  "  ");                                 }                                 strcat(select,colInfo[loop]->name);                         }                         strcat(select,  "  FROM  ");                         strcat(select,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(select,  "  ");                                 strcat(select,  whereClause);                         }                         if  (orderByClause[0]!='\0')                         {                                 strcat(select,  "  ORDER  BY  ");                                 strcat(select,  orderByClause);                         }                                                 if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)select,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doDelete() {         char  del[1280];         memset(del,  '\0',  1024);                 if  (tableClause[0]!='\0')         {                 //  Construct  a  DELETE  statement.                 if  (getNumCol())                 {                         strcpy(del,  "DELETE  *  ");                         strcat(del,  "  FROM  ");                         strcat(del,  tableClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(del,  "  ");                                 strcat(del,  whereClause);                         }                         if  ((lastRetCode  =  SQLExecDirect(hstmt,                                 (unsigned  char  *)del,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } int  CODBCCursor::doUpdate() {         char  update[1280];         memset(update,  '\0',  1024);                 if  (tableClause[0]!='\0'    setClause[0]!='\0')         {                 //  Construct  an  UPDATE  statement.                 if  (getNumCol())                 {                         strcpy(update,  "UPDATE  ");                         strcat(update,  tableClause);                         strcat(update,  "  SET  ");                         strcat(update,  setClause);                         if  (whereClause[0]!='\0')                         {                                 strcat(update,  "  ");                                 strcat(update,  whereClause);                         }                         if  ((lastRetCode=SQLExecDirect(hstmt,                                 (unsigned  char  *)update,SQL_NTS))!=SQL_SUCCESS)                         {                                 GetSQLError(SQL_HANDLE_STMT,  hstmt);                         }                         return(lastRetCode);                 }         }         return(SQL_ERROR); } RETCODE  CODBCCursor::fetch() {         int  col;         //  This  function  retrieves  the  data.  The  function  returns  a         //    single  row  at  a  time  and  moves  the  cursor  one  row  forward.         for  (col=1  ;  colInfo[col]!=0  ;  col++)         { memset (colInfo[col]->data,  0,  colInfo[col]->len);         }         lastRetCode  =  SQLFetch(hstmt);         if  (lastRetCode!=SQL_SUCCESS)         {                 GetSQLError(SQL_HANDLE_STMT,  hstmt);         }         return(lastRetCode); } 
') { strcat(update,""); strcat(update,whereClause); } if((lastRetCode=SQLExecDirect(hstmt, (unsignedchar*)update,SQL_NTS))!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_STMT,hstmt); } return(lastRetCode); } } return(SQL_ERROR); } RETCODECODBCCursor::fetch() { intcol; //Thisfunctionretrievesthedata.Thefunctionreturnsa //singlerowatatimeandmovesthecursoronerowforward. for(col=1;colInfo[col]!=0;col++) { memset(colInfo[col]->data,0,colInfo[col]->len); } lastRetCode=SQLFetch(hstmt); if(lastRetCode!=SQL_SUCCESS) { GetSQLError(SQL_HANDLE_STMT,hstmt); } return(lastRetCode); }

Once the critical section is relinquished, the class copies of the data source name, user name, and password are saved. Finally the connection to the database is made, using the ODBC API function SQLConnect . (Several other functions allow connection to the data source and are documented in the SDK and MSDN.) Note that the error-handling method, GetSQLError , does little more than get the error text and then print it to the screen. Real applications would log errors and do a better job of handling the presentation.

The destructor acts as the deallocation function for resources allocated in the constructor by closing the connection using SQLDisconnect and freeing the connection handle using SQLFreeHandle . Inside the critical section, the destructor decrements the reference count and frees the environment handle if the count has decreased to 0.

The ExecuteSQL method is the heart of the CODBCDatabase class. ExecuteSQL allows arbitrary SQL statements to be executed. This flexibility is useful for perfoming an action query such as an update or a delete that might not fit into the CODBCCursor structure. A statement handle is allocated, and if it is successful, the SQL statement is executed using SQLExecDirect . Finally the statement handle is freed and the method returns.

As I previously mentioned, the GetSQLError method collects the information about an error from an ODBC command in a reasonable fashion, but it presents the information poorly, by simply printing the error message to the screen. The underlying API for gathering error information resulting from ODBC API calls has changed dramatically in ODBC version 3. The old function that was used to gather error information, SQLError , had a rather odd calling convention that required passing all three types of handles to the error function or NULL values as placeholders. The SQLGetDiagRec function is a vast improvement. SQLGetDiagRec allows you to specify the handle that the error occurred with directly, and it also allows the return of as many diagnostic records as required to describe each error. The first error specified is virtually always enough to track down the cause of the problem, but the additional information could be useful in complicated error situations.

The CODBCCursor Class

The CODBCCursor constructor allocates resources and initializes class data members; the destructor frees the resources allocated in the constructor. Each CODBCCursor instance has its own critical section object that can safely be initialized in the constructor because one critical section object is not shared among all instances of the class (unlike the critical section object in CODBCDatabase , which is declared as static).

The bindColumn and assignColumn methods work together to bind columns in data sets. The data sets are returned in the data member of each element in the array of COL_INFO structures. This array contains information about each column, including the name of the field being bound and the data type of the field. The assignColumn method is used to actually determine the first element of the COL_INFO array available for use. The CODBCCursor 's critical section is entered within the bindColumn method so the column can be assigned safely even in multithreaded applications. Finally the SQLBindCol ODBC API function is called to actually bind the column in the query to the data member of the class, and the return value of the SQLBindCol function becomes the return value for the bindColumn method.

The doSelect , doDelete , and doUpdate member functions all perform the same task: they create SQL statements to pass on to ODBC based on the operation that is to be performed. This is possible because of the regular structure of the SELECT, DELETE, and UPDATE SQL statements. Given the table name, field name (for the SELECT and the UPDATE statements), the field values (for the UPDATE statement), and the restriction and sorting information (for the SELECT statement), creating the required SQL code is possible. (Behind the scenes, tools such as Microsoft Visual Studio perform exactly the same tasks when you are using two-way tools that allow the program to properly reflect changes in SQL code.) At the end of each method, the SQLExecDirect ODBC API function is called to execute the SQL code generated.

The fetch method is the final function defined in CODBCCursor . This method is responsible for getting the results and setting up the bound columns, and the code is quite straightforward. First the data member of each element of the COL_INFO structure array is initialized to NULL. This is not always necessary, but in some cases ODBC drivers do not properly set the bound field when the fields are NULL. Defensive programming dictates that when an error occurs in a module you cannot control, it is wise to try and prevent collateral damage based on that error.

NOTE
Changing this class to use SQLExtendedFetch to get more than a single record at a time would be an interesting exercise. It would require changes to the COL_INFO structure to allow it to handle the larger number of rows returned at one time, as well as changes to the fetch method of the class.


Inside Server-Based Applications
Inside Server-Based Applications (DV-MPS General)
ISBN: 1572318171
EAN: 2147483647
Year: 1999
Pages: 91

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net