1 /////////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Implementation of the wxDbTable class. 
   5 // Modified by: George Tasker 
  10 // Copyright:   (c) 1996 Remstar International, Inc. 
  11 // Licence:     wxWindows licence, plus: 
  12 // Notice:      This class library and its intellectual design are free of charge for use, 
  13 //              modification, enhancement, debugging under the following conditions: 
  14 //              1) These classes may only be used as part of the implementation of a 
  15 //                 wxWindows-based application 
  16 //              2) All enhancements and bug fixes are to be submitted back to the wxWindows 
  17 //                 user groups free of all charges for use with the wxWindows library. 
  18 //              3) These classes may not be distributed as part of any other class library, 
  19 //                 DLL, text (written or electronic), other than a complete distribution of 
  20 //                 the wxWindows GUI development toolkit. 
  21 /////////////////////////////////////////////////////////////////////////////// 
  28     #pragma implementation "dbtable.h" 
  31 #include  "wx/wxprec.h" 
  37 #ifdef DBDEBUG_CONSOLE 
  39     #include "wx/ioswrap.h" 
  43     #include "wx/string.h" 
  44     #include "wx/object.h" 
  48         #include "wx/msgdlg.h" 
  52 #include "wx/filefn.h" 
  61 #include "wx/dbtable.h" 
  64 // The HPUX preprocessor lines below were commented out on 8/20/97 
  65 // because macros.h currently redefines DEBUG and is unneeded. 
  67 // #    include <macros.h> 
  70 #    include <sys/minmax.h> 
  74 ULONG lastTableID 
= 0; 
  82 /********** wxDbColDef::wxDbColDef() Constructor **********/ 
  83 wxDbColDef::wxDbColDef() 
  89 bool wxDbColDef::Initialize() 
  92     DbDataType      
= DB_DATA_TYPE_INTEGER
; 
  93     SqlCtype        
= SQL_C_LONG
; 
  98     InsertAllowed   
= FALSE
; 
 104 }  // wxDbColDef::Initialize() 
 107 /********** wxDbTable::wxDbTable() Constructor **********/ 
 108 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 109                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 111     if (!initialize(pwxDb
, tblName
, numColumns
, qryTblName
, qryOnly
, tblPath
)) 
 113 }  // wxDbTable::wxDbTable() 
 116 /***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/ 
 117 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 118                     const wxChar 
*qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 120     wxString tempQryTblName
; 
 121     tempQryTblName 
= qryTblName
; 
 122     if (!initialize(pwxDb
, tblName
, numColumns
, tempQryTblName
, qryOnly
, tblPath
)) 
 124 }  // wxDbTable::wxDbTable() 
 127 /********** wxDbTable::~wxDbTable() **********/ 
 128 wxDbTable::~wxDbTable() 
 131 }  // wxDbTable::~wxDbTable() 
 134 bool wxDbTable::initialize(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 135                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 137     // Initializing member variables 
 138     pDb                 
= pwxDb
;                    // Pointer to the wxDb object 
 142     m_hstmtGridQuery               
= 0; 
 143     hstmtDefault        
= 0;                        // Initialized below 
 144     hstmtCount          
= 0;                        // Initialized first time it is needed 
 151     noCols              
= numColumns
;               // Number of cols in the table 
 152     where
.Empty();                                  // Where clause 
 153     orderBy
.Empty();                                // Order By clause 
 154     from
.Empty();                                   // From clause 
 155     selectForUpdate     
= FALSE
;                    // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase 
 160     queryTableName
.Empty(); 
 162     wxASSERT(tblName
.Length()); 
 168     tableName 
= tblName
;                        // Table Name 
 169     if (tblPath
.Length()) 
 170         tablePath 
= tblPath
;                    // Table Path - used for dBase files 
 174     if (qryTblName
.Length())                    // Name of the table/view to query 
 175         queryTableName 
= qryTblName
; 
 177         queryTableName 
= tblName
; 
 179     pDb
->incrementTableCount(); 
 182     tableID 
= ++lastTableID
; 
 183     s
.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), tblName
.c_str(), tableID
, pDb
); 
 186     wxTablesInUse 
*tableInUse
; 
 187     tableInUse            
= new wxTablesInUse(); 
 188     tableInUse
->tableName 
= tblName
; 
 189     tableInUse
->tableID   
= tableID
; 
 190     tableInUse
->pDb       
= pDb
; 
 191     TablesInUse
.Append(tableInUse
); 
 196     // Grab the HENV and HDBC from the wxDb object 
 197     henv 
= pDb
->GetHENV(); 
 198     hdbc 
= pDb
->GetHDBC(); 
 200     // Allocate space for column definitions 
 202         colDefs 
= new wxDbColDef
[noCols
];  // Points to the first column definition 
 204     // Allocate statement handles for the table 
 207         // Allocate a separate statement handle for performing inserts 
 208         if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
) 
 209             pDb
->DispAllErrors(henv
, hdbc
); 
 210         // Allocate a separate statement handle for performing deletes 
 211         if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
) 
 212             pDb
->DispAllErrors(henv
, hdbc
); 
 213         // Allocate a separate statement handle for performing updates 
 214         if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
) 
 215             pDb
->DispAllErrors(henv
, hdbc
); 
 217     // Allocate a separate statement handle for internal use 
 218     if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
) 
 219         pDb
->DispAllErrors(henv
, hdbc
); 
 221     // Set the cursor type for the statement handles 
 222     cursorType 
= SQL_CURSOR_STATIC
; 
 224     if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 226         // Check to see if cursor type is supported 
 227         pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 228         if (! wxStrcmp(pDb
->sqlState
, wxT("01S02")))  // Option Value Changed 
 230             // Datasource does not support static cursors.  Driver 
 231             // will substitute a cursor type.  Call SQLGetStmtOption() 
 232             // to determine which cursor type was selected. 
 233             if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
) 
 234                 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 235 #ifdef DBDEBUG_CONSOLE 
 236             cout 
<< wxT("Static cursor changed to: "); 
 239             case SQL_CURSOR_FORWARD_ONLY
: 
 240                 cout 
<< wxT("Forward Only"); 
 242             case SQL_CURSOR_STATIC
: 
 243                 cout 
<< wxT("Static"); 
 245             case SQL_CURSOR_KEYSET_DRIVEN
: 
 246                 cout 
<< wxT("Keyset Driven"); 
 248             case SQL_CURSOR_DYNAMIC
: 
 249                 cout 
<< wxT("Dynamic"); 
 252             cout 
<< endl 
<< endl
; 
 255             if (pDb
->FwdOnlyCursors() && cursorType 
!= SQL_CURSOR_FORWARD_ONLY
) 
 257                 // Force the use of a forward only cursor... 
 258                 cursorType 
= SQL_CURSOR_FORWARD_ONLY
; 
 259                 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 261                     // Should never happen 
 262                     pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 269             pDb
->DispNextError(); 
 270             pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 273 #ifdef DBDEBUG_CONSOLE 
 275         cout 
<< wxT("Cursor Type set to STATIC") << endl 
<< endl
; 
 280         // Set the cursor type for the INSERT statement handle 
 281         if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 282             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
 283         // Set the cursor type for the DELETE statement handle 
 284         if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 285             pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
); 
 286         // Set the cursor type for the UPDATE statement handle 
 287         if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 288             pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
); 
 291     // Make the default cursor the active cursor 
 292     hstmtDefault 
= GetNewCursor(FALSE
,FALSE
); 
 293     wxASSERT(hstmtDefault
); 
 294     hstmt 
= *hstmtDefault
; 
 298 }  // wxDbTable::initialize() 
 301 void wxDbTable::cleanup() 
 306         s
.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), tableName
.c_str(), tableID
, pDb
); 
 313         TablesInUse
.DeleteContents(TRUE
); 
 317         pNode 
= TablesInUse
.First(); 
 318         while (pNode 
&& !found
) 
 320             if (((wxTablesInUse 
*)pNode
->Data())->tableID 
== tableID
) 
 323                 if (!TablesInUse
.DeleteNode(pNode
)) 
 324                     wxLogDebug (s
,wxT("Unable to delete node!")); 
 327                 pNode 
= pNode
->Next(); 
 332             msg
.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s
.c_str()); 
 333             wxLogDebug (msg
,wxT("NOTICE...")); 
 338     // Decrement the wxDb table count 
 340         pDb
->decrementTableCount(); 
 342     // Delete memory allocated for column definitions 
 346     // Free statement handles 
 352 ODBC 3.0 says to use this form 
 353             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 355             if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
) 
 356                 pDb
->DispAllErrors(henv
, hdbc
); 
 362 ODBC 3.0 says to use this form 
 363             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 365             if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
) 
 366                 pDb
->DispAllErrors(henv
, hdbc
); 
 372 ODBC 3.0 says to use this form 
 373             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 375             if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
) 
 376                 pDb
->DispAllErrors(henv
, hdbc
); 
 382         if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
) 
 383             pDb
->DispAllErrors(henv
, hdbc
); 
 386     // Delete dynamically allocated cursors 
 388         DeleteCursor(hstmtDefault
); 
 391         DeleteCursor(hstmtCount
); 
 393     if (m_hstmtGridQuery
) 
 394         DeleteCursor(m_hstmtGridQuery
); 
 396 }  // wxDbTable::cleanup() 
 399 /***************************** PRIVATE FUNCTIONS *****************************/ 
 402 /********** wxDbTable::bindParams() **********/ 
 403 bool wxDbTable::bindParams(bool forUpdate
) 
 405     wxASSERT(!queryOnly
); 
 410     UDWORD  precision   
= 0; 
 413     // Bind each column of the table that should be bound 
 414     // to a parameter marker 
 418     for (i
=0, colNo
=1; i 
< noCols
; i
++) 
 422             if (!colDefs
[i
].Updateable
) 
 427             if (!colDefs
[i
].InsertAllowed
) 
 431         switch(colDefs
[i
].DbDataType
) 
 433             case DB_DATA_TYPE_VARCHAR
: 
 434                 fSqlType 
= pDb
->GetTypeInfVarchar().FsqlType
; 
 435                 precision 
= colDefs
[i
].SzDataObj
; 
 438                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 440                     colDefs
[i
].CbValue 
= SQL_NTS
; 
 442             case DB_DATA_TYPE_INTEGER
: 
 443                 fSqlType 
= pDb
->GetTypeInfInteger().FsqlType
; 
 444                 precision 
= pDb
->GetTypeInfInteger().Precision
; 
 447                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 449                     colDefs
[i
].CbValue 
= 0; 
 451             case DB_DATA_TYPE_FLOAT
: 
 452                 fSqlType 
= pDb
->GetTypeInfFloat().FsqlType
; 
 453                 precision 
= pDb
->GetTypeInfFloat().Precision
; 
 454                 scale 
= pDb
->GetTypeInfFloat().MaximumScale
; 
 455                 // SQL Sybase Anywhere v5.5 returned a negative number for the 
 456                 // MaxScale.  This caused ODBC to kick out an error on ibscale. 
 457                 // I check for this here and set the scale = precision. 
 459                 // scale = (short) precision; 
 461                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 463                     colDefs
[i
].CbValue 
= 0; 
 465             case DB_DATA_TYPE_DATE
: 
 466                 fSqlType 
= pDb
->GetTypeInfDate().FsqlType
; 
 467                 precision 
= pDb
->GetTypeInfDate().Precision
; 
 470                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 472                     colDefs
[i
].CbValue 
= 0; 
 474             case DB_DATA_TYPE_BLOB
: 
 475                 fSqlType 
= pDb
->GetTypeInfBlob().FsqlType
; 
 479                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 481                     colDefs
[i
].CbValue 
= SQL_LEN_DATA_AT_EXEC(colDefs
[i
].SzDataObj
); 
 486             if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 487                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 488                                  precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 490                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 495             if (SQLBindParameter(hstmtInsert
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 496                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 497                                  precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 499                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 504     // Completed successfully 
 507 }  // wxDbTable::bindParams() 
 510 /********** wxDbTable::bindInsertParams() **********/ 
 511 bool wxDbTable::bindInsertParams(void) 
 513     return bindParams(FALSE
); 
 514 }  // wxDbTable::bindInsertParams() 
 517 /********** wxDbTable::bindUpdateParams() **********/ 
 518 bool wxDbTable::bindUpdateParams(void) 
 520     return bindParams(TRUE
); 
 521 }  // wxDbTable::bindUpdateParams() 
 524 /********** wxDbTable::bindCols() **********/ 
 525 bool wxDbTable::bindCols(HSTMT cursor
) 
 527     // Bind each column of the table to a memory address for fetching data 
 529     for (i 
= 0; i 
< noCols
; i
++) 
 531         if (SQLBindCol(cursor
, (UWORD
)(i
+1), colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 532                        colDefs
[i
].SzDataObj
, &colDefs
[i
].CbValue 
) != SQL_SUCCESS
) 
 534           return (pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
 538     // Completed successfully 
 541 }  // wxDbTable::bindCols() 
 544 /********** wxDbTable::getRec() **********/ 
 545 bool wxDbTable::getRec(UWORD fetchType
) 
 549     if (!pDb
->FwdOnlyCursors()) 
 551         // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType 
 555         retcode 
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
); 
 556         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 558             if (retcode 
== SQL_NO_DATA_FOUND
) 
 561                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 565             // Set the Null member variable to indicate the Null state 
 566             // of each column just read in. 
 568             for (i 
= 0; i 
< noCols
; i
++) 
 569                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 574         // Fetch the next record from the record set 
 575         retcode 
= SQLFetch(hstmt
); 
 576         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 578             if (retcode 
== SQL_NO_DATA_FOUND
) 
 581                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 585             // Set the Null member variable to indicate the Null state 
 586             // of each column just read in. 
 588             for (i 
= 0; i 
< noCols
; i
++) 
 589                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 593     // Completed successfully 
 596 }  // wxDbTable::getRec() 
 599 /********** wxDbTable::execDelete() **********/ 
 600 bool wxDbTable::execDelete(const wxString 
&pSqlStmt
) 
 604     // Execute the DELETE statement 
 605     retcode 
= SQLExecDirect(hstmtDelete
, (UCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 607     if (retcode 
== SQL_SUCCESS 
|| 
 608         retcode 
== SQL_NO_DATA_FOUND 
|| 
 609         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 611         // Record deleted successfully 
 615     // Problem deleting record 
 616     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
 618 }  // wxDbTable::execDelete() 
 621 /********** wxDbTable::execUpdate() **********/ 
 622 bool wxDbTable::execUpdate(const wxString 
&pSqlStmt
) 
 626     // Execute the UPDATE statement 
 627     retcode 
= SQLExecDirect(hstmtUpdate
, (UCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 629     if (retcode 
== SQL_SUCCESS 
|| 
 630         retcode 
== SQL_NO_DATA_FOUND 
|| 
 631         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 633         // Record updated successfully 
 637     // Problem updating record 
 638     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 640 }  // wxDbTable::execUpdate() 
 643 /********** wxDbTable::query() **********/ 
 644 bool wxDbTable::query(int queryType
, bool forUpdate
, bool distinct
, const wxString 
&pSqlStmt
) 
 649         // The user may wish to select for update, but the DBMS may not be capable 
 650         selectForUpdate 
= CanSelectForUpdate(); 
 652         selectForUpdate 
= FALSE
; 
 654     // Set the SQL SELECT string 
 655     if (queryType 
!= DB_SELECT_STATEMENT
)               // A select statement was not passed in, 
 656     {                                                   // so generate a select statement. 
 657         BuildSelectStmt(sqlStmt
, queryType
, distinct
); 
 658         pDb
->WriteSqlLog(sqlStmt
); 
 661     // Make sure the cursor is closed first 
 662     if (!CloseCursor(hstmt
)) 
 665     // Execute the SQL SELECT statement 
 667     retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) (queryType 
== DB_SELECT_STATEMENT 
? pSqlStmt
.c_str() : sqlStmt
.c_str()), SQL_NTS
); 
 668     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 669         return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 671     // Completed successfully 
 674 }  // wxDbTable::query() 
 677 /***************************** PUBLIC FUNCTIONS *****************************/ 
 680 /********** wxDbTable::Open() **********/ 
 681 bool wxDbTable::Open(bool checkPrivileges
, bool checkTableExists
) 
 691     // Calculate the maximum size of the concatenated 
 692     // keys for use with wxDbGrid 
 694     for (i
=0; i 
< noCols
; i
++) 
 696         if (colDefs
[i
].KeyField
) 
 699             m_keysize 
+= colDefs
[i
].SzDataObj
; 
 704     // Verify that the table exists in the database 
 705     if (checkTableExists 
&& !pDb
->TableExists(tableName
, pDb
->GetUsername(), tablePath
)) 
 707         s 
= wxT("Table/view does not exist in the database"); 
 708         if ( *(pDb
->dbInf
.accessibleTables
) == wxT('Y')) 
 709             s 
+= wxT(", or you have no permissions.\n"); 
 713     else if (checkPrivileges
) 
 715         // Verify the user has rights to access the table. 
 716         // Shortcut boolean evaluation to optimize out call to 
 719         // Unfortunately this optimization doesn't seem to be 
 721         if (// *(pDb->dbInf.accessibleTables) == 'N' && 
 722             !pDb
->TablePrivileges(tableName
,wxT("SELECT"), pDb
->GetUsername(), pDb
->GetUsername(), tablePath
)) 
 723             s 
= wxT("Current logged in user does not have sufficient privileges to access this table.\n"); 
 730         if (!tablePath
.IsEmpty()) 
 731             p
.Printf(wxT("Error opening '%s/%s'.\n"),tablePath
.c_str(),tableName
.c_str()); 
 733             p
.Printf(wxT("Error opening '%s'.\n"), tableName
.c_str()); 
 736         pDb
->LogError(p
.GetData()); 
 741     // Bind the member variables for field exchange between 
 742     // the wxDbTable object and the ODBC record. 
 745         if (!bindInsertParams())                    // Inserts 
 748         if (!bindUpdateParams())                    // Updates 
 752     if (!bindCols(*hstmtDefault
))                   // Selects 
 755     if (!bindCols(hstmtInternal
))                   // Internal use only 
 759      * Do NOT bind the hstmtCount cursor!!! 
 762     // Build an insert statement using parameter markers 
 763     if (!queryOnly 
&& noCols 
> 0) 
 765         bool needComma 
= FALSE
; 
 766         sqlStmt
.Printf(wxT("INSERT INTO %s ("), pDb
->SQLTableName(tableName
.c_str())); 
 767         for (i 
= 0; i 
< noCols
; i
++) 
 769             if (! colDefs
[i
].InsertAllowed
) 
 773             sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
 774 //            sqlStmt += colDefs[i].ColName; 
 778         sqlStmt 
+= wxT(") VALUES ("); 
 780         int insertableCount 
= 0; 
 782         for (i 
= 0; i 
< noCols
; i
++) 
 784             if (! colDefs
[i
].InsertAllowed
) 
 794         // Prepare the insert statement for execution 
 797             if (SQLPrepare(hstmtInsert
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
 798                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 804     // Completed successfully 
 807 }  // wxDbTable::Open() 
 810 /********** wxDbTable::Query() **********/ 
 811 bool wxDbTable::Query(bool forUpdate
, bool distinct
) 
 814     return(query(DB_SELECT_WHERE
, forUpdate
, distinct
)); 
 816 }  // wxDbTable::Query() 
 819 /********** wxDbTable::QueryBySqlStmt() **********/ 
 820 bool wxDbTable::QueryBySqlStmt(const wxString 
&pSqlStmt
) 
 822     pDb
->WriteSqlLog(pSqlStmt
); 
 824     return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
)); 
 826 }  // wxDbTable::QueryBySqlStmt() 
 829 /********** wxDbTable::QueryMatching() **********/ 
 830 bool wxDbTable::QueryMatching(bool forUpdate
, bool distinct
) 
 833     return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
)); 
 835 }  // wxDbTable::QueryMatching() 
 838 /********** wxDbTable::QueryOnKeyFields() **********/ 
 839 bool wxDbTable::QueryOnKeyFields(bool forUpdate
, bool distinct
) 
 842     return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
)); 
 844 }  // wxDbTable::QueryOnKeyFields() 
 847 /********** wxDbTable::GetPrev() **********/ 
 848 bool wxDbTable::GetPrev(void) 
 850     if (pDb
->FwdOnlyCursors()) 
 852         wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 856         return(getRec(SQL_FETCH_PRIOR
)); 
 858 }  // wxDbTable::GetPrev() 
 861 /********** wxDbTable::operator-- **********/ 
 862 bool wxDbTable::operator--(int) 
 864     if (pDb
->FwdOnlyCursors()) 
 866         wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 870         return(getRec(SQL_FETCH_PRIOR
)); 
 872 }  // wxDbTable::operator-- 
 875 /********** wxDbTable::GetFirst() **********/ 
 876 bool wxDbTable::GetFirst(void) 
 878     if (pDb
->FwdOnlyCursors()) 
 880         wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 884         return(getRec(SQL_FETCH_FIRST
)); 
 886 }  // wxDbTable::GetFirst() 
 889 /********** wxDbTable::GetLast() **********/ 
 890 bool wxDbTable::GetLast(void) 
 892     if (pDb
->FwdOnlyCursors()) 
 894         wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 898         return(getRec(SQL_FETCH_LAST
)); 
 900 }  // wxDbTable::GetLast() 
 903 /********** wxDbTable::BuildDeleteStmt() **********/ 
 904 void wxDbTable::BuildDeleteStmt(wxString 
&pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
 906     wxASSERT(!queryOnly
); 
 910     wxString whereClause
; 
 914     // Handle the case of DeleteWhere() and the where clause is blank.  It should 
 915     // delete all records from the database in this case. 
 916     if (typeOfDel 
== DB_DEL_WHERE 
&& (pWhereClause
.Length() == 0)) 
 918         pSqlStmt
.Printf(wxT("DELETE FROM %s"), pDb
->SQLTableName(tableName
.c_str())); 
 922     pSqlStmt
.Printf(wxT("DELETE FROM %s WHERE "), pDb
->SQLTableName(tableName
.c_str())); 
 924     // Append the WHERE clause to the SQL DELETE statement 
 927         case DB_DEL_KEYFIELDS
: 
 928             // If the datasource supports the ROWID column, build 
 929             // the where on ROWID for efficiency purposes. 
 930             // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333' 
 934                 wxChar   rowid
[wxDB_ROWID_LEN
+1]; 
 936                 // Get the ROWID value.  If not successful retreiving the ROWID, 
 937                 // simply fall down through the code and build the WHERE clause 
 938                 // based on the key fields. 
 939                 if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
 941                     pSqlStmt 
+= wxT("ROWID = '"); 
 943                     pSqlStmt 
+= wxT("'"); 
 947             // Unable to delete by ROWID, so build a WHERE 
 948             // clause based on the keyfields. 
 949             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
 950             pSqlStmt 
+= whereClause
; 
 953             pSqlStmt 
+= pWhereClause
; 
 955         case DB_DEL_MATCHING
: 
 956             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
 957             pSqlStmt 
+= whereClause
; 
 961 }  // BuildDeleteStmt() 
 964 /***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/ 
 965 void wxDbTable::BuildDeleteStmt(wxChar 
*pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
 967     wxString tempSqlStmt
; 
 968     BuildDeleteStmt(tempSqlStmt
, typeOfDel
, pWhereClause
); 
 969     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
 970 }  // wxDbTable::BuildDeleteStmt() 
 973 /********** wxDbTable::BuildSelectStmt() **********/ 
 974 void wxDbTable::BuildSelectStmt(wxString 
&pSqlStmt
, int typeOfSelect
, bool distinct
) 
 976     wxString whereClause
; 
 979     // Build a select statement to query the database 
 980     pSqlStmt 
= wxT("SELECT "); 
 982     // SELECT DISTINCT values only? 
 984         pSqlStmt 
+= wxT("DISTINCT "); 
 986     // Was a FROM clause specified to join tables to the base table? 
 987     // Available for ::Query() only!!! 
 988     bool appendFromClause 
= FALSE
; 
 989 #if wxODBC_BACKWARD_COMPATABILITY 
 990     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from 
&& wxStrlen(from
)) 
 991         appendFromClause 
= TRUE
; 
 993     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from
.Length()) 
 994         appendFromClause 
= TRUE
; 
 997     // Add the column list 
 999     for (i 
= 0; i 
< noCols
; i
++) 
1001         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1002         if (appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) 
1004             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
.c_str()); 
1005 //            pSqlStmt += queryTableName; 
1006             pSqlStmt 
+= wxT("."); 
1008         pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1009 //        pSqlStmt += colDefs[i].ColName; 
1011             pSqlStmt 
+= wxT(","); 
1014     // If the datasource supports ROWID, get this column as well.  Exception: Don't retrieve 
1015     // the ROWID if querying distinct records.  The rowid will always be unique. 
1016     if (!distinct 
&& CanUpdByROWID()) 
1018         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1019         if (appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) 
1021             pSqlStmt 
+= wxT(","); 
1022             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1023 //            pSqlStmt += queryTableName; 
1024             pSqlStmt 
+= wxT(".ROWID"); 
1027             pSqlStmt 
+= wxT(",ROWID"); 
1030     // Append the FROM tablename portion 
1031     pSqlStmt 
+= wxT(" FROM "); 
1032     pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1033 //    pSqlStmt += queryTableName; 
1035     // Sybase uses the HOLDLOCK keyword to lock a record during query. 
1036     // The HOLDLOCK keyword follows the table name in the from clause. 
1037     // Each table in the from clause must specify HOLDLOCK or 
1038     // NOHOLDLOCK (the default).  Note: The "FOR UPDATE" clause 
1039     // is parsed but ignored in SYBASE Transact-SQL. 
1040     if (selectForUpdate 
&& (pDb
->Dbms() == dbmsSYBASE_ASA 
|| pDb
->Dbms() == dbmsSYBASE_ASE
)) 
1041         pSqlStmt 
+= wxT(" HOLDLOCK"); 
1043     if (appendFromClause
) 
1046     // Append the WHERE clause.  Either append the where clause for the class 
1047     // or build a where clause.  The typeOfSelect determines this. 
1048     switch(typeOfSelect
) 
1050         case DB_SELECT_WHERE
: 
1051 #if wxODBC_BACKWARD_COMPATABILITY 
1052             if (where 
&& wxStrlen(where
))   // May not want a where clause!!! 
1054             if (where
.Length())   // May not want a where clause!!! 
1057                 pSqlStmt 
+= wxT(" WHERE "); 
1061         case DB_SELECT_KEYFIELDS
: 
1062             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1063             if (whereClause
.Length()) 
1065                 pSqlStmt 
+= wxT(" WHERE "); 
1066                 pSqlStmt 
+= whereClause
; 
1069         case DB_SELECT_MATCHING
: 
1070             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
1071             if (whereClause
.Length()) 
1073                 pSqlStmt 
+= wxT(" WHERE "); 
1074                 pSqlStmt 
+= whereClause
; 
1079     // Append the ORDER BY clause 
1080 #if wxODBC_BACKWARD_COMPATABILITY 
1081     if (orderBy 
&& wxStrlen(orderBy
)) 
1083     if (orderBy
.Length()) 
1086         pSqlStmt 
+= wxT(" ORDER BY "); 
1087         pSqlStmt 
+= orderBy
; 
1090     // SELECT FOR UPDATE if told to do so and the datasource is capable.  Sybase 
1091     // parses the FOR UPDATE clause but ignores it.  See the comment above on the 
1092     // HOLDLOCK for Sybase. 
1093     if (selectForUpdate 
&& CanSelectForUpdate()) 
1094         pSqlStmt 
+= wxT(" FOR UPDATE"); 
1096 }  // wxDbTable::BuildSelectStmt() 
1099 /***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/ 
1100 void wxDbTable::BuildSelectStmt(wxChar 
*pSqlStmt
, int typeOfSelect
, bool distinct
) 
1102     wxString tempSqlStmt
; 
1103     BuildSelectStmt(tempSqlStmt
, typeOfSelect
, distinct
); 
1104     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1105 }  // wxDbTable::BuildSelectStmt() 
1108 /********** wxDbTable::BuildUpdateStmt() **********/ 
1109 void wxDbTable::BuildUpdateStmt(wxString 
&pSqlStmt
, int typeOfUpd
, const wxString 
&pWhereClause
) 
1111     wxASSERT(!queryOnly
); 
1115     wxString whereClause
; 
1116     whereClause
.Empty(); 
1118     bool firstColumn 
= TRUE
; 
1120     pSqlStmt
.Printf(wxT("UPDATE %s SET "), pDb
->SQLTableName(tableName
.Upper().c_str())); 
1122     // Append a list of columns to be updated 
1124     for (i 
= 0; i 
< noCols
; i
++) 
1126         // Only append Updateable columns 
1127         if (colDefs
[i
].Updateable
) 
1130                 pSqlStmt 
+= wxT(","); 
1132                 firstColumn 
= FALSE
; 
1134             pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1135 //            pSqlStmt += colDefs[i].ColName; 
1136             pSqlStmt 
+= wxT(" = ?"); 
1140     // Append the WHERE clause to the SQL UPDATE statement 
1141     pSqlStmt 
+= wxT(" WHERE "); 
1144         case DB_UPD_KEYFIELDS
: 
1145             // If the datasource supports the ROWID column, build 
1146             // the where on ROWID for efficiency purposes. 
1147             // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333' 
1148             if (CanUpdByROWID()) 
1151                 wxChar rowid
[wxDB_ROWID_LEN
+1]; 
1153                 // Get the ROWID value.  If not successful retreiving the ROWID, 
1154                 // simply fall down through the code and build the WHERE clause 
1155                 // based on the key fields. 
1156                 if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
1158                     pSqlStmt 
+= wxT("ROWID = '"); 
1160                     pSqlStmt 
+= wxT("'"); 
1164             // Unable to delete by ROWID, so build a WHERE 
1165             // clause based on the keyfields. 
1166             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1167             pSqlStmt 
+= whereClause
; 
1170             pSqlStmt 
+= pWhereClause
; 
1173 }  // BuildUpdateStmt() 
1176 /***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/ 
1177 void wxDbTable::BuildUpdateStmt(wxChar 
*pSqlStmt
, int typeOfUpd
, const wxString 
&pWhereClause
) 
1179     wxString tempSqlStmt
; 
1180     BuildUpdateStmt(tempSqlStmt
, typeOfUpd
, pWhereClause
); 
1181     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1182 }  // BuildUpdateStmt() 
1185 /********** wxDbTable::BuildWhereClause() **********/ 
1186 void wxDbTable::BuildWhereClause(wxString 
&pWhereClause
, int typeOfWhere
, 
1187                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1189  * Note: BuildWhereClause() currently ignores timestamp columns. 
1190  *       They are not included as part of the where clause. 
1193     bool moreThanOneColumn 
= FALSE
; 
1196     // Loop through the columns building a where clause as you go 
1198     for (i 
= 0; i 
< noCols
; i
++) 
1200         // Determine if this column should be included in the WHERE clause 
1201         if ((typeOfWhere 
== DB_WHERE_KEYFIELDS 
&& colDefs
[i
].KeyField
) || 
1202              (typeOfWhere 
== DB_WHERE_MATCHING  
&& (!IsColNull(i
)))) 
1204             // Skip over timestamp columns 
1205             if (colDefs
[i
].SqlCtype 
== SQL_C_TIMESTAMP
) 
1207             // If there is more than 1 column, join them with the keyword "AND" 
1208             if (moreThanOneColumn
) 
1209                 pWhereClause 
+= wxT(" AND "); 
1211                 moreThanOneColumn 
= TRUE
; 
1212             // Concatenate where phrase for the column 
1213             if (qualTableName
.Length()) 
1215                 pWhereClause 
+= pDb
->SQLTableName(qualTableName
); 
1216 //                pWhereClause += qualTableName; 
1217                 pWhereClause 
+= wxT("."); 
1219             pWhereClause 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1220 //            pWhereClause += colDefs[i].ColName; 
1221             if (useLikeComparison 
&& (colDefs
[i
].SqlCtype 
== SQL_C_CHAR
)) 
1222                 pWhereClause 
+= wxT(" LIKE "); 
1224                 pWhereClause 
+= wxT(" = "); 
1225             switch(colDefs
[i
].SqlCtype
) 
1228                     colValue
.Printf(wxT("'%s'"), (UCHAR FAR 
*) colDefs
[i
].PtrDataObj
); 
1231                     colValue
.Printf(wxT("%hi"), *((SWORD 
*) colDefs
[i
].PtrDataObj
)); 
1234                     colValue
.Printf(wxT("%hu"), *((UWORD 
*) colDefs
[i
].PtrDataObj
)); 
1237                     colValue
.Printf(wxT("%li"), *((SDWORD 
*) colDefs
[i
].PtrDataObj
)); 
1240                     colValue
.Printf(wxT("%lu"), *((UDWORD 
*) colDefs
[i
].PtrDataObj
)); 
1243                     colValue
.Printf(wxT("%.6f"), *((SFLOAT 
*) colDefs
[i
].PtrDataObj
)); 
1246                     colValue
.Printf(wxT("%.6f"), *((SDOUBLE 
*) colDefs
[i
].PtrDataObj
)); 
1249             pWhereClause 
+= colValue
; 
1252 }  // wxDbTable::BuildWhereClause() 
1255 /***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/ 
1256 void wxDbTable::BuildWhereClause(wxChar 
*pWhereClause
, int typeOfWhere
, 
1257                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1259     wxString tempSqlStmt
; 
1260     BuildWhereClause(tempSqlStmt
, typeOfWhere
, qualTableName
, useLikeComparison
); 
1261     wxStrcpy(pWhereClause
, tempSqlStmt
); 
1262 }  // wxDbTable::BuildWhereClause() 
1265 /********** wxDbTable::GetRowNum() **********/ 
1266 UWORD 
wxDbTable::GetRowNum(void) 
1270     if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
) 
1272         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1276     // Completed successfully 
1277     return((UWORD
) rowNum
); 
1279 }  // wxDbTable::GetRowNum() 
1282 /********** wxDbTable::CloseCursor() **********/ 
1283 bool wxDbTable::CloseCursor(HSTMT cursor
) 
1285     if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
) 
1286         return(pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
1288     // Completed successfully 
1291 }  // wxDbTable::CloseCursor() 
1294 /********** wxDbTable::CreateTable() **********/ 
1295 bool wxDbTable::CreateTable(bool attemptDrop
) 
1303 #ifdef DBDEBUG_CONSOLE 
1304     cout 
<< wxT("Creating Table ") << tableName 
<< wxT("...") << endl
; 
1308     if (attemptDrop 
&& !DropTable()) 
1312 #ifdef DBDEBUG_CONSOLE 
1313     for (i 
= 0; i 
< noCols
; i
++) 
1315         // Exclude derived columns since they are NOT part of the base table 
1316         if (colDefs
[i
].DerivedCol
) 
1318         cout 
<< i 
+ 1 << wxT(": ") << colDefs
[i
].ColName 
<< wxT("; "); 
1319         switch(colDefs
[i
].DbDataType
) 
1321             case DB_DATA_TYPE_VARCHAR
: 
1322                 cout 
<< pDb
->GetTypeInfVarchar().TypeName 
<< wxT("(") << colDefs
[i
].SzDataObj 
<< wxT(")"); 
1324             case DB_DATA_TYPE_INTEGER
: 
1325                 cout 
<< pDb
->GetTypeInfInteger().TypeName
; 
1327             case DB_DATA_TYPE_FLOAT
: 
1328                 cout 
<< pDb
->GetTypeInfFloat().TypeName
; 
1330             case DB_DATA_TYPE_DATE
: 
1331                 cout 
<< pDb
->GetTypeInfDate().TypeName
; 
1333             case DB_DATA_TYPE_BLOB
: 
1334                 cout 
<< pDb
->GetTypeInfBlob().TypeName
; 
1341     // Build a CREATE TABLE string from the colDefs structure. 
1342     bool needComma 
= FALSE
; 
1344     sqlStmt
.Printf(wxT("CREATE TABLE %s ("), pDb
->SQLTableName(tableName
.c_str())); 
1346     for (i 
= 0; i 
< noCols
; i
++) 
1348         // Exclude derived columns since they are NOT part of the base table 
1349         if (colDefs
[i
].DerivedCol
) 
1353             sqlStmt 
+= wxT(","); 
1355         sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1356 //        sqlStmt += colDefs[i].ColName; 
1357         sqlStmt 
+= wxT(" "); 
1359         switch(colDefs
[i
].DbDataType
) 
1361             case DB_DATA_TYPE_VARCHAR
: 
1362                 sqlStmt 
+= pDb
->GetTypeInfVarchar().TypeName
; 
1364             case DB_DATA_TYPE_INTEGER
: 
1365                 sqlStmt 
+= pDb
->GetTypeInfInteger().TypeName
; 
1367             case DB_DATA_TYPE_FLOAT
: 
1368                 sqlStmt 
+= pDb
->GetTypeInfFloat().TypeName
; 
1370             case DB_DATA_TYPE_DATE
: 
1371                 sqlStmt 
+= pDb
->GetTypeInfDate().TypeName
; 
1373             case DB_DATA_TYPE_BLOB
: 
1374                 sqlStmt 
+= pDb
->GetTypeInfBlob().TypeName
; 
1377         // For varchars, append the size of the string 
1378         if (colDefs
[i
].DbDataType 
== DB_DATA_TYPE_VARCHAR
)// || 
1379 //            colDefs[i].DbDataType == DB_DATA_TYPE_BLOB) 
1382             s
.Printf(wxT("(%d)"), colDefs
[i
].SzDataObj
); 
1386         if (pDb
->Dbms() == dbmsDB2 
|| 
1387             pDb
->Dbms() == dbmsMY_SQL 
|| 
1388             pDb
->Dbms() == dbmsSYBASE_ASE  
|| 
1389             pDb
->Dbms() == dbmsINTERBASE  
|| 
1390             pDb
->Dbms() == dbmsMS_SQL_SERVER
) 
1392             if (colDefs
[i
].KeyField
) 
1394                 sqlStmt 
+= wxT(" NOT NULL"); 
1400     // If there is a primary key defined, include it in the create statement 
1401     for (i 
= j 
= 0; i 
< noCols
; i
++) 
1403         if (colDefs
[i
].KeyField
) 
1409     if (j 
&& pDb
->Dbms() != dbmsDBASE
)  // Found a keyfield 
1411         switch (pDb
->Dbms()) 
1414             case dbmsSYBASE_ASA
: 
1415             case dbmsSYBASE_ASE
: 
1418                 // MySQL goes out on this one. We also declare the relevant key NON NULL above 
1419                 sqlStmt 
+= wxT(",PRIMARY KEY ("); 
1424                 sqlStmt 
+= wxT(",CONSTRAINT "); 
1425                 //  DB2 is limited to 18 characters for index names 
1426                 if (pDb
->Dbms() == dbmsDB2
) 
1428                     wxASSERT_MSG((tableName 
&& wxStrlen(tableName
) <= 13), wxT("DB2 table/index names must be no longer than 13 characters in length.\n\nTruncating table name to 13 characters.")); 
1429                     sqlStmt 
+= pDb
->SQLTableName(tableName
.substr(0, 13).c_str()); 
1430 //                    sqlStmt += tableName.substr(0, 13); 
1433                     sqlStmt 
+= pDb
->SQLTableName(tableName
.c_str()); 
1434 //                    sqlStmt += tableName; 
1436                 sqlStmt 
+= wxT("_PIDX PRIMARY KEY ("); 
1441         // List column name(s) of column(s) comprising the primary key 
1442         for (i 
= j 
= 0; i 
< noCols
; i
++) 
1444             if (colDefs
[i
].KeyField
) 
1446                 if (j
++) // Multi part key, comma separate names 
1447                     sqlStmt 
+= wxT(","); 
1448                 sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1449 //                sqlStmt += colDefs[i].ColName; 
1452         sqlStmt 
+= wxT(")"); 
1454         if (pDb
->Dbms() == dbmsINFORMIX 
|| 
1455             pDb
->Dbms() == dbmsSYBASE_ASA 
|| 
1456             pDb
->Dbms() == dbmsSYBASE_ASE
) 
1458             sqlStmt 
+= wxT(" CONSTRAINT "); 
1459             sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1460 //            sqlStmt += tableName; 
1461             sqlStmt 
+= wxT("_PIDX"); 
1464     // Append the closing parentheses for the create table statement 
1465     sqlStmt 
+= wxT(")"); 
1467     pDb
->WriteSqlLog(sqlStmt
); 
1469 #ifdef DBDEBUG_CONSOLE 
1470     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1473     // Execute the CREATE TABLE statement 
1474     RETCODE retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1475     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
1477         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1478         pDb
->RollbackTrans(); 
1483     // Commit the transaction and close the cursor 
1484     if (!pDb
->CommitTrans()) 
1486     if (!CloseCursor(hstmt
)) 
1489     // Database table created successfully 
1492 } // wxDbTable::CreateTable() 
1495 /********** wxDbTable::DropTable() **********/ 
1496 bool wxDbTable::DropTable() 
1498     // NOTE: This function returns TRUE if the Table does not exist, but 
1499     //       only for identified databases.  Code will need to be added 
1500     //       below for any other databases when those databases are defined 
1501     //       to handle this situation consistently 
1505     sqlStmt
.Printf(wxT("DROP TABLE %s"), pDb
->SQLTableName(tableName
.c_str())); 
1507     pDb
->WriteSqlLog(sqlStmt
); 
1509 #ifdef DBDEBUG_CONSOLE 
1510     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1513     RETCODE retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1514     if (retcode 
!= SQL_SUCCESS
) 
1516         // Check for "Base table not found" error and ignore 
1517         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1518         if (wxStrcmp(pDb
->sqlState
, wxT("S0002")) /*&& 
1519             wxStrcmp(pDb->sqlState, wxT("S1000"))*/)  // "Base table not found" 
1521             // Check for product specific error codes 
1522             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000")))   ||  // 5.x (and lower?) 
1523                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000")))   || 
1524                   (pDb
->Dbms() == dbmsPERVASIVE_SQL 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000")))   ||  // Returns an S1000 then an S0002 
1525                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))))) 
1527                 pDb
->DispNextError(); 
1528                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1529                 pDb
->RollbackTrans(); 
1530 //                CloseCursor(hstmt); 
1536     // Commit the transaction and close the cursor 
1537     if (! pDb
->CommitTrans()) 
1539     if (! CloseCursor(hstmt
)) 
1543 }  // wxDbTable::DropTable() 
1546 /********** wxDbTable::CreateIndex() **********/ 
1547 bool wxDbTable::CreateIndex(const wxString 
&idxName
, bool unique
, UWORD noIdxCols
, 
1548                                      wxDbIdxDef 
*pIdxDefs
, bool attemptDrop
) 
1552     // Drop the index first 
1553     if (attemptDrop 
&& !DropIndex(idxName
)) 
1556     // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions 
1557     // of an index have the columns defined as "NOT NULL".  During initial table creation though, 
1558     // it may not be known which columns are necessarily going to be part of an index (e.g. the 
1559     // table was created, then months later you determine that an additional index while 
1560     // give better performance, so you want to add an index). 
1562     // The following block of code will modify the column definition to make the column be 
1563     // defined with the "NOT NULL" qualifier. 
1564     if (pDb
->Dbms() == dbmsMY_SQL
) 
1569         for (i 
= 0; i 
< noIdxCols 
&& ok
; i
++) 
1573             // Find the column definition that has the ColName that matches the 
1574             // index column name.  We need to do this to get the DB_DATA_TYPE of 
1575             // the index column, as MySQL's syntax for the ALTER column requires 
1577             while (!found 
&& (j 
< this->noCols
)) 
1579                 if (wxStrcmp(colDefs
[j
].ColName
,pIdxDefs
[i
].ColName
) == 0) 
1587                 ok 
= pDb
->ModifyColumn(tableName
, pIdxDefs
[i
].ColName
, 
1588                                         colDefs
[j
].DbDataType
, colDefs
[j
].SzDataObj
, 
1593                     wxODBC_ERRORS retcode
; 
1594                     // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already 
1595                     // defined to be NOT NULL, but reportedly MySQL doesn't mind. 
1596                     // This line is just here for debug checking of the value 
1597                     retcode 
= (wxODBC_ERRORS
)pDb
->DB_STATUS
; 
1607             pDb
->RollbackTrans(); 
1612     // Build a CREATE INDEX statement 
1613     sqlStmt 
= wxT("CREATE "); 
1615         sqlStmt 
+= wxT("UNIQUE "); 
1617     sqlStmt 
+= wxT("INDEX "); 
1618     sqlStmt 
+= pDb
->SQLTableName(idxName
); 
1619     sqlStmt 
+= wxT(" ON "); 
1621     sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1622 //    sqlStmt += tableName; 
1623     sqlStmt 
+= wxT(" ("); 
1625     // Append list of columns making up index 
1627     for (i 
= 0; i 
< noIdxCols
; i
++) 
1629         sqlStmt 
+= pDb
->SQLColumnName(pIdxDefs
[i
].ColName
); 
1630 //        sqlStmt += pIdxDefs[i].ColName; 
1632         // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns 
1633         if (!((pDb
->Dbms() == dbmsMS_SQL_SERVER
) && (strncmp(pDb
->dbInf
.dbmsVer
,"07",2)==0)) && 
1634             !(pDb
->Dbms() == dbmsPOSTGRES
)) 
1636             if (pIdxDefs
[i
].Ascending
) 
1637                 sqlStmt 
+= wxT(" ASC"); 
1639                 sqlStmt 
+= wxT(" DESC"); 
1642             wxASSERT_MSG(pIdxDefs
[i
].Ascending
, "Datasource does not support DESCending index columns"); 
1644         if ((i 
+ 1) < noIdxCols
) 
1645             sqlStmt 
+= wxT(","); 
1648     // Append closing parentheses 
1649     sqlStmt 
+= wxT(")"); 
1651     pDb
->WriteSqlLog(sqlStmt
); 
1653 #ifdef DBDEBUG_CONSOLE 
1654     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1657     // Execute the CREATE INDEX statement 
1658     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
1660         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1661         pDb
->RollbackTrans(); 
1666     // Commit the transaction and close the cursor 
1667     if (! pDb
->CommitTrans()) 
1669     if (! CloseCursor(hstmt
)) 
1672     // Index Created Successfully 
1675 }  // wxDbTable::CreateIndex() 
1678 /********** wxDbTable::DropIndex() **********/ 
1679 bool wxDbTable::DropIndex(const wxString 
&idxName
) 
1681     // NOTE: This function returns TRUE if the Index does not exist, but 
1682     //       only for identified databases.  Code will need to be added 
1683     //       below for any other databases when those databases are defined 
1684     //       to handle this situation consistently 
1688     if (pDb
->Dbms() == dbmsACCESS 
|| pDb
->Dbms() == dbmsMY_SQL 
|| 
1689         pDb
->Dbms() == dbmsDBASE 
/*|| Paradox needs this syntax too when we add support*/) 
1690         sqlStmt
.Printf(wxT("DROP INDEX %s ON %s"),pDb
->SQLTableName(idxName
.c_str()), pDb
->SQLTableName(tableName
.c_str())); 
1691     else if ((pDb
->Dbms() == dbmsMS_SQL_SERVER
) || 
1692              (pDb
->Dbms() == dbmsSYBASE_ASE
)) 
1693         sqlStmt
.Printf(wxT("DROP INDEX %s.%s"), pDb
->SQLTableName(tableName
.c_str()), pDb
->SQLTableName(idxName
.c_str())); 
1695         sqlStmt
.Printf(wxT("DROP INDEX %s"),pDb
->SQLTableName(idxName
.c_str())); 
1697     pDb
->WriteSqlLog(sqlStmt
); 
1699 #ifdef DBDEBUG_CONSOLE 
1700     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1703     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
1705         // Check for "Index not found" error and ignore 
1706         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1707         if (wxStrcmp(pDb
->sqlState
,wxT("S0012")))  // "Index not found" 
1709             // Check for product specific error codes 
1710             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000"))) ||  // v5.x (and lower?) 
1711                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000"))) || 
1712                   (pDb
->Dbms() == dbmsMS_SQL_SERVER 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1713                   (pDb
->Dbms() == dbmsINTERBASE      
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1714                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("S0002"))) ||  // Base table not found 
1715                   (pDb
->Dbms() == dbmsMY_SQL        
&& !wxStrcmp(pDb
->sqlState
,wxT("42S12"))) ||  // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta 
1716                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))) 
1719                 pDb
->DispNextError(); 
1720                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1721                 pDb
->RollbackTrans(); 
1728     // Commit the transaction and close the cursor 
1729     if (! pDb
->CommitTrans()) 
1731     if (! CloseCursor(hstmt
)) 
1735 }  // wxDbTable::DropIndex() 
1738 /********** wxDbTable::SetOrderByColNums() **********/ 
1739 bool wxDbTable::SetOrderByColNums(UWORD first
, ... ) 
1741     int        colNo 
= first
;  // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS 
1747     va_start(argptr
, first
);     /* Initialize variable arguments. */ 
1748     while (!abort 
&& (colNo 
!= wxDB_NO_MORE_COLUMN_NUMBERS
)) 
1750         // Make sure the passed in column number 
1751         // is within the valid range of columns 
1753         // Valid columns are 0 thru noCols-1 
1754         if (colNo 
>= noCols 
|| colNo 
< 0) 
1761             tempStr 
+= wxT(","); 
1763         tempStr 
+= colDefs
[colNo
].ColName
; 
1764         colNo 
= va_arg (argptr
, int); 
1766     va_end (argptr
);              /* Reset variable arguments.      */ 
1768     SetOrderByClause(tempStr
); 
1771 }  // wxDbTable::SetOrderByColNums() 
1774 /********** wxDbTable::Insert() **********/ 
1775 int wxDbTable::Insert(void) 
1777     wxASSERT(!queryOnly
); 
1778     if (queryOnly 
|| !insertable
) 
1783     // Insert the record by executing the already prepared insert statement 
1785     retcode
=SQLExecute(hstmtInsert
); 
1786     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
1788         // Check to see if integrity constraint was violated 
1789         pDb
->GetNextError(henv
, hdbc
, hstmtInsert
); 
1790         if (! wxStrcmp(pDb
->sqlState
, wxT("23000")))  // Integrity constraint violated 
1791             return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
); 
1794             pDb
->DispNextError(); 
1795             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1800     // Record inserted into the datasource successfully 
1803 }  // wxDbTable::Insert() 
1806 /********** wxDbTable::Update() **********/ 
1807 bool wxDbTable::Update(void) 
1809     wxASSERT(!queryOnly
); 
1815     // Build the SQL UPDATE statement 
1816     BuildUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
); 
1818     pDb
->WriteSqlLog(sqlStmt
); 
1820 #ifdef DBDEBUG_CONSOLE 
1821     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1824     // Execute the SQL UPDATE statement 
1825     return(execUpdate(sqlStmt
)); 
1827 }  // wxDbTable::Update() 
1830 /********** wxDbTable::Update(pSqlStmt) **********/ 
1831 bool wxDbTable::Update(const wxString 
&pSqlStmt
) 
1833     wxASSERT(!queryOnly
); 
1837     pDb
->WriteSqlLog(pSqlStmt
); 
1839     return(execUpdate(pSqlStmt
)); 
1841 }  // wxDbTable::Update(pSqlStmt) 
1844 /********** wxDbTable::UpdateWhere() **********/ 
1845 bool wxDbTable::UpdateWhere(const wxString 
&pWhereClause
) 
1847     wxASSERT(!queryOnly
); 
1853     // Build the SQL UPDATE statement 
1854     BuildUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
); 
1856     pDb
->WriteSqlLog(sqlStmt
); 
1858 #ifdef DBDEBUG_CONSOLE 
1859     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1862     // Execute the SQL UPDATE statement 
1863     return(execUpdate(sqlStmt
)); 
1865 }  // wxDbTable::UpdateWhere() 
1868 /********** wxDbTable::Delete() **********/ 
1869 bool wxDbTable::Delete(void) 
1871     wxASSERT(!queryOnly
); 
1878     // Build the SQL DELETE statement 
1879     BuildDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
); 
1881     pDb
->WriteSqlLog(sqlStmt
); 
1883     // Execute the SQL DELETE statement 
1884     return(execDelete(sqlStmt
)); 
1886 }  // wxDbTable::Delete() 
1889 /********** wxDbTable::DeleteWhere() **********/ 
1890 bool wxDbTable::DeleteWhere(const wxString 
&pWhereClause
) 
1892     wxASSERT(!queryOnly
); 
1899     // Build the SQL DELETE statement 
1900     BuildDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
); 
1902     pDb
->WriteSqlLog(sqlStmt
); 
1904     // Execute the SQL DELETE statement 
1905     return(execDelete(sqlStmt
)); 
1907 }  // wxDbTable::DeleteWhere() 
1910 /********** wxDbTable::DeleteMatching() **********/ 
1911 bool wxDbTable::DeleteMatching(void) 
1913     wxASSERT(!queryOnly
); 
1920     // Build the SQL DELETE statement 
1921     BuildDeleteStmt(sqlStmt
, DB_DEL_MATCHING
); 
1923     pDb
->WriteSqlLog(sqlStmt
); 
1925     // Execute the SQL DELETE statement 
1926     return(execDelete(sqlStmt
)); 
1928 }  // wxDbTable::DeleteMatching() 
1931 /********** wxDbTable::IsColNull() **********/ 
1932 bool wxDbTable::IsColNull(UWORD colNo
) const 
1935     This logic is just not right.  It would indicate TRUE 
1936     if a numeric field were set to a value of 0. 
1938     switch(colDefs[colNo].SqlCtype) 
1941             return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0); 
1943             return((  *((SWORD *) colDefs[colNo].PtrDataObj))   == 0); 
1945             return((   *((UWORD*) colDefs[colNo].PtrDataObj))   == 0); 
1947             return(( *((SDWORD *) colDefs[colNo].PtrDataObj))   == 0); 
1949             return(( *((UDWORD *) colDefs[colNo].PtrDataObj))   == 0); 
1951             return(( *((SFLOAT *) colDefs[colNo].PtrDataObj))   == 0); 
1953             return((*((SDOUBLE *) colDefs[colNo].PtrDataObj))   == 0); 
1954         case SQL_C_TIMESTAMP: 
1955             TIMESTAMP_STRUCT *pDt; 
1956             pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj; 
1957             if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0) 
1965     return (colDefs
[colNo
].Null
); 
1966 }  // wxDbTable::IsColNull() 
1969 /********** wxDbTable::CanSelectForUpdate() **********/ 
1970 bool wxDbTable::CanSelectForUpdate(void) 
1975     if (pDb
->Dbms() == dbmsMY_SQL
) 
1978     if ((pDb
->Dbms() == dbmsORACLE
) || 
1979         (pDb
->dbInf
.posStmts 
& SQL_PS_SELECT_FOR_UPDATE
)) 
1984 }  // wxDbTable::CanSelectForUpdate() 
1987 /********** wxDbTable::CanUpdByROWID() **********/ 
1988 bool wxDbTable::CanUpdByROWID(void) 
1991  * NOTE: Returning FALSE for now until this can be debugged, 
1992  *        as the ROWID is not getting updated correctly 
1996     if (pDb->Dbms() == dbmsORACLE) 
2001 }  // wxDbTable::CanUpdByROWID() 
2004 /********** wxDbTable::IsCursorClosedOnCommit() **********/ 
2005 bool wxDbTable::IsCursorClosedOnCommit(void) 
2007     if (pDb
->dbInf
.cursorCommitBehavior 
== SQL_CB_PRESERVE
) 
2012 }  // wxDbTable::IsCursorClosedOnCommit() 
2016 /********** wxDbTable::ClearMemberVar() **********/ 
2017 void wxDbTable::ClearMemberVar(UWORD colNo
, bool setToNull
) 
2019     wxASSERT(colNo 
< noCols
); 
2021     switch(colDefs
[colNo
].SqlCtype
) 
2024             ((UCHAR FAR 
*) colDefs
[colNo
].PtrDataObj
)[0]    = 0; 
2027             *((SWORD 
*) colDefs
[colNo
].PtrDataObj
)          = 0; 
2030             *((UWORD
*) colDefs
[colNo
].PtrDataObj
)           = 0; 
2033             *((SDWORD 
*) colDefs
[colNo
].PtrDataObj
)         = 0; 
2036             *((UDWORD 
*) colDefs
[colNo
].PtrDataObj
)         = 0; 
2039             *((SFLOAT 
*) colDefs
[colNo
].PtrDataObj
)         = 0.0f
; 
2042             *((SDOUBLE 
*) colDefs
[colNo
].PtrDataObj
)        = 0.0f
; 
2044         case SQL_C_TIMESTAMP
: 
2045             TIMESTAMP_STRUCT 
*pDt
; 
2046             pDt 
= (TIMESTAMP_STRUCT 
*) colDefs
[colNo
].PtrDataObj
; 
2059 }  // wxDbTable::ClearMemberVar() 
2062 /********** wxDbTable::ClearMemberVars() **********/ 
2063 void wxDbTable::ClearMemberVars(bool setToNull
) 
2067     // Loop through the columns setting each member variable to zero 
2068     for (i
=0; i 
< noCols
; i
++) 
2069         ClearMemberVar(i
,setToNull
); 
2071 }  // wxDbTable::ClearMemberVars() 
2074 /********** wxDbTable::SetQueryTimeout() **********/ 
2075 bool wxDbTable::SetQueryTimeout(UDWORD nSeconds
) 
2077     if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2078         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
2079     if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2080         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
2081     if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2082         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
2083     if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2084         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
)); 
2086     // Completed Successfully 
2089 }  // wxDbTable::SetQueryTimeout() 
2092 /********** wxDbTable::SetColDefs() **********/ 
2093 void wxDbTable::SetColDefs(UWORD index
, const wxString 
&fieldName
, int dataType
, void *pData
, 
2094                            SWORD cType
, int size
, bool keyField
, bool upd
, 
2095                            bool insAllow
, bool derivedCol
) 
2097     if (!colDefs
)  // May happen if the database connection fails 
2100     if (fieldName
.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN
) 
2102         wxStrncpy(colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
); 
2103         colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0; 
2107         tmpMsg
.Printf(_T("Column name '%s' is too long. Truncated to '%s'."), 
2108                       fieldName
.c_str(),colDefs
[index
].ColName
); 
2110 #endif // __WXDEBUG__ 
2113         wxStrcpy(colDefs
[index
].ColName
, fieldName
); 
2115     colDefs
[index
].DbDataType       
= dataType
; 
2116     colDefs
[index
].PtrDataObj       
= pData
; 
2117     colDefs
[index
].SqlCtype         
= cType
; 
2118     colDefs
[index
].SzDataObj        
= size
; 
2119     colDefs
[index
].KeyField         
= keyField
; 
2120     colDefs
[index
].DerivedCol       
= derivedCol
; 
2121     // Derived columns by definition would NOT be "Insertable" or "Updateable" 
2124         colDefs
[index
].Updateable       
= FALSE
; 
2125         colDefs
[index
].InsertAllowed    
= FALSE
; 
2129         colDefs
[index
].Updateable       
= upd
; 
2130         colDefs
[index
].InsertAllowed    
= insAllow
; 
2133     colDefs
[index
].Null                 
= FALSE
; 
2135 }  // wxDbTable::SetColDefs() 
2138 /********** wxDbTable::SetColDefs() **********/ 
2139 wxDbColDataPtr
* wxDbTable::SetColDefs(wxDbColInf 
*pColInfs
, UWORD numCols
) 
2142     wxDbColDataPtr 
*pColDataPtrs 
= NULL
; 
2148         pColDataPtrs 
= new wxDbColDataPtr
[numCols
+1]; 
2150         for (index 
= 0; index 
< numCols
; index
++) 
2152             // Process the fields 
2153             switch (pColInfs
[index
].dbDataType
) 
2155                 case DB_DATA_TYPE_VARCHAR
: 
2156                    pColDataPtrs
[index
].PtrDataObj 
= new wxChar
[pColInfs
[index
].bufferLength
+1]; 
2157                    pColDataPtrs
[index
].SzDataObj  
= pColInfs
[index
].columnSize
; 
2158                    pColDataPtrs
[index
].SqlCtype   
= SQL_C_CHAR
; 
2160                 case DB_DATA_TYPE_INTEGER
: 
2161                     // Can be long or short 
2162                     if (pColInfs
[index
].bufferLength 
== sizeof(long)) 
2164                       pColDataPtrs
[index
].PtrDataObj 
= new long; 
2165                       pColDataPtrs
[index
].SzDataObj  
= sizeof(long); 
2166                       pColDataPtrs
[index
].SqlCtype   
= SQL_C_SLONG
; 
2170                         pColDataPtrs
[index
].PtrDataObj 
= new short; 
2171                         pColDataPtrs
[index
].SzDataObj  
= sizeof(short); 
2172                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_SSHORT
; 
2175                 case DB_DATA_TYPE_FLOAT
: 
2176                     // Can be float or double 
2177                     if (pColInfs
[index
].bufferLength 
== sizeof(float)) 
2179                         pColDataPtrs
[index
].PtrDataObj 
= new float; 
2180                         pColDataPtrs
[index
].SzDataObj  
= sizeof(float); 
2181                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_FLOAT
; 
2185                         pColDataPtrs
[index
].PtrDataObj 
= new double; 
2186                         pColDataPtrs
[index
].SzDataObj  
= sizeof(double); 
2187                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_DOUBLE
; 
2190                 case DB_DATA_TYPE_DATE
: 
2191                     pColDataPtrs
[index
].PtrDataObj 
= new TIMESTAMP_STRUCT
; 
2192                     pColDataPtrs
[index
].SzDataObj  
= sizeof(TIMESTAMP_STRUCT
); 
2193                     pColDataPtrs
[index
].SqlCtype   
= SQL_C_TIMESTAMP
; 
2195                 case DB_DATA_TYPE_BLOB
: 
2196                     wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns")); 
2197                     pColDataPtrs
[index
].PtrDataObj 
= /*BLOB ADDITION NEEDED*/NULL
; 
2198                     pColDataPtrs
[index
].SzDataObj  
= /*BLOB ADDITION NEEDED*/sizeof(void *); 
2199                     pColDataPtrs
[index
].SqlCtype   
= SQL_VARBINARY
; 
2202             if (pColDataPtrs
[index
].PtrDataObj 
!= NULL
) 
2203                 SetColDefs (index
,pColInfs
[index
].colName
,pColInfs
[index
].dbDataType
, pColDataPtrs
[index
].PtrDataObj
, pColDataPtrs
[index
].SqlCtype
, pColDataPtrs
[index
].SzDataObj
); 
2206                 // Unable to build all the column definitions, as either one of 
2207                 // the calls to "new" failed above, or there was a BLOB field 
2208                 // to have a column definition for.  If BLOBs are to be used, 
2209                 // the other form of ::SetColDefs() must be used, as it is impossible 
2210                 // to know the maximum size to create the PtrDataObj to be. 
2211                 delete [] pColDataPtrs
; 
2217     return (pColDataPtrs
); 
2219 } // wxDbTable::SetColDefs() 
2222 /********** wxDbTable::SetCursor() **********/ 
2223 void wxDbTable::SetCursor(HSTMT 
*hstmtActivate
) 
2225     if (hstmtActivate 
== wxDB_DEFAULT_CURSOR
) 
2226         hstmt 
= *hstmtDefault
; 
2228         hstmt 
= *hstmtActivate
; 
2230 }  // wxDbTable::SetCursor() 
2233 /********** wxDbTable::Count(const wxString &) **********/ 
2234 ULONG 
wxDbTable::Count(const wxString 
&args
) 
2240     // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement 
2241     sqlStmt  
= wxT("SELECT COUNT("); 
2243     sqlStmt 
+= wxT(") FROM "); 
2244     sqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
2245 //    sqlStmt += queryTableName; 
2246 #if wxODBC_BACKWARD_COMPATABILITY 
2247     if (from 
&& wxStrlen(from
)) 
2253     // Add the where clause if one is provided 
2254 #if wxODBC_BACKWARD_COMPATABILITY 
2255     if (where 
&& wxStrlen(where
)) 
2260         sqlStmt 
+= wxT(" WHERE "); 
2264     pDb
->WriteSqlLog(sqlStmt
); 
2266     // Initialize the Count cursor if it's not already initialized 
2269         hstmtCount 
= GetNewCursor(FALSE
,FALSE
); 
2270         wxASSERT(hstmtCount
); 
2275     // Execute the SQL statement 
2276     if (SQLExecDirect(*hstmtCount
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
2278         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2283     if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
) 
2285         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2289     // Obtain the result 
2290     if (SQLGetData(*hstmtCount
, (UWORD
)1, SQL_C_ULONG
, &count
, sizeof(count
), &cb
) != SQL_SUCCESS
) 
2292         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2297     if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
) 
2298         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2300     // Return the record count 
2303 }  // wxDbTable::Count() 
2306 /********** wxDbTable::Refresh() **********/ 
2307 bool wxDbTable::Refresh(void) 
2311     // Switch to the internal cursor so any active cursors are not corrupted 
2312     HSTMT currCursor 
= GetCursor(); 
2313     hstmt 
= hstmtInternal
; 
2314 #if wxODBC_BACKWARD_COMPATABILITY 
2315     // Save the where and order by clauses 
2316     char *saveWhere 
= where
; 
2317     char *saveOrderBy 
= orderBy
; 
2319     wxString saveWhere 
= where
; 
2320     wxString saveOrderBy 
= orderBy
; 
2322     // Build a where clause to refetch the record with.  Try and use the 
2323     // ROWID if it's available, ow use the key fields. 
2324     wxString whereClause
; 
2325     whereClause
.Empty(); 
2327     if (CanUpdByROWID()) 
2330         wxChar   rowid
[wxDB_ROWID_LEN
+1]; 
2332         // Get the ROWID value.  If not successful retreiving the ROWID, 
2333         // simply fall down through the code and build the WHERE clause 
2334         // based on the key fields. 
2335         if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
2337             whereClause 
+= pDb
->SQLTableName(queryTableName
); 
2338 //            whereClause += queryTableName; 
2339             whereClause 
+= wxT(".ROWID = '"); 
2340             whereClause 
+= rowid
; 
2341             whereClause 
+= wxT("'"); 
2345     // If unable to use the ROWID, build a where clause from the keyfields 
2346     if (wxStrlen(whereClause
) == 0) 
2347         BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
); 
2349     // Requery the record 
2350     where 
= whereClause
; 
2355     if (result 
&& !GetNext()) 
2358     // Switch back to original cursor 
2359     SetCursor(&currCursor
); 
2361     // Free the internal cursor 
2362     if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
) 
2363         pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
2365     // Restore the original where and order by clauses 
2367     orderBy 
= saveOrderBy
; 
2371 }  // wxDbTable::Refresh() 
2374 /********** wxDbTable::SetColNull() **********/ 
2375 bool wxDbTable::SetColNull(UWORD colNo
, bool set
) 
2379         colDefs
[colNo
].Null 
= set
; 
2380         if (set
)  // Blank out the values in the member variable 
2381             ClearMemberVar(colNo
,FALSE
);  // Must call with FALSE, or infinite recursion will happen 
2387 }  // wxDbTable::SetColNull() 
2390 /********** wxDbTable::SetColNull() **********/ 
2391 bool wxDbTable::SetColNull(const wxString 
&colName
, bool set
) 
2394     for (i 
= 0; i 
< noCols
; i
++) 
2396         if (!wxStricmp(colName
, colDefs
[i
].ColName
)) 
2402         colDefs
[i
].Null 
= set
; 
2403         if (set
)  // Blank out the values in the member variable 
2404             ClearMemberVar(i
,FALSE
);  // Must call with FALSE, or infinite recursion will happen 
2410 }  // wxDbTable::SetColNull() 
2413 /********** wxDbTable::GetNewCursor() **********/ 
2414 HSTMT 
*wxDbTable::GetNewCursor(bool setCursor
, bool bindColumns
) 
2416     HSTMT 
*newHSTMT 
= new HSTMT
; 
2421     if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
) 
2423         pDb
->DispAllErrors(henv
, hdbc
); 
2428     if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
2430         pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
); 
2437         if (!bindCols(*newHSTMT
)) 
2445         SetCursor(newHSTMT
); 
2449 }   // wxDbTable::GetNewCursor() 
2452 /********** wxDbTable::DeleteCursor() **********/ 
2453 bool wxDbTable::DeleteCursor(HSTMT 
*hstmtDel
) 
2457     if (!hstmtDel
)  // Cursor already deleted 
2461 ODBC 3.0 says to use this form 
2462     if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
2465     if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
) 
2467         pDb
->DispAllErrors(henv
, hdbc
); 
2475 }  // wxDbTable::DeleteCursor() 
2477 ////////////////////////////////////////////////////////////// 
2478 // wxDbGrid support functions 
2479 ////////////////////////////////////////////////////////////// 
2481 void wxDbTable::SetRowMode(const rowmode_t rowmode
) 
2483     if (!m_hstmtGridQuery
) 
2485         m_hstmtGridQuery 
= GetNewCursor(FALSE
,FALSE
); 
2486         if (!bindCols(*m_hstmtGridQuery
)) 
2490     m_rowmode 
= rowmode
; 
2493         case WX_ROW_MODE_QUERY
: 
2494             SetCursor(m_hstmtGridQuery
); 
2496         case WX_ROW_MODE_INDIVIDUAL
: 
2497             SetCursor(hstmtDefault
); 
2502 }  // wxDbTable::SetRowMode() 
2505 wxVariant 
wxDbTable::GetCol(const int col
) const 
2508     if ((col 
< noCols
) && (!IsColNull(col
))) 
2510         switch (colDefs
[col
].SqlCtype
) 
2514                 val 
= (char *)(colDefs
[col
].PtrDataObj
); 
2518                 val 
= *(long *)(colDefs
[col
].PtrDataObj
); 
2522                 val 
= (long int )(*(short *)(colDefs
[col
].PtrDataObj
)); 
2525                 val 
= (long)(*(unsigned long *)(colDefs
[col
].PtrDataObj
)); 
2528                 val 
= (long)(*(char *)(colDefs
[col
].PtrDataObj
)); 
2530             case SQL_C_UTINYINT
: 
2531                 val 
= (long)(*(unsigned char *)(colDefs
[col
].PtrDataObj
)); 
2534                 val 
= (long)(*(UWORD 
*)(colDefs
[col
].PtrDataObj
)); 
2537                 val 
= (DATE_STRUCT 
*)(colDefs
[col
].PtrDataObj
); 
2540                 val 
= (TIME_STRUCT 
*)(colDefs
[col
].PtrDataObj
); 
2542             case SQL_C_TIMESTAMP
: 
2543                 val 
= (TIMESTAMP_STRUCT 
*)(colDefs
[col
].PtrDataObj
); 
2546                 val 
= *(double *)(colDefs
[col
].PtrDataObj
); 
2553 }  // wxDbTable::GetCol() 
2556 void csstrncpyt(char *s
, const char *t
, int n
) 
2558     while ((*s
++ = *t
++) && --n
) 
2564 void wxDbTable::SetCol(const int col
, const wxVariant val
) 
2566     //FIXME: Add proper wxDateTime support to wxVariant.. 
2569     SetColNull(col
, val
.IsNull()); 
2573         if ((colDefs
[col
].SqlCtype 
== SQL_C_DATE
) 
2574             || (colDefs
[col
].SqlCtype 
== SQL_C_TIME
) 
2575             || (colDefs
[col
].SqlCtype 
== SQL_C_TIMESTAMP
)) 
2577             //Returns null if invalid! 
2578             if (!dateval
.ParseDate(val
.GetString())) 
2579                 SetColNull(col
,TRUE
); 
2582         switch (colDefs
[col
].SqlCtype
) 
2586                 csstrncpyt((char *)(colDefs
[col
].PtrDataObj
), 
2587                            val
.GetString().c_str(), 
2588                            colDefs
[col
].SzDataObj
-1); 
2592                 *(long *)(colDefs
[col
].PtrDataObj
) = val
; 
2596                 *(short *)(colDefs
[col
].PtrDataObj
) = val
.GetLong(); 
2599                 *(unsigned long *)(colDefs
[col
].PtrDataObj
) = val
.GetLong(); 
2602                 *(char *)(colDefs
[col
].PtrDataObj
) = val
.GetChar(); 
2604             case SQL_C_UTINYINT
: 
2605                 *(unsigned char *)(colDefs
[col
].PtrDataObj
) = val
.GetChar(); 
2608                 *(unsigned short *)(colDefs
[col
].PtrDataObj
) = val
.GetLong(); 
2610             //FIXME: Add proper wxDateTime support to wxVariant.. 
2613                     DATE_STRUCT 
*dataptr 
= 
2614                         (DATE_STRUCT 
*)colDefs
[col
].PtrDataObj
; 
2616                     dataptr
->year   
= dateval
.GetYear(); 
2617                     dataptr
->month  
= dateval
.GetMonth()+1; 
2618                     dataptr
->day    
= dateval
.GetDay(); 
2623                     TIME_STRUCT 
*dataptr 
= 
2624                         (TIME_STRUCT 
*)colDefs
[col
].PtrDataObj
; 
2626                     dataptr
->hour   
= dateval
.GetHour(); 
2627                     dataptr
->minute 
= dateval
.GetMinute(); 
2628                     dataptr
->second 
= dateval
.GetSecond(); 
2631             case SQL_C_TIMESTAMP
: 
2633                     TIMESTAMP_STRUCT 
*dataptr 
= 
2634                         (TIMESTAMP_STRUCT 
*)colDefs
[col
].PtrDataObj
; 
2635                     dataptr
->year   
= dateval
.GetYear(); 
2636                     dataptr
->month  
= dateval
.GetMonth()+1; 
2637                     dataptr
->day    
= dateval
.GetDay(); 
2639                     dataptr
->hour   
= dateval
.GetHour(); 
2640                     dataptr
->minute 
= dateval
.GetMinute(); 
2641                     dataptr
->second 
= dateval
.GetSecond(); 
2645                 *(double *)(colDefs
[col
].PtrDataObj
) = val
; 
2650     }  // if (!val.IsNull()) 
2651 }  // wxDbTable::SetCol() 
2654 GenericKey 
wxDbTable::GetKey() 
2659     blk 
= malloc(m_keysize
); 
2660     blkptr 
= (char *) blk
; 
2663     for (i
=0; i 
< noCols
; i
++) 
2665         if (colDefs
[i
].KeyField
) 
2667             memcpy(blkptr
,colDefs
[i
].PtrDataObj
, colDefs
[i
].SzDataObj
); 
2668             blkptr 
+= colDefs
[i
].SzDataObj
; 
2672     GenericKey k 
= GenericKey(blk
, m_keysize
); 
2676 }  // wxDbTable::GetKey() 
2679 void wxDbTable::SetKey(const GenericKey
& k
) 
2685     blkptr 
= (char *)blk
; 
2688     for (i
=0; i 
< noCols
; i
++) 
2690         if (colDefs
[i
].KeyField
) 
2692             SetColNull(i
, FALSE
); 
2693             memcpy(colDefs
[i
].PtrDataObj
, blkptr
, colDefs
[i
].SzDataObj
); 
2694             blkptr 
+= colDefs
[i
].SzDataObj
; 
2697 }  // wxDbTable::SetKey() 
2700 #endif  // wxUSE_ODBC