home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / common / dbtable.cpp < prev    next >
C/C++ Source or Header  |  2002-08-16  |  87KB  |  2,733 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name:        dbtable.cpp
  3. // Purpose:     Implementation of the wxDbTable class.
  4. // Author:      Doug Card
  5. // Modified by: George Tasker
  6. //              Bart Jourquin
  7. //              Mark Johnson
  8. // Created:     9.96
  9. // RCS-ID:      $Id: dbtable.cpp,v 1.68 2002/08/16 12:43:07 JS Exp $
  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. ///////////////////////////////////////////////////////////////////////////////
  22.  
  23. /*
  24. // SYNOPSIS START
  25. // SYNOPSIS STOP
  26. */
  27. #ifdef __GNUG__
  28.     #pragma implementation "dbtable.h"
  29. #endif
  30.  
  31. #include  "wx/wxprec.h"
  32.  
  33. #ifdef __BORLANDC__
  34.     #pragma hdrstop
  35. #endif
  36.  
  37. #ifdef DBDEBUG_CONSOLE
  38. #if wxUSE_IOSTREAMH
  39.     #include <iostream.h>
  40. #else
  41.     #include <iostream>
  42. #endif
  43.     #include "wx/ioswrap.h"
  44. #endif
  45.  
  46. #ifndef WX_PRECOMP
  47.     #include "wx/string.h"
  48.     #include "wx/object.h"
  49.     #include "wx/list.h"
  50.     #include "wx/utils.h"
  51.     #if wxUSE_GUI
  52.         #include "wx/msgdlg.h"
  53.     #endif
  54.     #include "wx/log.h"
  55. #endif
  56. #include "wx/filefn.h"
  57.  
  58. #if wxUSE_ODBC
  59.  
  60. #include <stdio.h>
  61. #include <stdlib.h>
  62. #include <string.h>
  63. //#include <assert.h>
  64.  
  65. #include "wx/dbtable.h"
  66.  
  67. #ifdef __UNIX__
  68. // The HPUX preprocessor lines below were commented out on 8/20/97
  69. // because macros.h currently redefines DEBUG and is unneeded.
  70. // #  ifdef HPUX
  71. // #    include <macros.h>
  72. // #  endif
  73. #  ifdef LINUX
  74. #    include <sys/minmax.h>
  75. #  endif
  76. #endif
  77.  
  78. ULONG lastTableID = 0;
  79.  
  80.  
  81. #ifdef __WXDEBUG__
  82.     wxList TablesInUse;
  83. #endif
  84.  
  85.  
  86. /********** wxDbColDef::wxDbColDef() Constructor **********/
  87. wxDbColDef::wxDbColDef()
  88. {
  89.     Initialize();
  90. }  // Constructor
  91.  
  92.  
  93. bool wxDbColDef::Initialize()
  94. {
  95.     ColName[0]      = 0;
  96.     DbDataType      = DB_DATA_TYPE_INTEGER;
  97.     SqlCtype        = SQL_C_LONG;
  98.     PtrDataObj      = NULL;
  99.     SzDataObj       = 0;
  100.     KeyField        = FALSE;
  101.     Updateable      = FALSE;
  102.     InsertAllowed   = FALSE;
  103.     DerivedCol      = FALSE;
  104.     CbValue         = 0;
  105.     Null = FALSE;
  106.  
  107.     return TRUE;
  108. }  // wxDbColDef::Initialize()
  109.  
  110.  
  111. /********** wxDbTable::wxDbTable() Constructor **********/
  112. wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
  113.                     const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
  114. {
  115.     if (!initialize(pwxDb, tblName, numColumns, qryTblName, qryOnly, tblPath))
  116.         cleanup();
  117. }  // wxDbTable::wxDbTable()
  118.  
  119.  
  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)
  123. {
  124.     wxString tempQryTblName;
  125.     tempQryTblName = qryTblName;
  126.     if (!initialize(pwxDb, tblName, numColumns, tempQryTblName, qryOnly, tblPath))
  127.         cleanup();
  128. }  // wxDbTable::wxDbTable()
  129.  
  130.  
  131. /********** wxDbTable::~wxDbTable() **********/
  132. wxDbTable::~wxDbTable()
  133. {
  134.     this->cleanup();
  135. }  // wxDbTable::~wxDbTable()
  136.  
  137.  
  138. bool wxDbTable::initialize(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
  139.                     const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
  140. {
  141.     // Initializing member variables
  142.     pDb                 = pwxDb;                    // Pointer to the wxDb object
  143.     henv                = 0;
  144.     hdbc                = 0;
  145.     hstmt               = 0;
  146.     m_hstmtGridQuery               = 0;
  147.     hstmtDefault        = 0;                        // Initialized below
  148.     hstmtCount          = 0;                        // Initialized first time it is needed
  149.     hstmtInsert         = 0;
  150.     hstmtDelete         = 0;
  151.     hstmtUpdate         = 0;
  152.     hstmtInternal       = 0;
  153.     colDefs             = 0;
  154.     tableID             = 0;
  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
  160.     queryOnly           = qryOnly;
  161.     insertable          = TRUE;
  162.     tablePath.Empty();
  163.     tableName.Empty();
  164.     queryTableName.Empty();
  165.  
  166.     wxASSERT(tblName.Length());
  167.     wxASSERT(pDb);
  168.  
  169.     if (!pDb)
  170.         return FALSE;
  171.  
  172.     tableName = tblName;                        // Table Name
  173.     if (tblPath.Length())
  174.         tablePath = tblPath;                    // Table Path - used for dBase files
  175.     else
  176.         tablePath.Empty();
  177.  
  178.     if (qryTblName.Length())                    // Name of the table/view to query
  179.         queryTableName = qryTblName;
  180.     else
  181.         queryTableName = tblName;
  182.  
  183.     pDb->incrementTableCount();
  184.  
  185.     wxString s;
  186.     tableID = ++lastTableID;
  187.     s.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), tblName.c_str(), tableID, pDb);
  188.  
  189. #ifdef __WXDEBUG__
  190.     wxTablesInUse *tableInUse;
  191.     tableInUse            = new wxTablesInUse();
  192.     tableInUse->tableName = tblName;
  193.     tableInUse->tableID   = tableID;
  194.     tableInUse->pDb       = pDb;
  195.     TablesInUse.Append(tableInUse);
  196. #endif
  197.  
  198.     pDb->WriteSqlLog(s);
  199.  
  200.     // Grab the HENV and HDBC from the wxDb object
  201.     henv = pDb->GetHENV();
  202.     hdbc = pDb->GetHDBC();
  203.  
  204.     // Allocate space for column definitions
  205.     if (noCols)
  206.         colDefs = new wxDbColDef[noCols];  // Points to the first column definition
  207.  
  208.     // Allocate statement handles for the table
  209.     if (!queryOnly)
  210.     {
  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);
  220.     }
  221.     // Allocate a separate statement handle for internal use
  222.     if (SQLAllocStmt(hdbc, &hstmtInternal) != SQL_SUCCESS)
  223.         pDb->DispAllErrors(henv, hdbc);
  224.  
  225.     // Set the cursor type for the statement handles
  226.     cursorType = SQL_CURSOR_STATIC;
  227.  
  228.     if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
  229.     {
  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
  233.         {
  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: ");
  241.             switch(cursorType)
  242.             {
  243.             case SQL_CURSOR_FORWARD_ONLY:
  244.                 cout << wxT("Forward Only");
  245.                 break;
  246.             case SQL_CURSOR_STATIC:
  247.                 cout << wxT("Static");
  248.                 break;
  249.             case SQL_CURSOR_KEYSET_DRIVEN:
  250.                 cout << wxT("Keyset Driven");
  251.                 break;
  252.             case SQL_CURSOR_DYNAMIC:
  253.                 cout << wxT("Dynamic");
  254.                 break;
  255.             }
  256.             cout << endl << endl;
  257. #endif
  258.             // BJO20000425
  259.             if (pDb->FwdOnlyCursors() && cursorType != SQL_CURSOR_FORWARD_ONLY)
  260.             {
  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)
  264.                 {
  265.                     // Should never happen
  266.                     pDb->GetNextError(henv, hdbc, hstmtInternal);
  267.                     return FALSE;
  268.                 }
  269.             }
  270.         }
  271.         else
  272.         {
  273.             pDb->DispNextError();
  274.             pDb->DispAllErrors(henv, hdbc, hstmtInternal);
  275.         }
  276.     }
  277. #ifdef DBDEBUG_CONSOLE
  278.     else
  279.         cout << wxT("Cursor Type set to STATIC") << endl << endl;
  280. #endif
  281.  
  282.     if (!queryOnly)
  283.     {
  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);
  293.     }
  294.  
  295.     // Make the default cursor the active cursor
  296.     hstmtDefault = GetNewCursor(FALSE,FALSE);
  297.     wxASSERT(hstmtDefault);
  298.     hstmt = *hstmtDefault;
  299.  
  300.     return TRUE;
  301.  
  302. }  // wxDbTable::initialize()
  303.  
  304.  
  305. void wxDbTable::cleanup()
  306. {
  307.     wxString s;
  308.     if (pDb)
  309.     {
  310.         s.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), tableName.c_str(), tableID, pDb);
  311.         pDb->WriteSqlLog(s);
  312.     }
  313.  
  314. #ifdef __WXDEBUG__
  315.     if (tableID)
  316.     {
  317.         TablesInUse.DeleteContents(TRUE);
  318.         bool found = FALSE;
  319.  
  320.         wxNode *pNode;
  321.         pNode = TablesInUse.First();
  322.         while (pNode && !found)
  323.         {
  324.             if (((wxTablesInUse *)pNode->Data())->tableID == tableID)
  325.             {
  326.                 found = TRUE;
  327.                 if (!TablesInUse.DeleteNode(pNode))
  328.                     wxLogDebug (s,wxT("Unable to delete node!"));
  329.             }
  330.             else
  331.                 pNode = pNode->Next();
  332.         }
  333.         if (!found)
  334.         {
  335.             wxString msg;
  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..."));
  338.         }
  339.     }
  340. #endif
  341.  
  342.     // Decrement the wxDb table count
  343.     if (pDb)
  344.         pDb->decrementTableCount();
  345.  
  346.     // Delete memory allocated for column definitions
  347.     if (colDefs)
  348.         delete [] colDefs;
  349.  
  350.     // Free statement handles
  351.     if (!queryOnly)
  352.     {
  353.         if (hstmtInsert)
  354.         {
  355. /*
  356. ODBC 3.0 says to use this form
  357.             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
  358. */
  359.             if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS)
  360.                 pDb->DispAllErrors(henv, hdbc);
  361.         }
  362.  
  363.         if (hstmtDelete)
  364.         {
  365. /*
  366. ODBC 3.0 says to use this form
  367.             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
  368. */
  369.             if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS)
  370.                 pDb->DispAllErrors(henv, hdbc);
  371.         }
  372.  
  373.         if (hstmtUpdate)
  374.         {
  375. /*
  376. ODBC 3.0 says to use this form
  377.             if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
  378. */
  379.             if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS)
  380.                 pDb->DispAllErrors(henv, hdbc);
  381.         }
  382.     }
  383.  
  384.     if (hstmtInternal)
  385.     {
  386.         if (SQLFreeStmt(hstmtInternal, SQL_DROP) != SQL_SUCCESS)
  387.             pDb->DispAllErrors(henv, hdbc);
  388.     }
  389.  
  390.     // Delete dynamically allocated cursors
  391.     if (hstmtDefault)
  392.         DeleteCursor(hstmtDefault);
  393.  
  394.     if (hstmtCount)
  395.         DeleteCursor(hstmtCount);
  396.  
  397.     if (m_hstmtGridQuery)
  398.         DeleteCursor(m_hstmtGridQuery);
  399.  
  400. }  // wxDbTable::cleanup()
  401.  
  402.  
  403. /***************************** PRIVATE FUNCTIONS *****************************/
  404.  
  405.  
  406. /********** wxDbTable::bindParams() **********/
  407. bool wxDbTable::bindParams(bool forUpdate)
  408. {
  409.     wxASSERT(!queryOnly);
  410.     if (queryOnly)
  411.         return(FALSE);
  412.  
  413.     SWORD   fSqlType    = 0;
  414.     UDWORD  precision   = 0;
  415.     SWORD   scale       = 0;
  416.  
  417.     // Bind each column of the table that should be bound
  418.     // to a parameter marker
  419.     int i;
  420.     UWORD colNo;
  421.  
  422.     for (i=0, colNo=1; i < noCols; i++)
  423.     {
  424.         if (forUpdate)
  425.         {
  426.             if (!colDefs[i].Updateable)
  427.                 continue;
  428.         }
  429.         else
  430.         {
  431.             if (!colDefs[i].InsertAllowed)
  432.                 continue;
  433.         }
  434.  
  435.         switch(colDefs[i].DbDataType)
  436.         {
  437.             case DB_DATA_TYPE_VARCHAR:
  438.                 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
  439.                 precision = colDefs[i].SzDataObj;
  440.                 scale = 0;
  441.                 if (colDefs[i].Null)
  442.                     colDefs[i].CbValue = SQL_NULL_DATA;
  443.                 else
  444.                     colDefs[i].CbValue = SQL_NTS;
  445.                 break;
  446.             case DB_DATA_TYPE_INTEGER:
  447.                 fSqlType = pDb->GetTypeInfInteger().FsqlType;
  448.                 precision = pDb->GetTypeInfInteger().Precision;
  449.                 scale = 0;
  450.                 if (colDefs[i].Null)
  451.                     colDefs[i].CbValue = SQL_NULL_DATA;
  452.                 else
  453.                     colDefs[i].CbValue = 0;
  454.                 break;
  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.
  462.                 //if (scale < 0)
  463.                 // scale = (short) precision;
  464.                 if (colDefs[i].Null)
  465.                     colDefs[i].CbValue = SQL_NULL_DATA;
  466.                 else
  467.                     colDefs[i].CbValue = 0;
  468.                 break;
  469.             case DB_DATA_TYPE_DATE:
  470.                 fSqlType = pDb->GetTypeInfDate().FsqlType;
  471.                 precision = pDb->GetTypeInfDate().Precision;
  472.                 scale = 0;
  473.                 if (colDefs[i].Null)
  474.                     colDefs[i].CbValue = SQL_NULL_DATA;
  475.                 else
  476.                     colDefs[i].CbValue = 0;
  477.                 break;
  478.             case DB_DATA_TYPE_BLOB:
  479.                 fSqlType = pDb->GetTypeInfBlob().FsqlType;
  480.                 precision = 50000;
  481.                 scale = 0;
  482.                 if (colDefs[i].Null)
  483.                     colDefs[i].CbValue = SQL_NULL_DATA;
  484.                 else
  485.                     colDefs[i].CbValue = SQL_LEN_DATA_AT_EXEC(colDefs[i].SzDataObj);
  486.                 break;
  487.         }
  488.         if (forUpdate)
  489.         {
  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)
  493.             {
  494.                 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
  495.             }
  496.         }
  497.         else
  498.         {
  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)
  502.             {
  503.                 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
  504.             }
  505.         }
  506.     }
  507.  
  508.     // Completed successfully
  509.     return(TRUE);
  510.  
  511. }  // wxDbTable::bindParams()
  512.  
  513.  
  514. /********** wxDbTable::bindInsertParams() **********/
  515. bool wxDbTable::bindInsertParams(void)
  516. {
  517.     return bindParams(FALSE);
  518. }  // wxDbTable::bindInsertParams()
  519.  
  520.  
  521. /********** wxDbTable::bindUpdateParams() **********/
  522. bool wxDbTable::bindUpdateParams(void)
  523. {
  524.     return bindParams(TRUE);
  525. }  // wxDbTable::bindUpdateParams()
  526.  
  527.  
  528. /********** wxDbTable::bindCols() **********/
  529. bool wxDbTable::bindCols(HSTMT cursor)
  530. {
  531.     static SDWORD cb;
  532.  
  533.     // Bind each column of the table to a memory address for fetching data
  534.     UWORD i;
  535.     for (i = 0; i < noCols; i++)
  536.     {
  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));
  541.     }
  542.  
  543.     // Completed successfully
  544.     return(TRUE);
  545.  
  546. }  // wxDbTable::bindCols()
  547.  
  548.  
  549. /********** wxDbTable::getRec() **********/
  550. bool wxDbTable::getRec(UWORD fetchType)
  551. {
  552.     RETCODE retcode;
  553.  
  554.     if (!pDb->FwdOnlyCursors())
  555.     {
  556.         // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
  557.         UDWORD  cRowsFetched;
  558.         UWORD   rowStatus;
  559.  
  560.         retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus);
  561.         if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  562.         {
  563.             if (retcode == SQL_NO_DATA_FOUND)
  564.                 return(FALSE);
  565.             else
  566.                 return(pDb->DispAllErrors(henv, hdbc, hstmt));
  567.         }
  568.         else
  569.         {
  570.             // Set the Null member variable to indicate the Null state
  571.             // of each column just read in.
  572.             int i;
  573.             for (i = 0; i < noCols; i++)
  574.                 colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA);
  575.         }
  576.     }
  577.     else
  578.     {
  579.         // Fetch the next record from the record set
  580.         retcode = SQLFetch(hstmt);
  581.         if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  582.         {
  583.             if (retcode == SQL_NO_DATA_FOUND)
  584.                 return(FALSE);
  585.             else
  586.                 return(pDb->DispAllErrors(henv, hdbc, hstmt));
  587.         }
  588.         else
  589.         {
  590.             // Set the Null member variable to indicate the Null state
  591.             // of each column just read in.
  592.             int i;
  593.             for (i = 0; i < noCols; i++)
  594.                 colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA);
  595.         }
  596.     }
  597.  
  598.     // Completed successfully
  599.     return(TRUE);
  600.  
  601. }  // wxDbTable::getRec()
  602.  
  603.  
  604. /********** wxDbTable::execDelete() **********/
  605. bool wxDbTable::execDelete(const wxString &pSqlStmt)
  606. {
  607.     RETCODE retcode;
  608.  
  609.     // Execute the DELETE statement
  610.     retcode = SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
  611.  
  612.     if (retcode == SQL_SUCCESS ||
  613.         retcode == SQL_NO_DATA_FOUND ||
  614.         retcode == SQL_SUCCESS_WITH_INFO)
  615.     {
  616.         // Record deleted successfully
  617.         return(TRUE);
  618.     }
  619.  
  620.     // Problem deleting record
  621.     return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
  622.  
  623. }  // wxDbTable::execDelete()
  624.  
  625.  
  626. /********** wxDbTable::execUpdate() **********/
  627. bool wxDbTable::execUpdate(const wxString &pSqlStmt)
  628. {
  629.     RETCODE retcode;
  630.  
  631.     // Execute the UPDATE statement
  632.     retcode = SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
  633.  
  634.     if (retcode == SQL_SUCCESS ||
  635.         retcode == SQL_NO_DATA_FOUND ||
  636.         retcode == SQL_SUCCESS_WITH_INFO)
  637.     {
  638.         // Record updated successfully
  639.         return(TRUE);
  640.     }
  641.  
  642.     // Problem updating record
  643.     return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
  644.  
  645. }  // wxDbTable::execUpdate()
  646.  
  647.  
  648. /********** wxDbTable::query() **********/
  649. bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const wxString &pSqlStmt)
  650. {
  651.     wxString sqlStmt;
  652.  
  653.     if (forUpdate)
  654.         // The user may wish to select for update, but the DBMS may not be capable
  655.         selectForUpdate = CanSelectForUpdate();
  656.     else
  657.         selectForUpdate = FALSE;
  658.  
  659.     // Set the SQL SELECT string
  660.     if (queryType != DB_SELECT_STATEMENT)               // A select statement was not passed in,
  661.     {                                                   // so generate a select statement.
  662.         BuildSelectStmt(sqlStmt, queryType, distinct);
  663.         pDb->WriteSqlLog(sqlStmt);
  664.     }
  665.  
  666.     // Make sure the cursor is closed first
  667.     if (!CloseCursor(hstmt))
  668.         return(FALSE);
  669.  
  670.     // Execute the SQL SELECT statement
  671.     int retcode;
  672.     retcode = SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt.c_str() : sqlStmt.c_str()), SQL_NTS);
  673.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  674.         return(pDb->DispAllErrors(henv, hdbc, hstmt));
  675.  
  676.     // Completed successfully
  677.     return(TRUE);
  678.  
  679. }  // wxDbTable::query()
  680.  
  681.  
  682. /***************************** PUBLIC FUNCTIONS *****************************/
  683.  
  684.  
  685. /********** wxDbTable::Open() **********/
  686. bool wxDbTable::Open(bool checkPrivileges, bool checkTableExists)
  687. {
  688.     if (!pDb)
  689.         return FALSE;
  690.  
  691.     int i;
  692.     wxString sqlStmt;
  693.     wxString s;
  694. //    int NumKeyCols=0;
  695.  
  696.     // Calculate the maximum size of the concatenated
  697.     // keys for use with wxDbGrid
  698.     m_keysize = 0;
  699.     for (i=0; i < noCols; i++)
  700.     {
  701.         if (colDefs[i].KeyField)
  702.         {
  703. //            NumKeyCols++;
  704.             m_keysize += colDefs[i].SzDataObj;
  705.         }
  706.     }
  707.  
  708.     s.Empty();
  709.     // Verify that the table exists in the database
  710.     if (checkTableExists && !pDb->TableExists(tableName, pDb->GetUsername(), tablePath))
  711.     {
  712.         s = wxT("Table/view does not exist in the database");
  713.         if ( *(pDb->dbInf.accessibleTables) == wxT('Y'))
  714.             s += wxT(", or you have no permissions.\n");
  715.         else
  716.             s += wxT(".\n");
  717.     }
  718.     else if (checkPrivileges)
  719.     {
  720.         // Verify the user has rights to access the table.
  721.         // Shortcut boolean evaluation to optimize out call to
  722.         // TablePrivileges
  723.         //
  724.         // Unfortunately this optimization doesn't seem to be
  725.         // reliable!
  726.         if (// *(pDb->dbInf.accessibleTables) == 'N' &&
  727.             !pDb->TablePrivileges(tableName,wxT("SELECT"), pDb->GetUsername(), pDb->GetUsername(), tablePath))
  728.             s = wxT("Current logged in user does not have sufficient privileges to access this table.\n");
  729.     }
  730.  
  731.     if (!s.IsEmpty())
  732.     {
  733.         wxString p;
  734.  
  735.         if (!tablePath.IsEmpty())
  736.             p.Printf(wxT("Error opening '%s/%s'.\n"),tablePath.c_str(),tableName.c_str());
  737.         else
  738.             p.Printf(wxT("Error opening '%s'.\n"), tableName.c_str());
  739.  
  740.         p += s;
  741.         pDb->LogError(p.GetData());
  742.  
  743.         return(FALSE);
  744.     }
  745.  
  746.     // Bind the member variables for field exchange between
  747.     // the wxDbTable object and the ODBC record.
  748.     if (!queryOnly)
  749.     {
  750.         if (!bindInsertParams())                    // Inserts
  751.             return(FALSE);
  752.  
  753.         if (!bindUpdateParams())                    // Updates
  754.             return(FALSE);
  755.     }
  756.  
  757.     if (!bindCols(*hstmtDefault))                   // Selects
  758.         return(FALSE);
  759.  
  760.     if (!bindCols(hstmtInternal))                   // Internal use only
  761.         return(FALSE);
  762.  
  763.      /*
  764.      * Do NOT bind the hstmtCount cursor!!!
  765.      */
  766.  
  767.     // Build an insert statement using parameter markers
  768.     if (!queryOnly && noCols > 0)
  769.     {
  770.         bool needComma = FALSE;
  771.         sqlStmt.Printf(wxT("INSERT INTO %s ("),
  772.                        pDb->SQLTableName(tableName.c_str()).c_str());
  773.         for (i = 0; i < noCols; i++)
  774.         {
  775.             if (! colDefs[i].InsertAllowed)
  776.                 continue;
  777.             if (needComma)
  778.                 sqlStmt += wxT(",");
  779.             sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
  780. //            sqlStmt += colDefs[i].ColName;
  781.             needComma = TRUE;
  782.         }
  783.         needComma = FALSE;
  784.         sqlStmt += wxT(") VALUES (");
  785.  
  786.         int insertableCount = 0;
  787.  
  788.         for (i = 0; i < noCols; i++)
  789.         {
  790.             if (! colDefs[i].InsertAllowed)
  791.                 continue;
  792.             if (needComma)
  793.                 sqlStmt += wxT(",");
  794.             sqlStmt += wxT("?");
  795.             needComma = TRUE;
  796.             insertableCount++;
  797.         }
  798.         sqlStmt += wxT(")");
  799.  
  800.         // Prepare the insert statement for execution
  801.         if (insertableCount)
  802.         {
  803.             if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  804.                 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
  805.         }
  806.         else
  807.             insertable= FALSE;
  808.     }
  809.  
  810.     // Completed successfully
  811.     return(TRUE);
  812.  
  813. }  // wxDbTable::Open()
  814.  
  815.  
  816. /********** wxDbTable::Query() **********/
  817. bool wxDbTable::Query(bool forUpdate, bool distinct)
  818. {
  819.  
  820.     return(query(DB_SELECT_WHERE, forUpdate, distinct));
  821.  
  822. }  // wxDbTable::Query()
  823.  
  824.  
  825. /********** wxDbTable::QueryBySqlStmt() **********/
  826. bool wxDbTable::QueryBySqlStmt(const wxString &pSqlStmt)
  827. {
  828.     pDb->WriteSqlLog(pSqlStmt);
  829.  
  830.     return(query(DB_SELECT_STATEMENT, FALSE, FALSE, pSqlStmt));
  831.  
  832. }  // wxDbTable::QueryBySqlStmt()
  833.  
  834.  
  835. /********** wxDbTable::QueryMatching() **********/
  836. bool wxDbTable::QueryMatching(bool forUpdate, bool distinct)
  837. {
  838.  
  839.     return(query(DB_SELECT_MATCHING, forUpdate, distinct));
  840.  
  841. }  // wxDbTable::QueryMatching()
  842.  
  843.  
  844. /********** wxDbTable::QueryOnKeyFields() **********/
  845. bool wxDbTable::QueryOnKeyFields(bool forUpdate, bool distinct)
  846. {
  847.  
  848.     return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
  849.  
  850. }  // wxDbTable::QueryOnKeyFields()
  851.  
  852.  
  853. /********** wxDbTable::GetPrev() **********/
  854. bool wxDbTable::GetPrev(void)
  855. {
  856.     if (pDb->FwdOnlyCursors())
  857.     {
  858.         wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
  859.         return FALSE;
  860.     }
  861.     else
  862.         return(getRec(SQL_FETCH_PRIOR));
  863.  
  864. }  // wxDbTable::GetPrev()
  865.  
  866.  
  867. /********** wxDbTable::operator-- **********/
  868. bool wxDbTable::operator--(int)
  869. {
  870.     if (pDb->FwdOnlyCursors())
  871.     {
  872.         wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable"));
  873.         return FALSE;
  874.     }
  875.     else
  876.         return(getRec(SQL_FETCH_PRIOR));
  877.  
  878. }  // wxDbTable::operator--
  879.  
  880.  
  881. /********** wxDbTable::GetFirst() **********/
  882. bool wxDbTable::GetFirst(void)
  883. {
  884.     if (pDb->FwdOnlyCursors())
  885.     {
  886.         wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable"));
  887.         return FALSE;
  888.     }
  889.     else
  890.         return(getRec(SQL_FETCH_FIRST));
  891.  
  892. }  // wxDbTable::GetFirst()
  893.  
  894.  
  895. /********** wxDbTable::GetLast() **********/
  896. bool wxDbTable::GetLast(void)
  897. {
  898.     if (pDb->FwdOnlyCursors())
  899.     {
  900.         wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
  901.         return FALSE;
  902.     }
  903.     else
  904.         return(getRec(SQL_FETCH_LAST));
  905.  
  906. }  // wxDbTable::GetLast()
  907.  
  908.  
  909. /********** wxDbTable::BuildDeleteStmt() **********/
  910. void wxDbTable::BuildDeleteStmt(wxString &pSqlStmt, int typeOfDel, const wxString &pWhereClause)
  911. {
  912.     wxASSERT(!queryOnly);
  913.     if (queryOnly)
  914.         return;
  915.  
  916.     wxString whereClause;
  917.  
  918.     whereClause.Empty();
  919.  
  920.     // Handle the case of DeleteWhere() and the where clause is blank.  It should
  921.     // delete all records from the database in this case.
  922.     if (typeOfDel == DB_DEL_WHERE && (pWhereClause.Length() == 0))
  923.     {
  924.         pSqlStmt.Printf(wxT("DELETE FROM %s"),
  925.                         pDb->SQLTableName(tableName.c_str()).c_str());
  926.         return;
  927.     }
  928.  
  929.     pSqlStmt.Printf(wxT("DELETE FROM %s WHERE "),
  930.                     pDb->SQLTableName(tableName.c_str()).c_str());
  931.  
  932.     // Append the WHERE clause to the SQL DELETE statement
  933.     switch(typeOfDel)
  934.     {
  935.         case DB_DEL_KEYFIELDS:
  936.             // If the datasource supports the ROWID column, build
  937.             // the where on ROWID for efficiency purposes.
  938.             // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
  939.             if (CanUpdByROWID())
  940.             {
  941.                 SDWORD cb;
  942.                 wxChar   rowid[wxDB_ROWID_LEN+1];
  943.  
  944.                 // Get the ROWID value.  If not successful retreiving the ROWID,
  945.                 // simply fall down through the code and build the WHERE clause
  946.                 // based on the key fields.
  947.                 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
  948.                 {
  949.                     pSqlStmt += wxT("ROWID = '");
  950.                     pSqlStmt += rowid;
  951.                     pSqlStmt += wxT("'");
  952.                     break;
  953.                 }
  954.             }
  955.             // Unable to delete by ROWID, so build a WHERE
  956.             // clause based on the keyfields.
  957.             BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
  958.             pSqlStmt += whereClause;
  959.             break;
  960.         case DB_DEL_WHERE:
  961.             pSqlStmt += pWhereClause;
  962.             break;
  963.         case DB_DEL_MATCHING:
  964.             BuildWhereClause(whereClause, DB_WHERE_MATCHING);
  965.             pSqlStmt += whereClause;
  966.             break;
  967.     }
  968.  
  969. }  // BuildDeleteStmt()
  970.  
  971.  
  972. /***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/
  973. void wxDbTable::BuildDeleteStmt(wxChar *pSqlStmt, int typeOfDel, const wxString &pWhereClause)
  974. {
  975.     wxString tempSqlStmt;
  976.     BuildDeleteStmt(tempSqlStmt, typeOfDel, pWhereClause);
  977.     wxStrcpy(pSqlStmt, tempSqlStmt);
  978. }  // wxDbTable::BuildDeleteStmt()
  979.  
  980.  
  981. /********** wxDbTable::BuildSelectStmt() **********/
  982. void wxDbTable::BuildSelectStmt(wxString &pSqlStmt, int typeOfSelect, bool distinct)
  983. {
  984.     wxString whereClause;
  985.     whereClause.Empty();
  986.  
  987.     // Build a select statement to query the database
  988.     pSqlStmt = wxT("SELECT ");
  989.  
  990.     // SELECT DISTINCT values only?
  991.     if (distinct)
  992.         pSqlStmt += wxT("DISTINCT ");
  993.  
  994.     // Was a FROM clause specified to join tables to the base table?
  995.     // Available for ::Query() only!!!
  996.     bool appendFromClause = FALSE;
  997. #if wxODBC_BACKWARD_COMPATABILITY
  998.     if (typeOfSelect == DB_SELECT_WHERE && from && wxStrlen(from))
  999.         appendFromClause = TRUE;
  1000. #else
  1001.     if (typeOfSelect == DB_SELECT_WHERE && from.Length())
  1002.         appendFromClause = TRUE;
  1003. #endif
  1004.  
  1005.     // Add the column list
  1006.     int i;
  1007.     for (i = 0; i < noCols; i++)
  1008.     {
  1009.         // If joining tables, the base table column names must be qualified to avoid ambiguity
  1010.         if (appendFromClause || pDb->Dbms() == dbmsACCESS)
  1011.         {
  1012.             pSqlStmt += pDb->SQLTableName(queryTableName.c_str());
  1013. //            pSqlStmt += queryTableName;
  1014.             pSqlStmt += wxT(".");
  1015.         }
  1016.         pSqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
  1017. //        pSqlStmt += colDefs[i].ColName;
  1018.         if (i + 1 < noCols)
  1019.             pSqlStmt += wxT(",");
  1020.     }
  1021.  
  1022.     // If the datasource supports ROWID, get this column as well.  Exception: Don't retrieve
  1023.     // the ROWID if querying distinct records.  The rowid will always be unique.
  1024.     if (!distinct && CanUpdByROWID())
  1025.     {
  1026.         // If joining tables, the base table column names must be qualified to avoid ambiguity
  1027.         if (appendFromClause || pDb->Dbms() == dbmsACCESS)
  1028.         {
  1029.             pSqlStmt += wxT(",");
  1030.             pSqlStmt += pDb->SQLTableName(queryTableName);
  1031. //            pSqlStmt += queryTableName;
  1032.             pSqlStmt += wxT(".ROWID");
  1033.         }
  1034.         else
  1035.             pSqlStmt += wxT(",ROWID");
  1036.     }
  1037.  
  1038.     // Append the FROM tablename portion
  1039.     pSqlStmt += wxT(" FROM ");
  1040.     pSqlStmt += pDb->SQLTableName(queryTableName);
  1041. //    pSqlStmt += queryTableName;
  1042.  
  1043.     // Sybase uses the HOLDLOCK keyword to lock a record during query.
  1044.     // The HOLDLOCK keyword follows the table name in the from clause.
  1045.     // Each table in the from clause must specify HOLDLOCK or
  1046.     // NOHOLDLOCK (the default).  Note: The "FOR UPDATE" clause
  1047.     // is parsed but ignored in SYBASE Transact-SQL.
  1048.     if (selectForUpdate && (pDb->Dbms() == dbmsSYBASE_ASA || pDb->Dbms() == dbmsSYBASE_ASE))
  1049.         pSqlStmt += wxT(" HOLDLOCK");
  1050.  
  1051.     if (appendFromClause)
  1052.         pSqlStmt += from;
  1053.  
  1054.     // Append the WHERE clause.  Either append the where clause for the class
  1055.     // or build a where clause.  The typeOfSelect determines this.
  1056.     switch(typeOfSelect)
  1057.     {
  1058.         case DB_SELECT_WHERE:
  1059. #if wxODBC_BACKWARD_COMPATABILITY
  1060.             if (where && wxStrlen(where))   // May not want a where clause!!!
  1061. #else
  1062.             if (where.Length())   // May not want a where clause!!!
  1063. #endif
  1064.             {
  1065.                 pSqlStmt += wxT(" WHERE ");
  1066.                 pSqlStmt += where;
  1067.             }
  1068.             break;
  1069.         case DB_SELECT_KEYFIELDS:
  1070.             BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
  1071.             if (whereClause.Length())
  1072.             {
  1073.                 pSqlStmt += wxT(" WHERE ");
  1074.                 pSqlStmt += whereClause;
  1075.             }
  1076.             break;
  1077.         case DB_SELECT_MATCHING:
  1078.             BuildWhereClause(whereClause, DB_WHERE_MATCHING);
  1079.             if (whereClause.Length())
  1080.             {
  1081.                 pSqlStmt += wxT(" WHERE ");
  1082.                 pSqlStmt += whereClause;
  1083.             }
  1084.             break;
  1085.     }
  1086.  
  1087.     // Append the ORDER BY clause
  1088. #if wxODBC_BACKWARD_COMPATABILITY
  1089.     if (orderBy && wxStrlen(orderBy))
  1090. #else
  1091.     if (orderBy.Length())
  1092. #endif
  1093.     {
  1094.         pSqlStmt += wxT(" ORDER BY ");
  1095.         pSqlStmt += orderBy;
  1096.     }
  1097.  
  1098.     // SELECT FOR UPDATE if told to do so and the datasource is capable.  Sybase
  1099.     // parses the FOR UPDATE clause but ignores it.  See the comment above on the
  1100.     // HOLDLOCK for Sybase.
  1101.     if (selectForUpdate && CanSelectForUpdate())
  1102.         pSqlStmt += wxT(" FOR UPDATE");
  1103.  
  1104. }  // wxDbTable::BuildSelectStmt()
  1105.  
  1106.  
  1107. /***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/
  1108. void wxDbTable::BuildSelectStmt(wxChar *pSqlStmt, int typeOfSelect, bool distinct)
  1109. {
  1110.     wxString tempSqlStmt;
  1111.     BuildSelectStmt(tempSqlStmt, typeOfSelect, distinct);
  1112.     wxStrcpy(pSqlStmt, tempSqlStmt);
  1113. }  // wxDbTable::BuildSelectStmt()
  1114.  
  1115.  
  1116. /********** wxDbTable::BuildUpdateStmt() **********/
  1117. void wxDbTable::BuildUpdateStmt(wxString &pSqlStmt, int typeOfUpd, const wxString &pWhereClause)
  1118. {
  1119.     wxASSERT(!queryOnly);
  1120.     if (queryOnly)
  1121.         return;
  1122.  
  1123.     wxString whereClause;
  1124.     whereClause.Empty();
  1125.  
  1126.     bool firstColumn = TRUE;
  1127.  
  1128.     pSqlStmt.Printf(wxT("UPDATE %s SET "),
  1129.                     pDb->SQLTableName(tableName.c_str()).c_str());
  1130.  
  1131.     // Append a list of columns to be updated
  1132.     int i;
  1133.     for (i = 0; i < noCols; i++)
  1134.     {
  1135.         // Only append Updateable columns
  1136.         if (colDefs[i].Updateable)
  1137.         {
  1138.             if (!firstColumn)
  1139.                 pSqlStmt += wxT(",");
  1140.             else
  1141.                 firstColumn = FALSE;
  1142.  
  1143.             pSqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
  1144. //            pSqlStmt += colDefs[i].ColName;
  1145.             pSqlStmt += wxT(" = ?");
  1146.         }
  1147.     }
  1148.  
  1149.     // Append the WHERE clause to the SQL UPDATE statement
  1150.     pSqlStmt += wxT(" WHERE ");
  1151.     switch(typeOfUpd)
  1152.     {
  1153.         case DB_UPD_KEYFIELDS:
  1154.             // If the datasource supports the ROWID column, build
  1155.             // the where on ROWID for efficiency purposes.
  1156.             // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
  1157.             if (CanUpdByROWID())
  1158.             {
  1159.                 SDWORD cb;
  1160.                 wxChar rowid[wxDB_ROWID_LEN+1];
  1161.  
  1162.                 // Get the ROWID value.  If not successful retreiving the ROWID,
  1163.                 // simply fall down through the code and build the WHERE clause
  1164.                 // based on the key fields.
  1165.                 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
  1166.                 {
  1167.                     pSqlStmt += wxT("ROWID = '");
  1168.                     pSqlStmt += rowid;
  1169.                     pSqlStmt += wxT("'");
  1170.                     break;
  1171.                 }
  1172.             }
  1173.             // Unable to delete by ROWID, so build a WHERE
  1174.             // clause based on the keyfields.
  1175.             BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
  1176.             pSqlStmt += whereClause;
  1177.             break;
  1178.         case DB_UPD_WHERE:
  1179.             pSqlStmt += pWhereClause;
  1180.             break;
  1181.     }
  1182. }  // BuildUpdateStmt()
  1183.  
  1184.  
  1185. /***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/
  1186. void wxDbTable::BuildUpdateStmt(wxChar *pSqlStmt, int typeOfUpd, const wxString &pWhereClause)
  1187. {
  1188.     wxString tempSqlStmt;
  1189.     BuildUpdateStmt(tempSqlStmt, typeOfUpd, pWhereClause);
  1190.     wxStrcpy(pSqlStmt, tempSqlStmt);
  1191. }  // BuildUpdateStmt()
  1192.  
  1193.  
  1194. /********** wxDbTable::BuildWhereClause() **********/
  1195. void wxDbTable::BuildWhereClause(wxString &pWhereClause, int typeOfWhere,
  1196.                                  const wxString &qualTableName, bool useLikeComparison)
  1197. /*
  1198.  * Note: BuildWhereClause() currently ignores timestamp columns.
  1199.  *       They are not included as part of the where clause.
  1200.  */
  1201. {
  1202.     bool moreThanOneColumn = FALSE;
  1203.     wxString colValue;
  1204.  
  1205.     // Loop through the columns building a where clause as you go
  1206.     int i;
  1207.     for (i = 0; i < noCols; i++)
  1208.     {
  1209.         // Determine if this column should be included in the WHERE clause
  1210.         if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[i].KeyField) ||
  1211.              (typeOfWhere == DB_WHERE_MATCHING  && (!IsColNull(i))))
  1212.         {
  1213.             // Skip over timestamp columns
  1214.             if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
  1215.                 continue;
  1216.             // If there is more than 1 column, join them with the keyword "AND"
  1217.             if (moreThanOneColumn)
  1218.                 pWhereClause += wxT(" AND ");
  1219.             else
  1220.                 moreThanOneColumn = TRUE;
  1221.             // Concatenate where phrase for the column
  1222.             if (qualTableName.Length())
  1223.             {
  1224.                 pWhereClause += pDb->SQLTableName(qualTableName);
  1225. //                pWhereClause += qualTableName;
  1226.                 pWhereClause += wxT(".");
  1227.             }
  1228.             pWhereClause += pDb->SQLColumnName(colDefs[i].ColName);
  1229. //            pWhereClause += colDefs[i].ColName;
  1230.             if (useLikeComparison && (colDefs[i].SqlCtype == SQL_C_CHAR))
  1231.                 pWhereClause += wxT(" LIKE ");
  1232.             else
  1233.                 pWhereClause += wxT(" = ");
  1234.             switch(colDefs[i].SqlCtype)
  1235.             {
  1236.                 case SQL_C_CHAR:
  1237.                     colValue.Printf(wxT("'%s'"), (UCHAR FAR *) colDefs[i].PtrDataObj);
  1238.                     break;
  1239.                 case SQL_C_SSHORT:
  1240.                     colValue.Printf(wxT("%hi"), *((SWORD *) colDefs[i].PtrDataObj));
  1241.                     break;
  1242.                 case SQL_C_USHORT:
  1243.                     colValue.Printf(wxT("%hu"), *((UWORD *) colDefs[i].PtrDataObj));
  1244.                     break;
  1245.                 case SQL_C_SLONG:
  1246.                     colValue.Printf(wxT("%li"), *((SDWORD *) colDefs[i].PtrDataObj));
  1247.                     break;
  1248.                 case SQL_C_ULONG:
  1249.                     colValue.Printf(wxT("%lu"), *((UDWORD *) colDefs[i].PtrDataObj));
  1250.                     break;
  1251.                 case SQL_C_FLOAT:
  1252.                     colValue.Printf(wxT("%.6f"), *((SFLOAT *) colDefs[i].PtrDataObj));
  1253.                     break;
  1254.                 case SQL_C_DOUBLE:
  1255.                     colValue.Printf(wxT("%.6f"), *((SDOUBLE *) colDefs[i].PtrDataObj));
  1256.                     break;
  1257.             }
  1258.             pWhereClause += colValue;
  1259.         }
  1260.     }
  1261. }  // wxDbTable::BuildWhereClause()
  1262.  
  1263.  
  1264. /***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/
  1265. void wxDbTable::BuildWhereClause(wxChar *pWhereClause, int typeOfWhere,
  1266.                                  const wxString &qualTableName, bool useLikeComparison)
  1267. {
  1268.     wxString tempSqlStmt;
  1269.     BuildWhereClause(tempSqlStmt, typeOfWhere, qualTableName, useLikeComparison);
  1270.     wxStrcpy(pWhereClause, tempSqlStmt);
  1271. }  // wxDbTable::BuildWhereClause()
  1272.  
  1273.  
  1274. /********** wxDbTable::GetRowNum() **********/
  1275. UWORD wxDbTable::GetRowNum(void)
  1276. {
  1277.     UDWORD rowNum;
  1278.  
  1279.     if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, (UCHAR*) &rowNum) != SQL_SUCCESS)
  1280.     {
  1281.         pDb->DispAllErrors(henv, hdbc, hstmt);
  1282.         return(0);
  1283.     }
  1284.  
  1285.     // Completed successfully
  1286.     return((UWORD) rowNum);
  1287.  
  1288. }  // wxDbTable::GetRowNum()
  1289.  
  1290.  
  1291. /********** wxDbTable::CloseCursor() **********/
  1292. bool wxDbTable::CloseCursor(HSTMT cursor)
  1293. {
  1294.     if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
  1295.         return(pDb->DispAllErrors(henv, hdbc, cursor));
  1296.  
  1297.     // Completed successfully
  1298.     return(TRUE);
  1299.  
  1300. }  // wxDbTable::CloseCursor()
  1301.  
  1302.  
  1303. /********** wxDbTable::CreateTable() **********/
  1304. bool wxDbTable::CreateTable(bool attemptDrop)
  1305. {
  1306.     if (!pDb)
  1307.         return FALSE;
  1308.  
  1309.     int i, j;
  1310.     wxString sqlStmt;
  1311.  
  1312. #ifdef DBDEBUG_CONSOLE
  1313.     cout << wxT("Creating Table ") << tableName << wxT("...") << endl;
  1314. #endif
  1315.  
  1316.     // Drop table first
  1317.     if (attemptDrop && !DropTable())
  1318.         return FALSE;
  1319.  
  1320.     // Create the table
  1321. #ifdef DBDEBUG_CONSOLE
  1322.     for (i = 0; i < noCols; i++)
  1323.     {
  1324.         // Exclude derived columns since they are NOT part of the base table
  1325.         if (colDefs[i].DerivedCol)
  1326.             continue;
  1327.         cout << i + 1 << wxT(": ") << colDefs[i].ColName << wxT("; ");
  1328.         switch(colDefs[i].DbDataType)
  1329.         {
  1330.             case DB_DATA_TYPE_VARCHAR:
  1331.                 cout << pDb->GetTypeInfVarchar().TypeName << wxT("(") << colDefs[i].SzDataObj << wxT(")");
  1332.                 break;
  1333.             case DB_DATA_TYPE_INTEGER:
  1334.                 cout << pDb->GetTypeInfInteger().TypeName;
  1335.                 break;
  1336.             case DB_DATA_TYPE_FLOAT:
  1337.                 cout << pDb->GetTypeInfFloat().TypeName;
  1338.                 break;
  1339.             case DB_DATA_TYPE_DATE:
  1340.                 cout << pDb->GetTypeInfDate().TypeName;
  1341.                 break;
  1342.             case DB_DATA_TYPE_BLOB:
  1343.                 cout << pDb->GetTypeInfBlob().TypeName;
  1344.                 break;
  1345.         }
  1346.         cout << endl;
  1347.     }
  1348. #endif
  1349.  
  1350.     // Build a CREATE TABLE string from the colDefs structure.
  1351.     bool needComma = FALSE;
  1352.  
  1353.     sqlStmt.Printf(wxT("CREATE TABLE %s ("),
  1354.                    pDb->SQLTableName(tableName.c_str()).c_str());
  1355.  
  1356.     for (i = 0; i < noCols; i++)
  1357.     {
  1358.         // Exclude derived columns since they are NOT part of the base table
  1359.         if (colDefs[i].DerivedCol)
  1360.             continue;
  1361.         // Comma Delimiter
  1362.         if (needComma)
  1363.             sqlStmt += wxT(",");
  1364.         // Column Name
  1365.         sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
  1366. //        sqlStmt += colDefs[i].ColName;
  1367.         sqlStmt += wxT(" ");
  1368.         // Column Type
  1369.         switch(colDefs[i].DbDataType)
  1370.         {
  1371.             case DB_DATA_TYPE_VARCHAR:
  1372.                 sqlStmt += pDb->GetTypeInfVarchar().TypeName;
  1373.                 break;
  1374.             case DB_DATA_TYPE_INTEGER:
  1375.                 sqlStmt += pDb->GetTypeInfInteger().TypeName;
  1376.                 break;
  1377.             case DB_DATA_TYPE_FLOAT:
  1378.                 sqlStmt += pDb->GetTypeInfFloat().TypeName;
  1379.                 break;
  1380.             case DB_DATA_TYPE_DATE:
  1381.                 sqlStmt += pDb->GetTypeInfDate().TypeName;
  1382.                 break;
  1383.             case DB_DATA_TYPE_BLOB:
  1384.                 sqlStmt += pDb->GetTypeInfBlob().TypeName;
  1385.                 break;
  1386.         }
  1387.         // For varchars, append the size of the string
  1388.         if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR &&
  1389.             (pDb->Dbms() != dbmsMY_SQL || pDb->GetTypeInfVarchar().TypeName != "text"))// ||
  1390. //            colDefs[i].DbDataType == DB_DATA_TYPE_BLOB)
  1391.         {
  1392.             wxString s;
  1393.             s.Printf(wxT("(%d)"), colDefs[i].SzDataObj);
  1394.             sqlStmt += s;
  1395.         }
  1396.  
  1397.         if (pDb->Dbms() == dbmsDB2 ||
  1398.             pDb->Dbms() == dbmsMY_SQL ||
  1399.             pDb->Dbms() == dbmsSYBASE_ASE  ||
  1400.             pDb->Dbms() == dbmsINTERBASE  ||
  1401.             pDb->Dbms() == dbmsMS_SQL_SERVER)
  1402.         {
  1403.             if (colDefs[i].KeyField)
  1404.             {
  1405.                 sqlStmt += wxT(" NOT NULL");
  1406.             }
  1407.         }
  1408.  
  1409.         needComma = TRUE;
  1410.     }
  1411.     // If there is a primary key defined, include it in the create statement
  1412.     for (i = j = 0; i < noCols; i++)
  1413.     {
  1414.         if (colDefs[i].KeyField)
  1415.         {
  1416.             j++;
  1417.             break;
  1418.         }
  1419.     }
  1420.     if (j && (pDb->Dbms() != dbmsDBASE) 
  1421.           && (pDb->Dbms() != dbmsXBASE_SEQUITER)
  1422.        )  // Found a keyfield
  1423.     {
  1424.         switch (pDb->Dbms())
  1425.         {
  1426.             case dbmsACCESS:
  1427.             case dbmsINFORMIX:
  1428.             case dbmsSYBASE_ASA:
  1429.             case dbmsSYBASE_ASE:
  1430.             case dbmsMY_SQL:
  1431.             {
  1432.                 // MySQL goes out on this one. We also declare the relevant key NON NULL above
  1433.                 sqlStmt += wxT(",PRIMARY KEY (");
  1434.                 break;
  1435.             }
  1436.             default:
  1437.             {
  1438.                 sqlStmt += wxT(",CONSTRAINT ");
  1439.                 //  DB2 is limited to 18 characters for index names
  1440.                 if (pDb->Dbms() == dbmsDB2)
  1441.                 {
  1442.                     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."));
  1443.                     sqlStmt += pDb->SQLTableName(tableName.substr(0, 13).c_str());
  1444. //                    sqlStmt += tableName.substr(0, 13);
  1445.                 }
  1446.                 else
  1447.                     sqlStmt += pDb->SQLTableName(tableName.c_str());
  1448. //                    sqlStmt += tableName;
  1449.  
  1450.                 sqlStmt += wxT("_PIDX PRIMARY KEY (");
  1451.                 break;
  1452.             }
  1453.         }
  1454.  
  1455.         // List column name(s) of column(s) comprising the primary key
  1456.         for (i = j = 0; i < noCols; i++)
  1457.         {
  1458.             if (colDefs[i].KeyField)
  1459.             {
  1460.                 if (j++) // Multi part key, comma separate names
  1461.                     sqlStmt += wxT(",");
  1462.                 sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
  1463.  
  1464.                 if (pDb->Dbms() == dbmsMY_SQL &&
  1465.                     colDefs[i].DbDataType ==  DB_DATA_TYPE_VARCHAR)
  1466.                 {
  1467.                     wxString s;
  1468.                     s.Printf(wxT("(%d)"), colDefs[i].SzDataObj);
  1469.                     sqlStmt += s;
  1470.                 }
  1471.             }
  1472.         }
  1473.         sqlStmt += wxT(")");
  1474.  
  1475.         if (pDb->Dbms() == dbmsINFORMIX ||
  1476.             pDb->Dbms() == dbmsSYBASE_ASA ||
  1477.             pDb->Dbms() == dbmsSYBASE_ASE)
  1478.         {
  1479.             sqlStmt += wxT(" CONSTRAINT ");
  1480.             sqlStmt += pDb->SQLTableName(tableName);
  1481. //            sqlStmt += tableName;
  1482.             sqlStmt += wxT("_PIDX");
  1483.         }
  1484.     }
  1485.     // Append the closing parentheses for the create table statement
  1486.     sqlStmt += wxT(")");
  1487.  
  1488.     pDb->WriteSqlLog(sqlStmt);
  1489.  
  1490. #ifdef DBDEBUG_CONSOLE
  1491.     cout << endl << sqlStmt.c_str() << endl;
  1492. #endif
  1493.  
  1494.     // Execute the CREATE TABLE statement
  1495.     RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
  1496.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  1497.     {
  1498.         pDb->DispAllErrors(henv, hdbc, hstmt);
  1499.         pDb->RollbackTrans();
  1500.         CloseCursor(hstmt);
  1501.         return(FALSE);
  1502.     }
  1503.  
  1504.     // Commit the transaction and close the cursor
  1505.     if (!pDb->CommitTrans())
  1506.         return(FALSE);
  1507.     if (!CloseCursor(hstmt))
  1508.         return(FALSE);
  1509.  
  1510.     // Database table created successfully
  1511.     return(TRUE);
  1512.  
  1513. } // wxDbTable::CreateTable()
  1514.  
  1515.  
  1516. /********** wxDbTable::DropTable() **********/
  1517. bool wxDbTable::DropTable()
  1518. {
  1519.     // NOTE: This function returns TRUE if the Table does not exist, but
  1520.     //       only for identified databases.  Code will need to be added
  1521.     //       below for any other databases when those databases are defined
  1522.     //       to handle this situation consistently
  1523.  
  1524.     wxString sqlStmt;
  1525.  
  1526.     sqlStmt.Printf(wxT("DROP TABLE %s"),
  1527.                    pDb->SQLTableName(tableName.c_str()).c_str());
  1528.  
  1529.     pDb->WriteSqlLog(sqlStmt);
  1530.  
  1531. #ifdef DBDEBUG_CONSOLE
  1532.     cout << endl << sqlStmt.c_str() << endl;
  1533. #endif
  1534.  
  1535.     RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
  1536.     if (retcode != SQL_SUCCESS)
  1537.     {
  1538.         // Check for "Base table not found" error and ignore
  1539.         pDb->GetNextError(henv, hdbc, hstmt);
  1540.         if (wxStrcmp(pDb->sqlState, wxT("S0002")) /*&&
  1541.             wxStrcmp(pDb->sqlState, wxT("S1000"))*/)  // "Base table not found"
  1542.         {
  1543.             // Check for product specific error codes
  1544.             if (!((pDb->Dbms() == dbmsSYBASE_ASA    && !wxStrcmp(pDb->sqlState,wxT("42000")))   ||  // 5.x (and lower?)
  1545.                   (pDb->Dbms() == dbmsSYBASE_ASE    && !wxStrcmp(pDb->sqlState,wxT("37000")))   ||
  1546.                   (pDb->Dbms() == dbmsPERVASIVE_SQL && !wxStrcmp(pDb->sqlState,wxT("S1000")))   ||  // Returns an S1000 then an S0002
  1547.                   (pDb->Dbms() == dbmsPOSTGRES      && !wxStrcmp(pDb->sqlState,wxT("08S01")))))
  1548.             {
  1549.                 pDb->DispNextError();
  1550.                 pDb->DispAllErrors(henv, hdbc, hstmt);
  1551.                 pDb->RollbackTrans();
  1552. //                CloseCursor(hstmt);
  1553.                 return(FALSE);
  1554.             }
  1555.         }
  1556.     }
  1557.  
  1558.     // Commit the transaction and close the cursor
  1559.     if (! pDb->CommitTrans())
  1560.         return(FALSE);
  1561.     if (! CloseCursor(hstmt))
  1562.         return(FALSE);
  1563.  
  1564.     return(TRUE);
  1565. }  // wxDbTable::DropTable()
  1566.  
  1567.  
  1568. /********** wxDbTable::CreateIndex() **********/
  1569. bool wxDbTable::CreateIndex(const wxString &idxName, bool unique, UWORD noIdxCols,
  1570.                                      wxDbIdxDef *pIdxDefs, bool attemptDrop)
  1571. {
  1572.     wxString sqlStmt;
  1573.  
  1574.     // Drop the index first
  1575.     if (attemptDrop && !DropIndex(idxName))
  1576.         return (FALSE);
  1577.  
  1578.     // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions
  1579.     // of an index have the columns defined as "NOT NULL".  During initial table creation though,
  1580.     // it may not be known which columns are necessarily going to be part of an index (e.g. the
  1581.     // table was created, then months later you determine that an additional index while
  1582.     // give better performance, so you want to add an index).
  1583.     //
  1584.     // The following block of code will modify the column definition to make the column be
  1585.     // defined with the "NOT NULL" qualifier.
  1586.     if (pDb->Dbms() == dbmsMY_SQL)
  1587.     {
  1588.         wxString sqlStmt;
  1589.         int i;
  1590.         bool ok = TRUE;
  1591.         for (i = 0; i < noIdxCols && ok; i++)
  1592.         {
  1593.             int   j = 0;
  1594.             bool  found = FALSE;
  1595.             // Find the column definition that has the ColName that matches the
  1596.             // index column name.  We need to do this to get the DB_DATA_TYPE of
  1597.             // the index column, as MySQL's syntax for the ALTER column requires
  1598.             // this information
  1599.             while (!found && (j < this->noCols))
  1600.             {
  1601.                 if (wxStrcmp(colDefs[j].ColName,pIdxDefs[i].ColName) == 0)
  1602.                     found = TRUE;
  1603.                 if (!found)
  1604.                     j++;
  1605.             }
  1606.  
  1607.             if (found)
  1608.             {
  1609.                 ok = pDb->ModifyColumn(tableName, pIdxDefs[i].ColName,
  1610.                                         colDefs[j].DbDataType, colDefs[j].SzDataObj,
  1611.                                         wxT("NOT NULL"));
  1612.  
  1613.                 if (!ok)
  1614.                 {
  1615.                     wxODBC_ERRORS retcode;
  1616.                     // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already
  1617.                     // defined to be NOT NULL, but reportedly MySQL doesn't mind.
  1618.                     // This line is just here for debug checking of the value
  1619.                     retcode = (wxODBC_ERRORS)pDb->DB_STATUS;
  1620.                 }
  1621.             }
  1622.             else
  1623.                 ok = FALSE;
  1624.         }
  1625.         if (ok)
  1626.             pDb->CommitTrans();
  1627.         else
  1628.         {
  1629.             pDb->RollbackTrans();
  1630.             return(FALSE);
  1631.         }
  1632.     }
  1633.  
  1634.     // Build a CREATE INDEX statement
  1635.     sqlStmt = wxT("CREATE ");
  1636.     if (unique)
  1637.         sqlStmt += wxT("UNIQUE ");
  1638.  
  1639.     sqlStmt += wxT("INDEX ");
  1640.     sqlStmt += pDb->SQLTableName(idxName);
  1641.     sqlStmt += wxT(" ON ");
  1642.  
  1643.     sqlStmt += pDb->SQLTableName(tableName);
  1644. //    sqlStmt += tableName;
  1645.     sqlStmt += wxT(" (");
  1646.  
  1647.     // Append list of columns making up index
  1648.     int i;
  1649.     for (i = 0; i < noIdxCols; i++)
  1650.     {
  1651.         sqlStmt += pDb->SQLColumnName(pIdxDefs[i].ColName);
  1652. //        sqlStmt += pIdxDefs[i].ColName;
  1653.  
  1654.         // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns
  1655.         if (!((pDb->Dbms() == dbmsMS_SQL_SERVER) && (strncmp(pDb->dbInf.dbmsVer,"07",2)==0)) &&
  1656.             !(pDb->Dbms() == dbmsPOSTGRES))
  1657.         {
  1658.             if (pIdxDefs[i].Ascending)
  1659.                 sqlStmt += wxT(" ASC");
  1660.             else
  1661.                 sqlStmt += wxT(" DESC");
  1662.         }
  1663.         else
  1664.             wxASSERT_MSG(pIdxDefs[i].Ascending, "Datasource does not support DESCending index columns");
  1665.  
  1666.         if ((i + 1) < noIdxCols)
  1667.             sqlStmt += wxT(",");
  1668.     }
  1669.  
  1670.     // Append closing parentheses
  1671.     sqlStmt += wxT(")");
  1672.  
  1673.     pDb->WriteSqlLog(sqlStmt);
  1674.  
  1675. #ifdef DBDEBUG_CONSOLE
  1676.     cout << endl << sqlStmt.c_str() << endl << endl;
  1677. #endif
  1678.  
  1679.     // Execute the CREATE INDEX statement
  1680.     if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  1681.     {
  1682.         pDb->DispAllErrors(henv, hdbc, hstmt);
  1683.         pDb->RollbackTrans();
  1684.         CloseCursor(hstmt);
  1685.         return(FALSE);
  1686.     }
  1687.  
  1688.     // Commit the transaction and close the cursor
  1689.     if (! pDb->CommitTrans())
  1690.         return(FALSE);
  1691.     if (! CloseCursor(hstmt))
  1692.         return(FALSE);
  1693.  
  1694.     // Index Created Successfully
  1695.     return(TRUE);
  1696.  
  1697. }  // wxDbTable::CreateIndex()
  1698.  
  1699.  
  1700. /********** wxDbTable::DropIndex() **********/
  1701. bool wxDbTable::DropIndex(const wxString &idxName)
  1702. {
  1703.     // NOTE: This function returns TRUE if the Index does not exist, but
  1704.     //       only for identified databases.  Code will need to be added
  1705.     //       below for any other databases when those databases are defined
  1706.     //       to handle this situation consistently
  1707.  
  1708.     wxString sqlStmt;
  1709.  
  1710.     if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL ||
  1711.         pDb->Dbms() == dbmsDBASE /*|| Paradox needs this syntax too when we add support*/)
  1712.         sqlStmt.Printf(wxT("DROP INDEX %s ON %s"),
  1713.                        pDb->SQLTableName(idxName.c_str()).c_str(),
  1714.                        pDb->SQLTableName(tableName.c_str()).c_str());
  1715.     else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) ||
  1716.              (pDb->Dbms() == dbmsSYBASE_ASE) ||
  1717.              (pDb->Dbms() == dbmsXBASE_SEQUITER))
  1718.         sqlStmt.Printf(wxT("DROP INDEX %s.%s"),
  1719.                        pDb->SQLTableName(tableName.c_str()).c_str(),
  1720.                        pDb->SQLTableName(idxName.c_str()).c_str());
  1721.     else
  1722.         sqlStmt.Printf(wxT("DROP INDEX %s"),
  1723.                        pDb->SQLTableName(idxName.c_str()).c_str());
  1724.  
  1725.     pDb->WriteSqlLog(sqlStmt);
  1726.  
  1727. #ifdef DBDEBUG_CONSOLE
  1728.     cout << endl << sqlStmt.c_str() << endl;
  1729. #endif
  1730.  
  1731.     if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  1732.     {
  1733.         // Check for "Index not found" error and ignore
  1734.         pDb->GetNextError(henv, hdbc, hstmt);
  1735.         if (wxStrcmp(pDb->sqlState,wxT("S0012")))  // "Index not found"
  1736.         {
  1737.             // Check for product specific error codes
  1738.             if (!((pDb->Dbms() == dbmsSYBASE_ASA    && !wxStrcmp(pDb->sqlState,wxT("42000"))) ||  // v5.x (and lower?)
  1739.                   (pDb->Dbms() == dbmsSYBASE_ASE    && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
  1740.                   (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
  1741.                   (pDb->Dbms() == dbmsINTERBASE      && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
  1742.                   (pDb->Dbms() == dbmsSYBASE_ASE    && !wxStrcmp(pDb->sqlState,wxT("S0002"))) ||  // Base table not found
  1743.                   (pDb->Dbms() == dbmsMY_SQL        && !wxStrcmp(pDb->sqlState,wxT("42S12"))) ||  // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta
  1744.                   (pDb->Dbms() == dbmsPOSTGRES      && !wxStrcmp(pDb->sqlState,wxT("08S01")))
  1745.                ))
  1746.             {
  1747.                 pDb->DispNextError();
  1748.                 pDb->DispAllErrors(henv, hdbc, hstmt);
  1749.                 pDb->RollbackTrans();
  1750.                 CloseCursor(hstmt);
  1751.                 return(FALSE);
  1752.             }
  1753.         }
  1754.     }
  1755.  
  1756.     // Commit the transaction and close the cursor
  1757.     if (! pDb->CommitTrans())
  1758.         return(FALSE);
  1759.     if (! CloseCursor(hstmt))
  1760.         return(FALSE);
  1761.  
  1762.     return(TRUE);
  1763. }  // wxDbTable::DropIndex()
  1764.  
  1765.  
  1766. /********** wxDbTable::SetOrderByColNums() **********/
  1767. bool wxDbTable::SetOrderByColNums(UWORD first, ... )
  1768. {
  1769.     int        colNo = first;  // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS
  1770.     va_list     argptr;
  1771.  
  1772.     bool        abort = FALSE;
  1773.     wxString    tempStr;
  1774.  
  1775.     va_start(argptr, first);     /* Initialize variable arguments. */
  1776.     while (!abort && (colNo != wxDB_NO_MORE_COLUMN_NUMBERS))
  1777.     {
  1778.         // Make sure the passed in column number
  1779.         // is within the valid range of columns
  1780.         //
  1781.         // Valid columns are 0 thru noCols-1
  1782.         if (colNo >= noCols || colNo < 0)
  1783.         {
  1784.             abort = TRUE;
  1785.             continue;
  1786.         }
  1787.  
  1788.         if (colNo != first)
  1789.             tempStr += wxT(",");
  1790.  
  1791.         tempStr += colDefs[colNo].ColName;
  1792.         colNo = va_arg (argptr, int);
  1793.     }
  1794.     va_end (argptr);              /* Reset variable arguments.      */
  1795.  
  1796.     SetOrderByClause(tempStr);
  1797.  
  1798.     return (!abort);
  1799. }  // wxDbTable::SetOrderByColNums()
  1800.  
  1801.  
  1802. /********** wxDbTable::Insert() **********/
  1803. int wxDbTable::Insert(void)
  1804. {
  1805.     wxASSERT(!queryOnly);
  1806.     if (queryOnly || !insertable)
  1807.         return(DB_FAILURE);
  1808.  
  1809.     bindInsertParams();
  1810.  
  1811.     // Insert the record by executing the already prepared insert statement
  1812.     RETCODE retcode;
  1813.     retcode=SQLExecute(hstmtInsert);
  1814.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  1815.     {
  1816.         // Check to see if integrity constraint was violated
  1817.         pDb->GetNextError(henv, hdbc, hstmtInsert);
  1818.         if (! wxStrcmp(pDb->sqlState, wxT("23000")))  // Integrity constraint violated
  1819.             return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
  1820.         else
  1821.         {
  1822.             pDb->DispNextError();
  1823.             pDb->DispAllErrors(henv, hdbc, hstmtInsert);
  1824.             return(DB_FAILURE);
  1825.         }
  1826.     }
  1827.  
  1828.     // Record inserted into the datasource successfully
  1829.     return(DB_SUCCESS);
  1830.  
  1831. }  // wxDbTable::Insert()
  1832.  
  1833.  
  1834. /********** wxDbTable::Update() **********/
  1835. bool wxDbTable::Update(void)
  1836. {
  1837.     wxASSERT(!queryOnly);
  1838.     if (queryOnly)
  1839.         return(FALSE);
  1840.  
  1841.     wxString sqlStmt;
  1842.  
  1843.     // Build the SQL UPDATE statement
  1844.     BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
  1845.  
  1846.     pDb->WriteSqlLog(sqlStmt);
  1847.  
  1848. #ifdef DBDEBUG_CONSOLE
  1849.     cout << endl << sqlStmt.c_str() << endl << endl;
  1850. #endif
  1851.  
  1852.     // Execute the SQL UPDATE statement
  1853.     return(execUpdate(sqlStmt));
  1854.  
  1855. }  // wxDbTable::Update()
  1856.  
  1857.  
  1858. /********** wxDbTable::Update(pSqlStmt) **********/
  1859. bool wxDbTable::Update(const wxString &pSqlStmt)
  1860. {
  1861.     wxASSERT(!queryOnly);
  1862.     if (queryOnly)
  1863.         return(FALSE);
  1864.  
  1865.     pDb->WriteSqlLog(pSqlStmt);
  1866.  
  1867.     return(execUpdate(pSqlStmt));
  1868.  
  1869. }  // wxDbTable::Update(pSqlStmt)
  1870.  
  1871.  
  1872. /********** wxDbTable::UpdateWhere() **********/
  1873. bool wxDbTable::UpdateWhere(const wxString &pWhereClause)
  1874. {
  1875.     wxASSERT(!queryOnly);
  1876.     if (queryOnly)
  1877.         return(FALSE);
  1878.  
  1879.     wxString sqlStmt;
  1880.  
  1881.     // Build the SQL UPDATE statement
  1882.     BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
  1883.  
  1884.     pDb->WriteSqlLog(sqlStmt);
  1885.  
  1886. #ifdef DBDEBUG_CONSOLE
  1887.     cout << endl << sqlStmt.c_str() << endl << endl;
  1888. #endif
  1889.  
  1890.     // Execute the SQL UPDATE statement
  1891.     return(execUpdate(sqlStmt));
  1892.  
  1893. }  // wxDbTable::UpdateWhere()
  1894.  
  1895.  
  1896. /********** wxDbTable::Delete() **********/
  1897. bool wxDbTable::Delete(void)
  1898. {
  1899.     wxASSERT(!queryOnly);
  1900.     if (queryOnly)
  1901.         return(FALSE);
  1902.  
  1903.     wxString sqlStmt;
  1904.     sqlStmt.Empty();
  1905.  
  1906.     // Build the SQL DELETE statement
  1907.     BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
  1908.  
  1909.     pDb->WriteSqlLog(sqlStmt);
  1910.  
  1911.     // Execute the SQL DELETE statement
  1912.     return(execDelete(sqlStmt));
  1913.  
  1914. }  // wxDbTable::Delete()
  1915.  
  1916.  
  1917. /********** wxDbTable::DeleteWhere() **********/
  1918. bool wxDbTable::DeleteWhere(const wxString &pWhereClause)
  1919. {
  1920.     wxASSERT(!queryOnly);
  1921.     if (queryOnly)
  1922.         return(FALSE);
  1923.  
  1924.     wxString sqlStmt;
  1925.     sqlStmt.Empty();
  1926.  
  1927.     // Build the SQL DELETE statement
  1928.     BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
  1929.  
  1930.     pDb->WriteSqlLog(sqlStmt);
  1931.  
  1932.     // Execute the SQL DELETE statement
  1933.     return(execDelete(sqlStmt));
  1934.  
  1935. }  // wxDbTable::DeleteWhere()
  1936.  
  1937.  
  1938. /********** wxDbTable::DeleteMatching() **********/
  1939. bool wxDbTable::DeleteMatching(void)
  1940. {
  1941.     wxASSERT(!queryOnly);
  1942.     if (queryOnly)
  1943.         return(FALSE);
  1944.  
  1945.     wxString sqlStmt;
  1946.     sqlStmt.Empty();
  1947.  
  1948.     // Build the SQL DELETE statement
  1949.     BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING);
  1950.  
  1951.     pDb->WriteSqlLog(sqlStmt);
  1952.  
  1953.     // Execute the SQL DELETE statement
  1954.     return(execDelete(sqlStmt));
  1955.  
  1956. }  // wxDbTable::DeleteMatching()
  1957.  
  1958.  
  1959. /********** wxDbTable::IsColNull() **********/
  1960. bool wxDbTable::IsColNull(UWORD colNo) const
  1961. {
  1962. /*
  1963.     This logic is just not right.  It would indicate TRUE
  1964.     if a numeric field were set to a value of 0.
  1965.  
  1966.     switch(colDefs[colNo].SqlCtype)
  1967.     {
  1968.         case SQL_C_CHAR:
  1969.             return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0);
  1970.         case SQL_C_SSHORT:
  1971.             return((  *((SWORD *) colDefs[colNo].PtrDataObj))   == 0);
  1972.         case SQL_C_USHORT:
  1973.             return((   *((UWORD*) colDefs[colNo].PtrDataObj))   == 0);
  1974.         case SQL_C_SLONG:
  1975.             return(( *((SDWORD *) colDefs[colNo].PtrDataObj))   == 0);
  1976.         case SQL_C_ULONG:
  1977.             return(( *((UDWORD *) colDefs[colNo].PtrDataObj))   == 0);
  1978.         case SQL_C_FLOAT:
  1979.             return(( *((SFLOAT *) colDefs[colNo].PtrDataObj))   == 0);
  1980.         case SQL_C_DOUBLE:
  1981.             return((*((SDOUBLE *) colDefs[colNo].PtrDataObj))   == 0);
  1982.         case SQL_C_TIMESTAMP:
  1983.             TIMESTAMP_STRUCT *pDt;
  1984.             pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
  1985.             if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0)
  1986.                 return(TRUE);
  1987.             else
  1988.                 return(FALSE);
  1989.         default:
  1990.             return(TRUE);
  1991.     }
  1992. */
  1993.     return (colDefs[colNo].Null);
  1994. }  // wxDbTable::IsColNull()
  1995.  
  1996.  
  1997. /********** wxDbTable::CanSelectForUpdate() **********/
  1998. bool wxDbTable::CanSelectForUpdate(void)
  1999. {
  2000.     if (queryOnly)
  2001.         return FALSE;
  2002.  
  2003.     if (pDb->Dbms() == dbmsMY_SQL)
  2004.         return FALSE;
  2005.  
  2006.     if ((pDb->Dbms() == dbmsORACLE) ||
  2007.         (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE))
  2008.         return(TRUE);
  2009.     else
  2010.         return(FALSE);
  2011.  
  2012. }  // wxDbTable::CanSelectForUpdate()
  2013.  
  2014.  
  2015. /********** wxDbTable::CanUpdByROWID() **********/
  2016. bool wxDbTable::CanUpdByROWID(void)
  2017. {
  2018. /*
  2019.  * NOTE: Returning FALSE for now until this can be debugged,
  2020.  *        as the ROWID is not getting updated correctly
  2021.  */
  2022.     return FALSE;
  2023. /*
  2024.     if (pDb->Dbms() == dbmsORACLE)
  2025.         return(TRUE);
  2026.     else
  2027.         return(FALSE);
  2028. */
  2029. }  // wxDbTable::CanUpdByROWID()
  2030.  
  2031.  
  2032. /********** wxDbTable::IsCursorClosedOnCommit() **********/
  2033. bool wxDbTable::IsCursorClosedOnCommit(void)
  2034. {
  2035.     if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
  2036.         return(FALSE);
  2037.     else
  2038.         return(TRUE);
  2039.  
  2040. }  // wxDbTable::IsCursorClosedOnCommit()
  2041.  
  2042.  
  2043.  
  2044. /********** wxDbTable::ClearMemberVar() **********/
  2045. void wxDbTable::ClearMemberVar(UWORD colNo, bool setToNull)
  2046. {
  2047.     wxASSERT(colNo < noCols);
  2048.  
  2049.     switch(colDefs[colNo].SqlCtype)
  2050.     {
  2051.         case SQL_C_CHAR:
  2052.             ((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0]    = 0;
  2053.             break;
  2054.         case SQL_C_SSHORT:
  2055.             *((SWORD *) colDefs[colNo].PtrDataObj)          = 0;
  2056.             break;
  2057.         case SQL_C_USHORT:
  2058.             *((UWORD*) colDefs[colNo].PtrDataObj)           = 0;
  2059.             break;
  2060.         case SQL_C_SLONG:
  2061.             *((SDWORD *) colDefs[colNo].PtrDataObj)         = 0;
  2062.             break;
  2063.         case SQL_C_ULONG:
  2064.             *((UDWORD *) colDefs[colNo].PtrDataObj)         = 0;
  2065.             break;
  2066.         case SQL_C_FLOAT:
  2067.             *((SFLOAT *) colDefs[colNo].PtrDataObj)         = 0.0f;
  2068.             break;
  2069.         case SQL_C_DOUBLE:
  2070.             *((SDOUBLE *) colDefs[colNo].PtrDataObj)        = 0.0f;
  2071.             break;
  2072.         case SQL_C_TIMESTAMP:
  2073.             TIMESTAMP_STRUCT *pDt;
  2074.             pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
  2075.             pDt->year = 0;
  2076.             pDt->month = 0;
  2077.             pDt->day = 0;
  2078.             pDt->hour = 0;
  2079.             pDt->minute = 0;
  2080.             pDt->second = 0;
  2081.             pDt->fraction = 0;
  2082.             break;
  2083.     }
  2084.  
  2085.     if (setToNull)
  2086.         SetColNull(colNo);
  2087. }  // wxDbTable::ClearMemberVar()
  2088.  
  2089.  
  2090. /********** wxDbTable::ClearMemberVars() **********/
  2091. void wxDbTable::ClearMemberVars(bool setToNull)
  2092. {
  2093.     int i;
  2094.  
  2095.     // Loop through the columns setting each member variable to zero
  2096.     for (i=0; i < noCols; i++)
  2097.         ClearMemberVar(i,setToNull);
  2098.  
  2099. }  // wxDbTable::ClearMemberVars()
  2100.  
  2101.  
  2102. /********** wxDbTable::SetQueryTimeout() **********/
  2103. bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
  2104. {
  2105.     if (SQLSetStmtOption(hstmtInsert, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
  2106.         return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
  2107.     if (SQLSetStmtOption(hstmtUpdate, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
  2108.         return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
  2109.     if (SQLSetStmtOption(hstmtDelete, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
  2110.         return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
  2111.     if (SQLSetStmtOption(hstmtInternal, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
  2112.         return(pDb->DispAllErrors(henv, hdbc, hstmtInternal));
  2113.  
  2114.     // Completed Successfully
  2115.     return(TRUE);
  2116.  
  2117. }  // wxDbTable::SetQueryTimeout()
  2118.  
  2119.  
  2120. /********** wxDbTable::SetColDefs() **********/
  2121. void wxDbTable::SetColDefs(UWORD index, const wxString &fieldName, int dataType, void *pData,
  2122.                            SWORD cType, int size, bool keyField, bool upd,
  2123.                            bool insAllow, bool derivedCol)
  2124. {
  2125.     wxASSERT_MSG( index < noCols,
  2126.                   _T("Specified column index exceeds the maximum number of columns for this table.") );
  2127.  
  2128.     if (!colDefs)  // May happen if the database connection fails
  2129.         return;
  2130.  
  2131.     if (fieldName.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN)
  2132.     {
  2133.         wxStrncpy(colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
  2134.         colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0;
  2135.  
  2136. #ifdef __WXDEBUG__
  2137.         wxString tmpMsg;
  2138.         tmpMsg.Printf(_T("Column name '%s' is too long. Truncated to '%s'."),
  2139.                       fieldName.c_str(),colDefs[index].ColName);
  2140.         wxFAIL_MSG(tmpMsg);
  2141. #endif // __WXDEBUG__
  2142.     }
  2143.     else
  2144.         wxStrcpy(colDefs[index].ColName, fieldName);
  2145.  
  2146.     colDefs[index].DbDataType       = dataType;
  2147.     colDefs[index].PtrDataObj       = pData;
  2148.     colDefs[index].SqlCtype         = cType;
  2149.     colDefs[index].SzDataObj        = size;
  2150.     colDefs[index].KeyField         = keyField;
  2151.     colDefs[index].DerivedCol       = derivedCol;
  2152.     // Derived columns by definition would NOT be "Insertable" or "Updateable"
  2153.     if (derivedCol)
  2154.     {
  2155.         colDefs[index].Updateable       = FALSE;
  2156.         colDefs[index].InsertAllowed    = FALSE;
  2157.     }
  2158.     else
  2159.     {
  2160.         colDefs[index].Updateable       = upd;
  2161.         colDefs[index].InsertAllowed    = insAllow;
  2162.     }
  2163.  
  2164.     colDefs[index].Null                 = FALSE;
  2165.  
  2166. }  // wxDbTable::SetColDefs()
  2167.  
  2168.  
  2169. /********** wxDbTable::SetColDefs() **********/
  2170. wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, UWORD numCols)
  2171. {
  2172.     wxASSERT(pColInfs);
  2173.     wxDbColDataPtr *pColDataPtrs = NULL;
  2174.  
  2175.     if (pColInfs)
  2176.     {
  2177.         UWORD index;
  2178.  
  2179.         pColDataPtrs = new wxDbColDataPtr[numCols+1];
  2180.  
  2181.         for (index = 0; index < numCols; index++)
  2182.         {
  2183.             // Process the fields
  2184.             switch (pColInfs[index].dbDataType)
  2185.             {
  2186.                 case DB_DATA_TYPE_VARCHAR:
  2187.                    pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferLength+1];
  2188.                    pColDataPtrs[index].SzDataObj  = pColInfs[index].columnSize;
  2189.                    pColDataPtrs[index].SqlCtype   = SQL_C_CHAR;
  2190.                    break;
  2191.                 case DB_DATA_TYPE_INTEGER:
  2192.                     // Can be long or short
  2193.                     if (pColInfs[index].bufferLength == sizeof(long))
  2194.                     {
  2195.                       pColDataPtrs[index].PtrDataObj = new long;
  2196.                       pColDataPtrs[index].SzDataObj  = sizeof(long);
  2197.                       pColDataPtrs[index].SqlCtype   = SQL_C_SLONG;
  2198.                     }
  2199.                     else
  2200.                     {
  2201.                         pColDataPtrs[index].PtrDataObj = new short;
  2202.                         pColDataPtrs[index].SzDataObj  = sizeof(short);
  2203.                         pColDataPtrs[index].SqlCtype   = SQL_C_SSHORT;
  2204.                     }
  2205.                     break;
  2206.                 case DB_DATA_TYPE_FLOAT:
  2207.                     // Can be float or double
  2208.                     if (pColInfs[index].bufferLength == sizeof(float))
  2209.                     {
  2210.                         pColDataPtrs[index].PtrDataObj = new float;
  2211.                         pColDataPtrs[index].SzDataObj  = sizeof(float);
  2212.                         pColDataPtrs[index].SqlCtype   = SQL_C_FLOAT;
  2213.                     }
  2214.                     else
  2215.                     {
  2216.                         pColDataPtrs[index].PtrDataObj = new double;
  2217.                         pColDataPtrs[index].SzDataObj  = sizeof(double);
  2218.                         pColDataPtrs[index].SqlCtype   = SQL_C_DOUBLE;
  2219.                     }
  2220.                     break;
  2221.                 case DB_DATA_TYPE_DATE:
  2222.                     pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT;
  2223.                     pColDataPtrs[index].SzDataObj  = sizeof(TIMESTAMP_STRUCT);
  2224.                     pColDataPtrs[index].SqlCtype   = SQL_C_TIMESTAMP;
  2225.                     break;
  2226.                 case DB_DATA_TYPE_BLOB:
  2227.                     wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns"));
  2228.                     pColDataPtrs[index].PtrDataObj = /*BLOB ADDITION NEEDED*/NULL;
  2229.                     pColDataPtrs[index].SzDataObj  = /*BLOB ADDITION NEEDED*/sizeof(void *);
  2230.                     pColDataPtrs[index].SqlCtype   = SQL_VARBINARY;
  2231.                     break;
  2232.             }
  2233.             if (pColDataPtrs[index].PtrDataObj != NULL)
  2234.                 SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj);
  2235.             else
  2236.             {
  2237.                 // Unable to build all the column definitions, as either one of
  2238.                 // the calls to "new" failed above, or there was a BLOB field
  2239.                 // to have a column definition for.  If BLOBs are to be used,
  2240.                 // the other form of ::SetColDefs() must be used, as it is impossible
  2241.                 // to know the maximum size to create the PtrDataObj to be.
  2242.                 delete [] pColDataPtrs;
  2243.                 return NULL;
  2244.             }
  2245.         }
  2246.     }
  2247.  
  2248.     return (pColDataPtrs);
  2249.  
  2250. } // wxDbTable::SetColDefs()
  2251.  
  2252.  
  2253. /********** wxDbTable::SetCursor() **********/
  2254. void wxDbTable::SetCursor(HSTMT *hstmtActivate)
  2255. {
  2256.     if (hstmtActivate == wxDB_DEFAULT_CURSOR)
  2257.         hstmt = *hstmtDefault;
  2258.     else
  2259.         hstmt = *hstmtActivate;
  2260.  
  2261. }  // wxDbTable::SetCursor()
  2262.  
  2263.  
  2264. /********** wxDbTable::Count(const wxString &) **********/
  2265. ULONG wxDbTable::Count(const wxString &args)
  2266. {
  2267.     ULONG count;
  2268.     wxString sqlStmt;
  2269.     SDWORD cb;
  2270.  
  2271.     // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
  2272.     sqlStmt  = wxT("SELECT COUNT(");
  2273.     sqlStmt += args;
  2274.     sqlStmt += wxT(") FROM ");
  2275.     sqlStmt += pDb->SQLTableName(queryTableName);
  2276. //    sqlStmt += queryTableName;
  2277. #if wxODBC_BACKWARD_COMPATABILITY
  2278.     if (from && wxStrlen(from))
  2279. #else
  2280.     if (from.Length())
  2281. #endif
  2282.         sqlStmt += from;
  2283.  
  2284.     // Add the where clause if one is provided
  2285. #if wxODBC_BACKWARD_COMPATABILITY
  2286.     if (where && wxStrlen(where))
  2287. #else
  2288.     if (where.Length())
  2289. #endif
  2290.     {
  2291.         sqlStmt += wxT(" WHERE ");
  2292.         sqlStmt += where;
  2293.     }
  2294.  
  2295.     pDb->WriteSqlLog(sqlStmt);
  2296.  
  2297.     // Initialize the Count cursor if it's not already initialized
  2298.     if (!hstmtCount)
  2299.     {
  2300.         hstmtCount = GetNewCursor(FALSE,FALSE);
  2301.         wxASSERT(hstmtCount);
  2302.         if (!hstmtCount)
  2303.             return(0);
  2304.     }
  2305.  
  2306.     // Execute the SQL statement
  2307.     if (SQLExecDirect(*hstmtCount, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  2308.     {
  2309.         pDb->DispAllErrors(henv, hdbc, *hstmtCount);
  2310.         return(0);
  2311.     }
  2312.  
  2313.     // Fetch the record
  2314.     if (SQLFetch(*hstmtCount) != SQL_SUCCESS)
  2315.     {
  2316.         pDb->DispAllErrors(henv, hdbc, *hstmtCount);
  2317.         return(0);
  2318.     }
  2319.  
  2320.     // Obtain the result
  2321.     if (SQLGetData(*hstmtCount, (UWORD)1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS)
  2322.     {
  2323.         pDb->DispAllErrors(henv, hdbc, *hstmtCount);
  2324.         return(0);
  2325.     }
  2326.  
  2327.     // Free the cursor
  2328.     if (SQLFreeStmt(*hstmtCount, SQL_CLOSE) != SQL_SUCCESS)
  2329.         pDb->DispAllErrors(henv, hdbc, *hstmtCount);
  2330.  
  2331.     // Return the record count
  2332.     return(count);
  2333.  
  2334. }  // wxDbTable::Count()
  2335.  
  2336.  
  2337. /********** wxDbTable::Refresh() **********/
  2338. bool wxDbTable::Refresh(void)
  2339. {
  2340.     bool result = TRUE;
  2341.  
  2342.     // Switch to the internal cursor so any active cursors are not corrupted
  2343.     HSTMT currCursor = GetCursor();
  2344.     hstmt = hstmtInternal;
  2345. #if wxODBC_BACKWARD_COMPATABILITY
  2346.     // Save the where and order by clauses
  2347.     char *saveWhere = where;
  2348.     char *saveOrderBy = orderBy;
  2349. #else
  2350.     wxString saveWhere = where;
  2351.     wxString saveOrderBy = orderBy;
  2352. #endif
  2353.     // Build a where clause to refetch the record with.  Try and use the
  2354.     // ROWID if it's available, ow use the key fields.
  2355.     wxString whereClause;
  2356.     whereClause.Empty();
  2357.  
  2358.     if (CanUpdByROWID())
  2359.     {
  2360.         SDWORD cb;
  2361.         wxChar   rowid[wxDB_ROWID_LEN+1];
  2362.  
  2363.         // Get the ROWID value.  If not successful retreiving the ROWID,
  2364.         // simply fall down through the code and build the WHERE clause
  2365.         // based on the key fields.
  2366.         if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
  2367.         {
  2368.             whereClause += pDb->SQLTableName(queryTableName);
  2369. //            whereClause += queryTableName;
  2370.             whereClause += wxT(".ROWID = '");
  2371.             whereClause += rowid;
  2372.             whereClause += wxT("'");
  2373.         }
  2374.     }
  2375.  
  2376.     // If unable to use the ROWID, build a where clause from the keyfields
  2377.     if (wxStrlen(whereClause) == 0)
  2378.         BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS, queryTableName);
  2379.  
  2380.     // Requery the record
  2381.     where = whereClause;
  2382.     orderBy.Empty();
  2383.     if (!Query())
  2384.         result = FALSE;
  2385.  
  2386.     if (result && !GetNext())
  2387.         result = FALSE;
  2388.  
  2389.     // Switch back to original cursor
  2390.     SetCursor(&currCursor);
  2391.  
  2392.     // Free the internal cursor
  2393.     if (SQLFreeStmt(hstmtInternal, SQL_CLOSE) != SQL_SUCCESS)
  2394.         pDb->DispAllErrors(henv, hdbc, hstmtInternal);
  2395.  
  2396.     // Restore the original where and order by clauses
  2397.     where   = saveWhere;
  2398.     orderBy = saveOrderBy;
  2399.  
  2400.     return(result);
  2401.  
  2402. }  // wxDbTable::Refresh()
  2403.  
  2404.  
  2405. /********** wxDbTable::SetColNull() **********/
  2406. bool wxDbTable::SetColNull(UWORD colNo, bool set)
  2407. {
  2408.     if (colNo < noCols)
  2409.     {
  2410.         colDefs[colNo].Null = set;
  2411.         if (set)  // Blank out the values in the member variable
  2412.             ClearMemberVar(colNo,FALSE);  // Must call with FALSE, or infinite recursion will happen
  2413.         return(TRUE);
  2414.     }
  2415.     else
  2416.         return(FALSE);
  2417.  
  2418. }  // wxDbTable::SetColNull()
  2419.  
  2420.  
  2421. /********** wxDbTable::SetColNull() **********/
  2422. bool wxDbTable::SetColNull(const wxString &colName, bool set)
  2423. {
  2424.     int i;
  2425.     for (i = 0; i < noCols; i++)
  2426.     {
  2427.         if (!wxStricmp(colName, colDefs[i].ColName))
  2428.             break;
  2429.     }
  2430.  
  2431.     if (i < noCols)
  2432.     {
  2433.         colDefs[i].Null = set;
  2434.         if (set)  // Blank out the values in the member variable
  2435.             ClearMemberVar(i,FALSE);  // Must call with FALSE, or infinite recursion will happen
  2436.         return(TRUE);
  2437.     }
  2438.     else
  2439.         return(FALSE);
  2440.  
  2441. }  // wxDbTable::SetColNull()
  2442.  
  2443.  
  2444. /********** wxDbTable::GetNewCursor() **********/
  2445. HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns)
  2446. {
  2447.     HSTMT *newHSTMT = new HSTMT;
  2448.     wxASSERT(newHSTMT);
  2449.     if (!newHSTMT)
  2450.         return(0);
  2451.  
  2452.     if (SQLAllocStmt(hdbc, newHSTMT) != SQL_SUCCESS)
  2453.     {
  2454.         pDb->DispAllErrors(henv, hdbc);
  2455.         delete newHSTMT;
  2456.         return(0);
  2457.     }
  2458.  
  2459.     if (SQLSetStmtOption(*newHSTMT, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
  2460.     {
  2461.         pDb->DispAllErrors(henv, hdbc, *newHSTMT);
  2462.         delete newHSTMT;
  2463.         return(0);
  2464.     }
  2465.  
  2466.     if (bindColumns)
  2467.     {
  2468.         if (!bindCols(*newHSTMT))
  2469.         {
  2470.             delete newHSTMT;
  2471.             return(0);
  2472.         }
  2473.     }
  2474.  
  2475.     if (setCursor)
  2476.         SetCursor(newHSTMT);
  2477.  
  2478.     return(newHSTMT);
  2479.  
  2480. }   // wxDbTable::GetNewCursor()
  2481.  
  2482.  
  2483. /********** wxDbTable::DeleteCursor() **********/
  2484. bool wxDbTable::DeleteCursor(HSTMT *hstmtDel)
  2485. {
  2486.     bool result = TRUE;
  2487.  
  2488.     if (!hstmtDel)  // Cursor already deleted
  2489.         return(result);
  2490.  
  2491. /*
  2492. ODBC 3.0 says to use this form
  2493.     if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
  2494.  
  2495. */
  2496.     if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
  2497.     {
  2498.         pDb->DispAllErrors(henv, hdbc);
  2499.         result = FALSE;
  2500.     }
  2501.  
  2502.     delete hstmtDel;
  2503.  
  2504.     return(result);
  2505.  
  2506. }  // wxDbTable::DeleteCursor()
  2507.  
  2508. //////////////////////////////////////////////////////////////
  2509. // wxDbGrid support functions
  2510. //////////////////////////////////////////////////////////////
  2511.  
  2512. void wxDbTable::SetRowMode(const rowmode_t rowmode)
  2513. {
  2514.     if (!m_hstmtGridQuery)
  2515.     {
  2516.         m_hstmtGridQuery = GetNewCursor(FALSE,FALSE);
  2517.         if (!bindCols(*m_hstmtGridQuery))
  2518.             return;
  2519.     }
  2520.  
  2521.     m_rowmode = rowmode;
  2522.     switch (m_rowmode)
  2523.     {
  2524.         case WX_ROW_MODE_QUERY:
  2525.             SetCursor(m_hstmtGridQuery);
  2526.             break;
  2527.         case WX_ROW_MODE_INDIVIDUAL:
  2528.             SetCursor(hstmtDefault);
  2529.             break;
  2530.         default:
  2531.             assert(0);
  2532.     }
  2533. }  // wxDbTable::SetRowMode()
  2534.  
  2535.  
  2536. wxVariant wxDbTable::GetCol(const int colNo) const
  2537. {
  2538.     wxVariant val;
  2539.     if ((colNo < noCols) && (!IsColNull(colNo)))
  2540.     {
  2541.         switch (colDefs[colNo].SqlCtype)
  2542.         {
  2543.             case SQL_CHAR:
  2544.             case SQL_VARCHAR:
  2545.                 val = (wxChar *)(colDefs[colNo].PtrDataObj);
  2546.                 break;
  2547.             case SQL_C_LONG:
  2548.             case SQL_C_SLONG:
  2549.                 val = *(long *)(colDefs[colNo].PtrDataObj);
  2550.                 break;
  2551.             case SQL_C_SHORT:
  2552.             case SQL_C_SSHORT:
  2553.                 val = (long int )(*(short *)(colDefs[colNo].PtrDataObj));
  2554.                 break;
  2555.             case SQL_C_ULONG:
  2556.                 val = (long)(*(unsigned long *)(colDefs[colNo].PtrDataObj));
  2557.                 break;
  2558.             case SQL_C_TINYINT:
  2559.                 val = (long)(*(char *)(colDefs[colNo].PtrDataObj));
  2560.                 break;
  2561.             case SQL_C_UTINYINT:
  2562.                 val = (long)(*(unsigned char *)(colDefs[colNo].PtrDataObj));
  2563.                 break;
  2564.             case SQL_C_USHORT:
  2565.                 val = (long)(*(UWORD *)(colDefs[colNo].PtrDataObj));
  2566.                 break;
  2567.             case SQL_C_DATE:
  2568.                 val = (DATE_STRUCT *)(colDefs[colNo].PtrDataObj);
  2569.                 break;
  2570.             case SQL_C_TIME:
  2571.                 val = (TIME_STRUCT *)(colDefs[colNo].PtrDataObj);
  2572.                 break;
  2573.             case SQL_C_TIMESTAMP:
  2574.                 val = (TIMESTAMP_STRUCT *)(colDefs[colNo].PtrDataObj);
  2575.                 break;
  2576.             case SQL_C_DOUBLE:
  2577.                 val = *(double *)(colDefs[colNo].PtrDataObj);
  2578.                 break;
  2579.             default:
  2580.                 assert(0);
  2581.         }
  2582.     }
  2583.     return val;
  2584. }  // wxDbTable::GetCol()
  2585.  
  2586.  
  2587. void csstrncpyt(char *s, const char *t, int n)
  2588. {
  2589.     while ((*s++ = *t++) && --n)
  2590.     {};
  2591.  
  2592.     *s = '\0';
  2593. }
  2594.  
  2595. void wxDbTable::SetCol(const int colNo, const wxVariant val)
  2596. {
  2597.     //FIXME: Add proper wxDateTime support to wxVariant..
  2598.     wxDateTime dateval;
  2599.  
  2600.     SetColNull(colNo, val.IsNull());
  2601.  
  2602.     if (!val.IsNull())
  2603.     {
  2604.         if ((colDefs[colNo].SqlCtype == SQL_C_DATE)
  2605.             || (colDefs[colNo].SqlCtype == SQL_C_TIME)
  2606.             || (colDefs[colNo].SqlCtype == SQL_C_TIMESTAMP))
  2607.         {
  2608.             //Returns null if invalid!
  2609.             if (!dateval.ParseDate(val.GetString()))
  2610.                 SetColNull(colNo, TRUE);
  2611.         }
  2612.  
  2613.         switch (colDefs[colNo].SqlCtype)
  2614.         {
  2615.             case SQL_CHAR:
  2616.             case SQL_VARCHAR:
  2617.                 csstrncpyt((char *)(colDefs[colNo].PtrDataObj),
  2618.                            val.GetString().c_str(),
  2619.                            colDefs[colNo].SzDataObj-1);
  2620.                 break;
  2621.             case SQL_C_LONG:
  2622.             case SQL_C_SLONG:
  2623.                 *(long *)(colDefs[colNo].PtrDataObj) = val;
  2624.                 break;
  2625.             case SQL_C_SHORT:
  2626.             case SQL_C_SSHORT:
  2627.                 *(short *)(colDefs[colNo].PtrDataObj) = val.GetLong();
  2628.                 break;
  2629.             case SQL_C_ULONG:
  2630.                 *(unsigned long *)(colDefs[colNo].PtrDataObj) = val.GetLong();
  2631.                 break;
  2632.             case SQL_C_TINYINT:
  2633.                 *(char *)(colDefs[colNo].PtrDataObj) = val.GetChar();
  2634.                 break;
  2635.             case SQL_C_UTINYINT:
  2636.                 *(unsigned char *)(colDefs[colNo].PtrDataObj) = val.GetChar();
  2637.                 break;
  2638.             case SQL_C_USHORT:
  2639.                 *(unsigned short *)(colDefs[colNo].PtrDataObj) = val.GetLong();
  2640.                 break;
  2641.             //FIXME: Add proper wxDateTime support to wxVariant..
  2642.             case SQL_C_DATE:
  2643.                 {
  2644.                     DATE_STRUCT *dataptr =
  2645.                         (DATE_STRUCT *)colDefs[colNo].PtrDataObj;
  2646.  
  2647.                     dataptr->year   = dateval.GetYear();
  2648.                     dataptr->month  = dateval.GetMonth()+1;
  2649.                     dataptr->day    = dateval.GetDay();
  2650.                 }
  2651.                 break;
  2652.             case SQL_C_TIME:
  2653.                 {
  2654.                     TIME_STRUCT *dataptr =
  2655.                         (TIME_STRUCT *)colDefs[colNo].PtrDataObj;
  2656.  
  2657.                     dataptr->hour   = dateval.GetHour();
  2658.                     dataptr->minute = dateval.GetMinute();
  2659.                     dataptr->second = dateval.GetSecond();
  2660.                 }
  2661.                 break;
  2662.             case SQL_C_TIMESTAMP:
  2663.                 {
  2664.                     TIMESTAMP_STRUCT *dataptr =
  2665.                         (TIMESTAMP_STRUCT *)colDefs[colNo].PtrDataObj;
  2666.                     dataptr->year   = dateval.GetYear();
  2667.                     dataptr->month  = dateval.GetMonth()+1;
  2668.                     dataptr->day    = dateval.GetDay();
  2669.  
  2670.                     dataptr->hour   = dateval.GetHour();
  2671.                     dataptr->minute = dateval.GetMinute();
  2672.                     dataptr->second = dateval.GetSecond();
  2673.                 }
  2674.                 break;
  2675.             case SQL_C_DOUBLE:
  2676.                 *(double *)(colDefs[colNo].PtrDataObj) = val;
  2677.                 break;
  2678.             default:
  2679.                 assert(0);
  2680.         }  // switch
  2681.     }  // if (!val.IsNull())
  2682. }  // wxDbTable::SetCol()
  2683.  
  2684.  
  2685. GenericKey wxDbTable::GetKey()
  2686. {
  2687.     void *blk;
  2688.     wxChar *blkptr;
  2689.  
  2690.     blk = malloc(m_keysize);
  2691.     blkptr = (wxChar *) blk;
  2692.  
  2693.     int i;
  2694.     for (i=0; i < noCols; i++)
  2695.     {
  2696.         if (colDefs[i].KeyField)
  2697.         {
  2698.             memcpy(blkptr,colDefs[i].PtrDataObj, colDefs[i].SzDataObj);
  2699.             blkptr += colDefs[i].SzDataObj;
  2700.         }
  2701.     }
  2702.  
  2703.     GenericKey k = GenericKey(blk, m_keysize);
  2704.     free(blk);
  2705.  
  2706.     return k;
  2707. }  // wxDbTable::GetKey()
  2708.  
  2709.  
  2710. void wxDbTable::SetKey(const GenericKey& k)
  2711. {
  2712.     void    *blk;
  2713.     wxChar  *blkptr;
  2714.  
  2715.     blk = k.GetBlk();
  2716.     blkptr = (wxChar *)blk;
  2717.  
  2718.     int i;
  2719.     for (i=0; i < noCols; i++)
  2720.     {
  2721.         if (colDefs[i].KeyField)
  2722.         {
  2723.             SetColNull(i, FALSE);
  2724.             memcpy(colDefs[i].PtrDataObj, blkptr, colDefs[i].SzDataObj);
  2725.             blkptr += colDefs[i].SzDataObj;
  2726.         }
  2727.     }
  2728. }  // wxDbTable::SetKey()
  2729.  
  2730.  
  2731. #endif  // wxUSE_ODBC
  2732.  
  2733.