1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/dbtable.cpp 
   3 // Purpose:     Implementation of the wxDbTable class. 
   5 // Modified by: George Tasker 
  10 // Copyright:   (c) 1996 Remstar International, Inc. 
  11 // Licence:     wxWindows licence 
  12 /////////////////////////////////////////////////////////////////////////////// 
  14 #include  "wx/wxprec.h" 
  23     #include "wx/object.h" 
  25     #include "wx/string.h" 
  30 #ifdef DBDEBUG_CONSOLE 
  36     #include "wx/ioswrap.h" 
  39 #include "wx/filefn.h" 
  45 #include "wx/dbtable.h" 
  48 // The HPUX preprocessor lines below were commented out on 8/20/97 
  49 // because macros.h currently redefines DEBUG and is unneeded. 
  51 // #    include <macros.h> 
  54 #    include <sys/minmax.h> 
  58 ULONG lastTableID 
= 0; 
  66 void csstrncpyt(wxChar 
*target
, const wxChar 
*source
, int n
) 
  68     while ( (*target
++ = *source
++) != '\0' && --n 
!= 0 ) 
  76 /********** wxDbColDef::wxDbColDef() Constructor **********/ 
  77 wxDbColDef::wxDbColDef() 
  83 bool wxDbColDef::Initialize() 
  86     DbDataType      
= DB_DATA_TYPE_INTEGER
; 
  87     SqlCtype        
= SQL_C_LONG
; 
  92     InsertAllowed   
= false; 
  98 }  // wxDbColDef::Initialize() 
 101 /********** wxDbTable::wxDbTable() Constructor **********/ 
 102 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 103                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 105     if (!initialize(pwxDb
, tblName
, numColumns
, qryTblName
, qryOnly
, tblPath
)) 
 107 }  // wxDbTable::wxDbTable() 
 110 /***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/ 
 111 #if WXWIN_COMPATIBILITY_2_4 
 112 wxDbTable::wxDbTable(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 113                     const wxChar 
*qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 115     wxString tempQryTblName
; 
 116     tempQryTblName 
= qryTblName
; 
 117     if (!initialize(pwxDb
, tblName
, numColumns
, tempQryTblName
, qryOnly
, tblPath
)) 
 119 }  // wxDbTable::wxDbTable() 
 120 #endif // WXWIN_COMPATIBILITY_2_4 
 123 /********** wxDbTable::~wxDbTable() **********/ 
 124 wxDbTable::~wxDbTable() 
 127 }  // wxDbTable::~wxDbTable() 
 130 bool wxDbTable::initialize(wxDb 
*pwxDb
, const wxString 
&tblName
, const UWORD numColumns
, 
 131                     const wxString 
&qryTblName
, bool qryOnly
, const wxString 
&tblPath
) 
 133     // Initializing member variables 
 134     pDb                 
= pwxDb
;                    // Pointer to the wxDb object 
 138     m_hstmtGridQuery               
= 0; 
 139     hstmtDefault        
= 0;                        // Initialized below 
 140     hstmtCount          
= 0;                        // Initialized first time it is needed 
 147     m_numCols           
= numColumns
;               // Number of columns in the table 
 148     where
.Empty();                                  // Where clause 
 149     orderBy
.Empty();                                // Order By clause 
 150     from
.Empty();                                   // From clause 
 151     selectForUpdate     
= false;                    // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase 
 156     queryTableName
.Empty(); 
 158     wxASSERT(tblName
.length()); 
 164     tableName 
= tblName
;                        // Table Name 
 165     if ((pDb
->Dbms() == dbmsORACLE
) || 
 166         (pDb
->Dbms() == dbmsFIREBIRD
) || 
 167         (pDb
->Dbms() == dbmsINTERBASE
)) 
 168         tableName 
= tableName
.Upper(); 
 170     if (tblPath
.length()) 
 171         tablePath 
= tblPath
;                    // Table Path - used for dBase files 
 175     if (qryTblName
.length())                    // Name of the table/view to query 
 176         queryTableName 
= qryTblName
; 
 178         queryTableName 
= tblName
; 
 180     if ((pDb
->Dbms() == dbmsORACLE
) || 
 181         (pDb
->Dbms() == dbmsFIREBIRD
) || 
 182         (pDb
->Dbms() == dbmsINTERBASE
)) 
 183         queryTableName 
= queryTableName
.Upper(); 
 185     pDb
->incrementTableCount(); 
 188     tableID 
= ++lastTableID
; 
 189     s
.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), 
 190              tblName
.c_str(), tableID
, wx_static_cast(void*, pDb
)); 
 193     wxTablesInUse 
*tableInUse
; 
 194     tableInUse            
= new wxTablesInUse(); 
 195     tableInUse
->tableName 
= tblName
; 
 196     tableInUse
->tableID   
= tableID
; 
 197     tableInUse
->pDb       
= pDb
; 
 198     TablesInUse
.Append(tableInUse
); 
 203     // Grab the HENV and HDBC from the wxDb object 
 204     henv 
= pDb
->GetHENV(); 
 205     hdbc 
= pDb
->GetHDBC(); 
 207     // Allocate space for column definitions 
 209         colDefs 
= new wxDbColDef
[m_numCols
];  // Points to the first column definition 
 211     // Allocate statement handles for the table 
 214         // Allocate a separate statement handle for performing inserts 
 215         if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
) 
 216             pDb
->DispAllErrors(henv
, hdbc
); 
 217         // Allocate a separate statement handle for performing deletes 
 218         if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
) 
 219             pDb
->DispAllErrors(henv
, hdbc
); 
 220         // Allocate a separate statement handle for performing updates 
 221         if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
) 
 222             pDb
->DispAllErrors(henv
, hdbc
); 
 224     // Allocate a separate statement handle for internal use 
 225     if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
) 
 226         pDb
->DispAllErrors(henv
, hdbc
); 
 228     // Set the cursor type for the statement handles 
 229     cursorType 
= SQL_CURSOR_STATIC
; 
 231     if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 233         // Check to see if cursor type is supported 
 234         pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 235         if (! wxStrcmp(pDb
->sqlState
, wxT("01S02")))  // Option Value Changed 
 237             // Datasource does not support static cursors.  Driver 
 238             // will substitute a cursor type.  Call SQLGetStmtOption() 
 239             // to determine which cursor type was selected. 
 240             if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
) 
 241                 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 242 #ifdef DBDEBUG_CONSOLE 
 243             cout 
<< wxT("Static cursor changed to: "); 
 246             case SQL_CURSOR_FORWARD_ONLY
: 
 247                 cout 
<< wxT("Forward Only"); 
 249             case SQL_CURSOR_STATIC
: 
 250                 cout 
<< wxT("Static"); 
 252             case SQL_CURSOR_KEYSET_DRIVEN
: 
 253                 cout 
<< wxT("Keyset Driven"); 
 255             case SQL_CURSOR_DYNAMIC
: 
 256                 cout 
<< wxT("Dynamic"); 
 259             cout 
<< endl 
<< endl
; 
 262             if (pDb
->FwdOnlyCursors() && cursorType 
!= SQL_CURSOR_FORWARD_ONLY
) 
 264                 // Force the use of a forward only cursor... 
 265                 cursorType 
= SQL_CURSOR_FORWARD_ONLY
; 
 266                 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
 268                     // Should never happen 
 269                     pDb
->GetNextError(henv
, hdbc
, hstmtInternal
); 
 276             pDb
->DispNextError(); 
 277             pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
 280 #ifdef DBDEBUG_CONSOLE 
 282         cout 
<< wxT("Cursor Type set to STATIC") << endl 
<< endl
; 
 287         // Set the cursor type for the INSERT statement handle 
 288         if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 289             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
 290         // Set the cursor type for the DELETE statement handle 
 291         if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 292             pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
); 
 293         // Set the cursor type for the UPDATE statement handle 
 294         if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
) 
 295             pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
); 
 298     // Make the default cursor the active cursor 
 299     hstmtDefault 
= GetNewCursor(false,false); 
 300     wxASSERT(hstmtDefault
); 
 301     hstmt 
= *hstmtDefault
; 
 305 }  // wxDbTable::initialize() 
 308 void wxDbTable::cleanup() 
 313         s
.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), 
 314                  tableName
.c_str(), tableID
, wx_static_cast(void*, pDb
)); 
 323         wxList::compatibility_iterator pNode
; 
 324         pNode 
= TablesInUse
.GetFirst(); 
 325         while (pNode 
&& !found
) 
 327             if (((wxTablesInUse 
*)pNode
->GetData())->tableID 
== tableID
) 
 330                 delete (wxTablesInUse 
*)pNode
->GetData(); 
 331                 TablesInUse
.Erase(pNode
); 
 334                 pNode 
= pNode
->GetNext(); 
 339             msg
.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s
.c_str()); 
 340             wxLogDebug (msg
,wxT("NOTICE...")); 
 345     // Decrement the wxDb table count 
 347         pDb
->decrementTableCount(); 
 349     // Delete memory allocated for column definitions 
 353     // Free statement handles 
 359 ODBC 3.0 says to use this form 
 360             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 362             if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
) 
 363                 pDb
->DispAllErrors(henv
, hdbc
); 
 369 ODBC 3.0 says to use this form 
 370             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 372             if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
) 
 373                 pDb
->DispAllErrors(henv
, hdbc
); 
 379 ODBC 3.0 says to use this form 
 380             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
 382             if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
) 
 383                 pDb
->DispAllErrors(henv
, hdbc
); 
 389         if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
) 
 390             pDb
->DispAllErrors(henv
, hdbc
); 
 393     // Delete dynamically allocated cursors 
 395         DeleteCursor(hstmtDefault
); 
 398         DeleteCursor(hstmtCount
); 
 400     if (m_hstmtGridQuery
) 
 401         DeleteCursor(m_hstmtGridQuery
); 
 403 }  // wxDbTable::cleanup() 
 406 /***************************** PRIVATE FUNCTIONS *****************************/ 
 409 void wxDbTable::setCbValueForColumn(int columnIndex
) 
 411     switch(colDefs
[columnIndex
].DbDataType
) 
 413         case DB_DATA_TYPE_VARCHAR
: 
 414         case DB_DATA_TYPE_MEMO
: 
 415             if (colDefs
[columnIndex
].Null
) 
 416                 colDefs
[columnIndex
].CbValue 
= SQL_NULL_DATA
; 
 418                 colDefs
[columnIndex
].CbValue 
= SQL_NTS
; 
 420         case DB_DATA_TYPE_INTEGER
: 
 421             if (colDefs
[columnIndex
].Null
) 
 422                 colDefs
[columnIndex
].CbValue 
= SQL_NULL_DATA
; 
 424                 colDefs
[columnIndex
].CbValue 
= 0; 
 426         case DB_DATA_TYPE_FLOAT
: 
 427             if (colDefs
[columnIndex
].Null
) 
 428                 colDefs
[columnIndex
].CbValue 
= SQL_NULL_DATA
; 
 430                 colDefs
[columnIndex
].CbValue 
= 0; 
 432         case DB_DATA_TYPE_DATE
: 
 433             if (colDefs
[columnIndex
].Null
) 
 434                 colDefs
[columnIndex
].CbValue 
= SQL_NULL_DATA
; 
 436                 colDefs
[columnIndex
].CbValue 
= 0; 
 438         case DB_DATA_TYPE_BLOB
: 
 439             if (colDefs
[columnIndex
].Null
) 
 440                 colDefs
[columnIndex
].CbValue 
= SQL_NULL_DATA
; 
 442                 if (colDefs
[columnIndex
].SqlCtype 
== SQL_C_WXCHAR
) 
 443                     colDefs
[columnIndex
].CbValue 
= SQL_NTS
; 
 445                     colDefs
[columnIndex
].CbValue 
= SQL_LEN_DATA_AT_EXEC(colDefs
[columnIndex
].SzDataObj
); 
 450 /********** wxDbTable::bindParams() **********/ 
 451 bool wxDbTable::bindParams(bool forUpdate
) 
 453     wxASSERT(!queryOnly
); 
 458     SDWORD  precision   
= 0; 
 461     // Bind each column of the table that should be bound 
 462     // to a parameter marker 
 466     for (i
=0, colNumber
=1; i 
< m_numCols
; i
++) 
 470             if (!colDefs
[i
].Updateable
) 
 475             if (!colDefs
[i
].InsertAllowed
) 
 479         switch(colDefs
[i
].DbDataType
) 
 481             case DB_DATA_TYPE_VARCHAR
: 
 482                 fSqlType 
= pDb
->GetTypeInfVarchar().FsqlType
; 
 483                 precision 
= colDefs
[i
].SzDataObj
; 
 486             case DB_DATA_TYPE_MEMO
: 
 487                 fSqlType 
= pDb
->GetTypeInfMemo().FsqlType
; 
 488                 precision 
= colDefs
[i
].SzDataObj
; 
 491             case DB_DATA_TYPE_INTEGER
: 
 492                 fSqlType 
= pDb
->GetTypeInfInteger().FsqlType
; 
 493                 precision 
= pDb
->GetTypeInfInteger().Precision
; 
 496             case DB_DATA_TYPE_FLOAT
: 
 497                 fSqlType 
= pDb
->GetTypeInfFloat().FsqlType
; 
 498                 precision 
= pDb
->GetTypeInfFloat().Precision
; 
 499                 scale 
= pDb
->GetTypeInfFloat().MaximumScale
; 
 500                 // SQL Sybase Anywhere v5.5 returned a negative number for the 
 501                 // MaxScale.  This caused ODBC to kick out an error on ibscale. 
 502                 // I check for this here and set the scale = precision. 
 504                 // scale = (short) precision; 
 506             case DB_DATA_TYPE_DATE
: 
 507                 fSqlType 
= pDb
->GetTypeInfDate().FsqlType
; 
 508                 precision 
= pDb
->GetTypeInfDate().Precision
; 
 511             case DB_DATA_TYPE_BLOB
: 
 512                 fSqlType 
= pDb
->GetTypeInfBlob().FsqlType
; 
 513                 precision 
= colDefs
[i
].SzDataObj
; 
 518         setCbValueForColumn(i
); 
 522             if (SQLBindParameter(hstmtUpdate
, colNumber
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 523                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 524                                  precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 526                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 531             if (SQLBindParameter(hstmtInsert
, colNumber
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
, 
 532                                  fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 533                                  precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
) 
 535                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 540     // Completed successfully 
 543 }  // wxDbTable::bindParams() 
 546 /********** wxDbTable::bindInsertParams() **********/ 
 547 bool wxDbTable::bindInsertParams(void) 
 549     return bindParams(false); 
 550 }  // wxDbTable::bindInsertParams() 
 553 /********** wxDbTable::bindUpdateParams() **********/ 
 554 bool wxDbTable::bindUpdateParams(void) 
 556     return bindParams(true); 
 557 }  // wxDbTable::bindUpdateParams() 
 560 /********** wxDbTable::bindCols() **********/ 
 561 bool wxDbTable::bindCols(HSTMT cursor
) 
 563     // Bind each column of the table to a memory address for fetching data 
 565     for (i 
= 0; i 
< m_numCols
; i
++) 
 567         if (SQLBindCol(cursor
, (UWORD
)(i
+1), colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
, 
 568                        colDefs
[i
].SzDataObj
, &colDefs
[i
].CbValue 
) != SQL_SUCCESS
) 
 569           return (pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
 572     // Completed successfully 
 574 }  // wxDbTable::bindCols() 
 577 /********** wxDbTable::getRec() **********/ 
 578 bool wxDbTable::getRec(UWORD fetchType
) 
 582     if (!pDb
->FwdOnlyCursors()) 
 584         // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType 
 585         SQLULEN cRowsFetched
; 
 588         retcode 
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
); 
 589         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 591             if (retcode 
== SQL_NO_DATA_FOUND
) 
 594                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 598             // Set the Null member variable to indicate the Null state 
 599             // of each column just read in. 
 601             for (i 
= 0; i 
< m_numCols
; i
++) 
 602                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 607         // Fetch the next record from the record set 
 608         retcode 
= SQLFetch(hstmt
); 
 609         if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 611             if (retcode 
== SQL_NO_DATA_FOUND
) 
 614                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 618             // Set the Null member variable to indicate the Null state 
 619             // of each column just read in. 
 621             for (i 
= 0; i 
< m_numCols
; i
++) 
 622                 colDefs
[i
].Null 
= (colDefs
[i
].CbValue 
== SQL_NULL_DATA
); 
 626     // Completed successfully 
 629 }  // wxDbTable::getRec() 
 632 /********** wxDbTable::execDelete() **********/ 
 633 bool wxDbTable::execDelete(const wxString 
&pSqlStmt
) 
 637     // Execute the DELETE statement 
 638     retcode 
= SQLExecDirect(hstmtDelete
, (SQLTCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 640     if (retcode 
== SQL_SUCCESS 
|| 
 641         retcode 
== SQL_NO_DATA_FOUND 
|| 
 642         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 644         // Record deleted successfully 
 648     // Problem deleting record 
 649     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
 651 }  // wxDbTable::execDelete() 
 654 /********** wxDbTable::execUpdate() **********/ 
 655 bool wxDbTable::execUpdate(const wxString 
&pSqlStmt
) 
 659     // Execute the UPDATE statement 
 660     retcode 
= SQLExecDirect(hstmtUpdate
, (SQLTCHAR FAR 
*) pSqlStmt
.c_str(), SQL_NTS
); 
 662     if (retcode 
== SQL_SUCCESS 
|| 
 663         retcode 
== SQL_NO_DATA_FOUND 
|| 
 664         retcode 
== SQL_SUCCESS_WITH_INFO
) 
 666         // Record updated successfully 
 669     else if (retcode 
== SQL_NEED_DATA
) 
 672         retcode 
= SQLParamData(hstmtUpdate
, &pParmID
); 
 673         while (retcode 
== SQL_NEED_DATA
) 
 675             // Find the parameter 
 677             for (i
=0; i 
< m_numCols
; i
++) 
 679                 if (colDefs
[i
].PtrDataObj 
== pParmID
) 
 681                     // We found it.  Store the parameter. 
 682                     retcode 
= SQLPutData(hstmtUpdate
, pParmID
, colDefs
[i
].SzDataObj
); 
 683                     if (retcode 
!= SQL_SUCCESS
) 
 685                         pDb
->DispNextError(); 
 686                         return pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
); 
 691             retcode 
= SQLParamData(hstmtUpdate
, &pParmID
); 
 693         if (retcode 
== SQL_SUCCESS 
|| 
 694             retcode 
== SQL_NO_DATA_FOUND 
|| 
 695             retcode 
== SQL_SUCCESS_WITH_INFO
) 
 697             // Record updated successfully 
 702     // Problem updating record 
 703     return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
 705 }  // wxDbTable::execUpdate() 
 708 /********** wxDbTable::query() **********/ 
 709 bool wxDbTable::query(int queryType
, bool forUpdate
, bool distinct
, const wxString 
&pSqlStmt
) 
 714         // The user may wish to select for update, but the DBMS may not be capable 
 715         selectForUpdate 
= CanSelectForUpdate(); 
 717         selectForUpdate 
= false; 
 719     // Set the SQL SELECT string 
 720     if (queryType 
!= DB_SELECT_STATEMENT
)               // A select statement was not passed in, 
 721     {                                                   // so generate a select statement. 
 722         BuildSelectStmt(sqlStmt
, queryType
, distinct
); 
 723         pDb
->WriteSqlLog(sqlStmt
); 
 726     // Make sure the cursor is closed first 
 727     if (!CloseCursor(hstmt
)) 
 730     // Execute the SQL SELECT statement 
 732     retcode 
= SQLExecDirect(hstmt
, (SQLTCHAR FAR 
*) (queryType 
== DB_SELECT_STATEMENT 
? pSqlStmt
.c_str() : sqlStmt
.c_str()), SQL_NTS
); 
 733     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 734         return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
)); 
 736     // Completed successfully 
 739 }  // wxDbTable::query() 
 742 /***************************** PUBLIC FUNCTIONS *****************************/ 
 745 /********** wxDbTable::Open() **********/ 
 746 bool wxDbTable::Open(bool checkPrivileges
, bool checkTableExists
) 
 755     // Calculate the maximum size of the concatenated 
 756     // keys for use with wxDbGrid 
 758     for (i
=0; i 
< m_numCols
; i
++) 
 760         if (colDefs
[i
].KeyField
) 
 762             m_keysize 
+= colDefs
[i
].SzDataObj
; 
 769     if (checkTableExists
) 
 771         if (pDb
->Dbms() == dbmsPOSTGRES
) 
 772             exists 
= pDb
->TableExists(tableName
, NULL
, tablePath
); 
 774             exists 
= pDb
->TableExists(tableName
, pDb
->GetUsername(), tablePath
); 
 777     // Verify that the table exists in the database 
 780         s 
= wxT("Table/view does not exist in the database"); 
 781         if ( *(pDb
->dbInf
.accessibleTables
) == wxT('Y')) 
 782             s 
+= wxT(", or you have no permissions.\n"); 
 786     else if (checkPrivileges
) 
 788         // Verify the user has rights to access the table. 
 789         bool hasPrivs 
wxDUMMY_INITIALIZE(true); 
 791         if (pDb
->Dbms() == dbmsPOSTGRES
) 
 792             hasPrivs 
= pDb
->TablePrivileges(tableName
, wxT("SELECT"), pDb
->GetUsername(), NULL
, tablePath
); 
 794             hasPrivs 
= pDb
->TablePrivileges(tableName
, wxT("SELECT"), pDb
->GetUsername(), pDb
->GetUsername(), tablePath
); 
 797             s 
= wxT("Connecting user does not have sufficient privileges to access this table.\n"); 
 804         if (!tablePath
.empty()) 
 805             p
.Printf(wxT("Error opening '%s/%s'.\n"),tablePath
.c_str(),tableName
.c_str()); 
 807             p
.Printf(wxT("Error opening '%s'.\n"), tableName
.c_str()); 
 810         pDb
->LogError(p
.GetData()); 
 815     // Bind the member variables for field exchange between 
 816     // the wxDbTable object and the ODBC record. 
 819         if (!bindInsertParams())                    // Inserts 
 822         if (!bindUpdateParams())                    // Updates 
 826     if (!bindCols(*hstmtDefault
))                   // Selects 
 829     if (!bindCols(hstmtInternal
))                   // Internal use only 
 833      * Do NOT bind the hstmtCount cursor!!! 
 836     // Build an insert statement using parameter markers 
 837     if (!queryOnly 
&& m_numCols 
> 0) 
 839         bool needComma 
= false; 
 840         sqlStmt
.Printf(wxT("INSERT INTO %s ("), 
 841                        pDb
->SQLTableName(tableName
.c_str()).c_str()); 
 842         for (i 
= 0; i 
< m_numCols
; i
++) 
 844             if (! colDefs
[i
].InsertAllowed
) 
 848             sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
 852         sqlStmt 
+= wxT(") VALUES ("); 
 854         int insertableCount 
= 0; 
 856         for (i 
= 0; i 
< m_numCols
; i
++) 
 858             if (! colDefs
[i
].InsertAllowed
) 
 868         // Prepare the insert statement for execution 
 871             if (SQLPrepare(hstmtInsert
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
 872                 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
 878     // Completed successfully 
 881 }  // wxDbTable::Open() 
 884 /********** wxDbTable::Query() **********/ 
 885 bool wxDbTable::Query(bool forUpdate
, bool distinct
) 
 888     return(query(DB_SELECT_WHERE
, forUpdate
, distinct
)); 
 890 }  // wxDbTable::Query() 
 893 /********** wxDbTable::QueryBySqlStmt() **********/ 
 894 bool wxDbTable::QueryBySqlStmt(const wxString 
&pSqlStmt
) 
 896     pDb
->WriteSqlLog(pSqlStmt
); 
 898     return(query(DB_SELECT_STATEMENT
, false, false, pSqlStmt
)); 
 900 }  // wxDbTable::QueryBySqlStmt() 
 903 /********** wxDbTable::QueryMatching() **********/ 
 904 bool wxDbTable::QueryMatching(bool forUpdate
, bool distinct
) 
 907     return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
)); 
 909 }  // wxDbTable::QueryMatching() 
 912 /********** wxDbTable::QueryOnKeyFields() **********/ 
 913 bool wxDbTable::QueryOnKeyFields(bool forUpdate
, bool distinct
) 
 916     return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
)); 
 918 }  // wxDbTable::QueryOnKeyFields() 
 921 /********** wxDbTable::GetPrev() **********/ 
 922 bool wxDbTable::GetPrev(void) 
 924     if (pDb
->FwdOnlyCursors()) 
 926         wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 930         return(getRec(SQL_FETCH_PRIOR
)); 
 932 }  // wxDbTable::GetPrev() 
 935 /********** wxDbTable::operator-- **********/ 
 936 bool wxDbTable::operator--(int) 
 938     if (pDb
->FwdOnlyCursors()) 
 940         wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 944         return(getRec(SQL_FETCH_PRIOR
)); 
 946 }  // wxDbTable::operator-- 
 949 /********** wxDbTable::GetFirst() **********/ 
 950 bool wxDbTable::GetFirst(void) 
 952     if (pDb
->FwdOnlyCursors()) 
 954         wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 958         return(getRec(SQL_FETCH_FIRST
)); 
 960 }  // wxDbTable::GetFirst() 
 963 /********** wxDbTable::GetLast() **********/ 
 964 bool wxDbTable::GetLast(void) 
 966     if (pDb
->FwdOnlyCursors()) 
 968         wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable")); 
 972         return(getRec(SQL_FETCH_LAST
)); 
 974 }  // wxDbTable::GetLast() 
 977 /********** wxDbTable::BuildDeleteStmt() **********/ 
 978 void wxDbTable::BuildDeleteStmt(wxString 
&pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
 980     wxASSERT(!queryOnly
); 
 984     wxString whereClause
; 
 988     // Handle the case of DeleteWhere() and the where clause is blank.  It should 
 989     // delete all records from the database in this case. 
 990     if (typeOfDel 
== DB_DEL_WHERE 
&& (pWhereClause
.length() == 0)) 
 992         pSqlStmt
.Printf(wxT("DELETE FROM %s"), 
 993                         pDb
->SQLTableName(tableName
.c_str()).c_str()); 
 997     pSqlStmt
.Printf(wxT("DELETE FROM %s WHERE "), 
 998                     pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1000     // Append the WHERE clause to the SQL DELETE statement 
1003         case DB_DEL_KEYFIELDS
: 
1004             // If the datasource supports the ROWID column, build 
1005             // the where on ROWID for efficiency purposes. 
1006             // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333' 
1007             if (CanUpdateByROWID()) 
1010                 wxChar   rowid
[wxDB_ROWID_LEN
+1]; 
1012                 // Get the ROWID value.  If not successful retreiving the ROWID, 
1013                 // simply fall down through the code and build the WHERE clause 
1014                 // based on the key fields. 
1015                 if (SQLGetData(hstmt
, (UWORD
)(m_numCols
+1), SQL_C_WXCHAR
, (UCHAR
*) rowid
, sizeof(rowid
), &cb
) == SQL_SUCCESS
) 
1017                     pSqlStmt 
+= wxT("ROWID = '"); 
1019                     pSqlStmt 
+= wxT("'"); 
1023             // Unable to delete by ROWID, so build a WHERE 
1024             // clause based on the keyfields. 
1025             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1026             pSqlStmt 
+= whereClause
; 
1029             pSqlStmt 
+= pWhereClause
; 
1031         case DB_DEL_MATCHING
: 
1032             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
1033             pSqlStmt 
+= whereClause
; 
1037 }  // BuildDeleteStmt() 
1040 /***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/ 
1041 void wxDbTable::BuildDeleteStmt(wxChar 
*pSqlStmt
, int typeOfDel
, const wxString 
&pWhereClause
) 
1043     wxString tempSqlStmt
; 
1044     BuildDeleteStmt(tempSqlStmt
, typeOfDel
, pWhereClause
); 
1045     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1046 }  // wxDbTable::BuildDeleteStmt() 
1049 /********** wxDbTable::BuildSelectStmt() **********/ 
1050 void wxDbTable::BuildSelectStmt(wxString 
&pSqlStmt
, int typeOfSelect
, bool distinct
) 
1052     wxString whereClause
; 
1053     whereClause
.Empty(); 
1055     // Build a select statement to query the database 
1056     pSqlStmt 
= wxT("SELECT "); 
1058     // SELECT DISTINCT values only? 
1060         pSqlStmt 
+= wxT("DISTINCT "); 
1062     // Was a FROM clause specified to join tables to the base table? 
1063     // Available for ::Query() only!!! 
1064     bool appendFromClause 
= false; 
1065 #if wxODBC_BACKWARD_COMPATABILITY 
1066     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from 
&& wxStrlen(from
)) 
1067         appendFromClause 
= true; 
1069     if (typeOfSelect 
== DB_SELECT_WHERE 
&& from
.length()) 
1070         appendFromClause 
= true; 
1073     // Add the column list 
1076     for (i 
= 0; i 
< m_numCols
; i
++) 
1078         tStr 
= colDefs
[i
].ColName
; 
1079         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1080         if ((appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) && tStr
.Find(wxT('.')) == wxNOT_FOUND
) 
1082             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
.c_str()); 
1083             pSqlStmt 
+= wxT("."); 
1085         pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1086         if (i 
+ 1 < m_numCols
) 
1087             pSqlStmt 
+= wxT(","); 
1090     // If the datasource supports ROWID, get this column as well.  Exception: Don't retrieve 
1091     // the ROWID if querying distinct records.  The rowid will always be unique. 
1092     if (!distinct 
&& CanUpdateByROWID()) 
1094         // If joining tables, the base table column names must be qualified to avoid ambiguity 
1095         if (appendFromClause 
|| pDb
->Dbms() == dbmsACCESS
) 
1097             pSqlStmt 
+= wxT(","); 
1098             pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1099             pSqlStmt 
+= wxT(".ROWID"); 
1102             pSqlStmt 
+= wxT(",ROWID"); 
1105     // Append the FROM tablename portion 
1106     pSqlStmt 
+= wxT(" FROM "); 
1107     pSqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
1108 //    pSqlStmt += queryTableName; 
1110     // Sybase uses the HOLDLOCK keyword to lock a record during query. 
1111     // The HOLDLOCK keyword follows the table name in the from clause. 
1112     // Each table in the from clause must specify HOLDLOCK or 
1113     // NOHOLDLOCK (the default).  Note: The "FOR UPDATE" clause 
1114     // is parsed but ignored in SYBASE Transact-SQL. 
1115     if (selectForUpdate 
&& (pDb
->Dbms() == dbmsSYBASE_ASA 
|| pDb
->Dbms() == dbmsSYBASE_ASE
)) 
1116         pSqlStmt 
+= wxT(" HOLDLOCK"); 
1118     if (appendFromClause
) 
1121     // Append the WHERE clause.  Either append the where clause for the class 
1122     // or build a where clause.  The typeOfSelect determines this. 
1123     switch(typeOfSelect
) 
1125         case DB_SELECT_WHERE
: 
1126 #if wxODBC_BACKWARD_COMPATABILITY 
1127             if (where 
&& wxStrlen(where
))   // May not want a where clause!!! 
1129             if (where
.length())   // May not want a where clause!!! 
1132                 pSqlStmt 
+= wxT(" WHERE "); 
1136         case DB_SELECT_KEYFIELDS
: 
1137             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1138             if (whereClause
.length()) 
1140                 pSqlStmt 
+= wxT(" WHERE "); 
1141                 pSqlStmt 
+= whereClause
; 
1144         case DB_SELECT_MATCHING
: 
1145             BuildWhereClause(whereClause
, DB_WHERE_MATCHING
); 
1146             if (whereClause
.length()) 
1148                 pSqlStmt 
+= wxT(" WHERE "); 
1149                 pSqlStmt 
+= whereClause
; 
1154     // Append the ORDER BY clause 
1155 #if wxODBC_BACKWARD_COMPATABILITY 
1156     if (orderBy 
&& wxStrlen(orderBy
)) 
1158     if (orderBy
.length()) 
1161         pSqlStmt 
+= wxT(" ORDER BY "); 
1162         pSqlStmt 
+= orderBy
; 
1165     // SELECT FOR UPDATE if told to do so and the datasource is capable.  Sybase 
1166     // parses the FOR UPDATE clause but ignores it.  See the comment above on the 
1167     // HOLDLOCK for Sybase. 
1168     if (selectForUpdate 
&& CanSelectForUpdate()) 
1169         pSqlStmt 
+= wxT(" FOR UPDATE"); 
1171 }  // wxDbTable::BuildSelectStmt() 
1174 /***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/ 
1175 void wxDbTable::BuildSelectStmt(wxChar 
*pSqlStmt
, int typeOfSelect
, bool distinct
) 
1177     wxString tempSqlStmt
; 
1178     BuildSelectStmt(tempSqlStmt
, typeOfSelect
, distinct
); 
1179     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1180 }  // wxDbTable::BuildSelectStmt() 
1183 /********** wxDbTable::BuildUpdateStmt() **********/ 
1184 void wxDbTable::BuildUpdateStmt(wxString 
&pSqlStmt
, int typeOfUpdate
, const wxString 
&pWhereClause
) 
1186     wxASSERT(!queryOnly
); 
1190     wxString whereClause
; 
1191     whereClause
.Empty(); 
1193     bool firstColumn 
= true; 
1195     pSqlStmt
.Printf(wxT("UPDATE %s SET "), 
1196                     pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1198     // Append a list of columns to be updated 
1200     for (i 
= 0; i 
< m_numCols
; i
++) 
1202         // Only append Updateable columns 
1203         if (colDefs
[i
].Updateable
) 
1206                 pSqlStmt 
+= wxT(","); 
1208                 firstColumn 
= false; 
1210             pSqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1211 //            pSqlStmt += colDefs[i].ColName; 
1212             pSqlStmt 
+= wxT(" = ?"); 
1216     // Append the WHERE clause to the SQL UPDATE statement 
1217     pSqlStmt 
+= wxT(" WHERE "); 
1218     switch(typeOfUpdate
) 
1220         case DB_UPD_KEYFIELDS
: 
1221             // If the datasource supports the ROWID column, build 
1222             // the where on ROWID for efficiency purposes. 
1223             // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333' 
1224             if (CanUpdateByROWID()) 
1227                 wxChar rowid
[wxDB_ROWID_LEN
+1]; 
1229                 // Get the ROWID value.  If not successful retreiving the ROWID, 
1230                 // simply fall down through the code and build the WHERE clause 
1231                 // based on the key fields. 
1232                 if (SQLGetData(hstmt
, (UWORD
)(m_numCols
+1), SQL_C_WXCHAR
, (UCHAR
*) rowid
, sizeof(rowid
), &cb
) == SQL_SUCCESS
) 
1234                     pSqlStmt 
+= wxT("ROWID = '"); 
1236                     pSqlStmt 
+= wxT("'"); 
1240             // Unable to delete by ROWID, so build a WHERE 
1241             // clause based on the keyfields. 
1242             BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
); 
1243             pSqlStmt 
+= whereClause
; 
1246             pSqlStmt 
+= pWhereClause
; 
1249 }  // BuildUpdateStmt() 
1252 /***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/ 
1253 void wxDbTable::BuildUpdateStmt(wxChar 
*pSqlStmt
, int typeOfUpdate
, const wxString 
&pWhereClause
) 
1255     wxString tempSqlStmt
; 
1256     BuildUpdateStmt(tempSqlStmt
, typeOfUpdate
, pWhereClause
); 
1257     wxStrcpy(pSqlStmt
, tempSqlStmt
); 
1258 }  // BuildUpdateStmt() 
1261 /********** wxDbTable::BuildWhereClause() **********/ 
1262 void wxDbTable::BuildWhereClause(wxString 
&pWhereClause
, int typeOfWhere
, 
1263                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1265  * Note: BuildWhereClause() currently ignores timestamp columns. 
1266  *       They are not included as part of the where clause. 
1269     bool moreThanOneColumn 
= false; 
1272     // Loop through the columns building a where clause as you go 
1274     for (colNumber 
= 0; colNumber 
< m_numCols
; colNumber
++) 
1276         // Determine if this column should be included in the WHERE clause 
1277         if ((typeOfWhere 
== DB_WHERE_KEYFIELDS 
&& colDefs
[colNumber
].KeyField
) || 
1278              (typeOfWhere 
== DB_WHERE_MATCHING  
&& (!IsColNull((UWORD
)colNumber
)))) 
1280             // Skip over timestamp columns 
1281             if (colDefs
[colNumber
].SqlCtype 
== SQL_C_TIMESTAMP
) 
1283             // If there is more than 1 column, join them with the keyword "AND" 
1284             if (moreThanOneColumn
) 
1285                 pWhereClause 
+= wxT(" AND "); 
1287                 moreThanOneColumn 
= true; 
1289             // Concatenate where phrase for the column 
1290             wxString tStr 
= colDefs
[colNumber
].ColName
; 
1292             if (qualTableName
.length() && tStr
.Find(wxT('.')) == wxNOT_FOUND
) 
1294                 pWhereClause 
+= pDb
->SQLTableName(qualTableName
); 
1295                 pWhereClause 
+= wxT("."); 
1297             pWhereClause 
+= pDb
->SQLColumnName(colDefs
[colNumber
].ColName
); 
1299             if (useLikeComparison 
&& (colDefs
[colNumber
].SqlCtype 
== SQL_C_WXCHAR
)) 
1300                 pWhereClause 
+= wxT(" LIKE "); 
1302                 pWhereClause 
+= wxT(" = "); 
1304             switch(colDefs
[colNumber
].SqlCtype
) 
1310                 //case SQL_C_WXCHAR:  SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR 
1311                     colValue
.Printf(wxT("'%s'"), GetDb()->EscapeSqlChars((UCHAR FAR 
*)colDefs
[colNumber
].PtrDataObj
).c_str()); 
1315                     colValue
.Printf(wxT("%hi"), *((SWORD 
*) colDefs
[colNumber
].PtrDataObj
)); 
1318                     colValue
.Printf(wxT("%hu"), *((UWORD 
*) colDefs
[colNumber
].PtrDataObj
)); 
1322                     colValue
.Printf(wxT("%li"), *((SDWORD 
*) colDefs
[colNumber
].PtrDataObj
)); 
1325                     colValue
.Printf(wxT("%lu"), *((UDWORD 
*) colDefs
[colNumber
].PtrDataObj
)); 
1328                     colValue
.Printf(wxT("%.6f"), *((SFLOAT 
*) colDefs
[colNumber
].PtrDataObj
)); 
1331                     colValue
.Printf(wxT("%.6f"), *((SDOUBLE 
*) colDefs
[colNumber
].PtrDataObj
)); 
1336                         strMsg
.Printf(wxT("wxDbTable::bindParams(): Unknown column type for colDefs %d colName %s"), 
1337                                     colNumber
,colDefs
[colNumber
].ColName
); 
1338                         wxFAIL_MSG(strMsg
.c_str()); 
1342             pWhereClause 
+= colValue
; 
1345 }  // wxDbTable::BuildWhereClause() 
1348 /***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/ 
1349 void wxDbTable::BuildWhereClause(wxChar 
*pWhereClause
, int typeOfWhere
, 
1350                                  const wxString 
&qualTableName
, bool useLikeComparison
) 
1352     wxString tempSqlStmt
; 
1353     BuildWhereClause(tempSqlStmt
, typeOfWhere
, qualTableName
, useLikeComparison
); 
1354     wxStrcpy(pWhereClause
, tempSqlStmt
); 
1355 }  // wxDbTable::BuildWhereClause() 
1358 /********** wxDbTable::GetRowNum() **********/ 
1359 UWORD 
wxDbTable::GetRowNum(void) 
1363     if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
) 
1365         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1369     // Completed successfully 
1370     return((UWORD
) rowNum
); 
1372 }  // wxDbTable::GetRowNum() 
1375 /********** wxDbTable::CloseCursor() **********/ 
1376 bool wxDbTable::CloseCursor(HSTMT cursor
) 
1378     if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
) 
1379         return(pDb
->DispAllErrors(henv
, hdbc
, cursor
)); 
1381     // Completed successfully 
1384 }  // wxDbTable::CloseCursor() 
1387 /********** wxDbTable::CreateTable() **********/ 
1388 bool wxDbTable::CreateTable(bool attemptDrop
) 
1396 #ifdef DBDEBUG_CONSOLE 
1397     cout 
<< wxT("Creating Table ") << tableName 
<< wxT("...") << endl
; 
1401     if (attemptDrop 
&& !DropTable()) 
1405 #ifdef DBDEBUG_CONSOLE 
1406     for (i 
= 0; i 
< m_numCols
; i
++) 
1408         // Exclude derived columns since they are NOT part of the base table 
1409         if (colDefs
[i
].DerivedCol
) 
1411         cout 
<< i 
+ 1 << wxT(": ") << colDefs
[i
].ColName 
<< wxT("; "); 
1412         switch(colDefs
[i
].DbDataType
) 
1414             case DB_DATA_TYPE_VARCHAR
: 
1415                 cout 
<< pDb
->GetTypeInfVarchar().TypeName 
<< wxT("(") << (int)(colDefs
[i
].SzDataObj 
/ sizeof(wxChar
)) << wxT(")"); 
1417             case DB_DATA_TYPE_MEMO
: 
1418                 cout 
<< pDb
->GetTypeInfMemo().TypeName
; 
1420             case DB_DATA_TYPE_INTEGER
: 
1421                 cout 
<< pDb
->GetTypeInfInteger().TypeName
; 
1423             case DB_DATA_TYPE_FLOAT
: 
1424                 cout 
<< pDb
->GetTypeInfFloat().TypeName
; 
1426             case DB_DATA_TYPE_DATE
: 
1427                 cout 
<< pDb
->GetTypeInfDate().TypeName
; 
1429             case DB_DATA_TYPE_BLOB
: 
1430                 cout 
<< pDb
->GetTypeInfBlob().TypeName
; 
1437     // Build a CREATE TABLE string from the colDefs structure. 
1438     bool needComma 
= false; 
1440     sqlStmt
.Printf(wxT("CREATE TABLE %s ("), 
1441                    pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1443     for (i 
= 0; i 
< m_numCols
; i
++) 
1445         // Exclude derived columns since they are NOT part of the base table 
1446         if (colDefs
[i
].DerivedCol
) 
1450             sqlStmt 
+= wxT(","); 
1452         sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1453 //        sqlStmt += colDefs[i].ColName; 
1454         sqlStmt 
+= wxT(" "); 
1456         switch(colDefs
[i
].DbDataType
) 
1458             case DB_DATA_TYPE_VARCHAR
: 
1459                 sqlStmt 
+= pDb
->GetTypeInfVarchar().TypeName
; 
1461             case DB_DATA_TYPE_MEMO
: 
1462                 sqlStmt 
+= pDb
->GetTypeInfMemo().TypeName
; 
1464             case DB_DATA_TYPE_INTEGER
: 
1465                 sqlStmt 
+= pDb
->GetTypeInfInteger().TypeName
; 
1467             case DB_DATA_TYPE_FLOAT
: 
1468                 sqlStmt 
+= pDb
->GetTypeInfFloat().TypeName
; 
1470             case DB_DATA_TYPE_DATE
: 
1471                 sqlStmt 
+= pDb
->GetTypeInfDate().TypeName
; 
1473             case DB_DATA_TYPE_BLOB
: 
1474                 sqlStmt 
+= pDb
->GetTypeInfBlob().TypeName
; 
1477         // For varchars, append the size of the string 
1478         if (colDefs
[i
].DbDataType 
== DB_DATA_TYPE_VARCHAR 
&& 
1479             (pDb
->Dbms() != dbmsMY_SQL 
|| pDb
->GetTypeInfVarchar().TypeName 
!= _T("text")))// || 
1480 //            colDefs[i].DbDataType == DB_DATA_TYPE_BLOB) 
1483             s
.Printf(wxT("(%d)"), (int)(colDefs
[i
].SzDataObj 
/ sizeof(wxChar
))); 
1487         if (pDb
->Dbms() == dbmsDB2 
|| 
1488             pDb
->Dbms() == dbmsMY_SQL 
|| 
1489             pDb
->Dbms() == dbmsSYBASE_ASE  
|| 
1490             pDb
->Dbms() == dbmsINTERBASE  
|| 
1491             pDb
->Dbms() == dbmsFIREBIRD  
|| 
1492             pDb
->Dbms() == dbmsMS_SQL_SERVER
) 
1494             if (colDefs
[i
].KeyField
) 
1496                 sqlStmt 
+= wxT(" NOT NULL"); 
1502     // If there is a primary key defined, include it in the create statement 
1503     for (i 
= j 
= 0; i 
< m_numCols
; i
++) 
1505         if (colDefs
[i
].KeyField
) 
1511     if ( j 
&& (pDb
->Dbms() != dbmsDBASE
) 
1512         && (pDb
->Dbms() != dbmsXBASE_SEQUITER
) )  // Found a keyfield 
1514         switch (pDb
->Dbms()) 
1518             case dbmsSYBASE_ASA
: 
1519             case dbmsSYBASE_ASE
: 
1523                 // MySQL goes out on this one. We also declare the relevant key NON NULL above 
1524                 sqlStmt 
+= wxT(",PRIMARY KEY ("); 
1529                 sqlStmt 
+= wxT(",CONSTRAINT "); 
1530                 //  DB2 is limited to 18 characters for index names 
1531                 if (pDb
->Dbms() == dbmsDB2
) 
1533                     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.")); 
1534                     sqlStmt 
+= pDb
->SQLTableName(tableName
.substr(0, 13).c_str()); 
1535 //                    sqlStmt += tableName.substr(0, 13); 
1538                     sqlStmt 
+= pDb
->SQLTableName(tableName
.c_str()); 
1539 //                    sqlStmt += tableName; 
1541                 sqlStmt 
+= wxT("_PIDX PRIMARY KEY ("); 
1546         // List column name(s) of column(s) comprising the primary key 
1547         for (i 
= j 
= 0; i 
< m_numCols
; i
++) 
1549             if (colDefs
[i
].KeyField
) 
1551                 if (j
++) // Multi part key, comma separate names 
1552                     sqlStmt 
+= wxT(","); 
1553                 sqlStmt 
+= pDb
->SQLColumnName(colDefs
[i
].ColName
); 
1555                 if (pDb
->Dbms() == dbmsMY_SQL 
&& 
1556                     colDefs
[i
].DbDataType 
==  DB_DATA_TYPE_VARCHAR
) 
1559                     s
.Printf(wxT("(%d)"), (int)(colDefs
[i
].SzDataObj 
/ sizeof(wxChar
))); 
1564         sqlStmt 
+= wxT(")"); 
1566         if (pDb
->Dbms() == dbmsINFORMIX 
|| 
1567             pDb
->Dbms() == dbmsSYBASE_ASA 
|| 
1568             pDb
->Dbms() == dbmsSYBASE_ASE
) 
1570             sqlStmt 
+= wxT(" CONSTRAINT "); 
1571             sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1572 //            sqlStmt += tableName; 
1573             sqlStmt 
+= wxT("_PIDX"); 
1576     // Append the closing parentheses for the create table statement 
1577     sqlStmt 
+= wxT(")"); 
1579     pDb
->WriteSqlLog(sqlStmt
); 
1581 #ifdef DBDEBUG_CONSOLE 
1582     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1585     // Execute the CREATE TABLE statement 
1586     RETCODE retcode 
= SQLExecDirect(hstmt
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1587     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
1589         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1590         pDb
->RollbackTrans(); 
1595     // Commit the transaction and close the cursor 
1596     if (!pDb
->CommitTrans()) 
1598     if (!CloseCursor(hstmt
)) 
1601     // Database table created successfully 
1604 } // wxDbTable::CreateTable() 
1607 /********** wxDbTable::DropTable() **********/ 
1608 bool wxDbTable::DropTable() 
1610     // NOTE: This function returns true if the Table does not exist, but 
1611     //       only for identified databases.  Code will need to be added 
1612     //       below for any other databases when those databases are defined 
1613     //       to handle this situation consistently 
1617     sqlStmt
.Printf(wxT("DROP TABLE %s"), 
1618                    pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1620     pDb
->WriteSqlLog(sqlStmt
); 
1622 #ifdef DBDEBUG_CONSOLE 
1623     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1626     RETCODE retcode 
= SQLExecDirect(hstmt
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1627     if (retcode 
!= SQL_SUCCESS
) 
1629         // Check for "Base table not found" error and ignore 
1630         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1631         if (wxStrcmp(pDb
->sqlState
, wxT("S0002")) /*&& 
1632             wxStrcmp(pDb->sqlState, wxT("S1000"))*/)  // "Base table not found" 
1634             // Check for product specific error codes 
1635             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000")))   ||  // 5.x (and lower?) 
1636                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000")))   || 
1637                   (pDb
->Dbms() == dbmsPERVASIVE_SQL 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000")))   ||  // Returns an S1000 then an S0002 
1638                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))))) 
1640                 pDb
->DispNextError(); 
1641                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1642                 pDb
->RollbackTrans(); 
1643 //                CloseCursor(hstmt); 
1649     // Commit the transaction and close the cursor 
1650     if (! pDb
->CommitTrans()) 
1652     if (! CloseCursor(hstmt
)) 
1656 }  // wxDbTable::DropTable() 
1659 /********** wxDbTable::CreateIndex() **********/ 
1660 bool wxDbTable::CreateIndex(const wxString 
&indexName
, bool unique
, UWORD numIndexColumns
, 
1661                                      wxDbIdxDef 
*pIndexDefs
, bool attemptDrop
) 
1665     // Drop the index first 
1666     if (attemptDrop 
&& !DropIndex(indexName
)) 
1669     // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions 
1670     // of an index have the columns defined as "NOT NULL".  During initial table creation though, 
1671     // it may not be known which columns are necessarily going to be part of an index (e.g. the 
1672     // table was created, then months later you determine that an additional index while 
1673     // give better performance, so you want to add an index). 
1675     // The following block of code will modify the column definition to make the column be 
1676     // defined with the "NOT NULL" qualifier. 
1677     if (pDb
->Dbms() == dbmsMY_SQL
) 
1682         for (i 
= 0; i 
< numIndexColumns 
&& ok
; i
++) 
1686             // Find the column definition that has the ColName that matches the 
1687             // index column name.  We need to do this to get the DB_DATA_TYPE of 
1688             // the index column, as MySQL's syntax for the ALTER column requires 
1690             while (!found 
&& (j 
< this->m_numCols
)) 
1692                 if (wxStrcmp(colDefs
[j
].ColName
,pIndexDefs
[i
].ColName
) == 0) 
1700                 ok 
= pDb
->ModifyColumn(tableName
, pIndexDefs
[i
].ColName
, 
1701                                         colDefs
[j
].DbDataType
, (int)(colDefs
[j
].SzDataObj 
/ sizeof(wxChar
)), 
1707                     // retcode is not used 
1708                     wxODBC_ERRORS retcode
; 
1709                     // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already 
1710                     // defined to be NOT NULL, but reportedly MySQL doesn't mind. 
1711                     // This line is just here for debug checking of the value 
1712                     retcode 
= (wxODBC_ERRORS
)pDb
->DB_STATUS
; 
1723             pDb
->RollbackTrans(); 
1728     // Build a CREATE INDEX statement 
1729     sqlStmt 
= wxT("CREATE "); 
1731         sqlStmt 
+= wxT("UNIQUE "); 
1733     sqlStmt 
+= wxT("INDEX "); 
1734     sqlStmt 
+= pDb
->SQLTableName(indexName
); 
1735     sqlStmt 
+= wxT(" ON "); 
1737     sqlStmt 
+= pDb
->SQLTableName(tableName
); 
1738 //    sqlStmt += tableName; 
1739     sqlStmt 
+= wxT(" ("); 
1741     // Append list of columns making up index 
1743     for (i 
= 0; i 
< numIndexColumns
; i
++) 
1745         sqlStmt 
+= pDb
->SQLColumnName(pIndexDefs
[i
].ColName
); 
1746 //        sqlStmt += pIndexDefs[i].ColName; 
1748         // MySQL requires a key length on VARCHAR keys 
1749         if ( pDb
->Dbms() == dbmsMY_SQL 
) 
1751             // Find the details on this column 
1753             for ( j 
= 0; j 
< m_numCols
; ++j 
) 
1755                 if ( wxStrcmp( pIndexDefs
[i
].ColName
, colDefs
[j
].ColName 
) == 0 ) 
1760             if ( colDefs
[j
].DbDataType 
==  DB_DATA_TYPE_VARCHAR
) 
1763                 s
.Printf(wxT("(%d)"), (int)(colDefs
[i
].SzDataObj 
/ sizeof(wxChar
))); 
1768         // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns 
1769         if (!((pDb
->Dbms() == dbmsMS_SQL_SERVER
) && (wxStrncmp(pDb
->dbInf
.dbmsVer
,_T("07"),2)==0)) && 
1770             !(pDb
->Dbms() == dbmsFIREBIRD
) && 
1771             !(pDb
->Dbms() == dbmsPOSTGRES
)) 
1773             if (pIndexDefs
[i
].Ascending
) 
1774                 sqlStmt 
+= wxT(" ASC"); 
1776                 sqlStmt 
+= wxT(" DESC"); 
1779             wxASSERT_MSG(pIndexDefs
[i
].Ascending
, _T("Datasource does not support DESCending index columns")); 
1781         if ((i 
+ 1) < numIndexColumns
) 
1782             sqlStmt 
+= wxT(","); 
1785     // Append closing parentheses 
1786     sqlStmt 
+= wxT(")"); 
1788     pDb
->WriteSqlLog(sqlStmt
); 
1790 #ifdef DBDEBUG_CONSOLE 
1791     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
1794     // Execute the CREATE INDEX statement 
1795     RETCODE retcode 
= SQLExecDirect(hstmt
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1796     if (retcode 
!= SQL_SUCCESS
) 
1798         pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1799         pDb
->RollbackTrans(); 
1804     // Commit the transaction and close the cursor 
1805     if (! pDb
->CommitTrans()) 
1807     if (! CloseCursor(hstmt
)) 
1810     // Index Created Successfully 
1813 }  // wxDbTable::CreateIndex() 
1816 /********** wxDbTable::DropIndex() **********/ 
1817 bool wxDbTable::DropIndex(const wxString 
&indexName
) 
1819     // NOTE: This function returns true if the Index does not exist, but 
1820     //       only for identified databases.  Code will need to be added 
1821     //       below for any other databases when those databases are defined 
1822     //       to handle this situation consistently 
1826     if (pDb
->Dbms() == dbmsACCESS 
|| pDb
->Dbms() == dbmsMY_SQL 
|| 
1827         pDb
->Dbms() == dbmsDBASE 
/*|| Paradox needs this syntax too when we add support*/) 
1828         sqlStmt
.Printf(wxT("DROP INDEX %s ON %s"), 
1829                        pDb
->SQLTableName(indexName
.c_str()).c_str(), 
1830                        pDb
->SQLTableName(tableName
.c_str()).c_str()); 
1831     else if ((pDb
->Dbms() == dbmsMS_SQL_SERVER
) || 
1832              (pDb
->Dbms() == dbmsSYBASE_ASE
) || 
1833              (pDb
->Dbms() == dbmsXBASE_SEQUITER
)) 
1834         sqlStmt
.Printf(wxT("DROP INDEX %s.%s"), 
1835                        pDb
->SQLTableName(tableName
.c_str()).c_str(), 
1836                        pDb
->SQLTableName(indexName
.c_str()).c_str()); 
1838         sqlStmt
.Printf(wxT("DROP INDEX %s"), 
1839                        pDb
->SQLTableName(indexName
.c_str()).c_str()); 
1841     pDb
->WriteSqlLog(sqlStmt
); 
1843 #ifdef DBDEBUG_CONSOLE 
1844     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1846     RETCODE retcode 
= SQLExecDirect(hstmt
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
); 
1847     if (retcode 
!= SQL_SUCCESS
) 
1849         // Check for "Index not found" error and ignore 
1850         pDb
->GetNextError(henv
, hdbc
, hstmt
); 
1851         if (wxStrcmp(pDb
->sqlState
,wxT("S0012")))  // "Index not found" 
1853             // Check for product specific error codes 
1854             if (!((pDb
->Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(pDb
->sqlState
,wxT("42000"))) ||  // v5.x (and lower?) 
1855                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("37000"))) || 
1856                   (pDb
->Dbms() == dbmsMS_SQL_SERVER 
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1857                   (pDb
->Dbms() == dbmsINTERBASE     
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1858                   (pDb
->Dbms() == dbmsMAXDB         
&& !wxStrcmp(pDb
->sqlState
,wxT("S1000"))) || 
1859                   (pDb
->Dbms() == dbmsFIREBIRD      
&& !wxStrcmp(pDb
->sqlState
,wxT("HY000"))) || 
1860                   (pDb
->Dbms() == dbmsSYBASE_ASE    
&& !wxStrcmp(pDb
->sqlState
,wxT("S0002"))) ||  // Base table not found 
1861                   (pDb
->Dbms() == dbmsMY_SQL        
&& !wxStrcmp(pDb
->sqlState
,wxT("42S12"))) ||  // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta 
1862                   (pDb
->Dbms() == dbmsPOSTGRES      
&& !wxStrcmp(pDb
->sqlState
,wxT("08S01"))) 
1865                 pDb
->DispNextError(); 
1866                 pDb
->DispAllErrors(henv
, hdbc
, hstmt
); 
1867                 pDb
->RollbackTrans(); 
1874     // Commit the transaction and close the cursor 
1875     if (! pDb
->CommitTrans()) 
1877     if (! CloseCursor(hstmt
)) 
1881 }  // wxDbTable::DropIndex() 
1884 /********** wxDbTable::SetOrderByColNums() **********/ 
1885 bool wxDbTable::SetOrderByColNums(UWORD first
, ... ) 
1887     int         colNumber 
= first
;  // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS 
1893     va_start(argptr
, first
);     /* Initialize variable arguments. */ 
1894     while (!abort 
&& (colNumber 
!= wxDB_NO_MORE_COLUMN_NUMBERS
)) 
1896         // Make sure the passed in column number 
1897         // is within the valid range of columns 
1899         // Valid columns are 0 thru m_numCols-1 
1900         if (colNumber 
>= m_numCols 
|| colNumber 
< 0) 
1906         if (colNumber 
!= first
) 
1907             tempStr 
+= wxT(","); 
1909         tempStr 
+= colDefs
[colNumber
].ColName
; 
1910         colNumber 
= va_arg (argptr
, int); 
1912     va_end (argptr
);              /* Reset variable arguments.      */ 
1914     SetOrderByClause(tempStr
); 
1917 }  // wxDbTable::SetOrderByColNums() 
1920 /********** wxDbTable::Insert() **********/ 
1921 int wxDbTable::Insert(void) 
1923     wxASSERT(!queryOnly
); 
1924     if (queryOnly 
|| !insertable
) 
1929     // Insert the record by executing the already prepared insert statement 
1931     retcode 
= SQLExecute(hstmtInsert
); 
1932     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO 
&& 
1933         retcode 
!= SQL_NEED_DATA
) 
1935         // Check to see if integrity constraint was violated 
1936         pDb
->GetNextError(henv
, hdbc
, hstmtInsert
); 
1937         if (! wxStrcmp(pDb
->sqlState
, wxT("23000")))  // Integrity constraint violated 
1938             return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
); 
1941             pDb
->DispNextError(); 
1942             pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1946     if (retcode 
== SQL_NEED_DATA
) 
1949         retcode 
= SQLParamData(hstmtInsert
, &pParmID
); 
1950         while (retcode 
== SQL_NEED_DATA
) 
1952             // Find the parameter 
1954             for (i
=0; i 
< m_numCols
; i
++) 
1956                 if (colDefs
[i
].PtrDataObj 
== pParmID
) 
1958                     // We found it.  Store the parameter. 
1959                     retcode 
= SQLPutData(hstmtInsert
, pParmID
, colDefs
[i
].SzDataObj
); 
1960                     if (retcode 
!= SQL_SUCCESS
) 
1962                         pDb
->DispNextError(); 
1963                         pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1969             retcode 
= SQLParamData(hstmtInsert
, &pParmID
); 
1970             if (retcode 
!= SQL_SUCCESS 
&& 
1971                 retcode 
!= SQL_SUCCESS_WITH_INFO
) 
1973                 // record was not inserted 
1974                 pDb
->DispNextError(); 
1975                 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
); 
1981     // Record inserted into the datasource successfully 
1984 }  // wxDbTable::Insert() 
1987 /********** wxDbTable::Update() **********/ 
1988 bool wxDbTable::Update(void) 
1990     wxASSERT(!queryOnly
); 
1996     // Build the SQL UPDATE statement 
1997     BuildUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
); 
1999     pDb
->WriteSqlLog(sqlStmt
); 
2001 #ifdef DBDEBUG_CONSOLE 
2002     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
2005     // Execute the SQL UPDATE statement 
2006     return(execUpdate(sqlStmt
)); 
2008 }  // wxDbTable::Update() 
2011 /********** wxDbTable::Update(pSqlStmt) **********/ 
2012 bool wxDbTable::Update(const wxString 
&pSqlStmt
) 
2014     wxASSERT(!queryOnly
); 
2018     pDb
->WriteSqlLog(pSqlStmt
); 
2020     return(execUpdate(pSqlStmt
)); 
2022 }  // wxDbTable::Update(pSqlStmt) 
2025 /********** wxDbTable::UpdateWhere() **********/ 
2026 bool wxDbTable::UpdateWhere(const wxString 
&pWhereClause
) 
2028     wxASSERT(!queryOnly
); 
2034     // Build the SQL UPDATE statement 
2035     BuildUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
); 
2037     pDb
->WriteSqlLog(sqlStmt
); 
2039 #ifdef DBDEBUG_CONSOLE 
2040     cout 
<< endl 
<< sqlStmt
.c_str() << endl 
<< endl
; 
2043     // Execute the SQL UPDATE statement 
2044     return(execUpdate(sqlStmt
)); 
2046 }  // wxDbTable::UpdateWhere() 
2049 /********** wxDbTable::Delete() **********/ 
2050 bool wxDbTable::Delete(void) 
2052     wxASSERT(!queryOnly
); 
2059     // Build the SQL DELETE statement 
2060     BuildDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
); 
2062     pDb
->WriteSqlLog(sqlStmt
); 
2064     // Execute the SQL DELETE statement 
2065     return(execDelete(sqlStmt
)); 
2067 }  // wxDbTable::Delete() 
2070 /********** wxDbTable::DeleteWhere() **********/ 
2071 bool wxDbTable::DeleteWhere(const wxString 
&pWhereClause
) 
2073     wxASSERT(!queryOnly
); 
2080     // Build the SQL DELETE statement 
2081     BuildDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
); 
2083     pDb
->WriteSqlLog(sqlStmt
); 
2085     // Execute the SQL DELETE statement 
2086     return(execDelete(sqlStmt
)); 
2088 }  // wxDbTable::DeleteWhere() 
2091 /********** wxDbTable::DeleteMatching() **********/ 
2092 bool wxDbTable::DeleteMatching(void) 
2094     wxASSERT(!queryOnly
); 
2101     // Build the SQL DELETE statement 
2102     BuildDeleteStmt(sqlStmt
, DB_DEL_MATCHING
); 
2104     pDb
->WriteSqlLog(sqlStmt
); 
2106     // Execute the SQL DELETE statement 
2107     return(execDelete(sqlStmt
)); 
2109 }  // wxDbTable::DeleteMatching() 
2112 /********** wxDbTable::IsColNull() **********/ 
2113 bool wxDbTable::IsColNull(UWORD colNumber
) const 
2116     This logic is just not right.  It would indicate true 
2117     if a numeric field were set to a value of 0. 
2119     switch(colDefs[colNumber].SqlCtype) 
2123         //case SQL_C_WXCHAR:  SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR 
2124             return(((UCHAR FAR *) colDefs[colNumber].PtrDataObj)[0] == 0); 
2126             return((  *((SWORD *) colDefs[colNumber].PtrDataObj))   == 0); 
2128             return((   *((UWORD*) colDefs[colNumber].PtrDataObj))   == 0); 
2130             return(( *((SDWORD *) colDefs[colNumber].PtrDataObj))   == 0); 
2132             return(( *((UDWORD *) colDefs[colNumber].PtrDataObj))   == 0); 
2134             return(( *((SFLOAT *) colDefs[colNumber].PtrDataObj))   == 0); 
2136             return((*((SDOUBLE *) colDefs[colNumber].PtrDataObj))   == 0); 
2137         case SQL_C_TIMESTAMP: 
2138             TIMESTAMP_STRUCT *pDt; 
2139             pDt = (TIMESTAMP_STRUCT *) colDefs[colNumber].PtrDataObj; 
2140             if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0) 
2148     return (colDefs
[colNumber
].Null
); 
2149 }  // wxDbTable::IsColNull() 
2152 /********** wxDbTable::CanSelectForUpdate() **********/ 
2153 bool wxDbTable::CanSelectForUpdate(void) 
2158     if (pDb
->Dbms() == dbmsMY_SQL
) 
2161     if ((pDb
->Dbms() == dbmsORACLE
) || 
2162         (pDb
->dbInf
.posStmts 
& SQL_PS_SELECT_FOR_UPDATE
)) 
2167 }  // wxDbTable::CanSelectForUpdate() 
2170 /********** wxDbTable::CanUpdateByROWID() **********/ 
2171 bool wxDbTable::CanUpdateByROWID(void) 
2174  * NOTE: Returning false for now until this can be debugged, 
2175  *        as the ROWID is not getting updated correctly 
2179     if (pDb->Dbms() == dbmsORACLE) 
2184 }  // wxDbTable::CanUpdateByROWID() 
2187 /********** wxDbTable::IsCursorClosedOnCommit() **********/ 
2188 bool wxDbTable::IsCursorClosedOnCommit(void) 
2190     if (pDb
->dbInf
.cursorCommitBehavior 
== SQL_CB_PRESERVE
) 
2195 }  // wxDbTable::IsCursorClosedOnCommit() 
2199 /********** wxDbTable::ClearMemberVar() **********/ 
2200 void wxDbTable::ClearMemberVar(UWORD colNumber
, bool setToNull
) 
2202     wxASSERT(colNumber 
< m_numCols
); 
2204     switch(colDefs
[colNumber
].SqlCtype
) 
2210         //case SQL_C_WXCHAR:  SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR 
2211             ((UCHAR FAR 
*) colDefs
[colNumber
].PtrDataObj
)[0]    = 0; 
2214             *((SWORD 
*) colDefs
[colNumber
].PtrDataObj
)          = 0; 
2217             *((UWORD
*) colDefs
[colNumber
].PtrDataObj
)           = 0; 
2221             *((SDWORD 
*) colDefs
[colNumber
].PtrDataObj
)         = 0; 
2224             *((UDWORD 
*) colDefs
[colNumber
].PtrDataObj
)         = 0; 
2227             *((SFLOAT 
*) colDefs
[colNumber
].PtrDataObj
)         = 0.0f
; 
2230             *((SDOUBLE 
*) colDefs
[colNumber
].PtrDataObj
)        = 0.0f
; 
2232         case SQL_C_TIMESTAMP
: 
2233             TIMESTAMP_STRUCT 
*pDt
; 
2234             pDt 
= (TIMESTAMP_STRUCT 
*) colDefs
[colNumber
].PtrDataObj
; 
2245             pDtd 
= (DATE_STRUCT 
*) colDefs
[colNumber
].PtrDataObj
; 
2252             pDtt 
= (TIME_STRUCT 
*) colDefs
[colNumber
].PtrDataObj
; 
2260         SetColNull(colNumber
); 
2261 }  // wxDbTable::ClearMemberVar() 
2264 /********** wxDbTable::ClearMemberVars() **********/ 
2265 void wxDbTable::ClearMemberVars(bool setToNull
) 
2269     // Loop through the columns setting each member variable to zero 
2270     for (i
=0; i 
< m_numCols
; i
++) 
2271         ClearMemberVar((UWORD
)i
,setToNull
); 
2273 }  // wxDbTable::ClearMemberVars() 
2276 /********** wxDbTable::SetQueryTimeout() **********/ 
2277 bool wxDbTable::SetQueryTimeout(UDWORD nSeconds
) 
2279     if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2280         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
)); 
2281     if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2282         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
)); 
2283     if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2284         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
)); 
2285     if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
) 
2286         return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
)); 
2288     // Completed Successfully 
2291 }  // wxDbTable::SetQueryTimeout() 
2294 /********** wxDbTable::SetColDefs() **********/ 
2295 bool wxDbTable::SetColDefs(UWORD index
, const wxString 
&fieldName
, int dataType
, void *pData
, 
2296                            SWORD cType
, int size
, bool keyField
, bool updateable
, 
2297                            bool insertAllowed
, bool derivedColumn
) 
2301     if (index 
>= m_numCols
)  // Columns numbers are zero based.... 
2303         tmpStr
.Printf(wxT("Specified column index (%d) exceeds the maximum number of columns (%d) registered for this table definition.  Column definition not added."), index
, m_numCols
); 
2309     if (!colDefs
)  // May happen if the database connection fails 
2312     if (fieldName
.length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN
) 
2314         wxStrncpy(colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
); 
2315         colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;  // Prevent buffer overrun 
2317         tmpStr
.Printf(wxT("Column name '%s' is too long. Truncated to '%s'."), 
2318                       fieldName
.c_str(),colDefs
[index
].ColName
); 
2323         wxStrcpy(colDefs
[index
].ColName
, fieldName
); 
2325     colDefs
[index
].DbDataType       
= dataType
; 
2326     colDefs
[index
].PtrDataObj       
= pData
; 
2327     colDefs
[index
].SqlCtype         
= cType
; 
2328     colDefs
[index
].SzDataObj        
= size
;  //TODO: glt ??? * sizeof(wxChar) ??? 
2329     colDefs
[index
].KeyField         
= keyField
; 
2330     colDefs
[index
].DerivedCol       
= derivedColumn
; 
2331     // Derived columns by definition would NOT be "Insertable" or "Updateable" 
2334         colDefs
[index
].Updateable       
= false; 
2335         colDefs
[index
].InsertAllowed    
= false; 
2339         colDefs
[index
].Updateable       
= updateable
; 
2340         colDefs
[index
].InsertAllowed    
= insertAllowed
; 
2343     colDefs
[index
].Null                 
= false; 
2347 }  // wxDbTable::SetColDefs() 
2350 /********** wxDbTable::SetColDefs() **********/ 
2351 wxDbColDataPtr
* wxDbTable::SetColDefs(wxDbColInf 
*pColInfs
, UWORD numCols
) 
2354     wxDbColDataPtr 
*pColDataPtrs 
= NULL
; 
2360         pColDataPtrs 
= new wxDbColDataPtr
[numCols
+1]; 
2362         for (index 
= 0; index 
< numCols
; index
++) 
2364             // Process the fields 
2365             switch (pColInfs
[index
].dbDataType
) 
2367                 case DB_DATA_TYPE_VARCHAR
: 
2368                    pColDataPtrs
[index
].PtrDataObj 
= new wxChar
[pColInfs
[index
].bufferSize
+(1*sizeof(wxChar
))]; 
2369                    pColDataPtrs
[index
].SzDataObj  
= pColInfs
[index
].bufferSize
+(1*sizeof(wxChar
)); 
2370                    pColDataPtrs
[index
].SqlCtype   
= SQL_C_WXCHAR
; 
2372                 case DB_DATA_TYPE_MEMO
: 
2373                    pColDataPtrs
[index
].PtrDataObj 
= new wxChar
[pColInfs
[index
].bufferSize
+(1*sizeof(wxChar
))]; 
2374                    pColDataPtrs
[index
].SzDataObj  
= pColInfs
[index
].bufferSize
+(1*sizeof(wxChar
)); 
2375                    pColDataPtrs
[index
].SqlCtype   
= SQL_C_WXCHAR
; 
2377                 case DB_DATA_TYPE_INTEGER
: 
2378                     // Can be long or short 
2379                     if (pColInfs
[index
].bufferSize 
== sizeof(long)) 
2381                       pColDataPtrs
[index
].PtrDataObj 
= new long; 
2382                       pColDataPtrs
[index
].SzDataObj  
= sizeof(long); 
2383                       pColDataPtrs
[index
].SqlCtype   
= SQL_C_SLONG
; 
2387                         pColDataPtrs
[index
].PtrDataObj 
= new short; 
2388                         pColDataPtrs
[index
].SzDataObj  
= sizeof(short); 
2389                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_SSHORT
; 
2392                 case DB_DATA_TYPE_FLOAT
: 
2393                     // Can be float or double 
2394                     if (pColInfs
[index
].bufferSize 
== sizeof(float)) 
2396                         pColDataPtrs
[index
].PtrDataObj 
= new float; 
2397                         pColDataPtrs
[index
].SzDataObj  
= sizeof(float); 
2398                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_FLOAT
; 
2402                         pColDataPtrs
[index
].PtrDataObj 
= new double; 
2403                         pColDataPtrs
[index
].SzDataObj  
= sizeof(double); 
2404                         pColDataPtrs
[index
].SqlCtype   
= SQL_C_DOUBLE
; 
2407                 case DB_DATA_TYPE_DATE
: 
2408                     pColDataPtrs
[index
].PtrDataObj 
= new TIMESTAMP_STRUCT
; 
2409                     pColDataPtrs
[index
].SzDataObj  
= sizeof(TIMESTAMP_STRUCT
); 
2410                     pColDataPtrs
[index
].SqlCtype   
= SQL_C_TIMESTAMP
; 
2412                 case DB_DATA_TYPE_BLOB
: 
2413                     wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns")); 
2414                     pColDataPtrs
[index
].PtrDataObj 
= /*BLOB ADDITION NEEDED*/NULL
; 
2415                     pColDataPtrs
[index
].SzDataObj  
= /*BLOB ADDITION NEEDED*/sizeof(void *); 
2416                     pColDataPtrs
[index
].SqlCtype   
= SQL_VARBINARY
; 
2419             if (pColDataPtrs
[index
].PtrDataObj 
!= NULL
) 
2420                 SetColDefs (index
,pColInfs
[index
].colName
,pColInfs
[index
].dbDataType
, pColDataPtrs
[index
].PtrDataObj
, pColDataPtrs
[index
].SqlCtype
, pColDataPtrs
[index
].SzDataObj
); 
2423                 // Unable to build all the column definitions, as either one of 
2424                 // the calls to "new" failed above, or there was a BLOB field 
2425                 // to have a column definition for.  If BLOBs are to be used, 
2426                 // the other form of ::SetColDefs() must be used, as it is impossible 
2427                 // to know the maximum size to create the PtrDataObj to be. 
2428                 delete [] pColDataPtrs
; 
2434     return (pColDataPtrs
); 
2436 } // wxDbTable::SetColDefs() 
2439 /********** wxDbTable::SetCursor() **********/ 
2440 void wxDbTable::SetCursor(HSTMT 
*hstmtActivate
) 
2442     if (hstmtActivate 
== wxDB_DEFAULT_CURSOR
) 
2443         hstmt 
= *hstmtDefault
; 
2445         hstmt 
= *hstmtActivate
; 
2447 }  // wxDbTable::SetCursor() 
2450 /********** wxDbTable::Count(const wxString &) **********/ 
2451 ULONG 
wxDbTable::Count(const wxString 
&args
) 
2457     // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement 
2458     sqlStmt  
= wxT("SELECT COUNT("); 
2460     sqlStmt 
+= wxT(") FROM "); 
2461     sqlStmt 
+= pDb
->SQLTableName(queryTableName
); 
2462 //    sqlStmt += queryTableName; 
2463 #if wxODBC_BACKWARD_COMPATABILITY 
2464     if (from 
&& wxStrlen(from
)) 
2470     // Add the where clause if one is provided 
2471 #if wxODBC_BACKWARD_COMPATABILITY 
2472     if (where 
&& wxStrlen(where
)) 
2477         sqlStmt 
+= wxT(" WHERE "); 
2481     pDb
->WriteSqlLog(sqlStmt
); 
2483     // Initialize the Count cursor if it's not already initialized 
2486         hstmtCount 
= GetNewCursor(false,false); 
2487         wxASSERT(hstmtCount
); 
2492     // Execute the SQL statement 
2493     if (SQLExecDirect(*hstmtCount
, (SQLTCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
2495         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2500     if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
) 
2502         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2506     // Obtain the result 
2507     if (SQLGetData(*hstmtCount
, (UWORD
)1, SQL_C_ULONG
, &count
, sizeof(count
), &cb
) != SQL_SUCCESS
) 
2509         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2514     if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
) 
2515         pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
); 
2517     // Return the record count 
2520 }  // wxDbTable::Count() 
2523 /********** wxDbTable::Refresh() **********/ 
2524 bool wxDbTable::Refresh(void) 
2528     // Switch to the internal cursor so any active cursors are not corrupted 
2529     HSTMT currCursor 
= GetCursor(); 
2530     hstmt 
= hstmtInternal
; 
2531 #if wxODBC_BACKWARD_COMPATABILITY 
2532     // Save the where and order by clauses 
2533     wxChar 
*saveWhere 
= where
; 
2534     wxChar 
*saveOrderBy 
= orderBy
; 
2536     wxString saveWhere 
= where
; 
2537     wxString saveOrderBy 
= orderBy
; 
2539     // Build a where clause to refetch the record with.  Try and use the 
2540     // ROWID if it's available, ow use the key fields. 
2541     wxString whereClause
; 
2542     whereClause
.Empty(); 
2544     if (CanUpdateByROWID()) 
2547         wxChar rowid
[wxDB_ROWID_LEN
+1]; 
2549         // Get the ROWID value.  If not successful retreiving the ROWID, 
2550         // simply fall down through the code and build the WHERE clause 
2551         // based on the key fields. 
2552         if (SQLGetData(hstmt
, (UWORD
)(m_numCols
+1), SQL_C_WXCHAR
, (UCHAR
*) rowid
, sizeof(rowid
), &cb
) == SQL_SUCCESS
) 
2554             whereClause 
+= pDb
->SQLTableName(queryTableName
); 
2555 //            whereClause += queryTableName; 
2556             whereClause 
+= wxT(".ROWID = '"); 
2557             whereClause 
+= rowid
; 
2558             whereClause 
+= wxT("'"); 
2562     // If unable to use the ROWID, build a where clause from the keyfields 
2563     if (wxStrlen(whereClause
) == 0) 
2564         BuildWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
); 
2566     // Requery the record 
2567     where 
= whereClause
; 
2572     if (result 
&& !GetNext()) 
2575     // Switch back to original cursor 
2576     SetCursor(&currCursor
); 
2578     // Free the internal cursor 
2579     if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
) 
2580         pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
); 
2582     // Restore the original where and order by clauses 
2584     orderBy 
= saveOrderBy
; 
2588 }  // wxDbTable::Refresh() 
2591 /********** wxDbTable::SetColNull() **********/ 
2592 bool wxDbTable::SetColNull(UWORD colNumber
, bool set
) 
2594     if (colNumber 
< m_numCols
) 
2596         colDefs
[colNumber
].Null 
= set
; 
2597         if (set
)  // Blank out the values in the member variable 
2598            ClearMemberVar(colNumber
, false);  // Must call with false here, or infinite recursion will happen 
2600         setCbValueForColumn(colNumber
); 
2607 }  // wxDbTable::SetColNull() 
2610 /********** wxDbTable::SetColNull() **********/ 
2611 bool wxDbTable::SetColNull(const wxString 
&colName
, bool set
) 
2614     for (colNumber 
= 0; colNumber 
< m_numCols
; colNumber
++) 
2616         if (!wxStricmp(colName
, colDefs
[colNumber
].ColName
)) 
2620     if (colNumber 
< m_numCols
) 
2622         colDefs
[colNumber
].Null 
= set
; 
2623         if (set
)  // Blank out the values in the member variable 
2624            ClearMemberVar((UWORD
)colNumber
,false);  // Must call with false here, or infinite recursion will happen 
2626         setCbValueForColumn(colNumber
); 
2633 }  // wxDbTable::SetColNull() 
2636 /********** wxDbTable::GetNewCursor() **********/ 
2637 HSTMT 
*wxDbTable::GetNewCursor(bool setCursor
, bool bindColumns
) 
2639     HSTMT 
*newHSTMT 
= new HSTMT
; 
2644     if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
) 
2646         pDb
->DispAllErrors(henv
, hdbc
); 
2651     if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
) 
2653         pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
); 
2660         if (!bindCols(*newHSTMT
)) 
2668         SetCursor(newHSTMT
); 
2672 }   // wxDbTable::GetNewCursor() 
2675 /********** wxDbTable::DeleteCursor() **********/ 
2676 bool wxDbTable::DeleteCursor(HSTMT 
*hstmtDel
) 
2680     if (!hstmtDel
)  // Cursor already deleted 
2684 ODBC 3.0 says to use this form 
2685     if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) 
2688     if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
) 
2690         pDb
->DispAllErrors(henv
, hdbc
); 
2698 }  // wxDbTable::DeleteCursor() 
2700 ////////////////////////////////////////////////////////////// 
2701 // wxDbGrid support functions 
2702 ////////////////////////////////////////////////////////////// 
2704 void wxDbTable::SetRowMode(const rowmode_t rowmode
) 
2706     if (!m_hstmtGridQuery
) 
2708         m_hstmtGridQuery 
= GetNewCursor(false,false); 
2709         if (!bindCols(*m_hstmtGridQuery
)) 
2713     m_rowmode 
= rowmode
; 
2716         case WX_ROW_MODE_QUERY
: 
2717             SetCursor(m_hstmtGridQuery
); 
2719         case WX_ROW_MODE_INDIVIDUAL
: 
2720             SetCursor(hstmtDefault
); 
2725 }  // wxDbTable::SetRowMode() 
2728 wxVariant 
wxDbTable::GetColumn(const int colNumber
) const 
2731     if ((colNumber 
< m_numCols
) && (!IsColNull((UWORD
)colNumber
))) 
2733         switch (colDefs
[colNumber
].SqlCtype
) 
2736     #if defined(SQL_WCHAR) 
2739     #if defined(SQL_WVARCHAR) 
2745                 val 
= (wxChar 
*)(colDefs
[colNumber
].PtrDataObj
); 
2749                 val 
= *(long *)(colDefs
[colNumber
].PtrDataObj
); 
2753                 val 
= (long int )(*(short *)(colDefs
[colNumber
].PtrDataObj
)); 
2756                 val 
= (long)(*(unsigned long *)(colDefs
[colNumber
].PtrDataObj
)); 
2759                 val 
= (long)(*(wxChar 
*)(colDefs
[colNumber
].PtrDataObj
)); 
2761             case SQL_C_UTINYINT
: 
2762                 val 
= (long)(*(wxChar 
*)(colDefs
[colNumber
].PtrDataObj
)); 
2765                 val 
= (long)(*(UWORD 
*)(colDefs
[colNumber
].PtrDataObj
)); 
2768                 val 
= (DATE_STRUCT 
*)(colDefs
[colNumber
].PtrDataObj
); 
2771                 val 
= (TIME_STRUCT 
*)(colDefs
[colNumber
].PtrDataObj
); 
2773             case SQL_C_TIMESTAMP
: 
2774                 val 
= (TIMESTAMP_STRUCT 
*)(colDefs
[colNumber
].PtrDataObj
); 
2777                 val 
= *(double *)(colDefs
[colNumber
].PtrDataObj
); 
2784 }  // wxDbTable::GetCol() 
2787 void wxDbTable::SetColumn(const int colNumber
, const wxVariant val
) 
2789     //FIXME: Add proper wxDateTime support to wxVariant.. 
2792     SetColNull((UWORD
)colNumber
, val
.IsNull()); 
2796         if ((colDefs
[colNumber
].SqlCtype 
== SQL_C_DATE
) 
2797             || (colDefs
[colNumber
].SqlCtype 
== SQL_C_TIME
) 
2798             || (colDefs
[colNumber
].SqlCtype 
== SQL_C_TIMESTAMP
)) 
2800             //Returns null if invalid! 
2801             if (!dateval
.ParseDate(val
.GetString())) 
2802                 SetColNull((UWORD
)colNumber
, true); 
2805         switch (colDefs
[colNumber
].SqlCtype
) 
2808     #if defined(SQL_WCHAR) 
2811     #if defined(SQL_WVARCHAR) 
2817                 csstrncpyt((wxChar 
*)(colDefs
[colNumber
].PtrDataObj
), 
2818                            val
.GetString().c_str(), 
2819                            colDefs
[colNumber
].SzDataObj
-1);  //TODO: glt ??? * sizeof(wxChar) ??? 
2823                 *(long *)(colDefs
[colNumber
].PtrDataObj
) = val
; 
2827                 *(short *)(colDefs
[colNumber
].PtrDataObj
) = (short)val
.GetLong(); 
2830                 *(unsigned long *)(colDefs
[colNumber
].PtrDataObj
) = val
.GetLong(); 
2833                 *(wxChar 
*)(colDefs
[colNumber
].PtrDataObj
) = val
.GetChar(); 
2835             case SQL_C_UTINYINT
: 
2836                 *(wxChar 
*)(colDefs
[colNumber
].PtrDataObj
) = val
.GetChar(); 
2839                 *(unsigned short *)(colDefs
[colNumber
].PtrDataObj
) = (unsigned short)val
.GetLong(); 
2841             //FIXME: Add proper wxDateTime support to wxVariant.. 
2844                     DATE_STRUCT 
*dataptr 
= 
2845                         (DATE_STRUCT 
*)colDefs
[colNumber
].PtrDataObj
; 
2847                     dataptr
->year   
= (SWORD
)dateval
.GetYear(); 
2848                     dataptr
->month  
= (UWORD
)(dateval
.GetMonth()+1); 
2849                     dataptr
->day    
= (UWORD
)dateval
.GetDay(); 
2854                     TIME_STRUCT 
*dataptr 
= 
2855                         (TIME_STRUCT 
*)colDefs
[colNumber
].PtrDataObj
; 
2857                     dataptr
->hour   
= dateval
.GetHour(); 
2858                     dataptr
->minute 
= dateval
.GetMinute(); 
2859                     dataptr
->second 
= dateval
.GetSecond(); 
2862             case SQL_C_TIMESTAMP
: 
2864                     TIMESTAMP_STRUCT 
*dataptr 
= 
2865                         (TIMESTAMP_STRUCT 
*)colDefs
[colNumber
].PtrDataObj
; 
2866                     dataptr
->year   
= (SWORD
)dateval
.GetYear(); 
2867                     dataptr
->month  
= (UWORD
)(dateval
.GetMonth()+1); 
2868                     dataptr
->day    
= (UWORD
)dateval
.GetDay(); 
2870                     dataptr
->hour   
= dateval
.GetHour(); 
2871                     dataptr
->minute 
= dateval
.GetMinute(); 
2872                     dataptr
->second 
= dateval
.GetSecond(); 
2876                 *(double *)(colDefs
[colNumber
].PtrDataObj
) = val
; 
2881     }  // if (!val.IsNull()) 
2882 }  // wxDbTable::SetCol() 
2885 GenericKey 
wxDbTable::GetKey() 
2890     blk 
= malloc(m_keysize
); 
2891     blkptr 
= (wxChar 
*) blk
; 
2894     for (i
=0; i 
< m_numCols
; i
++) 
2896         if (colDefs
[i
].KeyField
) 
2898             memcpy(blkptr
,colDefs
[i
].PtrDataObj
, colDefs
[i
].SzDataObj
); 
2899             blkptr 
+= colDefs
[i
].SzDataObj
; 
2903     GenericKey k 
= GenericKey(blk
, m_keysize
); 
2907 }  // wxDbTable::GetKey() 
2910 void wxDbTable::SetKey(const GenericKey
& k
) 
2916     blkptr 
= (wxChar 
*)blk
; 
2919     for (i
=0; i 
< m_numCols
; i
++) 
2921         if (colDefs
[i
].KeyField
) 
2923             SetColNull((UWORD
)i
, false); 
2924             memcpy(colDefs
[i
].PtrDataObj
, blkptr
, colDefs
[i
].SzDataObj
); 
2925             blkptr 
+= colDefs
[i
].SzDataObj
; 
2928 }  // wxDbTable::SetKey() 
2931 #endif  // wxUSE_ODBC