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.
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 .
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.
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:
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 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.