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 
  43     #include "wx/ioswrap.h" 
  47     #include "wx/string.h" 
  48     #include "wx/object.h" 
  52         #include "wx/msgdlg.h" 
  56 #include "wx/filefn.h" 
  65 #include "wx/dbtable.h" 
  68 // The HPUX preprocessor lines below were commented out on 8/20/97 
  69 // because macros.h currently redefines DEBUG and is unneeded. 
  71 // #    include <macros.h> 
  74 #    include <sys/minmax.h> 
  78 ULONG lastTableID 
= 0; 
  86 /********** wxDbColDef::wxDbColDef() Constructor **********/ 
  87 wxDbColDef::wxDbColDef() 
  93 bool wxDbColDef::Initialize() 
  96     DbDataType      
= DB_DATA_TYPE_INTEGER
; 
  97     SqlCtype        
= SQL_C_LONG
; 
 102     InsertAllowed   
= FALSE
; 
 108 }  // wxDbColDef::Initialize() 
 111 /********** wxDbTable::wxDbTable() Constructor **********/ 
 112 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 113                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 115     if (!initialize(pwxDb
, tblName
, numColumns
, qryTblName
, qryOnly
, tblPath
)) 
 117 }  // wxDbTable::wxDbTable() 
 120 /***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/ 
 121 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 122                     const wxChar 
*qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 124     wxString tempQryTblName
; 
 125     tempQryTblName 
= qryTblName
; 
 126     if (!initialize(pwxDb
, tblName
, numColumns
, tempQryTblName
, qryOnly
, tblPath
)) 
 128 }  // wxDbTable::wxDbTable() 
 131 /********** wxDbTable::~wxDbTable() **********/ 
 132 wxDbTable::~wxDbTable() 
 135 }  // wxDbTable::~wxDbTable() 
 138 bool wxDbTable::initialize(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 139                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 141     // Initializing member variables 
 142     pDb                 
= pwxDb
;                    // Pointer to the wxDb object 
 146     m_hstmtGridQuery               
= 0; 
 147     hstmtDefault        
= 0;                        // Initialized below 
 148     hstmtCount          
= 0;                        // Initialized first time it is needed 
 155     noCols              
= numColumns
;               // Number of cols in the table 
 156     where
.Empty();                                  // Where clause 
 157     orderBy
.Empty();                                // Order By clause 
 158     from
.Empty();                                   // From clause 
 159     selectForUpdate     
= FALSE
;                    // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase 
 164     queryTableName
.Empty(); 
 166     wxASSERT(tblName
.Length()); 
 172     tableName 
= tblName
;                        // Table Name 
 173     if (tblPath
.Length()) 
 174         tablePath 
= tblPath
;                    // Table Path - used for dBase files 
 178     if (qryTblName
.Length())                    // Name of the table/view to query 
 179         queryTableName 
= qryTblName
; 
 181         queryTableName 
= tblName
; 
 183     pDb
->incrementTableCount(); 
 186     tableID 
= ++lastTableID
; 
 187     s
.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), tblName
.c_str(), tableID
, pDb
); 
 190     wxTablesInUse 
*tableInUse
; 
 191     tableInUse            
= new wxTablesInUse(); 
 192     tableInUse
->tableName 
= tblName
; 
 193     tableInUse
->tableID   
= tableID
; 
 194     tableInUse
->pDb       
= pDb
; 
 195     TablesInUse
.Append(tableInUse
); 
 200     // Grab the HENV and HDBC from the wxDb object 
 201     henv 
= pDb
->GetHENV(); 
 202     hdbc 
= pDb
->GetHDBC(); 
 204     // Allocate space for column definitions 
 206         colDefs 
= new wxDbColDef
[noCols
];  // Points to the first column definition 
 208     // Allocate statement handles for the table 
 211         // Allocate a separate statement handle for performing inserts 
 212         if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
) 
 213             pDb
->DispAllErrors(henv
, hdbc
); 
 214         // Allocate a separate statement handle for performing deletes 
 215         if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
) 
 216             pDb
->DispAllErrors(henv
, hdbc
); 
 217         // Allocate a separate statement handle for performing updates 
 218         if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
) 
 219             pDb
->DispAllErrors(henv
, hdbc
); 
 221     // Allocate a separate statement handle for internal use 
 222     if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
) 
 223         pDb
->DispAllErrors(henv
, hdbc
); 
 225     // Set the cursor type for the statement handles 
 226     cursorType 
= SQL_CURSOR_STATIC
; 
 228     if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 230         // Check to see if cursor type is supported 
 231         pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 232         if (! wxStrcmp(pDb
->sqlState
, wxT("01S02")))  // Option Value Changed 
 234             // Datasource does not support static cursors.  Driver 
 235             // will substitute a cursor type.  Call SQLGetStmtOption() 
 236             // to determine which cursor type was selected. 
 237             if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
) 
 238                 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 239 #ifdef DBDEBUG_CONSOLE 
 240             cout 
<< wxT("Static cursor changed to: "); 
 243             case SQL_CURSOR_FORWARD_ONLY
: 
 244                 cout 
<< wxT("Forward Only"); 
 246             case SQL_CURSOR_STATIC
: 
 247                 cout 
<< wxT("Static"); 
 249             case SQL_CURSOR_KEYSET_DRIVEN
: 
 250                 cout 
<< wxT("Keyset Driven"); 
 252             case SQL_CURSOR_DYNAMIC
: 
 253                 cout 
<< wxT("Dynamic"); 
 256             cout 
<< endl 
<< endl
; 
 259             if (pDb
->FwdOnlyCursors() && cursorType 
!= SQL_CURSOR_FORWARD_ONLY
) 
 261                 // Force the use of a forward only cursor... 
 262                 cursorType 
= SQL_CURSOR_FORWARD_ONLY
; 
 263                 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 265                     // Should never happen 
 266                     pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 273             pDb
->DispNextError(); 
 274             pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 277 #ifdef DBDEBUG_CONSOLE 
 279         cout 
<< wxT("Cursor Type set to STATIC") << endl 
<< endl
; 
 284         // Set the cursor type for the INSERT statement handle 
 285         if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 286             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
 287         // Set the cursor type for the DELETE statement handle 
 288         if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 289             pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
); 
 290         // Set the cursor type for the UPDATE statement handle 
 291         if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 292             pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
); 
 295     // Make the default cursor the active cursor 
 296     hstmtDefault 
= GetNewCursor(FALSE
,FALSE
); 
 297     wxASSERT(hstmtDefault
); 
 298     hstmt 
= *hstmtDefault
; 
 302 }  // wxDbTable::initialize() 
 305 void wxDbTable::cleanup() 
 310         s
.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), tableName
.c_str(), tableID
, pDb
); 
 317         TablesInUse
.DeleteContents(TRUE
); 
 321         pNode 
= TablesInUse
.First(); 
 322         while (pNode 
&& !found
) 
 324             if (((wxTablesInUse 
*)pNode
->Data())->tableID 
== tableID
) 
 327                 if (!TablesInUse
.DeleteNode(pNode
)) 
 328                     wxLogDebug (s
,wxT("Unable to delete node!")); 
 331                 pNode 
= pNode
->Next(); 
 336             msg
.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s
.c_str()); 
 337             wxLogDebug (msg
,wxT("NOTICE...")); 
 342     // Decrement the wxDb table count 
 344         pDb
->decrementTableCount(); 
 346     // Delete memory allocated for column definitions 
 350     // Free statement handles 
 356 ODBC 3.0 says to use this form 
 357             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 359             if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
) 
 360                 pDb
->DispAllErrors(henv
, hdbc
); 
 366 ODBC 3.0 says to use this form 
 367             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 369             if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
) 
 370                 pDb
->DispAllErrors(henv
, hdbc
); 
 376 ODBC 3.0 says to use this form 
 377             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 379             if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
) 
 380                 pDb
->DispAllErrors(henv
, hdbc
); 
 386         if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
) 
 387             pDb
->DispAllErrors(henv
, hdbc
); 
 390     // Delete dynamically allocated cursors 
 392         DeleteCursor(hstmtDefault
); 
 395         DeleteCursor(hstmtCount
); 
 397     if (m_hstmtGridQuery
) 
 398         DeleteCursor(m_hstmtGridQuery
); 
 400 }  // wxDbTable::cleanup() 
 403 /***************************** PRIVATE FUNCTIONS *****************************/ 
 406 /********** wxDbTable::bindParams() **********/ 
 407 bool wxDbTable::bindParams(bool forUpdate
) 
 409     wxASSERT(!queryOnly
); 
 414     UDWORD  precision   
= 0; 
 417     // Bind each column of the table that should be bound 
 418     // to a parameter marker 
 422     for (i
=0, colNo
=1; i 
< noCols
; i
++) 
 426             if (!colDefs
[i
].Updateable
) 
 431             if (!colDefs
[i
].InsertAllowed
) 
 435         switch(colDefs
[i
].DbDataType
) 
 437             case DB_DATA_TYPE_VARCHAR
: 
 438                 fSqlType 
= pDb
->GetTypeInfVarchar().FsqlType
; 
 439                 precision 
= colDefs
[i
].SzDataObj
; 
 442                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 444                     colDefs
[i
].CbValue 
= SQL_NTS
; 
 446             case DB_DATA_TYPE_INTEGER
: 
 447                 fSqlType 
= pDb
->GetTypeInfInteger().FsqlType
; 
 448                 precision 
= pDb
->GetTypeInfInteger().Precision
; 
 451                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 453                     colDefs
[i
].CbValue 
= 0; 
 455             case DB_DATA_TYPE_FLOAT
: 
 456                 fSqlType 
= pDb
->GetTypeInfFloat().FsqlType
; 
 457                 precision 
= pDb
->GetTypeInfFloat().Precision
; 
 458                 scale 
= pDb
->GetTypeInfFloat().MaximumScale
; 
 459                 // SQL Sybase Anywhere v5.5 returned a negative number for the 
 460                 // MaxScale.  This caused ODBC to kick out an error on ibscale. 
 461                 // I check for this here and set the scale = precision. 
 463                 // scale = (short) precision; 
 465                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 467                     colDefs
[i
].CbValue 
= 0; 
 469             case DB_DATA_TYPE_DATE
: 
 470                 fSqlType 
= pDb
->GetTypeInfDate().FsqlType
; 
 471                 precision 
= pDb
->GetTypeInfDate().Precision
; 
 474                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 476                     colDefs
[i
].CbValue 
= 0; 
 478             case DB_DATA_TYPE_BLOB
: 
 479                 fSqlType 
= pDb
->GetTypeInfBlob().FsqlType
; 
 483                     colDefs
[i
].CbValue 
= SQL_NULL_DATA
; 
 485                     colDefs
[i
].CbValue 
= SQL_LEN_DATA_AT_EXEC(colDefs
[i
].SzDataObj
); 
 490             if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 491                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 492                                  precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 494                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 499             if (SQLBindParameter(hstmtInsert
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 500                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 501                                  precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 503                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 508     // Completed successfully 
 511 }  // wxDbTable::bindParams() 
 514 /********** wxDbTable::bindInsertParams() **********/ 
 515 bool wxDbTable::bindInsertParams(void) 
 517     return bindParams(FALSE
); 
 518 }  // wxDbTable::bindInsertParams() 
 521 /********** wxDbTable::bindUpdateParams() **********/ 
 522 bool wxDbTable::bindUpdateParams(void) 
 524     return bindParams(TRUE
); 
 525 }  // wxDbTable::bindUpdateParams() 
 528 /********** wxDbTable::bindCols() **********/ 
 529 bool wxDbTable::bindCols(HSTMT cursor
) 
 533     // Bind each column of the table to a memory address for fetching data 
 535     for (i 
= 0; i 
< noCols
; i
++) 
 537         cb 
= colDefs
[i
].CbValue
; 
 538         if (SQLBindCol(cursor
, (UWORD
)(i
+1), colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 539                        colDefs
[i
].SzDataObj
, &cb 
) != SQL_SUCCESS
) 
 540           return (pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
 543     // Completed successfully 
 546 }  // wxDbTable::bindCols() 
 549 /********** wxDbTable::getRec() **********/ 
 550 bool wxDbTable::getRec(UWORD fetchType
) 
 554     if (!pDb
->FwdOnlyCursors()) 
 556         // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType 
 560         retcode 
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
); 
 561         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 563             if (retcode 
== SQL_NO_DATA_FOUND
) 
 566                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 570             // Set the Null member variable to indicate the Null state 
 571             // of each column just read in. 
 573             for (i 
= 0; i 
< noCols
; i
++) 
 574                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 579         // Fetch the next record from the record set 
 580         retcode 
= SQLFetch(hstmt
); 
 581         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 583             if (retcode 
== SQL_NO_DATA_FOUND
) 
 586                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 590             // Set the Null member variable to indicate the Null state 
 591             // of each column just read in. 
 593             for (i 
= 0; i 
< noCols
; i
++) 
 594                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 598     // Completed successfully 
 601 }  // wxDbTable::getRec() 
 604 /********** wxDbTable::execDelete() **********/ 
 605 bool wxDbTable::execDelete(const wxString 
&pSqlStmt
) 
 609     // Execute the DELETE statement 
 610     retcode 
= SQLExecDirect(hstmtDelete
, (UCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 612     if (retcode 
== SQL_SUCCESS 
|| 
 613         retcode 
== SQL_NO_DATA_FOUND 
|| 
 614         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 616         // Record deleted successfully 
 620     // Problem deleting record 
 621     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
 623 }  // wxDbTable::execDelete() 
 626 /********** wxDbTable::execUpdate() **********/ 
 627 bool wxDbTable::execUpdate(const wxString 
&pSqlStmt
) 
 631     // Execute the UPDATE statement 
 632     retcode 
= SQLExecDirect(hstmtUpdate
, (UCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 634     if (retcode 
== SQL_SUCCESS 
|| 
 635         retcode 
== SQL_NO_DATA_FOUND 
|| 
 636         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 638         // Record updated successfully 
 641     else if (retcode 
== SQL_NEED_DATA
) 
 644         while ((retcode 
= SQLParamData(hstmtUpdate
, &pParmID
) == SQL_NEED_DATA
)) 
 646             // Find the parameter 
 648             for (i
=0; i 
< noCols
; i
++) 
 650                 if (colDefs
[i
].PtrDataObj 
== pParmID
) 
 652                     // We found it.  Store the parameter. 
 653                     retcode 
= SQLPutData(hstmtUpdate
, pParmID
, colDefs
[i
].SzDataObj
); 
 654                     if (retcode 
!= SQL_SUCCESS
) 
 656                         pDb
->DispNextError(); 
 657                         return pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
); 
 663         if (retcode 
== SQL_SUCCESS 
|| 
 664             retcode 
== SQL_NO_DATA_FOUND 
|| 
 665             retcode 
== SQL_SUCCESS_WITH_INFO
) 
 667             // Record updated successfully 
 672     // Problem updating record 
 673     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 675 }  // wxDbTable::execUpdate() 
 678 /********** wxDbTable::query() **********/ 
 679 bool wxDbTable::query(int queryType
, bool forUpdate
, bool distinct
, const wxString 
&pSqlStmt
) 
 684         // The user may wish to select for update, but the DBMS may not be capable 
 685         selectForUpdate 
= CanSelectForUpdate(); 
 687         selectForUpdate 
= FALSE
; 
 689     // Set the SQL SELECT string 
 690     if (queryType 
!= DB_SELECT_STATEMENT
)               // A select statement was not passed in, 
 691     {                                                   // so generate a select statement. 
 692         BuildSelectStmt(sqlStmt
, queryType
, distinct
); 
 693         pDb
->WriteSqlLog(sqlStmt
); 
 696     // Make sure the cursor is closed first 
 697     if (!CloseCursor(hstmt
)) 
 700     // Execute the SQL SELECT statement 
 702     retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) (queryType 
== DB_SELECT_STATEMENT 
? pSqlStmt
.c_str() : sqlStmt
.c_str()), SQL_NTS
); 
 703     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 704         return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 706     // Completed successfully 
 709 }  // wxDbTable::query() 
 712 /***************************** PUBLIC FUNCTIONS *****************************/ 
 715 /********** wxDbTable::Open() **********/ 
 716 bool wxDbTable::Open(bool checkPrivileges
, bool checkTableExists
) 
 726     // Calculate the maximum size of the concatenated 
 727     // keys for use with wxDbGrid 
 729     for (i
=0; i 
< noCols
; i
++) 
 731         if (colDefs
[i
].KeyField
) 
 734             m_keysize 
+= colDefs
[i
].SzDataObj
; 
 739     // Verify that the table exists in the database 
 740     if (checkTableExists 
&& !pDb
->TableExists(tableName
, pDb
->GetUsername(), tablePath
)) 
 742         s 
= wxT("Table/view does not exist in the database"); 
 743         if ( *(pDb
->dbInf
.accessibleTables
) == wxT('Y')) 
 744             s 
+= wxT(", or you have no permissions.\n"); 
 748     else if (checkPrivileges
) 
 750         // Verify the user has rights to access the table. 
 751         // Shortcut boolean evaluation to optimize out call to 
 754         // Unfortunately this optimization doesn't seem to be 
 756         if (// *(pDb->dbInf.accessibleTables) == 'N' && 
 757             !pDb
->TablePrivileges(tableName
,wxT("SELECT"), pDb
->GetUsername(), pDb
->GetUsername(), tablePath
)) 
 758             s 
= wxT("Current logged in user does not have sufficient privileges to access this table.\n"); 
 765         if (!tablePath
.IsEmpty()) 
 766             p
.Printf(wxT("Error opening '%s/%s'.\n"),tablePath
.c_str(),tableName
.c_str()); 
 768             p
.Printf(wxT("Error opening '%s'.\n"), tableName
.c_str()); 
 771         pDb
->LogError(p
.GetData()); 
 776     // Bind the member variables for field exchange between 
 777     // the wxDbTable object and the ODBC record. 
 780         if (!bindInsertParams())                    // Inserts 
 783         if (!bindUpdateParams())                    // Updates 
 787     if (!bindCols(*hstmtDefault
))                   // Selects 
 790     if (!bindCols(hstmtInternal
))                   // Internal use only 
 794      * Do NOT bind the hstmtCount cursor!!! 
 797     // Build an insert statement using parameter markers 
 798     if (!queryOnly 
&& noCols 
> 0) 
 800         bool needComma 
= FALSE
; 
 801         sqlStmt
.Printf(wxT("INSERT INTO %s ("), 
 802                        pDb
->SQLTableName(tableName
.c_str()).c_str()); 
 803         for (i 
= 0; i 
< noCols
; i
++) 
 805             if (! colDefs
[i
].InsertAllowed
) 
 809             sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
 810 //            sqlStmt += colDefs[i].ColName; 
 814         sqlStmt 
+= wxT(") VALUES ("); 
 816         int insertableCount 
= 0; 
 818         for (i 
= 0; i 
< noCols
; i
++) 
 820             if (! colDefs
[i
].InsertAllowed
) 
 830         // Prepare the insert statement for execution 
 833             if (SQLPrepare(hstmtInsert
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
 834                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 840     // Completed successfully 
 843 }  // wxDbTable::Open() 
 846 /********** wxDbTable::Query() **********/ 
 847 bool wxDbTable::Query(bool forUpdate
, bool distinct
) 
 850     return(query(DB_SELECT_WHERE
, forUpdate
, distinct
)); 
 852 }  // wxDbTable::Query() 
 855 /********** wxDbTable::QueryBySqlStmt() **********/ 
 856 bool wxDbTable::QueryBySqlStmt(const wxString 
&pSqlStmt
) 
 858     pDb
->WriteSqlLog(pSqlStmt
); 
 860     return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
)); 
 862 }  // wxDbTable::QueryBySqlStmt() 
 865 /********** wxDbTable::QueryMatching() **********/ 
 866 bool wxDbTable::QueryMatching(bool forUpdate
, bool distinct
) 
 869     return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
)); 
 871 }  // wxDbTable::QueryMatching() 
 874 /********** wxDbTable::QueryOnKeyFields() **********/ 
 875 bool wxDbTable::QueryOnKeyFields(bool forUpdate
, bool distinct
) 
 878     return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
)); 
 880 }  // wxDbTable::QueryOnKeyFields() 
 883 /********** wxDbTable::GetPrev() **********/ 
 884 bool wxDbTable::GetPrev(void) 
 886     if (pDb
->FwdOnlyCursors()) 
 888         wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 892         return(getRec(SQL_FETCH_PRIOR
)); 
 894 }  // wxDbTable::GetPrev() 
 897 /********** wxDbTable::operator-- **********/ 
 898 bool wxDbTable::operator--(int) 
 900     if (pDb
->FwdOnlyCursors()) 
 902         wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 906         return(getRec(SQL_FETCH_PRIOR
)); 
 908 }  // wxDbTable::operator-- 
 911 /********** wxDbTable::GetFirst() **********/ 
 912 bool wxDbTable::GetFirst(void) 
 914     if (pDb
->FwdOnlyCursors()) 
 916         wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 920         return(getRec(SQL_FETCH_FIRST
)); 
 922 }  // wxDbTable::GetFirst() 
 925 /********** wxDbTable::GetLast() **********/ 
 926 bool wxDbTable::GetLast(void) 
 928     if (pDb
->FwdOnlyCursors()) 
 930         wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 934         return(getRec(SQL_FETCH_LAST
)); 
 936 }  // wxDbTable::GetLast() 
 939 /********** wxDbTable::BuildDeleteStmt() **********/ 
 940 void wxDbTable::BuildDeleteStmt(wxString 
&pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
 942     wxASSERT(!queryOnly
); 
 946     wxString whereClause
; 
 950     // Handle the case of DeleteWhere() and the where clause is blank.  It should 
 951     // delete all records from the database in this case. 
 952     if (typeOfDel 
== DB_DEL_WHERE 
&& (pWhereClause
.Length() == 0)) 
 954         pSqlStmt
.Printf(wxT("DELETE FROM %s"), 
 955                         pDb
->SQLTableName(tableName
.c_str()).c_str()); 
 959     pSqlStmt
.Printf(wxT("DELETE FROM %s WHERE "), 
 960                     pDb
->SQLTableName(tableName
.c_str()).c_str()); 
 962     // Append the WHERE clause to the SQL DELETE statement 
 965         case DB_DEL_KEYFIELDS
: 
 966             // If the datasource supports the ROWID column, build 
 967             // the where on ROWID for efficiency purposes. 
 968             // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333' 
 972                 wxChar   rowid
[wxDB_ROWID_LEN
+1]; 
 974                 // Get the ROWID value.  If not successful retreiving the ROWID, 
 975                 // simply fall down through the code and build the WHERE clause 
 976                 // based on the key fields. 
 977                 if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
 979                     pSqlStmt 
+= wxT("ROWID = '"); 
 981                     pSqlStmt 
+= wxT("'"); 
 985             // Unable to delete by ROWID, so build a WHERE 
 986             // clause based on the keyfields. 
 987             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
 988             pSqlStmt 
+= whereClause
; 
 991             pSqlStmt 
+= pWhereClause
; 
 993         case DB_DEL_MATCHING
: 
 994             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
 995             pSqlStmt 
+= whereClause
; 
 999 }  // BuildDeleteStmt() 
1002 /***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/ 
1003 void wxDbTable::BuildDeleteStmt(wxChar 
*pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
1005     wxString tempSqlStmt
; 
1006     BuildDeleteStmt(tempSqlStmt
, typeOfDel
, pWhereClause
); 
1007     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1008 }  // wxDbTable::BuildDeleteStmt() 
1011 /********** wxDbTable::BuildSelectStmt() **********/ 
1012 void wxDbTable::BuildSelectStmt(wxString 
&pSqlStmt
, int typeOfSelect
, bool distinct
) 
1014     wxString whereClause
; 
1015     whereClause
.Empty(); 
1017     // Build a select statement to query the database 
1018     pSqlStmt 
= wxT("SELECT "); 
1020     // SELECT DISTINCT values only? 
1022         pSqlStmt 
+= wxT("DISTINCT "); 
1024     // Was a FROM clause specified to join tables to the base table? 
1025     // Available for ::Query() only!!! 
1026     bool appendFromClause 
= FALSE
; 
1027 #if wxODBC_BACKWARD_COMPATABILITY 
1028     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from 
&& wxStrlen(from
)) 
1029         appendFromClause 
= TRUE
; 
1031     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from
.Length()) 
1032         appendFromClause 
= TRUE
; 
1035     // Add the column list 
1037     for (i 
= 0; i 
< noCols
; i
++) 
1039         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1040         if (appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) 
1042             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
.c_str()); 
1043 //            pSqlStmt += queryTableName; 
1044             pSqlStmt 
+= wxT("."); 
1046         pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1047 //        pSqlStmt += colDefs[i].ColName; 
1049             pSqlStmt 
+= wxT(","); 
1052     // If the datasource supports ROWID, get this column as well.  Exception: Don't retrieve 
1053     // the ROWID if querying distinct records.  The rowid will always be unique. 
1054     if (!distinct 
&& CanUpdByROWID()) 
1056         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1057         if (appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) 
1059             pSqlStmt 
+= wxT(","); 
1060             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1061 //            pSqlStmt += queryTableName; 
1062             pSqlStmt 
+= wxT(".ROWID"); 
1065             pSqlStmt 
+= wxT(",ROWID"); 
1068     // Append the FROM tablename portion 
1069     pSqlStmt 
+= wxT(" FROM "); 
1070     pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1071 //    pSqlStmt += queryTableName; 
1073     // Sybase uses the HOLDLOCK keyword to lock a record during query. 
1074     // The HOLDLOCK keyword follows the table name in the from clause. 
1075     // Each table in the from clause must specify HOLDLOCK or 
1076     // NOHOLDLOCK (the default).  Note: The "FOR UPDATE" clause 
1077     // is parsed but ignored in SYBASE Transact-SQL. 
1078     if (selectForUpdate 
&& (pDb
->Dbms() == dbmsSYBASE_ASA 
|| pDb
->Dbms() == dbmsSYBASE_ASE
)) 
1079         pSqlStmt 
+= wxT(" HOLDLOCK"); 
1081     if (appendFromClause
) 
1084     // Append the WHERE clause.  Either append the where clause for the class 
1085     // or build a where clause.  The typeOfSelect determines this. 
1086     switch(typeOfSelect
) 
1088         case DB_SELECT_WHERE
: 
1089 #if wxODBC_BACKWARD_COMPATABILITY 
1090             if (where 
&& wxStrlen(where
))   // May not want a where clause!!! 
1092             if (where
.Length())   // May not want a where clause!!! 
1095                 pSqlStmt 
+= wxT(" WHERE "); 
1099         case DB_SELECT_KEYFIELDS
: 
1100             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1101             if (whereClause
.Length()) 
1103                 pSqlStmt 
+= wxT(" WHERE "); 
1104                 pSqlStmt 
+= whereClause
; 
1107         case DB_SELECT_MATCHING
: 
1108             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
1109             if (whereClause
.Length()) 
1111                 pSqlStmt 
+= wxT(" WHERE "); 
1112                 pSqlStmt 
+= whereClause
; 
1117     // Append the ORDER BY clause 
1118 #if wxODBC_BACKWARD_COMPATABILITY 
1119     if (orderBy 
&& wxStrlen(orderBy
)) 
1121     if (orderBy
.Length()) 
1124         pSqlStmt 
+= wxT(" ORDER BY "); 
1125         pSqlStmt 
+= orderBy
; 
1128     // SELECT FOR UPDATE if told to do so and the datasource is capable.  Sybase 
1129     // parses the FOR UPDATE clause but ignores it.  See the comment above on the 
1130     // HOLDLOCK for Sybase. 
1131     if (selectForUpdate 
&& CanSelectForUpdate()) 
1132         pSqlStmt 
+= wxT(" FOR UPDATE"); 
1134 }  // wxDbTable::BuildSelectStmt() 
1137 /***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/ 
1138 void wxDbTable::BuildSelectStmt(wxChar 
*pSqlStmt
, int typeOfSelect
, bool distinct
) 
1140     wxString tempSqlStmt
; 
1141     BuildSelectStmt(tempSqlStmt
, typeOfSelect
, distinct
); 
1142     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1143 }  // wxDbTable::BuildSelectStmt() 
1146 /********** wxDbTable::BuildUpdateStmt() **********/ 
1147 void wxDbTable::BuildUpdateStmt(wxString 
&pSqlStmt
, int typeOfUpd
, const wxString 
&pWhereClause
) 
1149     wxASSERT(!queryOnly
); 
1153     wxString whereClause
; 
1154     whereClause
.Empty(); 
1156     bool firstColumn 
= TRUE
; 
1158     pSqlStmt
.Printf(wxT("UPDATE %s SET "), 
1159                     pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1161     // Append a list of columns to be updated 
1163     for (i 
= 0; i 
< noCols
; i
++) 
1165         // Only append Updateable columns 
1166         if (colDefs
[i
].Updateable
) 
1169                 pSqlStmt 
+= wxT(","); 
1171                 firstColumn 
= FALSE
; 
1173             pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1174 //            pSqlStmt += colDefs[i].ColName; 
1175             pSqlStmt 
+= wxT(" = ?"); 
1179     // Append the WHERE clause to the SQL UPDATE statement 
1180     pSqlStmt 
+= wxT(" WHERE "); 
1183         case DB_UPD_KEYFIELDS
: 
1184             // If the datasource supports the ROWID column, build 
1185             // the where on ROWID for efficiency purposes. 
1186             // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333' 
1187             if (CanUpdByROWID()) 
1190                 wxChar rowid
[wxDB_ROWID_LEN
+1]; 
1192                 // Get the ROWID value.  If not successful retreiving the ROWID, 
1193                 // simply fall down through the code and build the WHERE clause 
1194                 // based on the key fields. 
1195                 if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
1197                     pSqlStmt 
+= wxT("ROWID = '"); 
1199                     pSqlStmt 
+= wxT("'"); 
1203             // Unable to delete by ROWID, so build a WHERE 
1204             // clause based on the keyfields. 
1205             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1206             pSqlStmt 
+= whereClause
; 
1209             pSqlStmt 
+= pWhereClause
; 
1212 }  // BuildUpdateStmt() 
1215 /***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/ 
1216 void wxDbTable::BuildUpdateStmt(wxChar 
*pSqlStmt
, int typeOfUpd
, const wxString 
&pWhereClause
) 
1218     wxString tempSqlStmt
; 
1219     BuildUpdateStmt(tempSqlStmt
, typeOfUpd
, pWhereClause
); 
1220     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1221 }  // BuildUpdateStmt() 
1224 /********** wxDbTable::BuildWhereClause() **********/ 
1225 void wxDbTable::BuildWhereClause(wxString 
&pWhereClause
, int typeOfWhere
, 
1226                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1228  * Note: BuildWhereClause() currently ignores timestamp columns. 
1229  *       They are not included as part of the where clause. 
1232     bool moreThanOneColumn 
= FALSE
; 
1235     // Loop through the columns building a where clause as you go 
1237     for (i 
= 0; i 
< noCols
; i
++) 
1239         // Determine if this column should be included in the WHERE clause 
1240         if ((typeOfWhere 
== DB_WHERE_KEYFIELDS 
&& colDefs
[i
].KeyField
) || 
1241              (typeOfWhere 
== DB_WHERE_MATCHING  
&& (!IsColNull(i
)))) 
1243             // Skip over timestamp columns 
1244             if (colDefs
[i
].SqlCtype 
== SQL_C_TIMESTAMP
) 
1246             // If there is more than 1 column, join them with the keyword "AND" 
1247             if (moreThanOneColumn
) 
1248                 pWhereClause 
+= wxT(" AND "); 
1250                 moreThanOneColumn 
= TRUE
; 
1251             // Concatenate where phrase for the column 
1252             if (qualTableName
.Length()) 
1254                 pWhereClause 
+= pDb
->SQLTableName(qualTableName
); 
1255 //                pWhereClause += qualTableName; 
1256                 pWhereClause 
+= wxT("."); 
1258             pWhereClause 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1259 //            pWhereClause += colDefs[i].ColName; 
1260             if (useLikeComparison 
&& (colDefs
[i
].SqlCtype 
== SQL_C_CHAR
)) 
1261                 pWhereClause 
+= wxT(" LIKE "); 
1263                 pWhereClause 
+= wxT(" = "); 
1264             switch(colDefs
[i
].SqlCtype
) 
1267                     colValue
.Printf(wxT("'%s'"), (UCHAR FAR 
*) colDefs
[i
].PtrDataObj
); 
1270                     colValue
.Printf(wxT("%hi"), *((SWORD 
*) colDefs
[i
].PtrDataObj
)); 
1273                     colValue
.Printf(wxT("%hu"), *((UWORD 
*) colDefs
[i
].PtrDataObj
)); 
1276                     colValue
.Printf(wxT("%li"), *((SDWORD 
*) colDefs
[i
].PtrDataObj
)); 
1279                     colValue
.Printf(wxT("%lu"), *((UDWORD 
*) colDefs
[i
].PtrDataObj
)); 
1282                     colValue
.Printf(wxT("%.6f"), *((SFLOAT 
*) colDefs
[i
].PtrDataObj
)); 
1285                     colValue
.Printf(wxT("%.6f"), *((SDOUBLE 
*) colDefs
[i
].PtrDataObj
)); 
1288             pWhereClause 
+= colValue
; 
1291 }  // wxDbTable::BuildWhereClause() 
1294 /***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/ 
1295 void wxDbTable::BuildWhereClause(wxChar 
*pWhereClause
, int typeOfWhere
, 
1296                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1298     wxString tempSqlStmt
; 
1299     BuildWhereClause(tempSqlStmt
, typeOfWhere
, qualTableName
, useLikeComparison
); 
1300     wxStrcpy(pWhereClause
, tempSqlStmt
); 
1301 }  // wxDbTable::BuildWhereClause() 
1304 /********** wxDbTable::GetRowNum() **********/ 
1305 UWORD 
wxDbTable::GetRowNum(void) 
1309     if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
) 
1311         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1315     // Completed successfully 
1316     return((UWORD
) rowNum
); 
1318 }  // wxDbTable::GetRowNum() 
1321 /********** wxDbTable::CloseCursor() **********/ 
1322 bool wxDbTable::CloseCursor(HSTMT cursor
) 
1324     if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
) 
1325         return(pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
1327     // Completed successfully 
1330 }  // wxDbTable::CloseCursor() 
1333 /********** wxDbTable::CreateTable() **********/ 
1334 bool wxDbTable::CreateTable(bool attemptDrop
) 
1342 #ifdef DBDEBUG_CONSOLE 
1343     cout 
<< wxT("Creating Table ") << tableName 
<< wxT("...") << endl
; 
1347     if (attemptDrop 
&& !DropTable()) 
1351 #ifdef DBDEBUG_CONSOLE 
1352     for (i 
= 0; i 
< noCols
; i
++) 
1354         // Exclude derived columns since they are NOT part of the base table 
1355         if (colDefs
[i
].DerivedCol
) 
1357         cout 
<< i 
+ 1 << wxT(": ") << colDefs
[i
].ColName 
<< wxT("; "); 
1358         switch(colDefs
[i
].DbDataType
) 
1360             case DB_DATA_TYPE_VARCHAR
: 
1361                 cout 
<< pDb
->GetTypeInfVarchar().TypeName 
<< wxT("(") << colDefs
[i
].SzDataObj 
<< wxT(")"); 
1363             case DB_DATA_TYPE_INTEGER
: 
1364                 cout 
<< pDb
->GetTypeInfInteger().TypeName
; 
1366             case DB_DATA_TYPE_FLOAT
: 
1367                 cout 
<< pDb
->GetTypeInfFloat().TypeName
; 
1369             case DB_DATA_TYPE_DATE
: 
1370                 cout 
<< pDb
->GetTypeInfDate().TypeName
; 
1372             case DB_DATA_TYPE_BLOB
: 
1373                 cout 
<< pDb
->GetTypeInfBlob().TypeName
; 
1380     // Build a CREATE TABLE string from the colDefs structure. 
1381     bool needComma 
= FALSE
; 
1383     sqlStmt
.Printf(wxT("CREATE TABLE %s ("), 
1384                    pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1386     for (i 
= 0; i 
< noCols
; i
++) 
1388         // Exclude derived columns since they are NOT part of the base table 
1389         if (colDefs
[i
].DerivedCol
) 
1393             sqlStmt 
+= wxT(","); 
1395         sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1396 //        sqlStmt += colDefs[i].ColName; 
1397         sqlStmt 
+= wxT(" "); 
1399         switch(colDefs
[i
].DbDataType
) 
1401             case DB_DATA_TYPE_VARCHAR
: 
1402                 sqlStmt 
+= pDb
->GetTypeInfVarchar().TypeName
; 
1404             case DB_DATA_TYPE_INTEGER
: 
1405                 sqlStmt 
+= pDb
->GetTypeInfInteger().TypeName
; 
1407             case DB_DATA_TYPE_FLOAT
: 
1408                 sqlStmt 
+= pDb
->GetTypeInfFloat().TypeName
; 
1410             case DB_DATA_TYPE_DATE
: 
1411                 sqlStmt 
+= pDb
->GetTypeInfDate().TypeName
; 
1413             case DB_DATA_TYPE_BLOB
: 
1414                 sqlStmt 
+= pDb
->GetTypeInfBlob().TypeName
; 
1417         // For varchars, append the size of the string 
1418         if (colDefs
[i
].DbDataType 
== DB_DATA_TYPE_VARCHAR 
&& 
1419             (pDb
->Dbms() != dbmsMY_SQL 
|| pDb
->GetTypeInfVarchar().TypeName 
!= "text"))// || 
1420 //            colDefs[i].DbDataType == DB_DATA_TYPE_BLOB) 
1423             s
.Printf(wxT("(%d)"), colDefs
[i
].SzDataObj
); 
1427         if (pDb
->Dbms() == dbmsDB2 
|| 
1428             pDb
->Dbms() == dbmsMY_SQL 
|| 
1429             pDb
->Dbms() == dbmsSYBASE_ASE  
|| 
1430             pDb
->Dbms() == dbmsINTERBASE  
|| 
1431             pDb
->Dbms() == dbmsMS_SQL_SERVER
) 
1433             if (colDefs
[i
].KeyField
) 
1435                 sqlStmt 
+= wxT(" NOT NULL"); 
1441     // If there is a primary key defined, include it in the create statement 
1442     for (i 
= j 
= 0; i 
< noCols
; i
++) 
1444         if (colDefs
[i
].KeyField
) 
1450     if (j 
&& (pDb
->Dbms() != dbmsDBASE
)  
1451                   && (pDb
->Dbms() != dbmsXBASE_SEQUITER
) 
1452            )  // Found a keyfield 
1454         switch (pDb
->Dbms()) 
1458             case dbmsSYBASE_ASA
: 
1459             case dbmsSYBASE_ASE
: 
1462                 // MySQL goes out on this one. We also declare the relevant key NON NULL above 
1463                 sqlStmt 
+= wxT(",PRIMARY KEY ("); 
1468                 sqlStmt 
+= wxT(",CONSTRAINT "); 
1469                 //  DB2 is limited to 18 characters for index names 
1470                 if (pDb
->Dbms() == dbmsDB2
) 
1472                     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.")); 
1473                     sqlStmt 
+= pDb
->SQLTableName(tableName
.substr(0, 13).c_str()); 
1474 //                    sqlStmt += tableName.substr(0, 13); 
1477                     sqlStmt 
+= pDb
->SQLTableName(tableName
.c_str()); 
1478 //                    sqlStmt += tableName; 
1480                 sqlStmt 
+= wxT("_PIDX PRIMARY KEY ("); 
1485         // List column name(s) of column(s) comprising the primary key 
1486         for (i 
= j 
= 0; i 
< noCols
; i
++) 
1488             if (colDefs
[i
].KeyField
) 
1490                 if (j
++) // Multi part key, comma separate names 
1491                     sqlStmt 
+= wxT(","); 
1492                 sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1494                 if (pDb
->Dbms() == dbmsMY_SQL 
&& 
1495                     colDefs
[i
].DbDataType 
==  DB_DATA_TYPE_VARCHAR
) 
1498                     s
.Printf(wxT("(%d)"), colDefs
[i
].SzDataObj
); 
1503         sqlStmt 
+= wxT(")"); 
1505         if (pDb
->Dbms() == dbmsINFORMIX 
|| 
1506             pDb
->Dbms() == dbmsSYBASE_ASA 
|| 
1507             pDb
->Dbms() == dbmsSYBASE_ASE
) 
1509             sqlStmt 
+= wxT(" CONSTRAINT "); 
1510             sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1511 //            sqlStmt += tableName; 
1512             sqlStmt 
+= wxT("_PIDX"); 
1515     // Append the closing parentheses for the create table statement 
1516     sqlStmt 
+= wxT(")"); 
1518     pDb
->WriteSqlLog(sqlStmt
); 
1520 #ifdef DBDEBUG_CONSOLE 
1521     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1524     // Execute the CREATE TABLE statement 
1525     RETCODE retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1526     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
1528         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1529         pDb
->RollbackTrans(); 
1534     // Commit the transaction and close the cursor 
1535     if (!pDb
->CommitTrans()) 
1537     if (!CloseCursor(hstmt
)) 
1540     // Database table created successfully 
1543 } // wxDbTable::CreateTable() 
1546 /********** wxDbTable::DropTable() **********/ 
1547 bool wxDbTable::DropTable() 
1549     // NOTE: This function returns TRUE if the Table does not exist, but 
1550     //       only for identified databases.  Code will need to be added 
1551     //       below for any other databases when those databases are defined 
1552     //       to handle this situation consistently 
1556     sqlStmt
.Printf(wxT("DROP TABLE %s"), 
1557                    pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1559     pDb
->WriteSqlLog(sqlStmt
); 
1561 #ifdef DBDEBUG_CONSOLE 
1562     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1565     RETCODE retcode 
= SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1566     if (retcode 
!= SQL_SUCCESS
) 
1568         // Check for "Base table not found" error and ignore 
1569         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1570         if (wxStrcmp(pDb
->sqlState
, wxT("S0002")) /*&& 
1571             wxStrcmp(pDb->sqlState, wxT("S1000"))*/)  // "Base table not found" 
1573             // Check for product specific error codes 
1574             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000")))   ||  // 5.x (and lower?) 
1575                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000")))   || 
1576                   (pDb
->Dbms() == dbmsPERVASIVE_SQL 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000")))   ||  // Returns an S1000 then an S0002 
1577                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))))) 
1579                 pDb
->DispNextError(); 
1580                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1581                 pDb
->RollbackTrans(); 
1582 //                CloseCursor(hstmt); 
1588     // Commit the transaction and close the cursor 
1589     if (! pDb
->CommitTrans()) 
1591     if (! CloseCursor(hstmt
)) 
1595 }  // wxDbTable::DropTable() 
1598 /********** wxDbTable::CreateIndex() **********/ 
1599 bool wxDbTable::CreateIndex(const wxString 
&idxName
, bool unique
, UWORD noIdxCols
, 
1600                                      wxDbIdxDef 
*pIdxDefs
, bool attemptDrop
) 
1604     // Drop the index first 
1605     if (attemptDrop 
&& !DropIndex(idxName
)) 
1608     // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions 
1609     // of an index have the columns defined as "NOT NULL".  During initial table creation though, 
1610     // it may not be known which columns are necessarily going to be part of an index (e.g. the 
1611     // table was created, then months later you determine that an additional index while 
1612     // give better performance, so you want to add an index). 
1614     // The following block of code will modify the column definition to make the column be 
1615     // defined with the "NOT NULL" qualifier. 
1616     if (pDb
->Dbms() == dbmsMY_SQL
) 
1621         for (i 
= 0; i 
< noIdxCols 
&& ok
; i
++) 
1625             // Find the column definition that has the ColName that matches the 
1626             // index column name.  We need to do this to get the DB_DATA_TYPE of 
1627             // the index column, as MySQL's syntax for the ALTER column requires 
1629             while (!found 
&& (j 
< this->noCols
)) 
1631                 if (wxStrcmp(colDefs
[j
].ColName
,pIdxDefs
[i
].ColName
) == 0) 
1639                 ok 
= pDb
->ModifyColumn(tableName
, pIdxDefs
[i
].ColName
, 
1640                                         colDefs
[j
].DbDataType
, colDefs
[j
].SzDataObj
, 
1645                     wxODBC_ERRORS retcode
; 
1646                     // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already 
1647                     // defined to be NOT NULL, but reportedly MySQL doesn't mind. 
1648                     // This line is just here for debug checking of the value 
1649                     retcode 
= (wxODBC_ERRORS
)pDb
->DB_STATUS
; 
1659             pDb
->RollbackTrans(); 
1664     // Build a CREATE INDEX statement 
1665     sqlStmt 
= wxT("CREATE "); 
1667         sqlStmt 
+= wxT("UNIQUE "); 
1669     sqlStmt 
+= wxT("INDEX "); 
1670     sqlStmt 
+= pDb
->SQLTableName(idxName
); 
1671     sqlStmt 
+= wxT(" ON "); 
1673     sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1674 //    sqlStmt += tableName; 
1675     sqlStmt 
+= wxT(" ("); 
1677     // Append list of columns making up index 
1679     for (i 
= 0; i 
< noIdxCols
; i
++) 
1681         sqlStmt 
+= pDb
->SQLColumnName(pIdxDefs
[i
].ColName
); 
1682 //        sqlStmt += pIdxDefs[i].ColName; 
1684         // MySQL requires a key length on VARCHAR keys 
1685         if ( pDb
->Dbms() == dbmsMY_SQL 
) 
1687             // Find the details on this column 
1689             for ( j 
= 0; j 
< noCols
; ++j 
) 
1691                 if ( wxStrcmp( pIdxDefs
[i
].ColName
, colDefs
[j
].ColName 
) == 0 ) 
1696             if ( colDefs
[j
].DbDataType 
==  DB_DATA_TYPE_VARCHAR
) 
1699                 s
.Printf(wxT("(%d)"), colDefs
[i
].SzDataObj
); 
1704         // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns 
1705         if (!((pDb
->Dbms() == dbmsMS_SQL_SERVER
) && (strncmp(pDb
->dbInf
.dbmsVer
,"07",2)==0)) && 
1706             !(pDb
->Dbms() == dbmsPOSTGRES
)) 
1708             if (pIdxDefs
[i
].Ascending
) 
1709                 sqlStmt 
+= wxT(" ASC"); 
1711                 sqlStmt 
+= wxT(" DESC"); 
1714             wxASSERT_MSG(pIdxDefs
[i
].Ascending
, "Datasource does not support DESCending index columns"); 
1716         if ((i 
+ 1) < noIdxCols
) 
1717             sqlStmt 
+= wxT(","); 
1720     // Append closing parentheses 
1721     sqlStmt 
+= wxT(")"); 
1723     pDb
->WriteSqlLog(sqlStmt
); 
1725 #ifdef DBDEBUG_CONSOLE 
1726     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1729     // Execute the CREATE INDEX statement 
1730     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
1732         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1733         pDb
->RollbackTrans(); 
1738     // Commit the transaction and close the cursor 
1739     if (! pDb
->CommitTrans()) 
1741     if (! CloseCursor(hstmt
)) 
1744     // Index Created Successfully 
1747 }  // wxDbTable::CreateIndex() 
1750 /********** wxDbTable::DropIndex() **********/ 
1751 bool wxDbTable::DropIndex(const wxString 
&idxName
) 
1753     // NOTE: This function returns TRUE if the Index does not exist, but 
1754     //       only for identified databases.  Code will need to be added 
1755     //       below for any other databases when those databases are defined 
1756     //       to handle this situation consistently 
1760     if (pDb
->Dbms() == dbmsACCESS 
|| pDb
->Dbms() == dbmsMY_SQL 
|| 
1761         pDb
->Dbms() == dbmsDBASE 
/*|| Paradox needs this syntax too when we add support*/) 
1762         sqlStmt
.Printf(wxT("DROP INDEX %s ON %s"), 
1763                        pDb
->SQLTableName(idxName
.c_str()).c_str(), 
1764                        pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1765     else if ((pDb
->Dbms() == dbmsMS_SQL_SERVER
) || 
1766              (pDb
->Dbms() == dbmsSYBASE_ASE
) || 
1767                          (pDb
->Dbms() == dbmsXBASE_SEQUITER
)) 
1768         sqlStmt
.Printf(wxT("DROP INDEX %s.%s"), 
1769                        pDb
->SQLTableName(tableName
.c_str()).c_str(), 
1770                        pDb
->SQLTableName(idxName
.c_str()).c_str()); 
1772         sqlStmt
.Printf(wxT("DROP INDEX %s"), 
1773                        pDb
->SQLTableName(idxName
.c_str()).c_str()); 
1775     pDb
->WriteSqlLog(sqlStmt
); 
1777 #ifdef DBDEBUG_CONSOLE 
1778     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1781     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
1783         // Check for "Index not found" error and ignore 
1784         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1785         if (wxStrcmp(pDb
->sqlState
,wxT("S0012")))  // "Index not found" 
1787             // Check for product specific error codes 
1788             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000"))) ||  // v5.x (and lower?) 
1789                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000"))) || 
1790                   (pDb
->Dbms() == dbmsMS_SQL_SERVER 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1791                   (pDb
->Dbms() == dbmsINTERBASE      
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1792                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("S0002"))) ||  // Base table not found 
1793                   (pDb
->Dbms() == dbmsMY_SQL        
&& !wxStrcmp(pDb
->sqlState
,wxT("42S12"))) ||  // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta 
1794                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))) 
1797                 pDb
->DispNextError(); 
1798                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1799                 pDb
->RollbackTrans(); 
1806     // Commit the transaction and close the cursor 
1807     if (! pDb
->CommitTrans()) 
1809     if (! CloseCursor(hstmt
)) 
1813 }  // wxDbTable::DropIndex() 
1816 /********** wxDbTable::SetOrderByColNums() **********/ 
1817 bool wxDbTable::SetOrderByColNums(UWORD first
, ... ) 
1819     int        colNo 
= first
;  // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS 
1825     va_start(argptr
, first
);     /* Initialize variable arguments. */ 
1826     while (!abort 
&& (colNo 
!= wxDB_NO_MORE_COLUMN_NUMBERS
)) 
1828         // Make sure the passed in column number 
1829         // is within the valid range of columns 
1831         // Valid columns are 0 thru noCols-1 
1832         if (colNo 
>= noCols 
|| colNo 
< 0) 
1839             tempStr 
+= wxT(","); 
1841         tempStr 
+= colDefs
[colNo
].ColName
; 
1842         colNo 
= va_arg (argptr
, int); 
1844     va_end (argptr
);              /* Reset variable arguments.      */ 
1846     SetOrderByClause(tempStr
); 
1849 }  // wxDbTable::SetOrderByColNums() 
1852 /********** wxDbTable::Insert() **********/ 
1853 int wxDbTable::Insert(void) 
1855     wxASSERT(!queryOnly
); 
1856     if (queryOnly 
|| !insertable
) 
1861     // Insert the record by executing the already prepared insert statement 
1863     retcode
=SQLExecute(hstmtInsert
); 
1864     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO 
&& 
1865         retcode 
!= SQL_NEED_DATA
) 
1867         // Check to see if integrity constraint was violated 
1868         pDb
->GetNextError(henv
, hdbc
, hstmtInsert
); 
1869         if (! wxStrcmp(pDb
->sqlState
, wxT("23000")))  // Integrity constraint violated 
1870             return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
); 
1873             pDb
->DispNextError(); 
1874             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1878     if (retcode 
== SQL_NEED_DATA
) 
1881         while ((retcode 
= SQLParamData(hstmtInsert
, &pParmID
) == SQL_NEED_DATA
)) 
1883             // Find the parameter 
1885             for (i
=0; i 
< noCols
; i
++) 
1887                 if (colDefs
[i
].PtrDataObj 
== pParmID
) 
1889                     // We found it.  Store the parameter. 
1890                     retcode 
= SQLPutData(hstmtInsert
, pParmID
, colDefs
[i
].SzDataObj
); 
1891                     if (retcode 
!= SQL_SUCCESS
) 
1893                         pDb
->DispNextError(); 
1894                         pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1903     // Record inserted into the datasource successfully 
1906 }  // wxDbTable::Insert() 
1909 /********** wxDbTable::Update() **********/ 
1910 bool wxDbTable::Update(void) 
1912     wxASSERT(!queryOnly
); 
1918     // Build the SQL UPDATE statement 
1919     BuildUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
); 
1921     pDb
->WriteSqlLog(sqlStmt
); 
1923 #ifdef DBDEBUG_CONSOLE 
1924     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1927     // Execute the SQL UPDATE statement 
1928     return(execUpdate(sqlStmt
)); 
1930 }  // wxDbTable::Update() 
1933 /********** wxDbTable::Update(pSqlStmt) **********/ 
1934 bool wxDbTable::Update(const wxString 
&pSqlStmt
) 
1936     wxASSERT(!queryOnly
); 
1940     pDb
->WriteSqlLog(pSqlStmt
); 
1942     return(execUpdate(pSqlStmt
)); 
1944 }  // wxDbTable::Update(pSqlStmt) 
1947 /********** wxDbTable::UpdateWhere() **********/ 
1948 bool wxDbTable::UpdateWhere(const wxString 
&pWhereClause
) 
1950     wxASSERT(!queryOnly
); 
1956     // Build the SQL UPDATE statement 
1957     BuildUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
); 
1959     pDb
->WriteSqlLog(sqlStmt
); 
1961 #ifdef DBDEBUG_CONSOLE 
1962     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1965     // Execute the SQL UPDATE statement 
1966     return(execUpdate(sqlStmt
)); 
1968 }  // wxDbTable::UpdateWhere() 
1971 /********** wxDbTable::Delete() **********/ 
1972 bool wxDbTable::Delete(void) 
1974     wxASSERT(!queryOnly
); 
1981     // Build the SQL DELETE statement 
1982     BuildDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
); 
1984     pDb
->WriteSqlLog(sqlStmt
); 
1986     // Execute the SQL DELETE statement 
1987     return(execDelete(sqlStmt
)); 
1989 }  // wxDbTable::Delete() 
1992 /********** wxDbTable::DeleteWhere() **********/ 
1993 bool wxDbTable::DeleteWhere(const wxString 
&pWhereClause
) 
1995     wxASSERT(!queryOnly
); 
2002     // Build the SQL DELETE statement 
2003     BuildDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
); 
2005     pDb
->WriteSqlLog(sqlStmt
); 
2007     // Execute the SQL DELETE statement 
2008     return(execDelete(sqlStmt
)); 
2010 }  // wxDbTable::DeleteWhere() 
2013 /********** wxDbTable::DeleteMatching() **********/ 
2014 bool wxDbTable::DeleteMatching(void) 
2016     wxASSERT(!queryOnly
); 
2023     // Build the SQL DELETE statement 
2024     BuildDeleteStmt(sqlStmt
, DB_DEL_MATCHING
); 
2026     pDb
->WriteSqlLog(sqlStmt
); 
2028     // Execute the SQL DELETE statement 
2029     return(execDelete(sqlStmt
)); 
2031 }  // wxDbTable::DeleteMatching() 
2034 /********** wxDbTable::IsColNull() **********/ 
2035 bool wxDbTable::IsColNull(UWORD colNo
) const 
2038     This logic is just not right.  It would indicate TRUE 
2039     if a numeric field were set to a value of 0. 
2041     switch(colDefs[colNo].SqlCtype) 
2044             return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0); 
2046             return((  *((SWORD *) colDefs[colNo].PtrDataObj))   == 0); 
2048             return((   *((UWORD*) colDefs[colNo].PtrDataObj))   == 0); 
2050             return(( *((SDWORD *) colDefs[colNo].PtrDataObj))   == 0); 
2052             return(( *((UDWORD *) colDefs[colNo].PtrDataObj))   == 0); 
2054             return(( *((SFLOAT *) colDefs[colNo].PtrDataObj))   == 0); 
2056             return((*((SDOUBLE *) colDefs[colNo].PtrDataObj))   == 0); 
2057         case SQL_C_TIMESTAMP: 
2058             TIMESTAMP_STRUCT *pDt; 
2059             pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj; 
2060             if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0) 
2068     return (colDefs
[colNo
].Null
); 
2069 }  // wxDbTable::IsColNull() 
2072 /********** wxDbTable::CanSelectForUpdate() **********/ 
2073 bool wxDbTable::CanSelectForUpdate(void) 
2078     if (pDb
->Dbms() == dbmsMY_SQL
) 
2081     if ((pDb
->Dbms() == dbmsORACLE
) || 
2082         (pDb
->dbInf
.posStmts 
& SQL_PS_SELECT_FOR_UPDATE
)) 
2087 }  // wxDbTable::CanSelectForUpdate() 
2090 /********** wxDbTable::CanUpdByROWID() **********/ 
2091 bool wxDbTable::CanUpdByROWID(void) 
2094  * NOTE: Returning FALSE for now until this can be debugged, 
2095  *        as the ROWID is not getting updated correctly 
2099     if (pDb->Dbms() == dbmsORACLE) 
2104 }  // wxDbTable::CanUpdByROWID() 
2107 /********** wxDbTable::IsCursorClosedOnCommit() **********/ 
2108 bool wxDbTable::IsCursorClosedOnCommit(void) 
2110     if (pDb
->dbInf
.cursorCommitBehavior 
== SQL_CB_PRESERVE
) 
2115 }  // wxDbTable::IsCursorClosedOnCommit() 
2119 /********** wxDbTable::ClearMemberVar() **********/ 
2120 void wxDbTable::ClearMemberVar(UWORD colNo
, bool setToNull
) 
2122     wxASSERT(colNo 
< noCols
); 
2124     switch(colDefs
[colNo
].SqlCtype
) 
2127             ((UCHAR FAR 
*) colDefs
[colNo
].PtrDataObj
)[0]    = 0; 
2130             *((SWORD 
*) colDefs
[colNo
].PtrDataObj
)          = 0; 
2133             *((UWORD
*) colDefs
[colNo
].PtrDataObj
)           = 0; 
2136             *((SDWORD 
*) colDefs
[colNo
].PtrDataObj
)         = 0; 
2139             *((UDWORD 
*) colDefs
[colNo
].PtrDataObj
)         = 0; 
2142             *((SFLOAT 
*) colDefs
[colNo
].PtrDataObj
)         = 0.0f
; 
2145             *((SDOUBLE 
*) colDefs
[colNo
].PtrDataObj
)        = 0.0f
; 
2147         case SQL_C_TIMESTAMP
: 
2148             TIMESTAMP_STRUCT 
*pDt
; 
2149             pDt 
= (TIMESTAMP_STRUCT 
*) colDefs
[colNo
].PtrDataObj
; 
2162 }  // wxDbTable::ClearMemberVar() 
2165 /********** wxDbTable::ClearMemberVars() **********/ 
2166 void wxDbTable::ClearMemberVars(bool setToNull
) 
2170     // Loop through the columns setting each member variable to zero 
2171     for (i
=0; i 
< noCols
; i
++) 
2172         ClearMemberVar(i
,setToNull
); 
2174 }  // wxDbTable::ClearMemberVars() 
2177 /********** wxDbTable::SetQueryTimeout() **********/ 
2178 bool wxDbTable::SetQueryTimeout(UDWORD nSeconds
) 
2180     if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2181         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
2182     if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2183         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
2184     if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2185         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
2186     if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2187         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
)); 
2189     // Completed Successfully 
2192 }  // wxDbTable::SetQueryTimeout() 
2195 /********** wxDbTable::SetColDefs() **********/ 
2196 void wxDbTable::SetColDefs(UWORD index
, const wxString 
&fieldName
, int dataType
, void *pData
, 
2197                            SWORD cType
, int size
, bool keyField
, bool upd
, 
2198                            bool insAllow
, bool derivedCol
) 
2200     wxASSERT_MSG( index 
< noCols
, 
2201                   _T("Specified column index exceeds the maximum number of columns for this table.") ); 
2203     if (!colDefs
)  // May happen if the database connection fails 
2206     if (fieldName
.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN
) 
2208         wxStrncpy(colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
); 
2209         colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0; 
2213         tmpMsg
.Printf(_T("Column name '%s' is too long. Truncated to '%s'."), 
2214                       fieldName
.c_str(),colDefs
[index
].ColName
); 
2216 #endif // __WXDEBUG__ 
2219         wxStrcpy(colDefs
[index
].ColName
, fieldName
); 
2221     colDefs
[index
].DbDataType       
= dataType
; 
2222     colDefs
[index
].PtrDataObj       
= pData
; 
2223     colDefs
[index
].SqlCtype         
= cType
; 
2224     colDefs
[index
].SzDataObj        
= size
; 
2225     colDefs
[index
].KeyField         
= keyField
; 
2226     colDefs
[index
].DerivedCol       
= derivedCol
; 
2227     // Derived columns by definition would NOT be "Insertable" or "Updateable" 
2230         colDefs
[index
].Updateable       
= FALSE
; 
2231         colDefs
[index
].InsertAllowed    
= FALSE
; 
2235         colDefs
[index
].Updateable       
= upd
; 
2236         colDefs
[index
].InsertAllowed    
= insAllow
; 
2239     colDefs
[index
].Null                 
= FALSE
; 
2241 }  // wxDbTable::SetColDefs() 
2244 /********** wxDbTable::SetColDefs() **********/ 
2245 wxDbColDataPtr
* wxDbTable::SetColDefs(wxDbColInf 
*pColInfs
, UWORD numCols
) 
2248     wxDbColDataPtr 
*pColDataPtrs 
= NULL
; 
2254         pColDataPtrs 
= new wxDbColDataPtr
[numCols
+1]; 
2256         for (index 
= 0; index 
< numCols
; index
++) 
2258             // Process the fields 
2259             switch (pColInfs
[index
].dbDataType
) 
2261                 case DB_DATA_TYPE_VARCHAR
: 
2262                    pColDataPtrs
[index
].PtrDataObj 
= new wxChar
[pColInfs
[index
].bufferLength
+1]; 
2263                    pColDataPtrs
[index
].SzDataObj  
= pColInfs
[index
].columnSize
; 
2264                    pColDataPtrs
[index
].SqlCtype   
= SQL_C_CHAR
; 
2266                 case DB_DATA_TYPE_INTEGER
: 
2267                     // Can be long or short 
2268                     if (pColInfs
[index
].bufferLength 
== sizeof(long)) 
2270                       pColDataPtrs
[index
].PtrDataObj 
= new long; 
2271                       pColDataPtrs
[index
].SzDataObj  
= sizeof(long); 
2272                       pColDataPtrs
[index
].SqlCtype   
= SQL_C_SLONG
; 
2276                         pColDataPtrs
[index
].PtrDataObj 
= new short; 
2277                         pColDataPtrs
[index
].SzDataObj  
= sizeof(short); 
2278                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_SSHORT
; 
2281                 case DB_DATA_TYPE_FLOAT
: 
2282                     // Can be float or double 
2283                     if (pColInfs
[index
].bufferLength 
== sizeof(float)) 
2285                         pColDataPtrs
[index
].PtrDataObj 
= new float; 
2286                         pColDataPtrs
[index
].SzDataObj  
= sizeof(float); 
2287                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_FLOAT
; 
2291                         pColDataPtrs
[index
].PtrDataObj 
= new double; 
2292                         pColDataPtrs
[index
].SzDataObj  
= sizeof(double); 
2293                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_DOUBLE
; 
2296                 case DB_DATA_TYPE_DATE
: 
2297                     pColDataPtrs
[index
].PtrDataObj 
= new TIMESTAMP_STRUCT
; 
2298                     pColDataPtrs
[index
].SzDataObj  
= sizeof(TIMESTAMP_STRUCT
); 
2299                     pColDataPtrs
[index
].SqlCtype   
= SQL_C_TIMESTAMP
; 
2301                 case DB_DATA_TYPE_BLOB
: 
2302                     wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns")); 
2303                     pColDataPtrs
[index
].PtrDataObj 
= /*BLOB ADDITION NEEDED*/NULL
; 
2304                     pColDataPtrs
[index
].SzDataObj  
= /*BLOB ADDITION NEEDED*/sizeof(void *); 
2305                     pColDataPtrs
[index
].SqlCtype   
= SQL_VARBINARY
; 
2308             if (pColDataPtrs
[index
].PtrDataObj 
!= NULL
) 
2309                 SetColDefs (index
,pColInfs
[index
].colName
,pColInfs
[index
].dbDataType
, pColDataPtrs
[index
].PtrDataObj
, pColDataPtrs
[index
].SqlCtype
, pColDataPtrs
[index
].SzDataObj
); 
2312                 // Unable to build all the column definitions, as either one of 
2313                 // the calls to "new" failed above, or there was a BLOB field 
2314                 // to have a column definition for.  If BLOBs are to be used, 
2315                 // the other form of ::SetColDefs() must be used, as it is impossible 
2316                 // to know the maximum size to create the PtrDataObj to be. 
2317                 delete [] pColDataPtrs
; 
2323     return (pColDataPtrs
); 
2325 } // wxDbTable::SetColDefs() 
2328 /********** wxDbTable::SetCursor() **********/ 
2329 void wxDbTable::SetCursor(HSTMT 
*hstmtActivate
) 
2331     if (hstmtActivate 
== wxDB_DEFAULT_CURSOR
) 
2332         hstmt 
= *hstmtDefault
; 
2334         hstmt 
= *hstmtActivate
; 
2336 }  // wxDbTable::SetCursor() 
2339 /********** wxDbTable::Count(const wxString &) **********/ 
2340 ULONG 
wxDbTable::Count(const wxString 
&args
) 
2346     // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement 
2347     sqlStmt  
= wxT("SELECT COUNT("); 
2349     sqlStmt 
+= wxT(") FROM "); 
2350     sqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
2351 //    sqlStmt += queryTableName; 
2352 #if wxODBC_BACKWARD_COMPATABILITY 
2353     if (from 
&& wxStrlen(from
)) 
2359     // Add the where clause if one is provided 
2360 #if wxODBC_BACKWARD_COMPATABILITY 
2361     if (where 
&& wxStrlen(where
)) 
2366         sqlStmt 
+= wxT(" WHERE "); 
2370     pDb
->WriteSqlLog(sqlStmt
); 
2372     // Initialize the Count cursor if it's not already initialized 
2375         hstmtCount 
= GetNewCursor(FALSE
,FALSE
); 
2376         wxASSERT(hstmtCount
); 
2381     // Execute the SQL statement 
2382     if (SQLExecDirect(*hstmtCount
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
2384         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2389     if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
) 
2391         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2395     // Obtain the result 
2396     if (SQLGetData(*hstmtCount
, (UWORD
)1, SQL_C_ULONG
, &count
, sizeof(count
), &cb
) != SQL_SUCCESS
) 
2398         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2403     if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
) 
2404         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2406     // Return the record count 
2409 }  // wxDbTable::Count() 
2412 /********** wxDbTable::Refresh() **********/ 
2413 bool wxDbTable::Refresh(void) 
2417     // Switch to the internal cursor so any active cursors are not corrupted 
2418     HSTMT currCursor 
= GetCursor(); 
2419     hstmt 
= hstmtInternal
; 
2420 #if wxODBC_BACKWARD_COMPATABILITY 
2421     // Save the where and order by clauses 
2422     char *saveWhere 
= where
; 
2423     char *saveOrderBy 
= orderBy
; 
2425     wxString saveWhere 
= where
; 
2426     wxString saveOrderBy 
= orderBy
; 
2428     // Build a where clause to refetch the record with.  Try and use the 
2429     // ROWID if it's available, ow use the key fields. 
2430     wxString whereClause
; 
2431     whereClause
.Empty(); 
2433     if (CanUpdByROWID()) 
2436         wxChar   rowid
[wxDB_ROWID_LEN
+1]; 
2438         // Get the ROWID value.  If not successful retreiving the ROWID, 
2439         // simply fall down through the code and build the WHERE clause 
2440         // based on the key fields. 
2441         if (SQLGetData(hstmt
, (UWORD
)(noCols
+1), SQL_C_CHAR
, (UCHAR
*) rowid
, wxDB_ROWID_LEN
, &cb
) == SQL_SUCCESS
) 
2443             whereClause 
+= pDb
->SQLTableName(queryTableName
); 
2444 //            whereClause += queryTableName; 
2445             whereClause 
+= wxT(".ROWID = '"); 
2446             whereClause 
+= rowid
; 
2447             whereClause 
+= wxT("'"); 
2451     // If unable to use the ROWID, build a where clause from the keyfields 
2452     if (wxStrlen(whereClause
) == 0) 
2453         BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
); 
2455     // Requery the record 
2456     where 
= whereClause
; 
2461     if (result 
&& !GetNext()) 
2464     // Switch back to original cursor 
2465     SetCursor(&currCursor
); 
2467     // Free the internal cursor 
2468     if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
) 
2469         pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
2471     // Restore the original where and order by clauses 
2473     orderBy 
= saveOrderBy
; 
2477 }  // wxDbTable::Refresh() 
2480 /********** wxDbTable::SetColNull() **********/ 
2481 bool wxDbTable::SetColNull(UWORD colNo
, bool set
) 
2485         colDefs
[colNo
].Null 
= set
; 
2486         if (set
)  // Blank out the values in the member variable 
2487             ClearMemberVar(colNo
,FALSE
);  // Must call with FALSE, or infinite recursion will happen 
2493 }  // wxDbTable::SetColNull() 
2496 /********** wxDbTable::SetColNull() **********/ 
2497 bool wxDbTable::SetColNull(const wxString 
&colName
, bool set
) 
2500     for (i 
= 0; i 
< noCols
; i
++) 
2502         if (!wxStricmp(colName
, colDefs
[i
].ColName
)) 
2508         colDefs
[i
].Null 
= set
; 
2509         if (set
)  // Blank out the values in the member variable 
2510             ClearMemberVar(i
,FALSE
);  // Must call with FALSE, or infinite recursion will happen 
2516 }  // wxDbTable::SetColNull() 
2519 /********** wxDbTable::GetNewCursor() **********/ 
2520 HSTMT 
*wxDbTable::GetNewCursor(bool setCursor
, bool bindColumns
) 
2522     HSTMT 
*newHSTMT 
= new HSTMT
; 
2527     if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
) 
2529         pDb
->DispAllErrors(henv
, hdbc
); 
2534     if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
2536         pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
); 
2543         if (!bindCols(*newHSTMT
)) 
2551         SetCursor(newHSTMT
); 
2555 }   // wxDbTable::GetNewCursor() 
2558 /********** wxDbTable::DeleteCursor() **********/ 
2559 bool wxDbTable::DeleteCursor(HSTMT 
*hstmtDel
) 
2563     if (!hstmtDel
)  // Cursor already deleted 
2567 ODBC 3.0 says to use this form 
2568     if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
2571     if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
) 
2573         pDb
->DispAllErrors(henv
, hdbc
); 
2581 }  // wxDbTable::DeleteCursor() 
2583 ////////////////////////////////////////////////////////////// 
2584 // wxDbGrid support functions 
2585 ////////////////////////////////////////////////////////////// 
2587 void wxDbTable::SetRowMode(const rowmode_t rowmode
) 
2589     if (!m_hstmtGridQuery
) 
2591         m_hstmtGridQuery 
= GetNewCursor(FALSE
,FALSE
); 
2592         if (!bindCols(*m_hstmtGridQuery
)) 
2596     m_rowmode 
= rowmode
; 
2599         case WX_ROW_MODE_QUERY
: 
2600             SetCursor(m_hstmtGridQuery
); 
2602         case WX_ROW_MODE_INDIVIDUAL
: 
2603             SetCursor(hstmtDefault
); 
2608 }  // wxDbTable::SetRowMode() 
2611 wxVariant 
wxDbTable::GetCol(const int colNo
) const 
2614     if ((colNo 
< noCols
) && (!IsColNull(colNo
))) 
2616         switch (colDefs
[colNo
].SqlCtype
) 
2620                 val 
= (wxChar 
*)(colDefs
[colNo
].PtrDataObj
); 
2624                 val 
= *(long *)(colDefs
[colNo
].PtrDataObj
); 
2628                 val 
= (long int )(*(short *)(colDefs
[colNo
].PtrDataObj
)); 
2631                 val 
= (long)(*(unsigned long *)(colDefs
[colNo
].PtrDataObj
)); 
2634                 val 
= (long)(*(char *)(colDefs
[colNo
].PtrDataObj
)); 
2636             case SQL_C_UTINYINT
: 
2637                 val 
= (long)(*(unsigned char *)(colDefs
[colNo
].PtrDataObj
)); 
2640                 val 
= (long)(*(UWORD 
*)(colDefs
[colNo
].PtrDataObj
)); 
2643                 val 
= (DATE_STRUCT 
*)(colDefs
[colNo
].PtrDataObj
); 
2646                 val 
= (TIME_STRUCT 
*)(colDefs
[colNo
].PtrDataObj
); 
2648             case SQL_C_TIMESTAMP
: 
2649                 val 
= (TIMESTAMP_STRUCT 
*)(colDefs
[colNo
].PtrDataObj
); 
2652                 val 
= *(double *)(colDefs
[colNo
].PtrDataObj
); 
2659 }  // wxDbTable::GetCol() 
2662 void csstrncpyt(char *s
, const char *t
, int n
) 
2664     while ( (*s
++ = *t
++) != '\0' && --n 
) 
2670 void wxDbTable::SetCol(const int colNo
, const wxVariant val
) 
2672     //FIXME: Add proper wxDateTime support to wxVariant.. 
2675     SetColNull(colNo
, val
.IsNull()); 
2679         if ((colDefs
[colNo
].SqlCtype 
== SQL_C_DATE
) 
2680             || (colDefs
[colNo
].SqlCtype 
== SQL_C_TIME
) 
2681             || (colDefs
[colNo
].SqlCtype 
== SQL_C_TIMESTAMP
)) 
2683             //Returns null if invalid! 
2684             if (!dateval
.ParseDate(val
.GetString())) 
2685                 SetColNull(colNo
, TRUE
); 
2688         switch (colDefs
[colNo
].SqlCtype
) 
2692                 csstrncpyt((char *)(colDefs
[colNo
].PtrDataObj
), 
2693                            val
.GetString().c_str(), 
2694                            colDefs
[colNo
].SzDataObj
-1); 
2698                 *(long *)(colDefs
[colNo
].PtrDataObj
) = val
; 
2702                 *(short *)(colDefs
[colNo
].PtrDataObj
) = val
.GetLong(); 
2705                 *(unsigned long *)(colDefs
[colNo
].PtrDataObj
) = val
.GetLong(); 
2708                 *(char *)(colDefs
[colNo
].PtrDataObj
) = val
.GetChar(); 
2710             case SQL_C_UTINYINT
: 
2711                 *(unsigned char *)(colDefs
[colNo
].PtrDataObj
) = val
.GetChar(); 
2714                 *(unsigned short *)(colDefs
[colNo
].PtrDataObj
) = val
.GetLong(); 
2716             //FIXME: Add proper wxDateTime support to wxVariant.. 
2719                     DATE_STRUCT 
*dataptr 
= 
2720                         (DATE_STRUCT 
*)colDefs
[colNo
].PtrDataObj
; 
2722                     dataptr
->year   
= dateval
.GetYear(); 
2723                     dataptr
->month  
= dateval
.GetMonth()+1; 
2724                     dataptr
->day    
= dateval
.GetDay(); 
2729                     TIME_STRUCT 
*dataptr 
= 
2730                         (TIME_STRUCT 
*)colDefs
[colNo
].PtrDataObj
; 
2732                     dataptr
->hour   
= dateval
.GetHour(); 
2733                     dataptr
->minute 
= dateval
.GetMinute(); 
2734                     dataptr
->second 
= dateval
.GetSecond(); 
2737             case SQL_C_TIMESTAMP
: 
2739                     TIMESTAMP_STRUCT 
*dataptr 
= 
2740                         (TIMESTAMP_STRUCT 
*)colDefs
[colNo
].PtrDataObj
; 
2741                     dataptr
->year   
= dateval
.GetYear(); 
2742                     dataptr
->month  
= dateval
.GetMonth()+1; 
2743                     dataptr
->day    
= dateval
.GetDay(); 
2745                     dataptr
->hour   
= dateval
.GetHour(); 
2746                     dataptr
->minute 
= dateval
.GetMinute(); 
2747                     dataptr
->second 
= dateval
.GetSecond(); 
2751                 *(double *)(colDefs
[colNo
].PtrDataObj
) = val
; 
2756     }  // if (!val.IsNull()) 
2757 }  // wxDbTable::SetCol() 
2760 GenericKey 
wxDbTable::GetKey() 
2765     blk 
= malloc(m_keysize
); 
2766     blkptr 
= (wxChar 
*) blk
; 
2769     for (i
=0; i 
< noCols
; i
++) 
2771         if (colDefs
[i
].KeyField
) 
2773             memcpy(blkptr
,colDefs
[i
].PtrDataObj
, colDefs
[i
].SzDataObj
); 
2774             blkptr 
+= colDefs
[i
].SzDataObj
; 
2778     GenericKey k 
= GenericKey(blk
, m_keysize
); 
2782 }  // wxDbTable::GetKey() 
2785 void wxDbTable::SetKey(const GenericKey
& k
) 
2791     blkptr 
= (wxChar 
*)blk
; 
2794     for (i
=0; i 
< noCols
; i
++) 
2796         if (colDefs
[i
].KeyField
) 
2798             SetColNull(i
, FALSE
); 
2799             memcpy(colDefs
[i
].PtrDataObj
, blkptr
, colDefs
[i
].SzDataObj
); 
2800             blkptr 
+= colDefs
[i
].SzDataObj
; 
2803 }  // wxDbTable::SetKey() 
2806 #endif  // wxUSE_ODBC