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 / db.cpp < prev    next >
C/C++ Source or Header  |  2002-08-16  |  148KB  |  3,966 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name:        db.cpp
  3. // Purpose:     Implementation of the wxDb class.  The wxDb class represents a connection
  4. //              to an ODBC data source.  The wxDb class allows operations on the data
  5. //              source such as opening and closing the data source.
  6. // Author:      Doug Card
  7. // Modified by: George Tasker
  8. //              Bart Jourquin
  9. //              Mark Johnson, wxWindows@mj10777.de
  10. // Mods:        Dec, 1998:
  11. //                -Added support for SQL statement logging and database cataloging
  12. // Mods:        April, 1999
  13. //                -Added QUERY_ONLY mode support to reduce default number of cursors
  14. //                -Added additional SQL logging code
  15. //                -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
  16. //                -Set ODBC option to only read committed writes to the DB so all
  17. //                   databases operate the same in that respect
  18. // Created:     9.96
  19. // RCS-ID:      $Id: db.cpp,v 1.76 2002/08/16 12:43:07 JS Exp $
  20. // Copyright:   (c) 1996 Remstar International, Inc.
  21. // Licence:     wxWindows licence, plus:
  22. // Notice:      This class library and its intellectual design are free of charge for use,
  23. //              modification, enhancement, debugging under the following conditions:
  24. //              1) These classes may only be used as part of the implementation of a
  25. //                 wxWindows-based application
  26. //              2) All enhancements and bug fixes are to be submitted back to the wxWindows
  27. //                 user groups free of all charges for use with the wxWindows library.
  28. //              3) These classes may not be distributed as part of any other class library,
  29. //                 DLL, text (written or electronic), other than a complete distribution of
  30. //                 the wxWindows GUI development toolkit.
  31. ///////////////////////////////////////////////////////////////////////////////
  32.  
  33. /*
  34. // SYNOPSIS START
  35. // SYNOPSIS STOP
  36. */
  37. #ifdef __GNUG__
  38.     #pragma implementation "db.h"
  39. #endif
  40.  
  41. #include "wx/wxprec.h"
  42.  
  43. #ifdef __BORLANDC__
  44.     #pragma hdrstop
  45. #endif
  46.  
  47. #ifdef DBDEBUG_CONSOLE
  48.     #include "wx/ioswrap.h"
  49. #endif
  50.  
  51. #ifndef WX_PRECOMP
  52.     #include "wx/string.h"
  53.     #include "wx/object.h"
  54.     #include "wx/list.h"
  55.     #include "wx/utils.h"
  56.     #if wxUSE_GUI
  57.         #include "wx/msgdlg.h"
  58.     #endif
  59.     #include "wx/log.h"
  60. #endif
  61. #include "wx/filefn.h"
  62. #include "wx/wxchar.h"
  63.  
  64. #if wxUSE_ODBC
  65.  
  66. #include <stdio.h>
  67. #include <string.h>
  68. #include <assert.h>
  69. #include <stdlib.h>
  70. #include <ctype.h>
  71.  
  72. #include "wx/db.h"
  73.  
  74. WXDLLEXPORT_DATA(wxDbList*) PtrBegDbList = 0;
  75.  
  76.  
  77. wxChar const *SQL_LOG_FILENAME         = wxT("sqllog.txt");
  78. wxChar const *SQL_CATALOG_FILENAME     = wxT("catalog.txt");
  79.  
  80. #ifdef __WXDEBUG__
  81.     extern wxList TablesInUse;
  82. #endif
  83.  
  84. // SQL Log defaults to be used by GetDbConnection
  85. wxDbSqlLogState SQLLOGstate = sqlLogOFF;
  86.  
  87. static wxString SQLLOGfn = SQL_LOG_FILENAME;
  88.  
  89. // The wxDb::errorList is copied to this variable when the wxDb object
  90. // is closed.  This way, the error list is still available after the
  91. // database object is closed.  This is necessary if the database
  92. // connection fails so the calling application can show the operator
  93. // why the connection failed.  Note: as each wxDb object is closed, it
  94. // will overwrite the errors of the previously destroyed wxDb object in
  95. // this variable.  NOTE: This occurs during a CLOSE, not a FREEing of the
  96. // connection
  97. wxChar DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
  98.  
  99.  
  100. // This type defines the return row-struct form
  101. // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
  102. typedef struct
  103. {
  104.    wxChar        tableQual[128+1];
  105.    wxChar        tableOwner[128+1];
  106.    wxChar        tableName[128+1];
  107.    wxChar        grantor[128+1];
  108.    wxChar        grantee[128+1];
  109.    wxChar        privilege[128+1];
  110.    wxChar        grantable[3+1];
  111. } wxDbTablePrivilegeInfo;
  112.  
  113.  
  114. /********** wxDbConnectInf Constructor - form 1 **********/
  115. wxDbConnectInf::wxDbConnectInf()
  116. {
  117.     Henv = 0;
  118.     freeHenvOnDestroy = FALSE;
  119.  
  120.     Initialize();
  121. }  // Constructor
  122.  
  123.  
  124. /********** wxDbConnectInf Constructor - form 2 **********/
  125. wxDbConnectInf::wxDbConnectInf(HENV henv, const wxString &dsn, const wxString &userID,
  126.                        const wxString &password, const wxString &defaultDir,
  127.                        const wxString &fileType, const wxString &description)
  128. {
  129.     Henv = 0;
  130.     freeHenvOnDestroy = FALSE;
  131.  
  132.     Initialize();
  133.  
  134.     if (henv)
  135.         SetHenv(henv);
  136.     else
  137.         AllocHenv();
  138.  
  139.     SetDsn(dsn);
  140.     SetUserID(userID);
  141.     SetPassword(password);
  142.     SetDescription(description);
  143.     SetFileType(fileType);
  144.     SetDefaultDir(defaultDir);
  145. }  // wxDbConnectInf Constructor
  146.  
  147.  
  148. wxDbConnectInf::~wxDbConnectInf()
  149. {
  150.     if (freeHenvOnDestroy)
  151.     {
  152.         FreeHenv();
  153.     }
  154. }  // wxDbConnectInf Destructor
  155.  
  156.  
  157.  
  158. /********** wxDbConnectInf::Initialize() **********/
  159. bool wxDbConnectInf::Initialize()
  160. {
  161.     freeHenvOnDestroy = FALSE;
  162.  
  163.     if (freeHenvOnDestroy && Henv)
  164.         FreeHenv();
  165.  
  166.     Henv = 0;
  167.     Dsn[0] = 0;
  168.     Uid[0] = 0;
  169.     AuthStr[0] = 0;
  170.     Description.Empty();
  171.     FileType.Empty();
  172.     DefaultDir.Empty();
  173.  
  174.     return TRUE;
  175. }  // wxDbConnectInf::Initialize()
  176.  
  177.  
  178. /********** wxDbConnectInf::AllocHenv() **********/
  179. bool wxDbConnectInf::AllocHenv()
  180. {
  181.     // This is here to help trap if you are getting a new henv
  182.     // without releasing an existing henv
  183.     wxASSERT(!Henv);
  184.  
  185.     // Initialize the ODBC Environment for Database Operations
  186.     if (SQLAllocEnv(&Henv) != SQL_SUCCESS)
  187.     {
  188.         wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
  189.         return FALSE;
  190.     }
  191.  
  192.     freeHenvOnDestroy = TRUE;
  193.  
  194.     return TRUE;
  195. }  // wxDbConnectInf::AllocHenv()
  196.  
  197.  
  198. void wxDbConnectInf::FreeHenv()
  199. {
  200.     wxASSERT(Henv);
  201.  
  202.     if (Henv)
  203.         SQLFreeEnv(Henv);
  204.  
  205.     Henv = 0;
  206.     freeHenvOnDestroy = FALSE;
  207.  
  208. }  // wxDbConnectInf::FreeHenv()
  209.  
  210.  
  211. void wxDbConnectInf::SetDsn(const wxString &dsn)
  212. {
  213.     wxASSERT(dsn.Length() < sizeof(Dsn));
  214.  
  215.     wxStrcpy(Dsn,dsn);
  216. }  // wxDbConnectInf::SetDsn()
  217.  
  218.  
  219. void wxDbConnectInf::SetUserID(const wxString &uid)
  220. {
  221.     wxASSERT(uid.Length() < sizeof(Uid));
  222.     wxStrcpy(Uid,uid);
  223. }  // wxDbConnectInf::SetUserID()
  224.  
  225.  
  226. void wxDbConnectInf::SetPassword(const wxString &password)
  227. {
  228.     wxASSERT(password.Length() < sizeof(AuthStr));
  229.  
  230.     wxStrcpy(AuthStr,password);
  231. }  // wxDbConnectInf::SetPassword()
  232.  
  233.  
  234.  
  235. /********** wxDbColFor Constructor **********/
  236. wxDbColFor::wxDbColFor()
  237. {
  238.     Initialize();
  239. }  // wxDbColFor::wxDbColFor()
  240.  
  241.  
  242. wxDbColFor::~wxDbColFor()
  243. {
  244. }  // wxDbColFor::~wxDbColFor()
  245.  
  246.  
  247. /********** wxDbColFor::Initialize() **********/
  248. void wxDbColFor::Initialize()
  249. {
  250.     s_Field.Empty();
  251.     int i;
  252.     for (i=0; i<7; i++)
  253.     {
  254.         s_Format[i].Empty();
  255.         s_Amount[i].Empty();
  256.         i_Amount[i] = 0;
  257.     }
  258.     i_Nation      = 0;                     // 0=EU, 1=UK, 2=International, 3=US
  259.     i_dbDataType  = 0;
  260.     i_sqlDataType = 0;
  261.     Format(1,DB_DATA_TYPE_VARCHAR,0,0,0);  // the Function that does the work
  262. }  // wxDbColFor::Initialize()
  263.  
  264.  
  265. /********** wxDbColFor::Format() **********/
  266. int wxDbColFor::Format(int Nation, int dbDataType, SWORD sqlDataType,
  267.                        short columnSize, short decimalDigits)
  268. {
  269.     // ----------------------------------------------------------------------------------------
  270.     // -- 19991224 : mj10777 : Create
  271.     // There is still a lot of work to do here, but it is a start
  272.     // It handles all the basic data-types that I have run into up to now
  273.     // The main work will have be with Dates and float Formatting
  274.     //    (US 1,000.00 ; EU 1.000,00)
  275.     // There are wxWindow plans for locale support and the new wxDateTime.  If
  276.     //    they define some constants (wxEUROPEAN) that can be gloably used,
  277.     //    they should be used here.
  278.     // ----------------------------------------------------------------------------------------
  279.     // There should also be a function to scan in a string to fill the variable
  280.     // ----------------------------------------------------------------------------------------
  281.     wxString tempStr;
  282.     i_Nation      = Nation;                                       // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
  283.     i_dbDataType  = dbDataType;
  284.     i_sqlDataType = sqlDataType;
  285.     s_Field.Printf(wxT("%s%d"),s_Amount[1].c_str(),i_Amount[1]);  // OK for VARCHAR, INTEGER and FLOAT
  286.  
  287.     if (i_dbDataType == 0)                                        // Filter unsupported dbDataTypes
  288.     {
  289.         if ((i_sqlDataType == SQL_VARCHAR) || (i_sqlDataType == SQL_LONGVARCHAR))
  290.             i_dbDataType = DB_DATA_TYPE_VARCHAR;
  291.         if ((i_sqlDataType == SQL_C_DATE) || (i_sqlDataType == SQL_C_TIMESTAMP))
  292.             i_dbDataType = DB_DATA_TYPE_DATE;
  293.         if (i_sqlDataType == SQL_C_BIT)
  294.             i_dbDataType = DB_DATA_TYPE_INTEGER;
  295.         if (i_sqlDataType == SQL_NUMERIC)
  296.             i_dbDataType = DB_DATA_TYPE_VARCHAR;
  297.         if (i_sqlDataType == SQL_REAL)
  298.             i_dbDataType = DB_DATA_TYPE_FLOAT;
  299.         if (i_sqlDataType == SQL_C_BINARY)
  300.             i_dbDataType = DB_DATA_TYPE_BLOB;
  301.     }
  302.  
  303.     if ((i_dbDataType == DB_DATA_TYPE_INTEGER) && (i_sqlDataType == SQL_C_DOUBLE))
  304.     {   // DBASE Numeric
  305.         i_dbDataType = DB_DATA_TYPE_FLOAT;
  306.     }
  307.  
  308.     switch(i_dbDataType)     // TBD: Still a lot of proper formatting to do
  309.     {
  310.         case DB_DATA_TYPE_VARCHAR:
  311.             s_Field = wxT("%s");
  312.             break;
  313.         case DB_DATA_TYPE_INTEGER:
  314.             s_Field = wxT("%d");
  315.             break;
  316.         case DB_DATA_TYPE_FLOAT:
  317.             if (decimalDigits == 0)
  318.                 decimalDigits = 2;
  319.             tempStr = wxT("%");
  320.             tempStr.Printf(wxT("%s%d.%d"),tempStr.c_str(),columnSize,decimalDigits);
  321.             s_Field.Printf(wxT("%sf"),tempStr.c_str());
  322.             break;
  323.         case DB_DATA_TYPE_DATE:
  324.             if (i_Nation == 0)      // timestamp       YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
  325.             {
  326.                 s_Field = wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
  327.             }
  328.             if (i_Nation == 1)      // European        DD.MM.YYYY HH:MM:SS.SSS
  329.             {
  330.                 s_Field = wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
  331.             }
  332.             if (i_Nation == 2)      // UK              DD/MM/YYYY HH:MM:SS.SSS
  333.             {
  334.                 s_Field = wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
  335.             }
  336.             if (i_Nation == 3)      // International   YYYY-MM-DD HH:MM:SS.SSS
  337.             {
  338.                 s_Field = wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
  339.             }
  340.             if (i_Nation == 4)      // US              MM/DD/YYYY HH:MM:SS.SSS
  341.             {
  342.                 s_Field = wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
  343.             }
  344.             break;
  345.           case DB_DATA_TYPE_BLOB:
  346.             s_Field.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType,sqlDataType);        //
  347.                 break;
  348.         default:
  349.             s_Field.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType,sqlDataType);        //
  350.             break;
  351.     };
  352.     return TRUE;
  353. }  // wxDbColFor::Format()
  354.  
  355.  
  356.  
  357. /********** wxDbColInf Constructor **********/
  358. wxDbColInf::wxDbColInf()
  359. {
  360.     Initialize();
  361. }  // wxDbColInf::wxDbColInf()
  362.  
  363.  
  364. /********** wxDbColInf Destructor ********/
  365. wxDbColInf::~wxDbColInf()
  366. {
  367.     if (pColFor)
  368.         delete pColFor;
  369.     pColFor = NULL;
  370. }  // wxDbColInf::~wxDbColInf()
  371.  
  372.  
  373. bool wxDbColInf::Initialize()
  374. {
  375.     catalog[0]      = 0;
  376.     schema[0]       = 0;
  377.     tableName[0]    = 0;
  378.     colName[0]      = 0;
  379.     sqlDataType     = 0;
  380.     typeName[0]     = 0;
  381.     columnSize      = 0;
  382.     bufferLength    = 0;
  383.     decimalDigits   = 0;
  384.     numPrecRadix    = 0;
  385.     nullable        = 0;
  386.     remarks[0]      = 0;
  387.     dbDataType      = 0;
  388.     PkCol           = 0;
  389.     PkTableName[0]  = 0;
  390.     FkCol           = 0;
  391.     FkTableName[0]  = 0;
  392.     pColFor         = NULL;
  393.  
  394.     return TRUE;
  395. }  // wxDbColInf::Initialize()
  396.  
  397.  
  398. /********** wxDbTableInf Constructor ********/
  399. wxDbTableInf::wxDbTableInf()
  400. {
  401.     Initialize();
  402. }  // wxDbTableInf::wxDbTableInf()
  403.  
  404.  
  405. /********** wxDbTableInf Constructor ********/
  406. wxDbTableInf::~wxDbTableInf()
  407. {
  408.     if (pColInf)
  409.         delete [] pColInf;
  410.     pColInf = NULL;
  411. }  // wxDbTableInf::~wxDbTableInf()
  412.  
  413.  
  414. bool wxDbTableInf::Initialize()
  415. {
  416.     tableName[0]    = 0;
  417.     tableType[0]    = 0;
  418.     tableRemarks[0] = 0;
  419.     numCols         = 0;
  420.     pColInf         = NULL;
  421.  
  422.     return TRUE;
  423. }  // wxDbTableInf::Initialize()
  424.  
  425.  
  426. /********** wxDbInf Constructor *************/
  427. wxDbInf::wxDbInf()
  428. {
  429.     Initialize();
  430. }  // wxDbInf::wxDbInf()
  431.  
  432.  
  433. /********** wxDbInf Destructor *************/
  434. wxDbInf::~wxDbInf()
  435. {
  436.   if (pTableInf)
  437.     delete [] pTableInf;
  438.   pTableInf = NULL;
  439. }  // wxDbInf::~wxDbInf()
  440.  
  441.  
  442. /********** wxDbInf::Initialize() *************/
  443. bool wxDbInf::Initialize()
  444. {
  445.     catalog[0]      = 0;
  446.     schema[0]       = 0;
  447.     numTables       = 0;
  448.     pTableInf       = NULL;
  449.  
  450.     return TRUE;
  451. }  // wxDbInf::Initialize()
  452.  
  453.  
  454. /********** wxDb Constructor **********/
  455. wxDb::wxDb(const HENV &aHenv, bool FwdOnlyCursors)
  456. {
  457.     // Copy the HENV into the db class
  458.     henv = aHenv;
  459.     fwdOnlyCursors = FwdOnlyCursors;
  460.  
  461.     initialize();
  462. } // wxDb::wxDb()
  463.  
  464.  
  465. /********** wxDb Destructor **********/
  466. wxDb::~wxDb()
  467. {
  468.     wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
  469.  
  470.     if (IsOpen())
  471.     {
  472.         Close();
  473.     }
  474. }  // wxDb destructor
  475.  
  476.  
  477.  
  478. /********** PRIVATE! wxDb::initialize PRIVATE! **********/
  479. /********** wxDb::initialize() **********/
  480. void wxDb::initialize()
  481. /*
  482.  * Private member function that sets all wxDb member variables to
  483.  * known values at creation of the wxDb
  484.  */
  485. {
  486.     int i;
  487.  
  488.     fpSqlLog      = 0;            // Sql Log file pointer
  489.     sqlLogState   = sqlLogOFF;    // By default, logging is turned off
  490.     nTables       = 0;
  491.     dbmsType      = dbmsUNIDENTIFIED;
  492.  
  493.     wxStrcpy(sqlState,wxEmptyString);
  494.     wxStrcpy(errorMsg,wxEmptyString);
  495.     nativeError = cbErrorMsg = 0;
  496.     for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
  497.         wxStrcpy(errorList[i], wxEmptyString);
  498.  
  499.     // Init typeInf structures
  500.     typeInfVarchar.TypeName.Empty();
  501.     typeInfVarchar.FsqlType      = 0;
  502.     typeInfVarchar.Precision     = 0;
  503.     typeInfVarchar.CaseSensitive = 0;
  504.     typeInfVarchar.MaximumScale  = 0;
  505.  
  506.     typeInfInteger.TypeName.Empty();
  507.     typeInfInteger.FsqlType      = 0;
  508.     typeInfInteger.Precision     = 0;
  509.     typeInfInteger.CaseSensitive = 0;
  510.     typeInfInteger.MaximumScale  = 0;
  511.  
  512.     typeInfFloat.TypeName.Empty();
  513.     typeInfFloat.FsqlType      = 0;
  514.     typeInfFloat.Precision     = 0;
  515.     typeInfFloat.CaseSensitive = 0;
  516.     typeInfFloat.MaximumScale  = 0;
  517.  
  518.     typeInfDate.TypeName.Empty();
  519.     typeInfDate.FsqlType      = 0;
  520.     typeInfDate.Precision     = 0;
  521.     typeInfDate.CaseSensitive = 0;
  522.     typeInfDate.MaximumScale  = 0;
  523.  
  524.     typeInfBlob.TypeName.Empty();
  525.     typeInfBlob.FsqlType      = 0;
  526.     typeInfBlob.Precision     = 0;
  527.     typeInfBlob.CaseSensitive = 0;
  528.     typeInfBlob.MaximumScale  = 0;
  529.  
  530.     // Error reporting is turned OFF by default
  531.     silent = TRUE;
  532.  
  533.     // Allocate a data source connection handle
  534.     if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
  535.         DispAllErrors(henv);
  536.  
  537.     // Initialize the db status flag
  538.     DB_STATUS = 0;
  539.  
  540.     // Mark database as not open as of yet
  541.     dbIsOpen = FALSE;
  542.     dbIsCached = FALSE;
  543. }  // wxDb::initialize()
  544.  
  545.  
  546. /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
  547. //
  548. // NOTE: Return value from this function MUST be copied
  549. //       immediately, as the value is not good after
  550. //       this function has left scope.
  551. //
  552. const wxChar *wxDb::convertUserID(const wxChar *userID, wxString &UserID)
  553. {
  554.     if (userID)
  555.     {
  556.         if (!wxStrlen(userID))
  557.             UserID = uid;
  558.         else
  559.             UserID = userID;
  560.     }
  561.     else
  562.         UserID.Empty();
  563.  
  564.     // dBase does not use user names, and some drivers fail if you try to pass one
  565.     if ( Dbms() == dbmsDBASE
  566.          || Dbms() == dbmsXBASE_SEQUITER )
  567.         UserID.Empty();
  568.  
  569.     // Oracle user names may only be in uppercase, so force
  570.     // the name to uppercase
  571.     if (Dbms() == dbmsORACLE)
  572.         UserID = UserID.Upper();
  573.  
  574.     return UserID.c_str();
  575. }  // wxDb::convertUserID()
  576.  
  577.  
  578. /********** wxDb::Open() **********/
  579. bool wxDb::Open(const wxString &Dsn, const wxString &Uid, const wxString &AuthStr, bool failOnDataTypeUnsupported)
  580. {
  581.     wxASSERT(Dsn.Length());
  582.     dsn        = Dsn;
  583.     uid        = Uid;
  584.     authStr    = AuthStr;
  585.  
  586.     RETCODE retcode;
  587.  
  588.     if (!FwdOnlyCursors())
  589.     {
  590.         // Specify that the ODBC cursor library be used, if needed.  This must be
  591.         // specified before the connection is made.
  592.         retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
  593.  
  594. #ifdef DBDEBUG_CONSOLE
  595.         if (retcode == SQL_SUCCESS)
  596.             cout << wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl;
  597.         else
  598.             cout << wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl;
  599. #endif
  600.     }
  601.  
  602.     // Connect to the data source
  603.     retcode = SQLConnect(hdbc, (UCHAR FAR *) dsn.c_str(), SQL_NTS,
  604.                          (UCHAR FAR *) uid.c_str(), SQL_NTS,
  605.                          (UCHAR FAR *) authStr.c_str(), SQL_NTS);
  606.  
  607.     if ((retcode != SQL_SUCCESS) &&
  608.         (retcode != SQL_SUCCESS_WITH_INFO))
  609.         return(DispAllErrors(henv, hdbc));
  610.  
  611. /*
  612.     If using Intersolv branded ODBC drivers, this is the place where you would substitute
  613.     your branded driver license information
  614.  
  615.     SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
  616.     SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
  617. */
  618.  
  619.     // Mark database as open
  620.     dbIsOpen = TRUE;
  621.  
  622.     // Allocate a statement handle for the database connection
  623.     if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
  624.         return(DispAllErrors(henv, hdbc));
  625.  
  626.     // Set Connection Options
  627.     if (!setConnectionOptions())
  628.         return(FALSE);
  629.  
  630.     // Query the data source for inf. about itself
  631.     if (!getDbInfo())
  632.         return(FALSE);
  633.  
  634.     // Query the data source regarding data type information
  635.  
  636.     //
  637.     // The way it was determined which SQL data types to use was by calling SQLGetInfo
  638.     // for all of the possible SQL data types to see which ones were supported.  If
  639.     // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
  640.     // fails with SQL_NO_DATA_FOUND.  This is ugly because I'm sure the three SQL data
  641.     // types I've selected below will not alway's be what we want.  These are just
  642.     // what happened to work against an Oracle 7/Intersolv combination.  The following is
  643.     // a complete list of the results I got back against the Oracle 7 database:
  644.     //
  645.     // SQL_BIGINT             SQL_NO_DATA_FOUND
  646.     // SQL_BINARY             SQL_NO_DATA_FOUND
  647.     // SQL_BIT                SQL_NO_DATA_FOUND
  648.     // SQL_CHAR               type name = 'CHAR', Precision = 255
  649.     // SQL_DATE               SQL_NO_DATA_FOUND
  650.     // SQL_DECIMAL            type name = 'NUMBER', Precision = 38
  651.     // SQL_DOUBLE             type name = 'NUMBER', Precision = 15
  652.     // SQL_FLOAT              SQL_NO_DATA_FOUND
  653.     // SQL_INTEGER            SQL_NO_DATA_FOUND
  654.     // SQL_LONGVARBINARY      type name = 'LONG RAW', Precision = 2 billion
  655.     // SQL_LONGVARCHAR        type name = 'LONG', Precision = 2 billion
  656.     // SQL_NUMERIC            SQL_NO_DATA_FOUND
  657.     // SQL_REAL               SQL_NO_DATA_FOUND
  658.     // SQL_SMALLINT           SQL_NO_DATA_FOUND
  659.     // SQL_TIME               SQL_NO_DATA_FOUND
  660.     // SQL_TIMESTAMP          type name = 'DATE', Precision = 19
  661.     // SQL_VARBINARY          type name = 'RAW', Precision = 255
  662.     // SQL_VARCHAR            type name = 'VARCHAR2', Precision = 2000
  663.     // =====================================================================
  664.     // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
  665.     //
  666.     // SQL_VARCHAR            type name = 'TEXT', Precision = 255
  667.     // SQL_TIMESTAMP          type name = 'DATETIME'
  668.     // SQL_DECIMAL            SQL_NO_DATA_FOUND
  669.     // SQL_NUMERIC            type name = 'CURRENCY', Precision = 19
  670.     // SQL_FLOAT              SQL_NO_DATA_FOUND
  671.     // SQL_REAL               type name = 'SINGLE', Precision = 7
  672.     // SQL_DOUBLE             type name = 'DOUBLE', Precision = 15
  673.     // SQL_INTEGER            type name = 'LONG', Precision = 10
  674.  
  675.     // VARCHAR = Variable length character string
  676.     if (!getDataTypeInfo(SQL_VARCHAR, typeInfVarchar))
  677.         if (!getDataTypeInfo(SQL_CHAR, typeInfVarchar))
  678.             return(FALSE);
  679.         else
  680.             typeInfVarchar.FsqlType = SQL_CHAR;
  681.     else
  682.         typeInfVarchar.FsqlType = SQL_VARCHAR;
  683.  
  684.     // Float
  685.     if (!getDataTypeInfo(SQL_DOUBLE,typeInfFloat))
  686.         if (!getDataTypeInfo(SQL_REAL,typeInfFloat))
  687.             if (!getDataTypeInfo(SQL_FLOAT,typeInfFloat))
  688.                 if (!getDataTypeInfo(SQL_DECIMAL,typeInfFloat))
  689.                     if (!getDataTypeInfo(SQL_NUMERIC,typeInfFloat))
  690.                     {
  691.                         if (failOnDataTypeUnsupported)
  692.                             return(FALSE);
  693.                     }
  694.                     else
  695.                         typeInfFloat.FsqlType = SQL_NUMERIC;
  696.                 else
  697.                     typeInfFloat.FsqlType = SQL_DECIMAL;
  698.             else
  699.                 typeInfFloat.FsqlType = SQL_FLOAT;
  700.         else
  701.             typeInfFloat.FsqlType = SQL_REAL;
  702.     else
  703.         typeInfFloat.FsqlType = SQL_DOUBLE;
  704.  
  705.     // Integer
  706.     if (!getDataTypeInfo(SQL_INTEGER, typeInfInteger))
  707.     {
  708.         // If SQL_INTEGER is not supported, use the floating point
  709.         // data type to store integers as well as floats
  710.         if (!getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
  711.         {
  712.             if (failOnDataTypeUnsupported)
  713.                 return(FALSE);
  714.         }
  715.         else
  716.             typeInfInteger.FsqlType = typeInfFloat.FsqlType;
  717.     }
  718.     else
  719.         typeInfInteger.FsqlType = SQL_INTEGER;
  720.  
  721.     // Date/Time
  722.     if (!getDataTypeInfo(SQL_TIMESTAMP,typeInfDate))
  723.     {
  724.         if (!getDataTypeInfo(SQL_DATE,typeInfDate))
  725.         {
  726. #ifdef SQL_DATETIME
  727.             if (getDataTypeInfo(SQL_DATETIME,typeInfDate))
  728.             {
  729.                 typeInfDate.FsqlType = SQL_TIME;
  730.             }
  731.             else
  732. #endif // SQL_DATETIME defined
  733.             {
  734.                 if (failOnDataTypeUnsupported)
  735.                     return(FALSE);
  736.             }
  737.         }
  738.         else
  739.             typeInfDate.FsqlType = SQL_DATE;
  740.     }
  741.     else
  742.         typeInfDate.FsqlType = SQL_TIMESTAMP;
  743.  
  744.  
  745.     if (!getDataTypeInfo(SQL_LONGVARBINARY, typeInfBlob))
  746.     {
  747.         if (!getDataTypeInfo(SQL_VARBINARY,typeInfBlob))
  748.         {
  749.             if (failOnDataTypeUnsupported)
  750.                 return(FALSE);
  751.         }
  752.         else
  753.             typeInfBlob.FsqlType = SQL_VARBINARY;
  754.     }
  755.     else
  756.         typeInfBlob.FsqlType = SQL_LONGVARBINARY;
  757.  
  758. //typeInfBlob.TypeName = "BLOB";
  759.  
  760. #ifdef DBDEBUG_CONSOLE
  761.     cout << wxT("VARCHAR DATA TYPE: ") << typeInfVarchar.TypeName << endl;
  762.     cout << wxT("INTEGER DATA TYPE: ") << typeInfInteger.TypeName << endl;
  763.     cout << wxT("FLOAT   DATA TYPE: ") << typeInfFloat.TypeName << endl;
  764.     cout << wxT("DATE    DATA TYPE: ") << typeInfDate.TypeName << endl;
  765.     cout << wxT("BLOB    DATA TYPE: ") << typeInfBlob.TypeName << endl;
  766.     cout << endl;
  767. #endif
  768.  
  769.     // Completed Successfully
  770.     return(TRUE);
  771.  
  772. } // wxDb::Open()
  773.  
  774.  
  775. bool wxDb::Open(wxDbConnectInf *dbConnectInf)
  776. {
  777.     return Open(dbConnectInf->GetDsn(), dbConnectInf->GetUserID(),
  778.                 dbConnectInf->GetPassword());
  779. }  // wxDb::Open()
  780.  
  781.  
  782. bool wxDb::Open(wxDb *copyDb)
  783. {
  784.     dsn        = copyDb->GetDatasourceName();
  785.     uid        = copyDb->GetUsername();
  786.     authStr    = copyDb->GetPassword();
  787.  
  788.     RETCODE retcode;
  789.  
  790.     if (!FwdOnlyCursors())
  791.     {
  792.         // Specify that the ODBC cursor library be used, if needed.  This must be
  793.         // specified before the connection is made.
  794.         retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
  795.  
  796. #ifdef DBDEBUG_CONSOLE
  797.         if (retcode == SQL_SUCCESS)
  798.             cout << wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl;
  799.         else
  800.             cout << wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl;
  801. #endif
  802.     }
  803.  
  804.     // Connect to the data source
  805.     retcode = SQLConnect(hdbc, (UCHAR FAR *) dsn.c_str(), SQL_NTS,
  806.                          (UCHAR FAR *) uid.c_str(), SQL_NTS,
  807.                          (UCHAR FAR *) authStr.c_str(), SQL_NTS);
  808.  
  809.     if (retcode == SQL_ERROR)
  810.         return(DispAllErrors(henv, hdbc));
  811.  
  812. /*
  813.     If using Intersolv branded ODBC drivers, this is the place where you would substitute
  814.     your branded driver license information
  815.  
  816.     SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
  817.     SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
  818. */
  819.  
  820.     // Mark database as open
  821.     dbIsOpen = TRUE;
  822.  
  823.     // Allocate a statement handle for the database connection
  824.     if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
  825.         return(DispAllErrors(henv, hdbc));
  826.  
  827.     // Set Connection Options
  828.     if (!setConnectionOptions())
  829.         return(FALSE);
  830.  
  831.     // Instead of Querying the data source for info about itself, it can just be copied
  832.     // from the wxDb instance that was passed in (copyDb).
  833.     wxStrcpy(dbInf.serverName,copyDb->dbInf.serverName);
  834.     wxStrcpy(dbInf.databaseName,copyDb->dbInf.databaseName);
  835.     wxStrcpy(dbInf.dbmsName,copyDb->dbInf.dbmsName);
  836.     wxStrcpy(dbInf.dbmsVer,copyDb->dbInf.dbmsVer);
  837.     dbInf.maxConnections = copyDb->dbInf.maxConnections;
  838.     dbInf.maxStmts = copyDb->dbInf.maxStmts;
  839.     wxStrcpy(dbInf.driverName,copyDb->dbInf.driverName);
  840.     wxStrcpy(dbInf.odbcVer,copyDb->dbInf.odbcVer);
  841.     wxStrcpy(dbInf.drvMgrOdbcVer,copyDb->dbInf.drvMgrOdbcVer);
  842.     wxStrcpy(dbInf.driverVer,copyDb->dbInf.driverVer);
  843.     dbInf.apiConfLvl = copyDb->dbInf.apiConfLvl;
  844.     dbInf.cliConfLvl = copyDb->dbInf.cliConfLvl;
  845.     dbInf.sqlConfLvl = copyDb->dbInf.sqlConfLvl;
  846.     wxStrcpy(dbInf.outerJoins,copyDb->dbInf.outerJoins);
  847.     wxStrcpy(dbInf.procedureSupport,copyDb->dbInf.procedureSupport);
  848.     wxStrcpy(dbInf.accessibleTables,copyDb->dbInf.accessibleTables);
  849.     dbInf.cursorCommitBehavior = copyDb->dbInf.cursorCommitBehavior;
  850.     dbInf.cursorRollbackBehavior = copyDb->dbInf.cursorRollbackBehavior;
  851.     dbInf.supportNotNullClause = copyDb->dbInf.supportNotNullClause;
  852.     wxStrcpy(dbInf.supportIEF,copyDb->dbInf.supportIEF);
  853.     dbInf.txnIsolation = copyDb->dbInf.txnIsolation;
  854.     dbInf.txnIsolationOptions = copyDb->dbInf.txnIsolationOptions;
  855.     dbInf.fetchDirections = copyDb->dbInf.fetchDirections;
  856.     dbInf.lockTypes = copyDb->dbInf.lockTypes;
  857.     dbInf.posOperations = copyDb->dbInf.posOperations;
  858.     dbInf.posStmts = copyDb->dbInf.posStmts;
  859.     dbInf.scrollConcurrency = copyDb->dbInf.scrollConcurrency;
  860.     dbInf.scrollOptions = copyDb->dbInf.scrollOptions;
  861.     dbInf.staticSensitivity = copyDb->dbInf.staticSensitivity;
  862.     dbInf.txnCapable = copyDb->dbInf.txnCapable;
  863.     dbInf.loginTimeout = copyDb->dbInf.loginTimeout;
  864.  
  865.     // VARCHAR = Variable length character string
  866.     typeInfVarchar.FsqlType         = copyDb->typeInfVarchar.FsqlType;
  867.     typeInfVarchar.TypeName         = copyDb->typeInfVarchar.TypeName;
  868.     typeInfVarchar.Precision        = copyDb->typeInfVarchar.Precision;
  869.     typeInfVarchar.CaseSensitive    = copyDb->typeInfVarchar.CaseSensitive;
  870.     typeInfVarchar.MaximumScale     = copyDb->typeInfVarchar.MaximumScale;
  871.  
  872.     // Float
  873.     typeInfFloat.FsqlType         = copyDb->typeInfFloat.FsqlType;
  874.     typeInfFloat.TypeName         = copyDb->typeInfFloat.TypeName;
  875.     typeInfFloat.Precision        = copyDb->typeInfFloat.Precision;
  876.     typeInfFloat.CaseSensitive    = copyDb->typeInfFloat.CaseSensitive;
  877.     typeInfFloat.MaximumScale     = copyDb->typeInfFloat.MaximumScale;
  878.  
  879.     // Integer
  880.     typeInfInteger.FsqlType         = copyDb->typeInfInteger.FsqlType;
  881.     typeInfInteger.TypeName         = copyDb->typeInfInteger.TypeName;
  882.     typeInfInteger.Precision        = copyDb->typeInfInteger.Precision;
  883.     typeInfInteger.CaseSensitive    = copyDb->typeInfInteger.CaseSensitive;
  884.     typeInfInteger.MaximumScale     = copyDb->typeInfInteger.MaximumScale;
  885.  
  886.     // Date/Time
  887.     typeInfDate.FsqlType         = copyDb->typeInfDate.FsqlType;
  888.     typeInfDate.TypeName         = copyDb->typeInfDate.TypeName;
  889.     typeInfDate.Precision        = copyDb->typeInfDate.Precision;
  890.     typeInfDate.CaseSensitive    = copyDb->typeInfDate.CaseSensitive;
  891.     typeInfDate.MaximumScale     = copyDb->typeInfDate.MaximumScale;
  892.  
  893.     // Blob
  894.     typeInfBlob.FsqlType         = copyDb->typeInfBlob.FsqlType;
  895.     typeInfBlob.TypeName         = copyDb->typeInfBlob.TypeName;
  896.     typeInfBlob.Precision        = copyDb->typeInfBlob.Precision;
  897.     typeInfBlob.CaseSensitive    = copyDb->typeInfBlob.CaseSensitive;
  898.     typeInfBlob.MaximumScale     = copyDb->typeInfBlob.MaximumScale;
  899.  
  900. #ifdef DBDEBUG_CONSOLE
  901.     cout << wxT("VARCHAR DATA TYPE: ") << typeInfVarchar.TypeName << endl;
  902.     cout << wxT("INTEGER DATA TYPE: ") << typeInfInteger.TypeName << endl;
  903.     cout << wxT("FLOAT   DATA TYPE: ") << typeInfFloat.TypeName << endl;
  904.     cout << wxT("DATE    DATA TYPE: ") << typeInfDate.TypeName << endl;
  905.     cout << wxT("BLOB    DATA TYPE: ") << typeInfBlob.TypeName << endl;
  906.     cout << endl;
  907. #endif
  908.  
  909.     // Completed Successfully
  910.     return(TRUE);
  911. } // wxDb::Open() 2
  912.  
  913.  
  914. /********** wxDb::setConnectionOptions() **********/
  915. bool wxDb::setConnectionOptions(void)
  916. /*
  917.  * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
  918.  */
  919. {
  920.     SWORD cb;
  921.  
  922.     // I need to get the DBMS name here, because some of the connection options
  923.     // are database specific and need to call the Dbms() function.
  924.     if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
  925.         return(DispAllErrors(henv, hdbc));
  926.  
  927.     SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
  928.     SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
  929. //  SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED);  // No dirty reads
  930.  
  931.     // By default, MS Sql Server closes cursors on commit and rollback.  The following
  932.     // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
  933.     // after a transaction.  This is a driver specific option and is not part of the
  934.     // ODBC standard.  Note: this behavior is specific to the ODBC interface to SQL Server.
  935.     // The database settings don't have any effect one way or the other.
  936.     if (Dbms() == dbmsMS_SQL_SERVER)
  937.     {
  938.         const long SQL_PRESERVE_CURSORS = 1204L;
  939.         const long SQL_PC_ON = 1L;
  940.         SQLSetConnectOption(hdbc, SQL_PRESERVE_CURSORS, SQL_PC_ON);
  941.     }
  942.  
  943.     // Display the connection options to verify them
  944. #ifdef DBDEBUG_CONSOLE
  945.     long l;
  946.     cout << wxT("****** CONNECTION OPTIONS ******") << endl;
  947.  
  948.     if (SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l) != SQL_SUCCESS)
  949.         return(DispAllErrors(henv, hdbc));
  950.     cout << wxT("AUTOCOMMIT: ") << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
  951.  
  952.     if (SQLGetConnectOption(hdbc, SQL_ODBC_CURSORS, &l) != SQL_SUCCESS)
  953.         return(DispAllErrors(henv, hdbc));
  954.     cout << wxT("ODBC CURSORS: ");
  955.     switch(l)
  956.         {
  957.         case(SQL_CUR_USE_IF_NEEDED):
  958.             cout << wxT("SQL_CUR_USE_IF_NEEDED");
  959.             break;
  960.         case(SQL_CUR_USE_ODBC):
  961.             cout << wxT("SQL_CUR_USE_ODBC");
  962.             break;
  963.         case(SQL_CUR_USE_DRIVER):
  964.             cout << wxT("SQL_CUR_USE_DRIVER");
  965.             break;
  966.         }
  967.         cout << endl;
  968.  
  969.     if (SQLGetConnectOption(hdbc, SQL_OPT_TRACE, &l) != SQL_SUCCESS)
  970.         return(DispAllErrors(henv, hdbc));
  971.     cout << wxT("TRACING: ") << (l == SQL_OPT_TRACE_OFF ? wxT("OFF") : wxT("ON")) << endl;
  972.  
  973.     cout << endl;
  974. #endif
  975.  
  976.     // Completed Successfully
  977.     return(TRUE);
  978.  
  979. } // wxDb::setConnectionOptions()
  980.  
  981.  
  982. /********** wxDb::getDbInfo() **********/
  983. bool wxDb::getDbInfo(void)
  984. {
  985.     SWORD cb;
  986.     RETCODE retcode;
  987.  
  988.     if (SQLGetInfo(hdbc, SQL_SERVER_NAME, (UCHAR*) dbInf.serverName, 80, &cb) != SQL_SUCCESS)
  989.         return(DispAllErrors(henv, hdbc));
  990.  
  991.     if (SQLGetInfo(hdbc, SQL_DATABASE_NAME, (UCHAR*) dbInf.databaseName, 128, &cb) != SQL_SUCCESS)
  992.         return(DispAllErrors(henv, hdbc));
  993.  
  994.     if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
  995.         return(DispAllErrors(henv, hdbc));
  996.  
  997.     // 16-Mar-1999
  998.     // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
  999.     // causing database connectivity to fail in some cases.
  1000.     retcode = SQLGetInfo(hdbc, SQL_DBMS_VER, (UCHAR*) dbInf.dbmsVer, 64, &cb);
  1001.  
  1002.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
  1003.         return(DispAllErrors(henv, hdbc));
  1004.  
  1005.     if (SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, (UCHAR*) &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb) != SQL_SUCCESS)
  1006.         return(DispAllErrors(henv, hdbc));
  1007.  
  1008.     if (SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, (UCHAR*) &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb) != SQL_SUCCESS)
  1009.         return(DispAllErrors(henv, hdbc));
  1010.  
  1011.     if (SQLGetInfo(hdbc, SQL_DRIVER_NAME, (UCHAR*) dbInf.driverName, 40, &cb) != SQL_SUCCESS)
  1012.         return(DispAllErrors(henv, hdbc));
  1013.  
  1014.     if (SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, (UCHAR*) dbInf.odbcVer, 60, &cb) == SQL_ERROR)
  1015.         return(DispAllErrors(henv, hdbc));
  1016.  
  1017.     retcode = SQLGetInfo(hdbc, SQL_ODBC_VER, (UCHAR*) dbInf.drvMgrOdbcVer, 60, &cb);
  1018.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  1019.         return(DispAllErrors(henv, hdbc));
  1020.  
  1021.     if (SQLGetInfo(hdbc, SQL_DRIVER_VER, (UCHAR*) dbInf.driverVer, 60, &cb) == SQL_ERROR)
  1022.         return(DispAllErrors(henv, hdbc));
  1023.  
  1024.     if (SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, (UCHAR*) &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb) != SQL_SUCCESS)
  1025.         return(DispAllErrors(henv, hdbc));
  1026.  
  1027.     if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
  1028. //        return(DispAllErrors(henv, hdbc));
  1029.     {
  1030.         // Not all drivers support this call - Nick Gorham(unixODBC)
  1031.         dbInf.cliConfLvl = 0;
  1032.     }
  1033.  
  1034.     if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
  1035.         return(DispAllErrors(henv, hdbc));
  1036.  
  1037.     if (SQLGetInfo(hdbc, SQL_OUTER_JOINS, (UCHAR*) dbInf.outerJoins, 2, &cb) != SQL_SUCCESS)
  1038.         return(DispAllErrors(henv, hdbc));
  1039.  
  1040.     if (SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
  1041.         return(DispAllErrors(henv, hdbc));
  1042.  
  1043.     if (SQLGetInfo(hdbc, SQL_ACCESSIBLE_TABLES, (UCHAR*) dbInf.accessibleTables, 2, &cb) != SQL_SUCCESS)
  1044.         return(DispAllErrors(henv, hdbc));
  1045.  
  1046.     if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
  1047.         return(DispAllErrors(henv, hdbc));
  1048.  
  1049.     if (SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, (UCHAR*) &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb) != SQL_SUCCESS)
  1050.         return(DispAllErrors(henv, hdbc));
  1051.  
  1052.     if (SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, (UCHAR*) &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb) != SQL_SUCCESS)
  1053.         return(DispAllErrors(henv, hdbc));
  1054.  
  1055.     if (SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, (UCHAR*) dbInf.supportIEF, 2, &cb) != SQL_SUCCESS)
  1056.         return(DispAllErrors(henv, hdbc));
  1057.  
  1058.     if (SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, (UCHAR*) &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb) != SQL_SUCCESS)
  1059.         return(DispAllErrors(henv, hdbc));
  1060.  
  1061.     if (SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, (UCHAR*) &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb) != SQL_SUCCESS)
  1062.         return(DispAllErrors(henv, hdbc));
  1063.  
  1064.     if (SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, (UCHAR*) &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb) != SQL_SUCCESS)
  1065.         return(DispAllErrors(henv, hdbc));
  1066.  
  1067.     if (SQLGetInfo(hdbc, SQL_LOCK_TYPES, (UCHAR*) &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb) != SQL_SUCCESS)
  1068.         return(DispAllErrors(henv, hdbc));
  1069.  
  1070.     if (SQLGetInfo(hdbc, SQL_POS_OPERATIONS, (UCHAR*) &dbInf.posOperations, sizeof(dbInf.posOperations), &cb) != SQL_SUCCESS)
  1071.         return(DispAllErrors(henv, hdbc));
  1072.  
  1073.     if (SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, (UCHAR*) &dbInf.posStmts, sizeof(dbInf.posStmts), &cb) != SQL_SUCCESS)
  1074.         return(DispAllErrors(henv, hdbc));
  1075.  
  1076.     if (SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, (UCHAR*) &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb) != SQL_SUCCESS)
  1077.         return(DispAllErrors(henv, hdbc));
  1078.  
  1079.     if (SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, (UCHAR*) &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb) != SQL_SUCCESS)
  1080.         return(DispAllErrors(henv, hdbc));
  1081.  
  1082.     if (SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, (UCHAR*) &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb) != SQL_SUCCESS)
  1083.         return(DispAllErrors(henv, hdbc));
  1084.  
  1085.     if (SQLGetInfo(hdbc, SQL_TXN_CAPABLE, (UCHAR*) &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb) != SQL_SUCCESS)
  1086.         return(DispAllErrors(henv, hdbc));
  1087.  
  1088.     if (SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, (UCHAR*) &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb) != SQL_SUCCESS)
  1089.         return(DispAllErrors(henv, hdbc));
  1090.  
  1091. #ifdef DBDEBUG_CONSOLE
  1092.     cout << wxT("***** DATA SOURCE INFORMATION *****") << endl;
  1093.     cout << wxT(wxT("SERVER Name: ") << dbInf.serverName << endl;
  1094.     cout << wxT("DBMS Name: ") << dbInf.dbmsName << wxT("; DBMS Version: ") << dbInf.dbmsVer << endl;
  1095.     cout << wxT("ODBC Version: ") << dbInf.odbcVer << wxT("; Driver Version: ") << dbInf.driverVer << endl;
  1096.  
  1097.     cout << wxT("API Conf. Level: ");
  1098.     switch(dbInf.apiConfLvl)
  1099.     {
  1100.         case SQL_OAC_NONE:      cout << wxT("None");       break;
  1101.         case SQL_OAC_LEVEL1:    cout << wxT("Level 1");    break;
  1102.         case SQL_OAC_LEVEL2:    cout << wxT("Level 2");    break;
  1103.     }
  1104.     cout << endl;
  1105.  
  1106.     cout << wxT("SAG CLI Conf. Level: ");
  1107.     switch(dbInf.cliConfLvl)
  1108.     {
  1109.         case SQL_OSCC_NOT_COMPLIANT:    cout << wxT("Not Compliant");    break;
  1110.         case SQL_OSCC_COMPLIANT:        cout << wxT("Compliant");        break;
  1111.     }
  1112.     cout << endl;
  1113.  
  1114.     cout << wxT("SQL Conf. Level: ");
  1115.     switch(dbInf.sqlConfLvl)
  1116.     {
  1117.         case SQL_OSC_MINIMUM:     cout << wxT("Minimum Grammar");     break;
  1118.         case SQL_OSC_CORE:        cout << wxT("Core Grammar");        break;
  1119.         case SQL_OSC_EXTENDED:    cout << wxT("Extended Grammar");    break;
  1120.     }
  1121.     cout << endl;
  1122.  
  1123.     cout << wxT("Max. Connections: ")       << dbInf.maxConnections   << endl;
  1124.     cout << wxT("Outer Joins: ")            << dbInf.outerJoins       << endl;
  1125.     cout << wxT("Support for Procedures: ") << dbInf.procedureSupport << endl;
  1126.     cout << wxT("All tables accessible : ") << dbInf.accessibleTables << endl;
  1127.     cout << wxT("Cursor COMMIT Behavior: ");
  1128.     switch(dbInf.cursorCommitBehavior)
  1129.     {
  1130.         case SQL_CB_DELETE:        cout << wxT("Delete cursors");      break;
  1131.         case SQL_CB_CLOSE:         cout << wxT("Close cursors");       break;
  1132.         case SQL_CB_PRESERVE:      cout << wxT("Preserve cursors");    break;
  1133.     }
  1134.     cout << endl;
  1135.  
  1136.     cout << wxT("Cursor ROLLBACK Behavior: ");
  1137.     switch(dbInf.cursorRollbackBehavior)
  1138.     {
  1139.         case SQL_CB_DELETE:      cout << wxT("Delete cursors");      break;
  1140.         case SQL_CB_CLOSE:       cout << wxT("Close cursors");       break;
  1141.         case SQL_CB_PRESERVE:    cout << wxT("Preserve cursors");    break;
  1142.     }
  1143.     cout << endl;
  1144.  
  1145.     cout << wxT("Support NOT NULL clause: ");
  1146.     switch(dbInf.supportNotNullClause)
  1147.     {
  1148.         case SQL_NNC_NULL:        cout << wxT("No");        break;
  1149.         case SQL_NNC_NON_NULL:    cout << wxT("Yes");       break;
  1150.     }
  1151.     cout << endl;
  1152.  
  1153.     cout << wxT("Support IEF (Ref. Integrity): ") << dbInf.supportIEF   << endl;
  1154.     cout << wxT("Login Timeout: ")                << dbInf.loginTimeout << endl;
  1155.  
  1156.     cout << endl << endl << wxT("more ...") << endl;
  1157.     getchar();
  1158.  
  1159.     cout << wxT("Default Transaction Isolation: ";
  1160.     switch(dbInf.txnIsolation)
  1161.     {
  1162.         case SQL_TXN_READ_UNCOMMITTED:  cout << wxT("Read Uncommitted");    break;
  1163.         case SQL_TXN_READ_COMMITTED:    cout << wxT("Read Committed");      break;
  1164.         case SQL_TXN_REPEATABLE_READ:   cout << wxT("Repeatable Read");     break;
  1165.         case SQL_TXN_SERIALIZABLE:      cout << wxT("Serializable");        break;
  1166. #ifdef ODBC_V20
  1167.         case SQL_TXN_VERSIONING:        cout << wxT("Versioning");          break;
  1168. #endif
  1169.     }
  1170.     cout << endl;
  1171.  
  1172.     cout << wxT("Transaction Isolation Options: ");
  1173.     if (dbInf.txnIsolationOptions & SQL_TXN_READ_UNCOMMITTED)
  1174.         cout << wxT("Read Uncommitted, ");
  1175.     if (dbInf.txnIsolationOptions & SQL_TXN_READ_COMMITTED)
  1176.         cout << wxT("Read Committed, ");
  1177.     if (dbInf.txnIsolationOptions & SQL_TXN_REPEATABLE_READ)
  1178.         cout << wxT("Repeatable Read, ");
  1179.     if (dbInf.txnIsolationOptions & SQL_TXN_SERIALIZABLE)
  1180.         cout << wxT("Serializable, ");
  1181. #ifdef ODBC_V20
  1182.     if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
  1183.         cout << wxT("Versioning");
  1184. #endif
  1185.     cout << endl;
  1186.  
  1187.     cout << wxT("Fetch Directions Supported:") << endl << wxT("   ");
  1188.     if (dbInf.fetchDirections & SQL_FD_FETCH_NEXT)
  1189.         cout << wxT("Next, ");
  1190.     if (dbInf.fetchDirections & SQL_FD_FETCH_PRIOR)
  1191.         cout << wxT("Prev, ");
  1192.     if (dbInf.fetchDirections & SQL_FD_FETCH_FIRST)
  1193.         cout << wxT("First, ");
  1194.     if (dbInf.fetchDirections & SQL_FD_FETCH_LAST)
  1195.         cout << wxT("Last, ");
  1196.     if (dbInf.fetchDirections & SQL_FD_FETCH_ABSOLUTE)
  1197.         cout << wxT("Absolute, ");
  1198.     if (dbInf.fetchDirections & SQL_FD_FETCH_RELATIVE)
  1199.         cout << wxT("Relative, ");
  1200. #ifdef ODBC_V20
  1201.     if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
  1202.         cout << wxT("Resume, ");
  1203. #endif
  1204.     if (dbInf.fetchDirections & SQL_FD_FETCH_BOOKMARK)
  1205.         cout << wxT("Bookmark");
  1206.     cout << endl;
  1207.  
  1208.     cout << wxT("Lock Types Supported (SQLSetPos): ");
  1209.     if (dbInf.lockTypes & SQL_LCK_NO_CHANGE)
  1210.         cout << wxT("No Change, ");
  1211.     if (dbInf.lockTypes & SQL_LCK_EXCLUSIVE)
  1212.         cout << wxT("Exclusive, ");
  1213.     if (dbInf.lockTypes & SQL_LCK_UNLOCK)
  1214.         cout << wxT("UnLock");
  1215.     cout << endl;
  1216.  
  1217.     cout << wxT("Position Operations Supported (SQLSetPos): ");
  1218.     if (dbInf.posOperations & SQL_POS_POSITION)
  1219.         cout << wxT("Position, ");
  1220.     if (dbInf.posOperations & SQL_POS_REFRESH)
  1221.         cout << wxT("Refresh, ");
  1222.     if (dbInf.posOperations & SQL_POS_UPDATE)
  1223.         cout << wxT("Upd, "));
  1224.     if (dbInf.posOperations & SQL_POS_DELETE)
  1225.         cout << wxT("Del, ");
  1226.     if (dbInf.posOperations & SQL_POS_ADD)
  1227.         cout << wxT("Add");
  1228.     cout << endl;
  1229.  
  1230.     cout << wxT("Positioned Statements Supported: ");
  1231.     if (dbInf.posStmts & SQL_PS_POSITIONED_DELETE)
  1232.         cout << wxT("Pos delete, ");
  1233.     if (dbInf.posStmts & SQL_PS_POSITIONED_UPDATE)
  1234.         cout << wxT("Pos update, ");
  1235.     if (dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
  1236.         cout << wxT("Select for update");
  1237.     cout << endl;
  1238.  
  1239.     cout << wxT("Scroll Concurrency: ");
  1240.     if (dbInf.scrollConcurrency & SQL_SCCO_READ_ONLY)
  1241.         cout << wxT("Read Only, ");
  1242.     if (dbInf.scrollConcurrency & SQL_SCCO_LOCK)
  1243.         cout << wxT("Lock, ");
  1244.     if (dbInf.scrollConcurrency & SQL_SCCO_OPT_ROWVER)
  1245.         cout << wxT("Opt. Rowver, ");
  1246.     if (dbInf.scrollConcurrency & SQL_SCCO_OPT_VALUES)
  1247.         cout << wxT("Opt. Values");
  1248.     cout << endl;
  1249.  
  1250.     cout << wxT("Scroll Options: ");
  1251.     if (dbInf.scrollOptions & SQL_SO_FORWARD_ONLY)
  1252.         cout << wxT("Fwd Only, ");
  1253.     if (dbInf.scrollOptions & SQL_SO_STATIC)
  1254.         cout << wxT("Static, ");
  1255.     if (dbInf.scrollOptions & SQL_SO_KEYSET_DRIVEN)
  1256.         cout << wxT("Keyset Driven, ");
  1257.     if (dbInf.scrollOptions & SQL_SO_DYNAMIC)
  1258.         cout << wxT("Dynamic, ");
  1259.     if (dbInf.scrollOptions & SQL_SO_MIXED)
  1260.         cout << wxT("Mixed");
  1261.     cout << endl;
  1262.  
  1263.     cout << wxT("Static Sensitivity: ");
  1264.     if (dbInf.staticSensitivity & SQL_SS_ADDITIONS)
  1265.         cout << wxT("Additions, ");
  1266.     if (dbInf.staticSensitivity & SQL_SS_DELETIONS)
  1267.         cout << wxT("Deletions, ");
  1268.     if (dbInf.staticSensitivity & SQL_SS_UPDATES)
  1269.         cout << wxT("Updates");
  1270.     cout << endl;
  1271.  
  1272.     cout << wxT("Transaction Capable?: ");
  1273.     switch(dbInf.txnCapable)
  1274.     {
  1275.         case SQL_TC_NONE:          cout << wxT("No");            break;
  1276.         case SQL_TC_DML:           cout << wxT("DML Only");      break;
  1277.         case SQL_TC_DDL_COMMIT:    cout << wxT("DDL Commit");    break;
  1278.         case SQL_TC_DDL_IGNORE:    cout << wxT("DDL Ignore");    break;
  1279.         case SQL_TC_ALL:           cout << wxT("DDL & DML");     break;
  1280.     }
  1281.     cout << endl;
  1282.  
  1283.     cout << endl;
  1284. #endif
  1285.  
  1286.     // Completed Successfully
  1287.     return(TRUE);
  1288.  
  1289. } // wxDb::getDbInfo()
  1290.  
  1291.  
  1292. /********** wxDb::getDataTypeInfo() **********/
  1293. bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
  1294. {
  1295. /*
  1296.  * fSqlType will be something like SQL_VARCHAR.  This parameter determines
  1297.  * the data type inf. is gathered for.
  1298.  *
  1299.  * wxDbSqlTypeInfo is a structure that is filled in with data type information,
  1300.  */
  1301.     RETCODE retcode;
  1302.     SDWORD  cbRet;
  1303.  
  1304.     // Get information about the data type specified
  1305.     if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
  1306.         return(DispAllErrors(henv, hdbc, hstmt));
  1307.  
  1308.     // Fetch the record
  1309.     retcode = SQLFetch(hstmt);
  1310.     if (retcode != SQL_SUCCESS)
  1311.     {
  1312. #ifdef DBDEBUG_CONSOLE
  1313.         if (retcode == SQL_NO_DATA_FOUND)
  1314.             cout << wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl;
  1315. #endif
  1316.         DispAllErrors(henv, hdbc, hstmt);
  1317.         SQLFreeStmt(hstmt, SQL_CLOSE);
  1318.         return(FALSE);
  1319.     }
  1320.  
  1321.     wxChar typeName[DB_TYPE_NAME_LEN+1];
  1322.  
  1323.     // Obtain columns from the record
  1324.     if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) typeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
  1325.         return(DispAllErrors(henv, hdbc, hstmt));
  1326.  
  1327.     structSQLTypeInfo.TypeName = typeName;
  1328.  
  1329.     // BJO 20000503: no more needed with new GetColumns...
  1330. #if  OLD_GETCOLUMNS
  1331.     // BJO 991209
  1332.     if (Dbms() == dbmsMY_SQL)
  1333.     {
  1334.         if (structSQLTypeInfo.TypeName == wxT("middleint"))
  1335.             structSQLTypeInfo.TypeName = wxT("mediumint");
  1336.         else if (structSQLTypeInfo.TypeName == wxT("middleint unsigned"))
  1337.             structSQLTypeInfo.TypeName = wxT("mediumint unsigned");
  1338.         else if (structSQLTypeInfo.TypeName == wxT("integer"))
  1339.             structSQLTypeInfo.TypeName = wxT("int");
  1340.         else if (structSQLTypeInfo.TypeName == wxT("integer unsigned"))
  1341.             structSQLTypeInfo.TypeName = wxT("int unsigned");
  1342.         else if (structSQLTypeInfo.TypeName == wxT("middleint"))
  1343.             structSQLTypeInfo.TypeName = wxT("mediumint");
  1344.         else if (structSQLTypeInfo.TypeName == wxT("varchar"))
  1345.             structSQLTypeInfo.TypeName = wxT("char");
  1346.     }
  1347.  
  1348.     // BJO 20000427 : OpenLink driver
  1349.     if (!wxStrncmp(dbInf.driverName, wxT("oplodbc"), 7) ||
  1350.         !wxStrncmp(dbInf.driverName, wxT("OLOD"), 4))
  1351.     {
  1352.         if (structSQLTypeInfo.TypeName == wxT("double precision"))
  1353.             structSQLTypeInfo.TypeName = wxT("real");
  1354.     }
  1355. #endif
  1356.  
  1357.     if (SQLGetData(hstmt, 3, SQL_C_LONG, (UCHAR*) &structSQLTypeInfo.Precision, 0, &cbRet) != SQL_SUCCESS)
  1358.         return(DispAllErrors(henv, hdbc, hstmt));
  1359.     if (SQLGetData(hstmt, 8, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.CaseSensitive, 0, &cbRet) != SQL_SUCCESS)
  1360.         return(DispAllErrors(henv, hdbc, hstmt));
  1361. //    if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
  1362. //        return(DispAllErrors(henv, hdbc, hstmt));
  1363.  
  1364.     if (SQLGetData(hstmt, 15, SQL_C_SHORT,(UCHAR*)  &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
  1365.         return(DispAllErrors(henv, hdbc, hstmt));
  1366.  
  1367.     if (structSQLTypeInfo.MaximumScale < 0)
  1368.         structSQLTypeInfo.MaximumScale = 0;
  1369.  
  1370.     // Close the statement handle which closes open cursors
  1371.     if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
  1372.         return(DispAllErrors(henv, hdbc, hstmt));
  1373.  
  1374.     // Completed Successfully
  1375.     return(TRUE);
  1376.  
  1377. } // wxDb::getDataTypeInfo()
  1378.  
  1379.  
  1380. /********** wxDb::Close() **********/
  1381. void wxDb::Close(void)
  1382. {
  1383.     // Close the Sql Log file
  1384.     if (fpSqlLog)
  1385.     {
  1386.         fclose(fpSqlLog);
  1387.         fpSqlLog = 0;
  1388.     }
  1389.  
  1390.     // Free statement handle
  1391.     if (dbIsOpen)
  1392.     {
  1393.         if (SQLFreeStmt(hstmt, SQL_DROP) != SQL_SUCCESS)
  1394.             DispAllErrors(henv, hdbc);
  1395.     }
  1396.  
  1397.     // Disconnect from the datasource
  1398.     if (SQLDisconnect(hdbc) != SQL_SUCCESS)
  1399.         DispAllErrors(henv, hdbc);
  1400.  
  1401.     // Free the connection to the datasource
  1402.     if (SQLFreeConnect(hdbc) != SQL_SUCCESS)
  1403.         DispAllErrors(henv, hdbc);
  1404.  
  1405.     // There should be zero Ctable objects still connected to this db object
  1406.     wxASSERT(nTables == 0);
  1407.  
  1408. #ifdef __WXDEBUG__
  1409.     wxTablesInUse *tiu;
  1410.     wxNode *pNode;
  1411.     pNode = TablesInUse.First();
  1412.     wxString s,s2;
  1413.     while (pNode)
  1414.     {
  1415.         tiu = (wxTablesInUse *)pNode->Data();
  1416.         if (tiu->pDb == this)
  1417.         {
  1418.             s.Printf(wxT("(%-20s)     tableID:[%6lu]     pDb:[%p]"), tiu->tableName,tiu->tableID,tiu->pDb);
  1419.             s2.Printf(wxT("Orphaned found using pDb:[%p]"),this);
  1420.             wxLogDebug (s.c_str(),s2.c_str());
  1421.         }
  1422.         pNode = pNode->Next();
  1423.     }
  1424. #endif
  1425.  
  1426.     // Copy the error messages to a global variable
  1427.     int i;
  1428.     for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
  1429.         wxStrcpy(DBerrorList[i], errorList[i]);
  1430.  
  1431.     dbmsType = dbmsUNIDENTIFIED;
  1432.     dbIsOpen = FALSE;
  1433.  
  1434. } // wxDb::Close()
  1435.  
  1436.  
  1437. /********** wxDb::CommitTrans() **********/
  1438. bool wxDb::CommitTrans(void)
  1439. {
  1440.     if (this)
  1441.     {
  1442.         // Commit the transaction
  1443.         if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
  1444.             return(DispAllErrors(henv, hdbc));
  1445.     }
  1446.  
  1447.     // Completed successfully
  1448.     return(TRUE);
  1449.  
  1450. } // wxDb::CommitTrans()
  1451.  
  1452.  
  1453. /********** wxDb::RollbackTrans() **********/
  1454. bool wxDb::RollbackTrans(void)
  1455. {
  1456.     // Rollback the transaction
  1457.     if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
  1458.         return(DispAllErrors(henv, hdbc));
  1459.  
  1460.     // Completed successfully
  1461.     return(TRUE);
  1462.  
  1463. } // wxDb::RollbackTrans()
  1464.  
  1465.  
  1466. /********** wxDb::DispAllErrors() **********/
  1467. bool wxDb::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
  1468. /*
  1469.  * This function is called internally whenever an error condition prevents the user's
  1470.  * request from being executed.  This function will query the datasource as to the
  1471.  * actual error(s) that just occured on the previous request of the datasource.
  1472.  *
  1473.  * The function will retrieve each error condition from the datasource and
  1474.  * Printf the codes/text values into a string which it then logs via logError().
  1475.  * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
  1476.  * window and program execution will be paused until the user presses a key.
  1477.  *
  1478.  * This function always returns a FALSE, so that functions which call this function
  1479.  * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
  1480.  * of the users request, so that the calling code can then process the error msg log
  1481.  */
  1482. {
  1483.     wxString odbcErrMsg;
  1484.  
  1485.     while (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
  1486.     {
  1487.         odbcErrMsg.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState, nativeError, errorMsg);
  1488.         logError(odbcErrMsg, sqlState);
  1489.         if (!silent)
  1490.         {
  1491. #ifdef DBDEBUG_CONSOLE
  1492.             // When run in console mode, use standard out to display errors.
  1493.             cout << odbcErrMsg.c_str() << endl;
  1494.             cout << wxT("Press any key to continue...") << endl;
  1495.             getchar();
  1496. #endif
  1497.  
  1498. #ifdef __WXDEBUG__
  1499.             wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
  1500. #endif
  1501.         }
  1502.     }
  1503.  
  1504.     return(FALSE);  // This function always returns FALSE.
  1505.  
  1506. } // wxDb::DispAllErrors()
  1507.  
  1508.  
  1509. /********** wxDb::GetNextError() **********/
  1510. bool wxDb::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
  1511. {
  1512.     if (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
  1513.         return(TRUE);
  1514.     else
  1515.         return(FALSE);
  1516.  
  1517. } // wxDb::GetNextError()
  1518.  
  1519.  
  1520. /********** wxDb::DispNextError() **********/
  1521. void wxDb::DispNextError(void)
  1522. {
  1523.     wxString odbcErrMsg;
  1524.  
  1525.     odbcErrMsg.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState, nativeError, errorMsg);
  1526.     logError(odbcErrMsg, sqlState);
  1527.  
  1528.     if (silent)
  1529.         return;
  1530.  
  1531. #ifdef DBDEBUG_CONSOLE
  1532.     // When run in console mode, use standard out to display errors.
  1533.     cout << odbcErrMsg.c_str() << endl;
  1534.     cout << wxT("Press any key to continue...")  << endl;
  1535.     getchar();
  1536. #endif
  1537.  
  1538. #ifdef __WXDEBUG__
  1539.     wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE"));
  1540. #endif  // __WXDEBUG__
  1541.  
  1542. } // wxDb::DispNextError()
  1543.  
  1544.  
  1545. /********** wxDb::logError() **********/
  1546. void wxDb::logError(const wxString &errMsg, const wxString &SQLState)
  1547. {
  1548.     wxASSERT(errMsg.Length());
  1549.  
  1550.     static int pLast = -1;
  1551.     int dbStatus;
  1552.  
  1553.     if (++pLast == DB_MAX_ERROR_HISTORY)
  1554.     {
  1555.         int i;
  1556.         for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
  1557.             wxStrcpy(errorList[i], errorList[i+1]);
  1558.         pLast--;
  1559.     }
  1560.  
  1561.     wxStrcpy(errorList[pLast], errMsg);
  1562.  
  1563.     if (SQLState.Length())
  1564.         if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
  1565.             DB_STATUS = dbStatus;
  1566.  
  1567.     // Add the errmsg to the sql log
  1568.     WriteSqlLog(errMsg);
  1569.  
  1570. }  // wxDb::logError()
  1571.  
  1572.  
  1573. /**********wxDb::TranslateSqlState()  **********/
  1574. int wxDb::TranslateSqlState(const wxString &SQLState)
  1575. {
  1576.     if (!wxStrcmp(SQLState, wxT("01000")))
  1577.         return(DB_ERR_GENERAL_WARNING);
  1578.     if (!wxStrcmp(SQLState, wxT("01002")))
  1579.         return(DB_ERR_DISCONNECT_ERROR);
  1580.     if (!wxStrcmp(SQLState, wxT("01004")))
  1581.         return(DB_ERR_DATA_TRUNCATED);
  1582.     if (!wxStrcmp(SQLState, wxT("01006")))
  1583.         return(DB_ERR_PRIV_NOT_REVOKED);
  1584.     if (!wxStrcmp(SQLState, wxT("01S00")))
  1585.         return(DB_ERR_INVALID_CONN_STR_ATTR);
  1586.     if (!wxStrcmp(SQLState, wxT("01S01")))
  1587.         return(DB_ERR_ERROR_IN_ROW);
  1588.     if (!wxStrcmp(SQLState, wxT("01S02")))
  1589.         return(DB_ERR_OPTION_VALUE_CHANGED);
  1590.     if (!wxStrcmp(SQLState, wxT("01S03")))
  1591.         return(DB_ERR_NO_ROWS_UPD_OR_DEL);
  1592.     if (!wxStrcmp(SQLState, wxT("01S04")))
  1593.         return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
  1594.     if (!wxStrcmp(SQLState, wxT("07001")))
  1595.         return(DB_ERR_WRONG_NO_OF_PARAMS);
  1596.     if (!wxStrcmp(SQLState, wxT("07006")))
  1597.         return(DB_ERR_DATA_TYPE_ATTR_VIOL);
  1598.     if (!wxStrcmp(SQLState, wxT("08001")))
  1599.         return(DB_ERR_UNABLE_TO_CONNECT);
  1600.     if (!wxStrcmp(SQLState, wxT("08002")))
  1601.         return(DB_ERR_CONNECTION_IN_USE);
  1602.     if (!wxStrcmp(SQLState, wxT("08003")))
  1603.         return(DB_ERR_CONNECTION_NOT_OPEN);
  1604.     if (!wxStrcmp(SQLState, wxT("08004")))
  1605.         return(DB_ERR_REJECTED_CONNECTION);
  1606.     if (!wxStrcmp(SQLState, wxT("08007")))
  1607.         return(DB_ERR_CONN_FAIL_IN_TRANS);
  1608.     if (!wxStrcmp(SQLState, wxT("08S01")))
  1609.         return(DB_ERR_COMM_LINK_FAILURE);
  1610.     if (!wxStrcmp(SQLState, wxT("21S01")))
  1611.         return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
  1612.     if (!wxStrcmp(SQLState, wxT("21S02")))
  1613.         return(DB_ERR_DERIVED_TABLE_MISMATCH);
  1614.     if (!wxStrcmp(SQLState, wxT("22001")))
  1615.         return(DB_ERR_STRING_RIGHT_TRUNC);
  1616.     if (!wxStrcmp(SQLState, wxT("22003")))
  1617.         return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
  1618.     if (!wxStrcmp(SQLState, wxT("22005")))
  1619.         return(DB_ERR_ERROR_IN_ASSIGNMENT);
  1620.     if (!wxStrcmp(SQLState, wxT("22008")))
  1621.         return(DB_ERR_DATETIME_FLD_OVERFLOW);
  1622.     if (!wxStrcmp(SQLState, wxT("22012")))
  1623.         return(DB_ERR_DIVIDE_BY_ZERO);
  1624.     if (!wxStrcmp(SQLState, wxT("22026")))
  1625.         return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
  1626.     if (!wxStrcmp(SQLState, wxT("23000")))
  1627.         return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
  1628.     if (!wxStrcmp(SQLState, wxT("24000")))
  1629.         return(DB_ERR_INVALID_CURSOR_STATE);
  1630.     if (!wxStrcmp(SQLState, wxT("25000")))
  1631.         return(DB_ERR_INVALID_TRANS_STATE);
  1632.     if (!wxStrcmp(SQLState, wxT("28000")))
  1633.         return(DB_ERR_INVALID_AUTH_SPEC);
  1634.     if (!wxStrcmp(SQLState, wxT("34000")))
  1635.         return(DB_ERR_INVALID_CURSOR_NAME);
  1636.     if (!wxStrcmp(SQLState, wxT("37000")))
  1637.         return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
  1638.     if (!wxStrcmp(SQLState, wxT("3C000")))
  1639.         return(DB_ERR_DUPLICATE_CURSOR_NAME);
  1640.     if (!wxStrcmp(SQLState, wxT("40001")))
  1641.         return(DB_ERR_SERIALIZATION_FAILURE);
  1642.     if (!wxStrcmp(SQLState, wxT("42000")))
  1643.         return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
  1644.     if (!wxStrcmp(SQLState, wxT("70100")))
  1645.         return(DB_ERR_OPERATION_ABORTED);
  1646.     if (!wxStrcmp(SQLState, wxT("IM001")))
  1647.         return(DB_ERR_UNSUPPORTED_FUNCTION);
  1648.     if (!wxStrcmp(SQLState, wxT("IM002")))
  1649.         return(DB_ERR_NO_DATA_SOURCE);
  1650.     if (!wxStrcmp(SQLState, wxT("IM003")))
  1651.         return(DB_ERR_DRIVER_LOAD_ERROR);
  1652.     if (!wxStrcmp(SQLState, wxT("IM004")))
  1653.         return(DB_ERR_SQLALLOCENV_FAILED);
  1654.     if (!wxStrcmp(SQLState, wxT("IM005")))
  1655.         return(DB_ERR_SQLALLOCCONNECT_FAILED);
  1656.     if (!wxStrcmp(SQLState, wxT("IM006")))
  1657.         return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
  1658.     if (!wxStrcmp(SQLState, wxT("IM007")))
  1659.         return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
  1660.     if (!wxStrcmp(SQLState, wxT("IM008")))
  1661.         return(DB_ERR_DIALOG_FAILED);
  1662.     if (!wxStrcmp(SQLState, wxT("IM009")))
  1663.         return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
  1664.     if (!wxStrcmp(SQLState, wxT("IM010")))
  1665.         return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
  1666.     if (!wxStrcmp(SQLState, wxT("IM011")))
  1667.         return(DB_ERR_DRIVER_NAME_TOO_LONG);
  1668.     if (!wxStrcmp(SQLState, wxT("IM012")))
  1669.         return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
  1670.     if (!wxStrcmp(SQLState, wxT("IM013")))
  1671.         return(DB_ERR_TRACE_FILE_ERROR);
  1672.     if (!wxStrcmp(SQLState, wxT("S0001")))
  1673.         return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
  1674.     if (!wxStrcmp(SQLState, wxT("S0002")))
  1675.         return(DB_ERR_TABLE_NOT_FOUND);
  1676.     if (!wxStrcmp(SQLState, wxT("S0011")))
  1677.         return(DB_ERR_INDEX_ALREADY_EXISTS);
  1678.     if (!wxStrcmp(SQLState, wxT("S0012")))
  1679.         return(DB_ERR_INDEX_NOT_FOUND);
  1680.     if (!wxStrcmp(SQLState, wxT("S0021")))
  1681.         return(DB_ERR_COLUMN_ALREADY_EXISTS);
  1682.     if (!wxStrcmp(SQLState, wxT("S0022")))
  1683.         return(DB_ERR_COLUMN_NOT_FOUND);
  1684.     if (!wxStrcmp(SQLState, wxT("S0023")))
  1685.         return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
  1686.     if (!wxStrcmp(SQLState, wxT("S1000")))
  1687.         return(DB_ERR_GENERAL_ERROR);
  1688.     if (!wxStrcmp(SQLState, wxT("S1001")))
  1689.         return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
  1690.     if (!wxStrcmp(SQLState, wxT("S1002")))
  1691.         return(DB_ERR_INVALID_COLUMN_NUMBER);
  1692.     if (!wxStrcmp(SQLState, wxT("S1003")))
  1693.         return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
  1694.     if (!wxStrcmp(SQLState, wxT("S1004")))
  1695.         return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
  1696.     if (!wxStrcmp(SQLState, wxT("S1008")))
  1697.         return(DB_ERR_OPERATION_CANCELLED);
  1698.     if (!wxStrcmp(SQLState, wxT("S1009")))
  1699.         return(DB_ERR_INVALID_ARGUMENT_VALUE);
  1700.     if (!wxStrcmp(SQLState, wxT("S1010")))
  1701.         return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
  1702.     if (!wxStrcmp(SQLState, wxT("S1011")))
  1703.         return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
  1704.     if (!wxStrcmp(SQLState, wxT("S1012")))
  1705.         return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
  1706.     if (!wxStrcmp(SQLState, wxT("S1015")))
  1707.         return(DB_ERR_NO_CURSOR_NAME_AVAIL);
  1708.     if (!wxStrcmp(SQLState, wxT("S1090")))
  1709.         return(DB_ERR_INVALID_STR_OR_BUF_LEN);
  1710.     if (!wxStrcmp(SQLState, wxT("S1091")))
  1711.         return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
  1712.     if (!wxStrcmp(SQLState, wxT("S1092")))
  1713.         return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
  1714.     if (!wxStrcmp(SQLState, wxT("S1093")))
  1715.         return(DB_ERR_INVALID_PARAM_NO);
  1716.     if (!wxStrcmp(SQLState, wxT("S1094")))
  1717.         return(DB_ERR_INVALID_SCALE_VALUE);
  1718.     if (!wxStrcmp(SQLState, wxT("S1095")))
  1719.         return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
  1720.     if (!wxStrcmp(SQLState, wxT("S1096")))
  1721.         return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
  1722.     if (!wxStrcmp(SQLState, wxT("S1097")))
  1723.         return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
  1724.     if (!wxStrcmp(SQLState, wxT("S1098")))
  1725.         return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
  1726.     if (!wxStrcmp(SQLState, wxT("S1099")))
  1727.         return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
  1728.     if (!wxStrcmp(SQLState, wxT("S1100")))
  1729.         return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
  1730.     if (!wxStrcmp(SQLState, wxT("S1101")))
  1731.         return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
  1732.     if (!wxStrcmp(SQLState, wxT("S1103")))
  1733.         return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
  1734.     if (!wxStrcmp(SQLState, wxT("S1104")))
  1735.         return(DB_ERR_INVALID_PRECISION_VALUE);
  1736.     if (!wxStrcmp(SQLState, wxT("S1105")))
  1737.         return(DB_ERR_INVALID_PARAM_TYPE);
  1738.     if (!wxStrcmp(SQLState, wxT("S1106")))
  1739.         return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
  1740.     if (!wxStrcmp(SQLState, wxT("S1107")))
  1741.         return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
  1742.     if (!wxStrcmp(SQLState, wxT("S1108")))
  1743.         return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
  1744.     if (!wxStrcmp(SQLState, wxT("S1109")))
  1745.         return(DB_ERR_INVALID_CURSOR_POSITION);
  1746.     if (!wxStrcmp(SQLState, wxT("S1110")))
  1747.         return(DB_ERR_INVALID_DRIVER_COMPLETION);
  1748.     if (!wxStrcmp(SQLState, wxT("S1111")))
  1749.         return(DB_ERR_INVALID_BOOKMARK_VALUE);
  1750.     if (!wxStrcmp(SQLState, wxT("S1C00")))
  1751.         return(DB_ERR_DRIVER_NOT_CAPABLE);
  1752.     if (!wxStrcmp(SQLState, wxT("S1T00")))
  1753.         return(DB_ERR_TIMEOUT_EXPIRED);
  1754.  
  1755.     // No match
  1756.     return(0);
  1757.  
  1758. }  // wxDb::TranslateSqlState()
  1759.  
  1760.  
  1761. /**********  wxDb::Grant() **********/
  1762. bool wxDb::Grant(int privileges, const wxString &tableName, const wxString &userList)
  1763. {
  1764.     wxString sqlStmt;
  1765.  
  1766.     // Build the grant statement
  1767.     sqlStmt  = wxT("GRANT ");
  1768.     if (privileges == DB_GRANT_ALL)
  1769.         sqlStmt += wxT("ALL");
  1770.     else
  1771.     {
  1772.         int c = 0;
  1773.         if (privileges & DB_GRANT_SELECT)
  1774.         {
  1775.             sqlStmt += wxT("SELECT");
  1776.             c++;
  1777.         }
  1778.         if (privileges & DB_GRANT_INSERT)
  1779.         {
  1780.             if (c++)
  1781.                 sqlStmt += wxT(", ");
  1782.             sqlStmt += wxT("INSERT");
  1783.         }
  1784.         if (privileges & DB_GRANT_UPDATE)
  1785.         {
  1786.             if (c++)
  1787.                 sqlStmt += wxT(", ");
  1788.             sqlStmt += wxT("UPDATE");
  1789.         }
  1790.         if (privileges & DB_GRANT_DELETE)
  1791.         {
  1792.             if (c++)
  1793.                 sqlStmt += wxT(", ");
  1794.             sqlStmt += wxT("DELETE");
  1795.         }
  1796.     }
  1797.  
  1798.     sqlStmt += wxT(" ON ");
  1799.     sqlStmt += SQLTableName(tableName);
  1800.     sqlStmt += wxT(" TO ");
  1801.     sqlStmt += userList;
  1802.  
  1803. #ifdef DBDEBUG_CONSOLE
  1804.     cout << endl << sqlStmt.c_str() << endl;
  1805. #endif
  1806.  
  1807.     WriteSqlLog(sqlStmt);
  1808.  
  1809.     return(ExecSql(sqlStmt));
  1810.  
  1811. }  // wxDb::Grant()
  1812.  
  1813.  
  1814. /********** wxDb::CreateView() **********/
  1815. bool wxDb::CreateView(const wxString &viewName, const wxString &colList,
  1816.                       const wxString &pSqlStmt, bool attemptDrop)
  1817. {
  1818.     wxString sqlStmt;
  1819.  
  1820.     // Drop the view first
  1821.     if (attemptDrop && !DropView(viewName))
  1822.         return FALSE;
  1823.  
  1824.     // Build the create view statement
  1825.     sqlStmt  = wxT("CREATE VIEW ");
  1826.     sqlStmt += viewName;
  1827.  
  1828.     if (colList.Length())
  1829.     {
  1830.         sqlStmt += wxT(" (");
  1831.         sqlStmt += colList;
  1832.         sqlStmt += wxT(")");
  1833.     }
  1834.  
  1835.     sqlStmt += wxT(" AS ");
  1836.     sqlStmt += pSqlStmt;
  1837.  
  1838.     WriteSqlLog(sqlStmt);
  1839.  
  1840. #ifdef DBDEBUG_CONSOLE
  1841.     cout << sqlStmt.c_str() << endl;
  1842. #endif
  1843.  
  1844.     return(ExecSql(sqlStmt));
  1845.  
  1846. }  // wxDb::CreateView()
  1847.  
  1848.  
  1849. /********** wxDb::DropView()  **********/
  1850. bool wxDb::DropView(const wxString &viewName)
  1851. {
  1852. /*
  1853.  * NOTE: This function returns TRUE if the View does not exist, but
  1854.  *       only for identified databases.  Code will need to be added
  1855.  *            below for any other databases when those databases are defined
  1856.  *       to handle this situation consistently
  1857.  */
  1858.     wxString sqlStmt;
  1859.  
  1860.     sqlStmt.Printf(wxT("DROP VIEW %s"), viewName.c_str());
  1861.  
  1862.     WriteSqlLog(sqlStmt);
  1863.  
  1864. #ifdef DBDEBUG_CONSOLE
  1865.     cout << endl << sqlStmt.c_str() << endl;
  1866. #endif
  1867.  
  1868.     if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  1869.     {
  1870.         // Check for "Base table not found" error and ignore
  1871.         GetNextError(henv, hdbc, hstmt);
  1872.         if (wxStrcmp(sqlState,wxT("S0002")))  // "Base table not found"
  1873.         {
  1874.             // Check for product specific error codes
  1875.             if (!((Dbms() == dbmsSYBASE_ASA    && !wxStrcmp(sqlState,wxT("42000")))))  // 5.x (and lower?)
  1876.             {
  1877.                 DispNextError();
  1878.                 DispAllErrors(henv, hdbc, hstmt);
  1879.                 RollbackTrans();
  1880.                 return(FALSE);
  1881.             }
  1882.         }
  1883.     }
  1884.  
  1885.     // Commit the transaction
  1886.     if (!CommitTrans())
  1887.         return(FALSE);
  1888.  
  1889.     return TRUE;
  1890.  
  1891. }  // wxDb::DropView()
  1892.  
  1893.  
  1894. /********** wxDb::ExecSql()  **********/
  1895. bool wxDb::ExecSql(const wxString &pSqlStmt)
  1896. {
  1897.     RETCODE retcode;
  1898.  
  1899.     SQLFreeStmt(hstmt, SQL_CLOSE);
  1900.  
  1901.     retcode = SQLExecDirect(hstmt, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
  1902.     if (retcode == SQL_SUCCESS ||
  1903.         (Dbms() == dbmsDB2 && (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_NO_DATA_FOUND)))
  1904.     {
  1905.         return(TRUE);
  1906.     }
  1907.     else
  1908.     {
  1909.         DispAllErrors(henv, hdbc, hstmt);
  1910.         return(FALSE);
  1911.     }
  1912.  
  1913. }  // wxDb::ExecSql()
  1914.  
  1915.  
  1916. /********** wxDb::GetNext()  **********/
  1917. bool wxDb::GetNext(void)
  1918. {
  1919.     if (SQLFetch(hstmt) == SQL_SUCCESS)
  1920.         return(TRUE);
  1921.     else
  1922.     {
  1923.         DispAllErrors(henv, hdbc, hstmt);
  1924.         return(FALSE);
  1925.     }
  1926.  
  1927. }  // wxDb::GetNext()
  1928.  
  1929.  
  1930. /********** wxDb::GetData()  **********/
  1931. bool wxDb::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SDWORD FAR *cbReturned)
  1932. {
  1933.     wxASSERT(pData);
  1934.     wxASSERT(cbReturned);
  1935.  
  1936.     if (SQLGetData(hstmt, colNo, cType, pData, maxLen, cbReturned) == SQL_SUCCESS)
  1937.         return(TRUE);
  1938.     else
  1939.     {
  1940.         DispAllErrors(henv, hdbc, hstmt);
  1941.         return(FALSE);
  1942.     }
  1943.  
  1944. }  // wxDb::GetData()
  1945.  
  1946.  
  1947. /********** wxDb::GetKeyFields() **********/
  1948. int wxDb::GetKeyFields(const wxString &tableName, wxDbColInf* colInf, UWORD noCols)
  1949. {
  1950.     wxChar       szPkTable[DB_MAX_TABLE_NAME_LEN+1];  /* Primary key table name */
  1951.     wxChar       szFkTable[DB_MAX_TABLE_NAME_LEN+1];  /* Foreign key table name */
  1952.     short        iKeySeq;
  1953. //    SQLSMALLINT  iKeySeq;
  1954.     wxChar       szPkCol[DB_MAX_COLUMN_NAME_LEN+1];   /* Primary key column     */
  1955.     wxChar       szFkCol[DB_MAX_COLUMN_NAME_LEN+1];   /* Foreign key column     */
  1956.     SQLRETURN    retcode;
  1957.     SDWORD       cb;
  1958.     SWORD        i;
  1959.     wxString     tempStr;
  1960.     /*
  1961.      * -----------------------------------------------------------------------
  1962.      * -- 19991224 : mj10777 : Create                                   ------
  1963.      * --          : Three things are done and stored here :            ------
  1964.      * --          : 1) which Column(s) is/are Primary Key(s)           ------
  1965.      * --          : 2) which tables use this Key as a Foreign Key      ------
  1966.      * --          : 3) which columns are Foreign Key and the name      ------
  1967.      * --          :     of the Table where the Key is the Primary Key  -----
  1968.      * --          : Called from GetColumns(const wxString &tableName,  ------
  1969.      * --                           int *numCols,const wxChar *userID ) ------
  1970.      * -----------------------------------------------------------------------
  1971.      */
  1972.  
  1973.     /*---------------------------------------------------------------------*/
  1974.     /* Get the names of the columns in the primary key.                    */
  1975.     /*---------------------------------------------------------------------*/
  1976.     retcode = SQLPrimaryKeys(hstmt,
  1977.                              NULL, 0,                               /* Catalog name  */
  1978.                              NULL, 0,                               /* Schema name   */
  1979.                              (UCHAR FAR *) tableName.c_str(), SQL_NTS); /* Table name    */
  1980.  
  1981.     /*---------------------------------------------------------------------*/
  1982.     /* Fetch and display the result set. This will be a list of the        */
  1983.     /* columns in the primary key of the tableName table.                  */
  1984.     /*---------------------------------------------------------------------*/
  1985.     while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
  1986.     {
  1987.         retcode = SQLFetch(hstmt);
  1988.         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  1989.         {
  1990.             GetData( 4, SQL_C_CHAR,   szPkCol,     DB_MAX_COLUMN_NAME_LEN+1, &cb);
  1991.             GetData( 5, SQL_C_SSHORT, &iKeySeq,    0,                        &cb);
  1992.             //-------
  1993.             for (i=0;i<noCols;i++)                          // Find the Column name
  1994.                 if (!wxStrcmp(colInf[i].colName,szPkCol))   // We have found the Column
  1995.                     colInf[i].PkCol = iKeySeq;              // Which Primary Key is this (first, second usw.) ?
  1996.         }  // if
  1997.     }  // while
  1998.     SQLFreeStmt(hstmt, SQL_CLOSE);  /* Close the cursor (the hstmt is still allocated).      */
  1999.  
  2000.     /*---------------------------------------------------------------------*/
  2001.     /* Get all the foreign keys that refer to tableName primary key.       */
  2002.     /*---------------------------------------------------------------------*/
  2003.     retcode = SQLForeignKeys(hstmt,
  2004.                              NULL, 0,                            /* Primary catalog */
  2005.                              NULL, 0,                            /* Primary schema  */
  2006.                              (UCHAR FAR *)tableName.c_str(), SQL_NTS,/* Primary table   */
  2007.                              NULL, 0,                            /* Foreign catalog */
  2008.                              NULL, 0,                            /* Foreign schema  */
  2009.                              NULL, 0);                           /* Foreign table   */
  2010.  
  2011.     /*---------------------------------------------------------------------*/
  2012.     /* Fetch and display the result set. This will be all of the foreign   */
  2013.     /* keys in other tables that refer to the tableName  primary key.      */
  2014.     /*---------------------------------------------------------------------*/
  2015.     tempStr.Empty();
  2016.     szPkCol[0] = 0;
  2017.     while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
  2018.     {
  2019.         retcode = SQLFetch(hstmt);
  2020.         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  2021.         {
  2022.             GetData( 3, SQL_C_CHAR,   szPkTable,   DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2023.             GetData( 4, SQL_C_CHAR,   szPkCol,     DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2024.             GetData( 5, SQL_C_SSHORT, &iKeySeq,    0,                        &cb);
  2025.             GetData( 7, SQL_C_CHAR,   szFkTable,   DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2026.             GetData( 8, SQL_C_CHAR,   szFkCol,     DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2027.             tempStr.Printf(wxT("%s[%s] "),tempStr.c_str(),szFkTable);  // [ ] in case there is a blank in the Table name
  2028.         }  // if
  2029.     }  // while
  2030.  
  2031.     tempStr.Trim();     // Get rid of any unneeded blanks
  2032.     if (!tempStr.IsEmpty())
  2033.     {
  2034.         for (i=0; i<noCols; i++)
  2035.         {   // Find the Column name
  2036.             if (!wxStrcmp(colInf[i].colName, szPkCol))           // We have found the Column, store the Information
  2037.                 wxStrcpy(colInf[i].PkTableName, tempStr.c_str());  // Name of the Tables where this Primary Key is used as a Foreign Key
  2038.         }
  2039.     }  // if
  2040.  
  2041.     SQLFreeStmt(hstmt, SQL_CLOSE);  /* Close the cursor (the hstmt is still allocated). */
  2042.  
  2043.     /*---------------------------------------------------------------------*/
  2044.     /* Get all the foreign keys in the tablename table.                    */
  2045.     /*---------------------------------------------------------------------*/
  2046.     retcode = SQLForeignKeys(hstmt,
  2047.                              NULL, 0,                             /* Primary catalog   */
  2048.                              NULL, 0,                             /* Primary schema    */
  2049.                              NULL, 0,                             /* Primary table     */
  2050.                              NULL, 0,                             /* Foreign catalog   */
  2051.                              NULL, 0,                             /* Foreign schema    */
  2052.                              (UCHAR *)tableName.c_str(), SQL_NTS);/* Foreign table     */
  2053.  
  2054.     /*---------------------------------------------------------------------*/
  2055.     /*  Fetch and display the result set. This will be all of the          */
  2056.     /*  primary keys in other tables that are referred to by foreign       */
  2057.     /*  keys in the tableName table.                                       */
  2058.     /*---------------------------------------------------------------------*/
  2059.     i = 0;
  2060.     while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
  2061.     {
  2062.         retcode = SQLFetch(hstmt);
  2063.         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  2064.         {
  2065.             GetData( 3, SQL_C_CHAR,   szPkTable,   DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2066.             GetData( 5, SQL_C_SSHORT, &iKeySeq,    0,                        &cb);
  2067.             GetData( 8, SQL_C_CHAR,   szFkCol,     DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2068.             //-------
  2069.             for (i=0; i<noCols; i++)                            // Find the Column name
  2070.             {
  2071.                 if (!wxStrcmp(colInf[i].colName,szFkCol))       // We have found the (Foreign Key) Column
  2072.                 {
  2073.                     colInf[i].FkCol = iKeySeq;                  // Which Foreign Key is this (first, second usw.) ?
  2074.                     wxStrcpy(colInf[i].FkTableName,szPkTable);  // Name of the Table where this Foriegn is the Primary Key
  2075.                 } // if
  2076.             }  // for
  2077.         }  // if
  2078.     }  // while
  2079.     SQLFreeStmt(hstmt, SQL_CLOSE);  /* Close the cursor (the hstmt is still allocated). */
  2080.  
  2081.     return TRUE;
  2082.  
  2083. }  // wxDb::GetKeyFields()
  2084.  
  2085.  
  2086. #if OLD_GETCOLUMNS
  2087. /********** wxDb::GetColumns() **********/
  2088. wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *userID)
  2089. /*
  2090.  *        1) The last array element of the tableName[] argument must be zero (null).
  2091.  *            This is how the end of the array is detected.
  2092.  *        2) This function returns an array of wxDbColInf structures.  If no columns
  2093.  *            were found, or an error occured, this pointer will be zero (null).  THE
  2094.  *            CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
  2095.  *            IS FINISHED WITH IT.  i.e.
  2096.  *
  2097.  *            wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
  2098.  *            if (colInf)
  2099.  *            {
  2100.  *                // Use the column inf
  2101.  *                .......
  2102.  *                // Destroy the memory
  2103.  *                delete [] colInf;
  2104.  *            }
  2105.  *
  2106.  * userID is evaluated in the following manner:
  2107.  *        userID == NULL  ... UserID is ignored
  2108.  *        userID == ""    ... UserID set equal to 'this->uid'
  2109.  *        userID != ""    ... UserID set equal to 'userID'
  2110.  *
  2111.  * NOTE: ALL column bindings associated with this wxDb instance are unbound
  2112.  *       by this function.  This function should use its own wxDb instance
  2113.  *       to avoid undesired unbinding of columns.
  2114.  */
  2115. {
  2116.     UWORD       noCols = 0;
  2117.     UWORD       colNo  = 0;
  2118.     wxDbColInf *colInf = 0;
  2119.  
  2120.     RETCODE  retcode;
  2121.     SDWORD   cb;
  2122.  
  2123.     wxString TableName;
  2124.  
  2125.     wxString UserID;
  2126.     convertUserID(userID,UserID);
  2127.  
  2128.     // Pass 1 - Determine how many columns there are.
  2129.     // Pass 2 - Allocate the wxDbColInf array and fill in
  2130.     //                the array with the column information.
  2131.     int pass;
  2132.     for (pass = 1; pass <= 2; pass++)
  2133.     {
  2134.         if (pass == 2)
  2135.         {
  2136.             if (noCols == 0)  // Probably a bogus table name(s)
  2137.                 break;
  2138.             // Allocate n wxDbColInf objects to hold the column information
  2139.             colInf = new wxDbColInf[noCols+1];
  2140.             if (!colInf)
  2141.                 break;
  2142.             // Mark the end of the array
  2143.             wxStrcpy(colInf[noCols].tableName,wxEmptyString);
  2144.             wxStrcpy(colInf[noCols].colName,wxEmptyString);
  2145.             colInf[noCols].sqlDataType = 0;
  2146.         }
  2147.         // Loop through each table name
  2148.         int tbl;
  2149.         for (tbl = 0; tableName[tbl]; tbl++)
  2150.         {
  2151.             TableName = tableName[tbl];
  2152.             // Oracle and Interbase table names are uppercase only, so force
  2153.             // the name to uppercase just in case programmer forgot to do this
  2154.             if ((Dbms() == dbmsORACLE) ||
  2155.                 (Dbms() == dbmsINTERBASE))
  2156.                 TableName = TableName.Upper();
  2157.  
  2158.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2159.  
  2160.             // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
  2161.             // use the call below that leaves out the user name
  2162.             if (!UserID.IsEmpty() &&
  2163.                 Dbms() != dbmsMY_SQL &&
  2164.                 Dbms() != dbmsACCESS &&
  2165.                 Dbms() != dbmsMS_SQL_SERVER)
  2166.             {
  2167.                 retcode = SQLColumns(hstmt,
  2168.                                      NULL, 0,                                // All qualifiers
  2169.                                      (UCHAR *) UserID.c_str(), SQL_NTS,      // Owner
  2170.                                      (UCHAR *) TableName.c_str(), SQL_NTS,
  2171.                                      NULL, 0);                               // All columns
  2172.             }
  2173.             else
  2174.             {
  2175.                 retcode = SQLColumns(hstmt,
  2176.                                      NULL, 0,                                // All qualifiers
  2177.                                      NULL, 0,                                // Owner
  2178.                                      (UCHAR *) TableName.c_str(), SQL_NTS,
  2179.                                      NULL, 0);                               // All columns
  2180.             }
  2181.             if (retcode != SQL_SUCCESS)
  2182.             {  // Error occured, abort
  2183.                 DispAllErrors(henv, hdbc, hstmt);
  2184.                 if (colInf)
  2185.                     delete [] colInf;
  2186.                 SQLFreeStmt(hstmt, SQL_CLOSE);
  2187.                 return(0);
  2188.             }
  2189.  
  2190.             while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
  2191.             {
  2192.                 if (pass == 1)  // First pass, just add up the number of columns
  2193.                     noCols++;
  2194.                 else  // Pass 2; Fill in the array of structures
  2195.                 {
  2196.                     if (colNo < noCols)  // Some extra error checking to prevent memory overwrites
  2197.                     {
  2198.                         // NOTE: Only the ODBC 1.x fields are retrieved
  2199.                         GetData( 1, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].catalog,      128+1,                    &cb);
  2200.                         GetData( 2, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].schema,       128+1,                    &cb);
  2201.                         GetData( 3, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].tableName,    DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2202.                         GetData( 4, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].colName,      DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2203.                         GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType,  0,                        &cb);
  2204.                         GetData( 6, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].typeName,     128+1,                    &cb);
  2205.                         GetData( 7, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].columnSize,   0,                        &cb);
  2206.                         GetData( 8, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].bufferLength, 0,                        &cb);
  2207.                         GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0,                        &cb);
  2208.                         GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0,                        &cb);
  2209.                         GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable,     0,                        &cb);
  2210.                         GetData(12, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].remarks,      254+1,                    &cb);
  2211.  
  2212.                         // Determine the wxDb data type that is used to represent the native data type of this data source
  2213.                         colInf[colNo].dbDataType = 0;
  2214.                         if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
  2215.                         {
  2216. #ifdef _IODBC_
  2217.                             // IODBC does not return a correct columnSize, so we set
  2218.                             // columnSize = bufferLength if no column size was returned
  2219.                             // IODBC returns the columnSize in bufferLength.. (bug)
  2220.                             if (colInf[colNo].columnSize < 1)
  2221.                             {
  2222.                                colInf[colNo].columnSize = colInf[colNo].bufferLength;
  2223.                             }
  2224. #endif
  2225.                             colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
  2226.                         }
  2227.                         else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
  2228.                             colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
  2229.                         else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
  2230.                             colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
  2231.                         else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
  2232.                             colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
  2233.                         else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
  2234.                             colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
  2235.                         colNo++;
  2236.                     }
  2237.                 }
  2238.             }
  2239.             if (retcode != SQL_NO_DATA_FOUND)
  2240.             {  // Error occured, abort
  2241.                 DispAllErrors(henv, hdbc, hstmt);
  2242.                 if (colInf)
  2243.                     delete [] colInf;
  2244.                 SQLFreeStmt(hstmt, SQL_CLOSE);
  2245.                 return(0);
  2246.             }
  2247.         }
  2248.     }
  2249.  
  2250.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2251.     return colInf;
  2252.  
  2253. }  // wxDb::GetColumns()
  2254.  
  2255.  
  2256. /********** wxDb::GetColumns() **********/
  2257.  
  2258. wxDbColInf *wxDb::GetColumns(const wxString &tableName, UWORD *numCols, const wxChar *userID)
  2259. //
  2260. // Same as the above GetColumns() function except this one gets columns
  2261. // only for a single table, and if 'numCols' is not NULL, the number of
  2262. // columns stored in the returned wxDbColInf is set in '*numCols'
  2263. //
  2264. // userID is evaluated in the following manner:
  2265. //        userID == NULL  ... UserID is ignored
  2266. //        userID == ""    ... UserID set equal to 'this->uid'
  2267. //        userID != ""    ... UserID set equal to 'userID'
  2268. //
  2269. // NOTE: ALL column bindings associated with this wxDb instance are unbound
  2270. //       by this function.  This function should use its own wxDb instance
  2271. //       to avoid undesired unbinding of columns.
  2272.  
  2273. {
  2274.     UWORD       noCols = 0;
  2275.     UWORD       colNo  = 0;
  2276.     wxDbColInf *colInf = 0;
  2277.  
  2278.     RETCODE  retcode;
  2279.     SDWORD   cb;
  2280.  
  2281.     wxString TableName;
  2282.  
  2283.     wxString UserID;
  2284.     convertUserID(userID,UserID);
  2285.  
  2286.     // Pass 1 - Determine how many columns there are.
  2287.     // Pass 2 - Allocate the wxDbColInf array and fill in
  2288.     //                the array with the column information.
  2289.     int pass;
  2290.     for (pass = 1; pass <= 2; pass++)
  2291.     {
  2292.         if (pass == 2)
  2293.         {
  2294.             if (noCols == 0)  // Probably a bogus table name(s)
  2295.                 break;
  2296.             // Allocate n wxDbColInf objects to hold the column information
  2297.             colInf = new wxDbColInf[noCols+1];
  2298.             if (!colInf)
  2299.                 break;
  2300.             // Mark the end of the array
  2301.             wxStrcpy(colInf[noCols].tableName, wxEmptyString);
  2302.             wxStrcpy(colInf[noCols].colName, wxEmptyString);
  2303.             colInf[noCols].sqlDataType  = 0;
  2304.         }
  2305.  
  2306.         TableName = tableName;
  2307.         // Oracle and Interbase table names are uppercase only, so force
  2308.         // the name to uppercase just in case programmer forgot to do this
  2309.         if ((Dbms() == dbmsORACLE) ||
  2310.             (Dbms() == dbmsINTERBASE))
  2311.             TableName = TableName.Upper();
  2312.  
  2313.         SQLFreeStmt(hstmt, SQL_CLOSE);
  2314.  
  2315.         // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
  2316.         // use the call below that leaves out the user name
  2317.         if (!UserID.IsEmpty() &&
  2318.             Dbms() != dbmsMY_SQL &&
  2319.             Dbms() != dbmsACCESS &&
  2320.             Dbms() != dbmsMS_SQL_SERVER)
  2321.         {
  2322.             retcode = SQLColumns(hstmt,
  2323.                                  NULL, 0,                                // All qualifiers
  2324.                                  (UCHAR *) UserID.c_str(), SQL_NTS,    // Owner
  2325.                                  (UCHAR *) TableName.c_str(), SQL_NTS,
  2326.                                  NULL, 0);                               // All columns
  2327.         }
  2328.         else
  2329.         {
  2330.             retcode = SQLColumns(hstmt,
  2331.                                  NULL, 0,                                 // All qualifiers
  2332.                                  NULL, 0,                                 // Owner
  2333.                                  (UCHAR *) TableName.c_str(), SQL_NTS,
  2334.                                  NULL, 0);                                // All columns
  2335.         }
  2336.         if (retcode != SQL_SUCCESS)
  2337.         {  // Error occured, abort
  2338.             DispAllErrors(henv, hdbc, hstmt);
  2339.             if (colInf)
  2340.                 delete [] colInf;
  2341.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2342.             if (numCols)
  2343.                 *numCols = 0;
  2344.             return(0);
  2345.         }
  2346.  
  2347.         while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
  2348.         {
  2349.             if (pass == 1)  // First pass, just add up the number of columns
  2350.                 noCols++;
  2351.             else  // Pass 2; Fill in the array of structures
  2352.             {
  2353.                 if (colNo < noCols)  // Some extra error checking to prevent memory overwrites
  2354.                 {
  2355.                     // NOTE: Only the ODBC 1.x fields are retrieved
  2356.                     GetData( 1, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].catalog,      128+1,                    &cb);
  2357.                     GetData( 2, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].schema,       128+1,                    &cb);
  2358.                     GetData( 3, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].tableName,    DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2359.                     GetData( 4, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].colName,      DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2360.                     GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType,  0,                        &cb);
  2361.                     GetData( 6, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].typeName,     128+1,                    &cb);
  2362.                     GetData( 7, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].columnSize,   0,                        &cb);
  2363.                     // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
  2364.                     GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0,                        &cb);
  2365.                     GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0,                        &cb);
  2366.                     GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0,                        &cb);
  2367.                     GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable,     0,                        &cb);
  2368.                     GetData(12, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].remarks,      254+1,                    &cb);
  2369.                     // Start Values for Primary/Foriegn Key (=No)
  2370.                     colInf[colNo].PkCol = 0;           // Primary key column   0=No; 1= First Key, 2 = Second Key etc.
  2371.                     colInf[colNo].PkTableName[0] = 0;  // Tablenames where Primary Key is used as a Foreign Key
  2372.                     colInf[colNo].FkCol = 0;           // Foreign key column   0=No; 1= First Key, 2 = Second Key etc.
  2373.                     colInf[colNo].FkTableName[0] = 0;  // Foreign key table name
  2374.  
  2375.                     // BJO 20000428 : Virtuoso returns type names with upper cases!
  2376.                     if (Dbms() == dbmsVIRTUOSO)
  2377.                     {
  2378.                         wxString s = colInf[colNo].typeName;
  2379.                         s = s.MakeLower();
  2380.                         wxStrcmp(colInf[colNo].typeName, s.c_str());
  2381.                     }
  2382.  
  2383.                     // Determine the wxDb data type that is used to represent the native data type of this data source
  2384.                     colInf[colNo].dbDataType = 0;
  2385.                     if (!wxStricmp(typeInfVarchar.TypeName, colInf[colNo].typeName))
  2386.                     {
  2387. #ifdef _IODBC_
  2388.                         // IODBC does not return a correct columnSize, so we set
  2389.                         // columnSize = bufferLength if no column size was returned
  2390.                         // IODBC returns the columnSize in bufferLength.. (bug)
  2391.                         if (colInf[colNo].columnSize < 1)
  2392.                         {
  2393.                            colInf[colNo].columnSize = colInf[colNo].bufferLength;
  2394.                         }
  2395. #endif
  2396.  
  2397.                         colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
  2398.                     }
  2399.                     else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
  2400.                         colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
  2401.                     else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
  2402.                         colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
  2403.                     else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
  2404.                         colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
  2405.                     else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
  2406.                         colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
  2407.  
  2408.                     colNo++;
  2409.                 }
  2410.             }
  2411.         }
  2412.         if (retcode != SQL_NO_DATA_FOUND)
  2413.         {  // Error occured, abort
  2414.             DispAllErrors(henv, hdbc, hstmt);
  2415.             if (colInf)
  2416.                 delete [] colInf;
  2417.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2418.             if (numCols)
  2419.                 *numCols = 0;
  2420.             return(0);
  2421.         }
  2422.     }
  2423.  
  2424.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2425.  
  2426.     // Store Primary and Foriegn Keys
  2427.     GetKeyFields(tableName,colInf,noCols);
  2428.  
  2429.     if (numCols)
  2430.         *numCols = noCols;
  2431.     return colInf;
  2432.  
  2433. }  // wxDb::GetColumns()
  2434.  
  2435.  
  2436. #else  // New GetColumns
  2437.  
  2438.  
  2439. /*
  2440.     BJO 20000503
  2441.     These are tentative new GetColumns members which should be more database
  2442.     independant and which always returns the columns in the order they were
  2443.     created.
  2444.  
  2445.     - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
  2446.       wxChar* userID)) calls the second implementation for each separate table
  2447.       before merging the results. This makes the code easier to maintain as
  2448.       only one member (the second) makes the real work
  2449.     - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
  2450.       wxChar *userID) is a little bit improved
  2451.     - It doesn't anymore rely on the type-name to find out which database-type
  2452.       each column has
  2453.     - It ends by sorting the columns, so that they are returned in the same
  2454.       order they were created
  2455. */
  2456.  
  2457. typedef struct
  2458. {
  2459.     UWORD noCols;
  2460.     wxDbColInf *colInf;
  2461. } _TableColumns;
  2462.  
  2463.  
  2464. wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *userID)
  2465. {
  2466.     int i, j;
  2467.     // The last array element of the tableName[] argument must be zero (null).
  2468.     // This is how the end of the array is detected.
  2469.  
  2470.     UWORD noCols = 0;
  2471.  
  2472.     // How many tables ?
  2473.     int tbl;
  2474.     for (tbl = 0 ; tableName[tbl]; tbl++);
  2475.  
  2476.     // Create a table to maintain the columns for each separate table
  2477.     _TableColumns *TableColumns = new _TableColumns[tbl];
  2478.  
  2479.     // Fill the table
  2480.     for (i = 0 ; i < tbl ; i++)
  2481.  
  2482.     {
  2483.         TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
  2484.         if (TableColumns[i].colInf == NULL)
  2485.             return NULL;
  2486.         noCols += TableColumns[i].noCols;
  2487.     }
  2488.  
  2489.     // Now merge all the separate table infos
  2490.     wxDbColInf *colInf = new wxDbColInf[noCols+1];
  2491.  
  2492.     // Mark the end of the array
  2493.     wxStrcpy(colInf[noCols].tableName, wxEmptyString);
  2494.     wxStrcpy(colInf[noCols].colName, wxEmptyString);
  2495.     colInf[noCols].sqlDataType  = 0;
  2496.  
  2497.     // Merge ...
  2498.     int offset = 0;
  2499.  
  2500.     for (i = 0 ; i < tbl ; i++)
  2501.     {
  2502.         for (j = 0 ; j < TableColumns[i].noCols ; j++)
  2503.         {
  2504.             colInf[offset++] = TableColumns[i].colInf[j];
  2505.         }
  2506.     }
  2507.  
  2508.     delete [] TableColumns;
  2509.  
  2510.     return colInf;
  2511. }  // wxDb::GetColumns()  -- NEW
  2512.  
  2513.  
  2514. wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const wxChar *userID)
  2515. //
  2516. // Same as the above GetColumns() function except this one gets columns
  2517. // only for a single table, and if 'numCols' is not NULL, the number of
  2518. // columns stored in the returned wxDbColInf is set in '*numCols'
  2519. //
  2520. // userID is evaluated in the following manner:
  2521. //        userID == NULL  ... UserID is ignored
  2522. //        userID == ""    ... UserID set equal to 'this->uid'
  2523. //        userID != ""    ... UserID set equal to 'userID'
  2524. //
  2525. // NOTE: ALL column bindings associated with this wxDb instance are unbound
  2526. //       by this function.  This function should use its own wxDb instance
  2527. //       to avoid undesired unbinding of columns.
  2528. {
  2529.     UWORD       noCols = 0;
  2530.     UWORD       colNo  = 0;
  2531.     wxDbColInf *colInf = 0;
  2532.  
  2533.     RETCODE  retcode;
  2534.     SDWORD   cb;
  2535.  
  2536.     wxString TableName;
  2537.  
  2538.     wxString UserID;
  2539.     convertUserID(userID,UserID);
  2540.  
  2541.     // Pass 1 - Determine how many columns there are.
  2542.     // Pass 2 - Allocate the wxDbColInf array and fill in
  2543.     //                the array with the column information.
  2544.     int pass;
  2545.     for (pass = 1; pass <= 2; pass++)
  2546.     {
  2547.         if (pass == 2)
  2548.         {
  2549.             if (noCols == 0)  // Probably a bogus table name(s)
  2550.                 break;
  2551.             // Allocate n wxDbColInf objects to hold the column information
  2552.             colInf = new wxDbColInf[noCols+1];
  2553.             if (!colInf)
  2554.                 break;
  2555.             // Mark the end of the array
  2556.             wxStrcpy(colInf[noCols].tableName, wxEmptyString);
  2557.             wxStrcpy(colInf[noCols].colName, wxEmptyString);
  2558.             colInf[noCols].sqlDataType = 0;
  2559.         }
  2560.  
  2561.         TableName = tableName;
  2562.         // Oracle and Interbase table names are uppercase only, so force
  2563.         // the name to uppercase just in case programmer forgot to do this
  2564.         if ((Dbms() == dbmsORACLE) ||
  2565.             (Dbms() == dbmsINTERBASE))
  2566.             TableName = TableName.Upper();
  2567.  
  2568.         SQLFreeStmt(hstmt, SQL_CLOSE);
  2569.  
  2570.         // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
  2571.         // use the call below that leaves out the user name
  2572.         if (!UserID.IsEmpty() &&
  2573.             Dbms() != dbmsMY_SQL &&
  2574.             Dbms() != dbmsACCESS &&
  2575.             Dbms() != dbmsMS_SQL_SERVER)
  2576.         {
  2577.             retcode = SQLColumns(hstmt,
  2578.                                  NULL, 0,                              // All qualifiers
  2579.                                  (UCHAR *) UserID.c_str(), SQL_NTS,    // Owner
  2580.                                  (UCHAR *) TableName.c_str(), SQL_NTS,
  2581.                                  NULL, 0);                             // All columns
  2582.         }
  2583.         else
  2584.         {
  2585.             retcode = SQLColumns(hstmt,
  2586.                                  NULL, 0,                              // All qualifiers
  2587.                                  NULL, 0,                              // Owner
  2588.                                  (UCHAR *) TableName.c_str(), SQL_NTS,
  2589.                                  NULL, 0);                             // All columns
  2590.         }
  2591.         if (retcode != SQL_SUCCESS)
  2592.         {  // Error occured, abort
  2593.             DispAllErrors(henv, hdbc, hstmt);
  2594.             if (colInf)
  2595.                 delete [] colInf;
  2596.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2597.             if (numCols)
  2598.                 *numCols = 0;
  2599.             return(0);
  2600.         }
  2601.  
  2602.         while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
  2603.         {
  2604.             if (pass == 1)  // First pass, just add up the number of columns
  2605.                 noCols++;
  2606.             else  // Pass 2; Fill in the array of structures
  2607.             {
  2608.                 if (colNo < noCols)  // Some extra error checking to prevent memory overwrites
  2609.                 {
  2610.                     // NOTE: Only the ODBC 1.x fields are retrieved
  2611.                     GetData( 1, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].catalog,      128+1,                    &cb);
  2612.                     GetData( 2, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].schema,       128+1,                    &cb);
  2613.                     GetData( 3, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].tableName,    DB_MAX_TABLE_NAME_LEN+1,  &cb);
  2614.                     GetData( 4, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].colName,      DB_MAX_COLUMN_NAME_LEN+1, &cb);
  2615.                     GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType,  0,                        &cb);
  2616.                     GetData( 6, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].typeName,     128+1,                    &cb);
  2617.                     GetData( 7, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].columnSize,   0,                        &cb);
  2618.                     GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0,                        &cb);
  2619.                     GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0,                        &cb);
  2620.                     GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0,                        &cb);
  2621.                     GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable,     0,                        &cb);
  2622.                     GetData(12, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].remarks,      254+1,                    &cb);
  2623.                     // Start Values for Primary/Foriegn Key (=No)
  2624.                     colInf[colNo].PkCol = 0;           // Primary key column   0=No; 1= First Key, 2 = Second Key etc.
  2625.                     colInf[colNo].PkTableName[0] = 0;  // Tablenames where Primary Key is used as a Foreign Key
  2626.                     colInf[colNo].FkCol = 0;           // Foreign key column   0=No; 1= First Key, 2 = Second Key etc.
  2627.                     colInf[colNo].FkTableName[0] = 0;  // Foreign key table name
  2628.  
  2629. #ifdef _IODBC_
  2630.                     // IODBC does not return a correct columnSize, so we set
  2631.                     // columnSize = bufferLength if no column size was returned
  2632.                     // IODBC returns the columnSize in bufferLength.. (bug)
  2633.                     if (colInf[colNo].columnSize < 1)
  2634.                     {
  2635.                        colInf[colNo].columnSize = colInf[colNo].bufferLength;
  2636.                     }
  2637. #endif
  2638.  
  2639.                     // Determine the wxDb data type that is used to represent the native data type of this data source
  2640.                     colInf[colNo].dbDataType = 0;
  2641.                     // Get the intern datatype
  2642.                     switch (colInf[colNo].sqlDataType)
  2643.                     {
  2644.                         case SQL_VARCHAR:
  2645.                         case SQL_CHAR:
  2646.                             colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
  2647.                         break;
  2648.  
  2649.                         case SQL_TINYINT:
  2650.                         case SQL_SMALLINT:
  2651.                         case SQL_INTEGER:
  2652.                             colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
  2653.                             break;
  2654.                         case SQL_DOUBLE:
  2655.                         case SQL_DECIMAL:
  2656.                         case SQL_NUMERIC:
  2657.                         case SQL_FLOAT:
  2658.                         case SQL_REAL:
  2659.                             colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
  2660.                             break;
  2661.                         case SQL_DATE:
  2662.                             colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
  2663.                             break;
  2664.                         case SQL_BINARY:
  2665.                             colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
  2666.                             break;
  2667. #ifdef __WXDEBUG__
  2668.                         default:
  2669.                             wxString errMsg;
  2670.                             errMsg.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf[colNo].sqlDataType);
  2671.                             wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
  2672. #endif
  2673.                     }
  2674.                     colNo++;
  2675.                 }
  2676.             }
  2677.         }
  2678.         if (retcode != SQL_NO_DATA_FOUND)
  2679.         {  // Error occured, abort
  2680.             DispAllErrors(henv, hdbc, hstmt);
  2681.             if (colInf)
  2682.                 delete [] colInf;
  2683.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2684.             if (numCols)
  2685.                 *numCols = 0;
  2686.             return(0);
  2687.         }
  2688.     }
  2689.  
  2690.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2691.  
  2692.     // Store Primary and Foreign Keys
  2693.     GetKeyFields(tableName,colInf,noCols);
  2694.  
  2695.     ///////////////////////////////////////////////////////////////////////////
  2696.     // Now sort the the columns in order to make them appear in the right order
  2697.     ///////////////////////////////////////////////////////////////////////////
  2698.  
  2699.     // Build a generic SELECT statement which returns 0 rows
  2700.     wxString Stmt;
  2701.  
  2702.     Stmt.Printf(wxT("select * from \"%s\" where 0=1"), tableName);
  2703.  
  2704.     // Execute query
  2705.     if (SQLExecDirect(hstmt, (UCHAR FAR *) Stmt.c_str(), SQL_NTS) != SQL_SUCCESS)
  2706.     {
  2707.         DispAllErrors(henv, hdbc, hstmt);
  2708.         return NULL;
  2709.     }
  2710.  
  2711.     // Get the number of result columns
  2712.     if (SQLNumResultCols (hstmt, &noCols) != SQL_SUCCESS)
  2713.     {
  2714.         DispAllErrors(henv, hdbc, hstmt);
  2715.         return NULL;
  2716.     }
  2717.  
  2718.     if (noCols == 0) // Probably a bogus table name
  2719.         return NULL;
  2720.  
  2721.     //  Get the name
  2722.     int i;
  2723.     short colNum;
  2724.     UCHAR name[100];
  2725.     SWORD Sword;
  2726.     SDWORD Sdword;
  2727.     for (colNum = 0; colNum < noCols; colNum++)
  2728.     {
  2729.         if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
  2730.             name, sizeof(name),
  2731.             &Sword, &Sdword) != SQL_SUCCESS)
  2732.         {
  2733.             DispAllErrors(henv, hdbc, hstmt);
  2734.             return NULL;
  2735.         }
  2736.  
  2737.         wxString Name1 = name;
  2738.         Name1 = Name1.Upper();
  2739.  
  2740.         // Where is this name in the array ?
  2741.         for (i = colNum ; i < noCols ; i++)
  2742.         {
  2743.             wxString Name2 =  colInf[i].colName;
  2744.             Name2 = Name2.Upper();
  2745.             if (Name2 == Name1)
  2746.             {
  2747.                 if (colNum != i) // swap to sort
  2748.                 {
  2749.                     wxDbColInf tmpColInf = colInf[colNum];
  2750.                     colInf[colNum] =  colInf[i];
  2751.                     colInf[i] = tmpColInf;
  2752.                 }
  2753.                 break;
  2754.             }
  2755.         }
  2756.     }
  2757.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2758.  
  2759.     ///////////////////////////////////////////////////////////////////////////
  2760.     // End sorting
  2761.     ///////////////////////////////////////////////////////////////////////////
  2762.  
  2763.     if (numCols)
  2764.         *numCols = noCols;
  2765.     return colInf;
  2766.  
  2767. }  // wxDb::GetColumns()
  2768.  
  2769.  
  2770. #endif  // #else OLD_GETCOLUMNS
  2771.  
  2772.  
  2773. /********** wxDb::GetColumnCount() **********/
  2774. int wxDb::GetColumnCount(const wxString &tableName, const wxChar *userID)
  2775. /*
  2776.  * Returns a count of how many columns are in a table.
  2777.  * If an error occurs in computing the number of columns
  2778.  * this function will return a -1 for the count
  2779.  *
  2780.  * userID is evaluated in the following manner:
  2781.  *        userID == NULL  ... UserID is ignored
  2782.  *        userID == ""    ... UserID set equal to 'this->uid'
  2783.  *        userID != ""    ... UserID set equal to 'userID'
  2784.  *
  2785.  * NOTE: ALL column bindings associated with this wxDb instance are unbound
  2786.  *       by this function.  This function should use its own wxDb instance
  2787.  *       to avoid undesired unbinding of columns.
  2788.  */
  2789. {
  2790.     UWORD    noCols = 0;
  2791.  
  2792.     RETCODE  retcode;
  2793.  
  2794.     wxString TableName;
  2795.  
  2796.     wxString UserID;
  2797.     convertUserID(userID,UserID);
  2798.  
  2799.     TableName = tableName;
  2800.     // Oracle and Interbase table names are uppercase only, so force
  2801.     // the name to uppercase just in case programmer forgot to do this
  2802.     if ((Dbms() == dbmsORACLE) ||
  2803.         (Dbms() == dbmsINTERBASE))
  2804.         TableName = TableName.Upper();
  2805.  
  2806.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2807.  
  2808.     // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
  2809.     // use the call below that leaves out the user name
  2810.     if (!UserID.IsEmpty() &&
  2811.         Dbms() != dbmsMY_SQL &&
  2812.         Dbms() != dbmsACCESS &&
  2813.         Dbms() != dbmsMS_SQL_SERVER)
  2814.     {
  2815.         retcode = SQLColumns(hstmt,
  2816.                              NULL, 0,                                // All qualifiers
  2817.                              (UCHAR *) UserID.c_str(), SQL_NTS,      // Owner
  2818.                              (UCHAR *) TableName.c_str(), SQL_NTS,
  2819.                              NULL, 0);                               // All columns
  2820.     }
  2821.     else
  2822.     {
  2823.         retcode = SQLColumns(hstmt,
  2824.                              NULL, 0,                                // All qualifiers
  2825.                              NULL, 0,                                // Owner
  2826.                              (UCHAR *) TableName.c_str(), SQL_NTS,
  2827.                              NULL, 0);                               // All columns
  2828.     }
  2829.     if (retcode != SQL_SUCCESS)
  2830.     {  // Error occured, abort
  2831.         DispAllErrors(henv, hdbc, hstmt);
  2832.         SQLFreeStmt(hstmt, SQL_CLOSE);
  2833.         return(-1);
  2834.     }
  2835.  
  2836.     // Count the columns
  2837.     while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
  2838.         noCols++;
  2839.  
  2840.     if (retcode != SQL_NO_DATA_FOUND)
  2841.     {  // Error occured, abort
  2842.         DispAllErrors(henv, hdbc, hstmt);
  2843.         SQLFreeStmt(hstmt, SQL_CLOSE);
  2844.         return(-1);
  2845.     }
  2846.  
  2847.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2848.     return noCols;
  2849.  
  2850. }  // wxDb::GetColumnCount()
  2851.  
  2852.  
  2853. /********** wxDb::GetCatalog() *******/
  2854. wxDbInf *wxDb::GetCatalog(const wxChar *userID)
  2855. /*
  2856.  * ---------------------------------------------------------------------
  2857.  * -- 19991203 : mj10777 : Create                                 ------
  2858.  * --          : Creates a wxDbInf with Tables / Cols Array       ------
  2859.  * --          : uses SQLTables and fills pTableInf;              ------
  2860.  * --          : pColInf is set to NULL and numCols to 0;         ------
  2861.  * --          : returns pDbInf (wxDbInf)                         ------
  2862.  * --            - if unsuccesfull (pDbInf == NULL)               ------
  2863.  * --          : pColInf can be filled with GetColumns(..);       ------
  2864.  * --          : numCols   can be filled with GetColumnCount(..); ------
  2865.  * ---------------------------------------------------------------------
  2866.  *
  2867.  * userID is evaluated in the following manner:
  2868.  *        userID == NULL  ... UserID is ignored
  2869.  *        userID == ""    ... UserID set equal to 'this->uid'
  2870.  *        userID != ""    ... UserID set equal to 'userID'
  2871.  *
  2872.  * NOTE: ALL column bindings associated with this wxDb instance are unbound
  2873.  *       by this function.  This function should use its own wxDb instance
  2874.  *       to avoid undesired unbinding of columns.
  2875.  */
  2876. {
  2877.     wxDbInf *pDbInf = NULL; // Array of catalog entries
  2878.     int      noTab = 0;     // Counter while filling table entries
  2879.     int      pass;
  2880.     RETCODE  retcode;
  2881.     SDWORD   cb;
  2882.     wxString tblNameSave;
  2883.  
  2884.     wxString UserID;
  2885.     convertUserID(userID,UserID);
  2886.  
  2887.     //-------------------------------------------------------------
  2888.     pDbInf = new wxDbInf;          // Create the Database Array
  2889.     //-------------------------------------------------------------
  2890.     // Table Information
  2891.     // Pass 1 - Determine how many Tables there are.
  2892.     // Pass 2 - Create the Table array and fill it
  2893.     //        - Create the Cols array = NULL
  2894.     //-------------------------------------------------------------
  2895.  
  2896.     for (pass = 1; pass <= 2; pass++)
  2897.     {
  2898.         SQLFreeStmt(hstmt, SQL_CLOSE);   // Close if Open
  2899.         tblNameSave.Empty();
  2900.  
  2901.         if (!UserID.IsEmpty() &&
  2902.             Dbms() != dbmsMY_SQL &&
  2903.             Dbms() != dbmsACCESS &&
  2904.             Dbms() != dbmsMS_SQL_SERVER)
  2905.         {
  2906.             retcode = SQLTables(hstmt,
  2907.                                 NULL, 0,                             // All qualifiers
  2908.                                 (UCHAR *) UserID.c_str(), SQL_NTS,   // User specified
  2909.                                 NULL, 0,                             // All tables
  2910.                                 NULL, 0);                            // All columns
  2911.         }
  2912.         else
  2913.         {
  2914.             retcode = SQLTables(hstmt,
  2915.                                 NULL, 0,           // All qualifiers
  2916.                                 NULL, 0,           // User specified
  2917.                                 NULL, 0,           // All tables
  2918.                                 NULL, 0);          // All columns
  2919.         }
  2920.  
  2921.         if (retcode != SQL_SUCCESS)
  2922.         {
  2923.             DispAllErrors(henv, hdbc, hstmt);
  2924.             pDbInf = NULL;
  2925.             SQLFreeStmt(hstmt, SQL_CLOSE);
  2926.             return pDbInf;
  2927.         }
  2928.  
  2929.         while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)   // Table Information
  2930.         {
  2931.             if (pass == 1)  // First pass, just count the Tables
  2932.             {
  2933.                 if (pDbInf->numTables == 0)
  2934.                 {
  2935.                     GetData( 1, SQL_C_CHAR,   (UCHAR*)  pDbInf->catalog,  128+1, &cb);
  2936.                     GetData( 2, SQL_C_CHAR,   (UCHAR*)  pDbInf->schema,   128+1, &cb);
  2937.                  }
  2938.                  pDbInf->numTables++;      // Counter for Tables
  2939.             }  // if (pass == 1)
  2940.             if (pass == 2) // Create and fill the Table entries
  2941.             {
  2942.                 if (pDbInf->pTableInf == NULL)   // Has the Table Array been created
  2943.                 {  // no, then create the Array
  2944.                     pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
  2945.                     noTab = 0;
  2946.                 } // if (pDbInf->pTableInf == NULL)   // Has the Table Array been created
  2947.  
  2948.                 GetData( 3, SQL_C_CHAR,   (UCHAR*)  (pDbInf->pTableInf+noTab)->tableName,    DB_MAX_TABLE_NAME_LEN+1, &cb);
  2949.                 GetData( 4, SQL_C_CHAR,   (UCHAR*)  (pDbInf->pTableInf+noTab)->tableType,    30+1,                    &cb);
  2950.                 GetData( 5, SQL_C_CHAR,   (UCHAR*)  (pDbInf->pTableInf+noTab)->tableRemarks, 254+1,                   &cb);
  2951.  
  2952.                 noTab++;
  2953.             }  // if
  2954.         }  // while
  2955.     }  // for
  2956.     SQLFreeStmt(hstmt, SQL_CLOSE);
  2957.  
  2958.     // Query how many columns are in each table
  2959.     for (noTab=0;noTab<pDbInf->numTables;noTab++)
  2960.     {
  2961.         (pDbInf->pTableInf+noTab)->numCols = GetColumnCount((pDbInf->pTableInf+noTab)->tableName,UserID);
  2962.     }
  2963.  
  2964.     return pDbInf;
  2965.  
  2966. }  // wxDb::GetCatalog()
  2967.  
  2968.  
  2969. /********** wxDb::Catalog() **********/
  2970. bool wxDb::Catalog(const wxChar *userID, const wxString &fileName)
  2971. /*
  2972.  * Creates the text file specified in 'filename' which will contain
  2973.  * a minimal data dictionary of all tables accessible by the user specified
  2974.  * in 'userID'
  2975.  *
  2976.  * userID is evaluated in the following manner:
  2977.  *        userID == NULL  ... UserID is ignored
  2978.  *        userID == ""    ... UserID set equal to 'this->uid'
  2979.  *        userID != ""    ... UserID set equal to 'userID'
  2980.  *
  2981.  * NOTE: ALL column bindings associated with this wxDb instance are unbound
  2982.  *       by this function.  This function should use its own wxDb instance
  2983.  *       to avoid undesired unbinding of columns.
  2984.  */
  2985. {
  2986.     wxASSERT(fileName.Length());
  2987.  
  2988.     RETCODE   retcode;
  2989.     SDWORD    cb;
  2990.     wxChar    tblName[DB_MAX_TABLE_NAME_LEN+1];
  2991.     wxString  tblNameSave;
  2992.     wxChar    colName[DB_MAX_COLUMN_NAME_LEN+1];
  2993.     SWORD     sqlDataType;
  2994.     wxChar    typeName[30+1];
  2995.     SDWORD    precision, length;
  2996.  
  2997.     FILE *fp = fopen(fileName.c_str(),wxT("wt"));
  2998.     if (fp == NULL)
  2999.         return(FALSE);
  3000.  
  3001.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3002.  
  3003.     wxString UserID;
  3004.     convertUserID(userID,UserID);
  3005.  
  3006.     if (!UserID.IsEmpty() &&
  3007.         Dbms() != dbmsMY_SQL &&
  3008.         Dbms() != dbmsACCESS &&
  3009.         Dbms() != dbmsINTERBASE &&
  3010.         Dbms() != dbmsMS_SQL_SERVER)
  3011.     {
  3012.         retcode = SQLColumns(hstmt,
  3013.                              NULL, 0,                                // All qualifiers
  3014.                              (UCHAR *) UserID.c_str(), SQL_NTS,      // User specified
  3015.                              NULL, 0,                                // All tables
  3016.                              NULL, 0);                               // All columns
  3017.     }
  3018.     else
  3019.     {
  3020.         retcode = SQLColumns(hstmt,
  3021.                              NULL, 0,    // All qualifiers
  3022.                              NULL, 0,    // User specified
  3023.                              NULL, 0,    // All tables
  3024.                              NULL, 0);   // All columns
  3025.     }
  3026.     if (retcode != SQL_SUCCESS)
  3027.     {
  3028.         DispAllErrors(henv, hdbc, hstmt);
  3029.         fclose(fp);
  3030.         return(FALSE);
  3031.     }
  3032.  
  3033.     wxString outStr;
  3034.     tblNameSave.Empty();
  3035.     int cnt = 0;
  3036.  
  3037.     while (TRUE)
  3038.     {
  3039.         retcode = SQLFetch(hstmt);
  3040.         if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  3041.             break;
  3042.  
  3043.         if (wxStrcmp(tblName, tblNameSave.c_str()))
  3044.         {
  3045.             if (cnt)
  3046.                 fputs(wxT("\n"), fp);
  3047.             fputs(wxT("================================ "), fp);
  3048.             fputs(wxT("================================ "), fp);
  3049.             fputs(wxT("===================== "), fp);
  3050.             fputs(wxT("========= "), fp);
  3051.             fputs(wxT("=========\n"), fp);
  3052.             outStr.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
  3053.                 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
  3054.             fputs(outStr.c_str(), fp);
  3055.             fputs(wxT("================================ "), fp);
  3056.             fputs(wxT("================================ "), fp);
  3057.             fputs(wxT("===================== "), fp);
  3058.             fputs(wxT("========= "), fp);
  3059.             fputs(wxT("=========\n"), fp);
  3060.             tblNameSave = tblName;
  3061.         }
  3062.  
  3063.       GetData(3,SQL_C_CHAR,  (UCHAR *) tblName,     DB_MAX_TABLE_NAME_LEN+1, &cb);
  3064.       GetData(4,SQL_C_CHAR,  (UCHAR *) colName,     DB_MAX_COLUMN_NAME_LEN+1,&cb);
  3065.       GetData(5,SQL_C_SSHORT,(UCHAR *)&sqlDataType, 0,                       &cb);
  3066.       GetData(6,SQL_C_CHAR,  (UCHAR *) typeName,    sizeof(typeName),        &cb);
  3067.       GetData(7,SQL_C_SLONG, (UCHAR *)&precision,   0,                       &cb);
  3068.       GetData(8,SQL_C_SLONG, (UCHAR *)&length,      0,                       &cb);
  3069.  
  3070.         outStr.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
  3071.             tblName, colName, sqlDataType, typeName, precision, length);
  3072.         if (fputs(outStr.c_str(), fp) == EOF)
  3073.         {
  3074.             SQLFreeStmt(hstmt, SQL_CLOSE);
  3075.             fclose(fp);
  3076.             return(FALSE);
  3077.         }
  3078.         cnt++;
  3079.     }
  3080.  
  3081.     if (retcode != SQL_NO_DATA_FOUND)
  3082.         DispAllErrors(henv, hdbc, hstmt);
  3083.  
  3084.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3085.  
  3086.     fclose(fp);
  3087.     return(retcode == SQL_NO_DATA_FOUND);
  3088.  
  3089. }  // wxDb::Catalog()
  3090.  
  3091.  
  3092. bool wxDb::TableExists(const wxString &tableName, const wxChar *userID, const wxString &tablePath)
  3093. /*
  3094.  * Table name can refer to a table, view, alias or synonym.  Returns TRUE
  3095.  * if the object exists in the database.  This function does not indicate
  3096.  * whether or not the user has privleges to query or perform other functions
  3097.  * on the table.
  3098.  *
  3099.  * userID is evaluated in the following manner:
  3100.  *        userID == NULL  ... UserID is ignored
  3101.  *        userID == ""    ... UserID set equal to 'this->uid'
  3102.  *        userID != ""    ... UserID set equal to 'userID'
  3103.  */
  3104. {
  3105.     wxASSERT(tableName.Length());
  3106.  
  3107.     wxString TableName;
  3108.  
  3109.     if (Dbms() == dbmsDBASE)
  3110.     {
  3111.         wxString dbName;
  3112.         if (tablePath.Length())
  3113.             dbName.Printf(wxT("%s/%s.dbf"), tablePath.c_str(), tableName.c_str());
  3114.         else
  3115.             dbName.Printf(wxT("%s.dbf"), tableName.c_str());
  3116.  
  3117.         bool exists;
  3118.         exists = wxFileExists(dbName);
  3119.         return exists;
  3120.     }
  3121.  
  3122.     wxString UserID;
  3123.     convertUserID(userID,UserID);
  3124.  
  3125.     TableName = tableName;
  3126.     // Oracle and Interbase table names are uppercase only, so force
  3127.     // the name to uppercase just in case programmer forgot to do this
  3128.     if ((Dbms() == dbmsORACLE) ||
  3129.         (Dbms() == dbmsINTERBASE))
  3130.         TableName = TableName.Upper();
  3131.  
  3132.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3133.     RETCODE retcode;
  3134.  
  3135.     // Some databases cannot accept a user name when looking up table names,
  3136.     // so we use the call below that leaves out the user name
  3137.     if (!UserID.IsEmpty() &&
  3138.         Dbms() != dbmsMY_SQL &&
  3139.         Dbms() != dbmsACCESS &&
  3140.         Dbms() != dbmsMS_SQL_SERVER &&
  3141.         Dbms() != dbmsDB2 &&
  3142.         Dbms() != dbmsINTERBASE &&
  3143.         Dbms() != dbmsPERVASIVE_SQL)
  3144.     {
  3145.         retcode = SQLTables(hstmt,
  3146.                             NULL, 0,                                  // All qualifiers
  3147.                             (UCHAR *) UserID.c_str(), SQL_NTS,        // Only tables owned by this user
  3148.                             (UCHAR FAR *)TableName.c_str(), SQL_NTS,
  3149.                             NULL, 0);                                 // All table types
  3150.     }
  3151.     else
  3152.     {
  3153.         retcode = SQLTables(hstmt,
  3154.                             NULL, 0,                                  // All qualifiers
  3155.                             NULL, 0,                                  // All owners
  3156.                             (UCHAR FAR *)TableName.c_str(), SQL_NTS,
  3157.                             NULL, 0);                                 // All table types
  3158.     }
  3159.     if (retcode != SQL_SUCCESS)
  3160.         return(DispAllErrors(henv, hdbc, hstmt));
  3161.  
  3162.     retcode = SQLFetch(hstmt);
  3163.     if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
  3164.     {
  3165.         SQLFreeStmt(hstmt, SQL_CLOSE);
  3166.         return(DispAllErrors(henv, hdbc, hstmt));
  3167.     }
  3168.  
  3169.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3170.  
  3171.     return(TRUE);
  3172.  
  3173. }  // wxDb::TableExists()
  3174.  
  3175.  
  3176. /********** wxDb::TablePrivileges() **********/
  3177. bool wxDb::TablePrivileges(const wxString &tableName, const wxString &priv, const wxChar *userID,
  3178.                             const wxChar *schema, const wxString &tablePath)
  3179. {
  3180.     wxASSERT(tableName.Length());
  3181.  
  3182.     wxDbTablePrivilegeInfo  result;
  3183.     SDWORD  cbRetVal;
  3184.     RETCODE retcode;
  3185.  
  3186.     // We probably need to be able to dynamically set this based on
  3187.     // the driver type, and state.
  3188.     wxChar curRole[]=wxT("public");
  3189.  
  3190.     wxString TableName;
  3191.  
  3192.     wxString UserID,Schema;
  3193.     convertUserID(userID,UserID);
  3194.     convertUserID(schema,Schema);
  3195.  
  3196.     TableName = tableName;
  3197.     // Oracle and Interbase table names are uppercase only, so force
  3198.     // the name to uppercase just in case programmer forgot to do this
  3199.     if ((Dbms() == dbmsORACLE) ||
  3200.         (Dbms() == dbmsINTERBASE))
  3201.         TableName = TableName.Upper();
  3202.  
  3203.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3204.  
  3205.     // Some databases cannot accept a user name when looking up table names,
  3206.     // so we use the call below that leaves out the user name
  3207.     if (!Schema.IsEmpty() &&
  3208.         Dbms() != dbmsMY_SQL &&
  3209.         Dbms() != dbmsACCESS &&
  3210.         Dbms() != dbmsMS_SQL_SERVER)
  3211.     {
  3212.         retcode = SQLTablePrivileges(hstmt,
  3213.                                      NULL, 0,                                    // Catalog
  3214.                                      (UCHAR FAR *)Schema.c_str(), SQL_NTS,               // Schema
  3215.                                      (UCHAR FAR *)TableName.c_str(), SQL_NTS);
  3216.     }
  3217.     else
  3218.     {
  3219.         retcode = SQLTablePrivileges(hstmt,
  3220.                                      NULL, 0,                                    // Catalog
  3221.                                      NULL, 0,                                    // Schema
  3222.                                      (UCHAR FAR *)TableName.c_str(), SQL_NTS);
  3223.     }
  3224.  
  3225. #ifdef DBDEBUG_CONSOLE
  3226.     fprintf(stderr ,wxT("SQLTablePrivileges() returned %i \n"),retcode);
  3227. #endif
  3228.  
  3229.     if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
  3230.         return(DispAllErrors(henv, hdbc, hstmt));
  3231.  
  3232.     bool failed = FALSE;
  3233.     retcode = SQLFetch(hstmt);
  3234.     while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  3235.     {
  3236.         if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) result.tableQual, sizeof(result.tableQual), &cbRetVal) != SQL_SUCCESS)
  3237.             failed = TRUE;
  3238.  
  3239.         if (!failed && SQLGetData(hstmt, 2, SQL_C_CHAR, (UCHAR*) result.tableOwner, sizeof(result.tableOwner), &cbRetVal) != SQL_SUCCESS)
  3240.             failed = TRUE;
  3241.  
  3242.         if (!failed && SQLGetData(hstmt, 3, SQL_C_CHAR, (UCHAR*) result.tableName, sizeof(result.tableName), &cbRetVal) != SQL_SUCCESS)
  3243.             failed = TRUE;
  3244.  
  3245.         if (!failed && SQLGetData(hstmt, 4, SQL_C_CHAR, (UCHAR*) result.grantor, sizeof(result.grantor), &cbRetVal) != SQL_SUCCESS)
  3246.             failed = TRUE;
  3247.  
  3248.         if (!failed && SQLGetData(hstmt, 5, SQL_C_CHAR, (UCHAR*) result.grantee, sizeof(result.grantee), &cbRetVal) != SQL_SUCCESS)
  3249.             failed = TRUE;
  3250.  
  3251.         if (!failed && SQLGetData(hstmt, 6, SQL_C_CHAR, (UCHAR*) result.privilege, sizeof(result.privilege), &cbRetVal) != SQL_SUCCESS)
  3252.             failed = TRUE;
  3253.  
  3254.         if (!failed && SQLGetData(hstmt, 7, SQL_C_CHAR, (UCHAR*) result.grantable, sizeof(result.grantable), &cbRetVal) != SQL_SUCCESS)
  3255.             failed = TRUE;
  3256.  
  3257.         if (failed)
  3258.         {
  3259.             return(DispAllErrors(henv, hdbc, hstmt));
  3260.         }
  3261. #ifdef DBDEBUG_CONSOLE
  3262.         fprintf(stderr,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
  3263.                 result.privilege,result.tableOwner,result.tableName,
  3264.                 result.grantor, result.grantee);
  3265. #endif
  3266.  
  3267.         if (UserID.IsSameAs(result.tableOwner,FALSE))
  3268.         {
  3269.             SQLFreeStmt(hstmt, SQL_CLOSE);
  3270.             return TRUE;
  3271.         }
  3272.  
  3273.         if (UserID.IsSameAs(result.grantee,FALSE) &&
  3274.             !wxStrcmp(result.privilege,priv))
  3275.         {
  3276.             SQLFreeStmt(hstmt, SQL_CLOSE);
  3277.             return TRUE;
  3278.         }
  3279.  
  3280.         if (!wxStrcmp(result.grantee,curRole) &&
  3281.             !wxStrcmp(result.privilege,priv))
  3282.         {
  3283.             SQLFreeStmt(hstmt, SQL_CLOSE);
  3284.             return TRUE;
  3285.         }
  3286.  
  3287.         retcode = SQLFetch(hstmt);
  3288.     }
  3289.  
  3290.     SQLFreeStmt(hstmt, SQL_CLOSE);
  3291.     return FALSE;
  3292.  
  3293. }  // wxDb::TablePrivileges
  3294.  
  3295.  
  3296. const wxString wxDb::SQLTableName(const wxChar *tableName)
  3297. {
  3298.     wxString TableName;
  3299.  
  3300.     if (Dbms() == dbmsACCESS)
  3301.         TableName = '"';
  3302.     TableName += tableName;
  3303.     if (Dbms() == dbmsACCESS)
  3304.         TableName += '"';
  3305.  
  3306.     return TableName;
  3307. }  // wxDb::SQLTableName()
  3308.  
  3309.  
  3310. const wxString wxDb::SQLColumnName(const wxChar *colName)
  3311. {
  3312.     wxString ColName;
  3313.  
  3314.     if (Dbms() == dbmsACCESS)
  3315.         ColName = '"';
  3316.     ColName += colName;
  3317.     if (Dbms() == dbmsACCESS)
  3318.         ColName += '"';
  3319.  
  3320.     return ColName;
  3321. }  // wxDb::SQLColumnName()
  3322.  
  3323.  
  3324. /********** wxDb::SetSqlLogging() **********/
  3325. bool wxDb::SetSqlLogging(wxDbSqlLogState state, const wxString &filename, bool append)
  3326. {
  3327.     wxASSERT(state == sqlLogON  || state == sqlLogOFF);
  3328.     wxASSERT(state == sqlLogOFF || filename.Length());
  3329.  
  3330.     if (state == sqlLogON)
  3331.     {
  3332.         if (fpSqlLog == 0)
  3333.         {
  3334.             fpSqlLog = fopen(filename, (append ? wxT("at") : wxT("wt")));
  3335.             if (fpSqlLog == NULL)
  3336.                 return(FALSE);
  3337.         }
  3338.     }
  3339.     else  // sqlLogOFF
  3340.     {
  3341.         if (fpSqlLog)
  3342.         {
  3343.             if (fclose(fpSqlLog))
  3344.                 return(FALSE);
  3345.             fpSqlLog = 0;
  3346.         }
  3347.     }
  3348.  
  3349.     sqlLogState = state;
  3350.     return(TRUE);
  3351.  
  3352. }  // wxDb::SetSqlLogging()
  3353.  
  3354.  
  3355. /********** wxDb::WriteSqlLog() **********/
  3356. bool wxDb::WriteSqlLog(const wxString &logMsg)
  3357. {
  3358.     wxASSERT(logMsg.Length());
  3359.  
  3360.     if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
  3361.         return(FALSE);
  3362.  
  3363.     if (fputs(wxT("\n"),   fpSqlLog) == EOF)
  3364.         return(FALSE);
  3365.     if (fputs(logMsg, fpSqlLog) == EOF)
  3366.         return(FALSE);
  3367.     if (fputs(wxT("\n"),   fpSqlLog) == EOF)
  3368.         return(FALSE);
  3369.  
  3370.     return(TRUE);
  3371.  
  3372. }  // wxDb::WriteSqlLog()
  3373.  
  3374.  
  3375. /********** wxDb::Dbms() **********/
  3376. wxDBMS wxDb::Dbms(void)
  3377. /*
  3378.  * Be aware that not all database engines use the exact same syntax, and not
  3379.  * every ODBC compliant database is compliant to the same level of compliancy.
  3380.  * Some manufacturers support the minimum Level 1 compliancy, and others up
  3381.  * through Level 3.  Others support subsets of features for levels above 1.
  3382.  *
  3383.  * If you find an inconsistency between the wxDb class and a specific database
  3384.  * engine, and an identifier to this section, and special handle the database in
  3385.  * the area where behavior is non-conforming with the other databases.
  3386.  *
  3387.  *
  3388.  * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
  3389.  * ---------------------------------------------------
  3390.  *
  3391.  * ORACLE
  3392.  *        - Currently the only database supported by the class to support VIEWS
  3393.  *
  3394.  * DBASE
  3395.  *        - Does not support the SQL_TIMESTAMP structure
  3396.  *        - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
  3397.  *        - Does not automatically create the primary index if the 'keyField' param of SetColDef
  3398.  *            is TRUE.  The user must create ALL indexes from their program.
  3399.  *        - Table names can only be 8 characters long
  3400.  *        - Column names can only be 10 characters long
  3401.  *
  3402.  * SYBASE (all)
  3403.  *        - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
  3404.  *            after every table name involved in the query/join if that tables matching record(s)
  3405.  *            are to be locked
  3406.  *        - Ignores the keywords 'FOR UPDATE'.  Use the HOLDLOCK functionality described above
  3407.  *
  3408.  * SYBASE (Enterprise)
  3409.  *        - If a column is part of the Primary Key, the column cannot be NULL
  3410.  *        - Maximum row size is somewhere in the neighborhood of 1920 bytes
  3411.  *
  3412.  * MY_SQL
  3413.  *        - If a column is part of the Primary Key, the column cannot be NULL
  3414.  *        - Cannot support selecting for update [::CanSelectForUpdate()].  Always returns FALSE
  3415.  *        - Columns that are part of primary or secondary keys must be defined as being NOT NULL
  3416.  *            when they are created.  Some code is added in ::CreateIndex to try to adjust the
  3417.  *            column definition if it is not defined correctly, but it is experimental
  3418.  *        - Does not support sub-queries in SQL statements
  3419.  *
  3420.  * POSTGRES
  3421.  *        - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
  3422.  *        - Does not support sub-queries in SQL statements
  3423.  *
  3424.  * DB2
  3425.  *        - Primary keys must be declared as NOT NULL
  3426.  *        - Table and index names must not be longer than 13 characters in length (technically
  3427.  *          table names can be up to 18 characters, but the primary index is created using the
  3428.  *          base table name plus "_PIDX", so the limit if the table has a primary index is 13.
  3429.  *
  3430.  * PERVASIVE SQL
  3431.  *
  3432.  * INTERBASE
  3433.  *        - Columns that are part of primary keys must be defined as being NOT NULL
  3434.  *          when they are created.  Some code is added in ::CreateIndex to try to adjust the
  3435.  *          column definition if it is not defined correctly, but it is experimental
  3436.  */
  3437. {
  3438.     // Should only need to do this once for each new database connection
  3439.     // so return the value we already determined it to be to save time
  3440.     // and lots of string comparisons
  3441.     if (dbmsType != dbmsUNIDENTIFIED)
  3442.         return(dbmsType);
  3443.  
  3444.     wxChar baseName[25+1];
  3445.     wxStrncpy(baseName,dbInf.dbmsName,25);
  3446.     baseName[25] = 0;
  3447.  
  3448.     // RGG 20001025 : add support for Interbase
  3449.     // GT : Integrated to base classes on 20001121
  3450.     if (!wxStricmp(dbInf.dbmsName,wxT("Interbase")))
  3451.         return((wxDBMS)(dbmsType = dbmsINTERBASE));
  3452.  
  3453.     // BJO 20000428 : add support for Virtuoso
  3454.     if (!wxStricmp(dbInf.dbmsName,wxT("OpenLink Virtuoso VDBMS")))
  3455.       return((wxDBMS)(dbmsType = dbmsVIRTUOSO));
  3456.  
  3457.     if (!wxStricmp(dbInf.dbmsName,wxT("Adaptive Server Anywhere")))
  3458.         return((wxDBMS)(dbmsType = dbmsSYBASE_ASA));
  3459.  
  3460.     // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
  3461.     // connected through an OpenLink driver.
  3462.     // Is it also returned by Sybase Adapatitve server?
  3463.     // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
  3464.     if (!wxStricmp(dbInf.dbmsName,wxT("SQL Server")))
  3465.     {
  3466.       if (!wxStrncmp(dbInf.driverName, wxT("oplodbc"), 7) ||
  3467.           !wxStrncmp(dbInf.driverName, wxT("OLOD"), 4))
  3468.             return ((wxDBMS)(dbmsMS_SQL_SERVER));
  3469.         else
  3470.             return ((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
  3471.     }
  3472.  
  3473.     if (!wxStricmp(dbInf.dbmsName,wxT("Microsoft SQL Server")))
  3474.         return((wxDBMS)(dbmsType = dbmsMS_SQL_SERVER));
  3475.     if (!wxStricmp(dbInf.dbmsName,wxT("MySQL")))
  3476.         return((wxDBMS)(dbmsType = dbmsMY_SQL));
  3477.     if (!wxStricmp(dbInf.dbmsName,wxT("PostgreSQL")))  // v6.5.0
  3478.         return((wxDBMS)(dbmsType = dbmsPOSTGRES));
  3479.  
  3480.     baseName[9] = 0;
  3481.     if (!wxStricmp(dbInf.dbmsName,wxT("Pervasive")))
  3482.         return((wxDBMS)(dbmsType = dbmsPERVASIVE_SQL));
  3483.  
  3484.     baseName[8] = 0;
  3485.     if (!wxStricmp(baseName,wxT("Informix")))
  3486.         return((wxDBMS)(dbmsType = dbmsINFORMIX));
  3487.  
  3488.     baseName[6] = 0;
  3489.     if (!wxStricmp(baseName,wxT("Oracle")))
  3490.         return((wxDBMS)(dbmsType = dbmsORACLE));
  3491.     if (!wxStricmp(dbInf.dbmsName,wxT("ACCESS")))
  3492.         return((wxDBMS)(dbmsType = dbmsACCESS));
  3493.     if (!wxStricmp(dbInf.dbmsName,wxT("MySQL")))
  3494.         return((wxDBMS)(dbmsType = dbmsMY_SQL));
  3495.     if (!wxStricmp(baseName,wxT("Sybase")))
  3496.       return((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
  3497.  
  3498.     baseName[5] = 0;
  3499.     if (!wxStricmp(baseName,wxT("DBASE")))
  3500.         return((wxDBMS)(dbmsType = dbmsDBASE));
  3501.  
  3502.     if (!wxStricmp(baseName,wxT("xBase")))
  3503.         return((wxDBMS)(dbmsType = dbmsXBASE_SEQUITER));
  3504.     
  3505.     if (!wxStricmp(baseName,wxT("MySQL")))
  3506.         return((wxDBMS)(dbmsType = dbmsMY_SQL));
  3507.  
  3508.     baseName[3] = 0;
  3509.     if (!wxStricmp(baseName,wxT("DB2")))
  3510.         return((wxDBMS)(dbmsType = dbmsDBASE));
  3511.  
  3512.     return((wxDBMS)(dbmsType = dbmsUNIDENTIFIED));
  3513.  
  3514. }  // wxDb::Dbms()
  3515.  
  3516.  
  3517. bool wxDb::ModifyColumn(const wxString &tableName, const wxString &columnName,
  3518.                         int dataType, ULONG columnLength,
  3519.                         const wxString &optionalParam)
  3520. {
  3521.     wxASSERT(tableName.Length());
  3522.     wxASSERT(columnName.Length());
  3523.     wxASSERT((dataType == DB_DATA_TYPE_VARCHAR && columnLength > 0) ||
  3524.              dataType != DB_DATA_TYPE_VARCHAR);
  3525.  
  3526.     // Must specify a columnLength if modifying a VARCHAR type column
  3527.     if (dataType == DB_DATA_TYPE_VARCHAR && !columnLength)
  3528.         return FALSE;
  3529.  
  3530.     wxString dataTypeName;
  3531.     wxString sqlStmt;
  3532.     wxString alterSlashModify;
  3533.  
  3534.     switch(dataType)
  3535.     {
  3536.         case DB_DATA_TYPE_VARCHAR :
  3537.             dataTypeName = typeInfVarchar.TypeName;
  3538.             break;
  3539.         case DB_DATA_TYPE_INTEGER :
  3540.             dataTypeName = typeInfInteger.TypeName;
  3541.             break;
  3542.         case DB_DATA_TYPE_FLOAT :
  3543.             dataTypeName = typeInfFloat.TypeName;
  3544.             break;
  3545.         case DB_DATA_TYPE_DATE :
  3546.             dataTypeName = typeInfDate.TypeName;
  3547.             break;
  3548.         case DB_DATA_TYPE_BLOB :
  3549.             dataTypeName = typeInfBlob.TypeName;
  3550.             break;
  3551.         default:
  3552.             return FALSE;
  3553.     }
  3554.  
  3555.     // Set the modify or alter syntax depending on the type of database connected to
  3556.     switch (Dbms())
  3557.     {
  3558.         case dbmsORACLE :
  3559.             alterSlashModify = "MODIFY";
  3560.             break;
  3561.         case dbmsMS_SQL_SERVER :
  3562.             alterSlashModify = "ALTER COLUMN";
  3563.             break;
  3564.         case dbmsUNIDENTIFIED :
  3565.             return FALSE;
  3566.         case dbmsSYBASE_ASA :
  3567.         case dbmsSYBASE_ASE :
  3568.         case dbmsMY_SQL :
  3569.         case dbmsPOSTGRES :
  3570.         case dbmsACCESS :
  3571.         case dbmsDBASE :
  3572.         case dbmsXBASE_SEQUITER :
  3573.         default :
  3574.             alterSlashModify = "MODIFY";
  3575.             break;
  3576.     }
  3577.  
  3578.     // create the SQL statement
  3579.     sqlStmt.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName.c_str(), alterSlashModify.c_str(),
  3580.               columnName.c_str(), dataTypeName.c_str());
  3581.  
  3582.     // For varchars only, append the size of the column
  3583.     if (dataType == DB_DATA_TYPE_VARCHAR &&
  3584.         (Dbms() != dbmsMY_SQL || dataTypeName != "text"))
  3585.     {
  3586.         wxString s;
  3587.         s.Printf(wxT("(%d)"), columnLength);
  3588.         sqlStmt += s;
  3589.     }
  3590.  
  3591.     // for passing things like "NOT NULL"
  3592.     if (optionalParam.Length())
  3593.     {
  3594.         sqlStmt += wxT(" ");
  3595.         sqlStmt += optionalParam;
  3596.     }
  3597.  
  3598.     return ExecSql(sqlStmt);
  3599.  
  3600. } // wxDb::ModifyColumn()
  3601.  
  3602.  
  3603. /********** wxDbGetConnection() **********/
  3604. wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCursors)
  3605. {
  3606.     wxDbList *pList;
  3607.  
  3608.     // Used to keep a pointer to a DB connection that matches the requested
  3609.     // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
  3610.     // data types can be copied from it (using the wxDb::Open(wxDb *) function)
  3611.     // rather than having to re-query the datasource to get all the values
  3612.     // using the wxDb::Open(Dsn,Uid,AuthStr) function
  3613.     wxDb *matchingDbConnection = NULL;
  3614.  
  3615.     // Scan the linked list searching for an available database connection
  3616.     // that's already been opened but is currently not in use.
  3617.     for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
  3618.     {
  3619.         // The database connection must be for the same datasource
  3620.         // name and must currently not be in use.
  3621.         if (pList->Free &&
  3622.             (pList->PtrDb->FwdOnlyCursors() == FwdOnlyCursors) &&
  3623.             (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn)))  // Found a free connection
  3624.         {
  3625.             pList->Free = FALSE;
  3626.             return(pList->PtrDb);
  3627.         }
  3628.  
  3629.         if (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn) &&
  3630.             !wxStrcmp(pDbConfig->GetUserID(), pList->Uid) &&
  3631.             !wxStrcmp(pDbConfig->GetPassword(), pList->AuthStr))
  3632.             matchingDbConnection = pList->PtrDb;
  3633.     }
  3634.  
  3635.     // No available connections.  A new connection must be made and
  3636.     // appended to the end of the linked list.
  3637.     if (PtrBegDbList)
  3638.     {
  3639.         // Find the end of the list
  3640.         for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
  3641.         // Append a new list item
  3642.         pList->PtrNext = new wxDbList;
  3643.         pList->PtrNext->PtrPrev = pList;
  3644.         pList = pList->PtrNext;
  3645.     }
  3646.     else  // Empty list
  3647.     {
  3648.         // Create the first node on the list
  3649.         pList = PtrBegDbList = new wxDbList;
  3650.         pList->PtrPrev = 0;
  3651.     }
  3652.  
  3653.     // Initialize new node in the linked list
  3654.     pList->PtrNext  = 0;
  3655.     pList->Free     = FALSE;
  3656.     pList->Dsn      = pDbConfig->GetDsn();
  3657.     pList->Uid      = pDbConfig->GetUserID();
  3658.     pList->AuthStr  = pDbConfig->GetPassword();
  3659.  
  3660.     pList->PtrDb = new wxDb(pDbConfig->GetHenv(), FwdOnlyCursors);
  3661.  
  3662.     bool opened = FALSE;
  3663.  
  3664.     if (!matchingDbConnection)
  3665.         opened = pList->PtrDb->Open(pDbConfig->GetDsn(), pDbConfig->GetUserID(), pDbConfig->GetPassword());
  3666.     else
  3667.         opened = pList->PtrDb->Open(matchingDbConnection);
  3668.  
  3669.     // Connect to the datasource
  3670.     if (opened)
  3671.     {
  3672.         pList->PtrDb->setCached(TRUE);  // Prevent a user from deleting a cached connection
  3673.         pList->PtrDb->SetSqlLogging(SQLLOGstate,SQLLOGfn,TRUE);
  3674.         return(pList->PtrDb);
  3675.     }
  3676.     else  // Unable to connect, destroy list item
  3677.     {
  3678.         if (pList->PtrPrev)
  3679.             pList->PtrPrev->PtrNext = 0;
  3680.         else
  3681.             PtrBegDbList = 0;                // Empty list again
  3682.         pList->PtrDb->CommitTrans();    // Commit any open transactions on wxDb object
  3683.         pList->PtrDb->Close();            // Close the wxDb object
  3684.         delete pList->PtrDb;                // Deletes the wxDb object
  3685.         delete pList;                        // Deletes the linked list object
  3686.         return(0);
  3687.     }
  3688.  
  3689. }  // wxDbGetConnection()
  3690.  
  3691.  
  3692. /********** wxDbFreeConnection() **********/
  3693. bool WXDLLEXPORT wxDbFreeConnection(wxDb *pDb)
  3694. {
  3695.     wxDbList *pList;
  3696.  
  3697.     // Scan the linked list searching for the database connection
  3698.     for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
  3699.     {
  3700.         if (pList->PtrDb == pDb)  // Found it, now free it!!!
  3701.             return (pList->Free = TRUE);
  3702.     }
  3703.  
  3704.     // Never found the database object, return failure
  3705.     return(FALSE);
  3706.  
  3707. }  // wxDbFreeConnection()
  3708.  
  3709.  
  3710. /********** wxDbCloseConnections() **********/
  3711. void WXDLLEXPORT wxDbCloseConnections(void)
  3712. {
  3713.     wxDbList *pList, *pNext;
  3714.  
  3715.     // Traverse the linked list closing database connections and freeing memory as I go.
  3716.     for (pList = PtrBegDbList; pList; pList = pNext)
  3717.     {
  3718.         pNext = pList->PtrNext;       // Save the pointer to next
  3719.         pList->PtrDb->CommitTrans();  // Commit any open transactions on wxDb object
  3720.         pList->PtrDb->Close();        // Close the wxDb object
  3721.         pList->PtrDb->setCached(FALSE);  // Allows deletion of the wxDb instance
  3722.         delete pList->PtrDb;          // Deletes the wxDb object
  3723.         delete pList;                 // Deletes the linked list object
  3724.     }
  3725.  
  3726.     // Mark the list as empty
  3727.     PtrBegDbList = 0;
  3728.  
  3729. }  // wxDbCloseConnections()
  3730.  
  3731.  
  3732. /********** wxDbConnectionsInUse() **********/
  3733. int WXDLLEXPORT wxDbConnectionsInUse(void)
  3734. {
  3735.     wxDbList *pList;
  3736.     int cnt = 0;
  3737.  
  3738.     // Scan the linked list counting db connections that are currently in use
  3739.     for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
  3740.     {
  3741.         if (pList->Free == FALSE)
  3742.             cnt++;
  3743.     }
  3744.  
  3745.     return(cnt);
  3746.  
  3747. }  // wxDbConnectionsInUse()
  3748.  
  3749.  
  3750.  
  3751. /********** wxDbLogExtendedErrorMsg() **********/
  3752. // DEBUG ONLY function
  3753. const wxChar WXDLLEXPORT *wxDbLogExtendedErrorMsg(const wxChar *userText,
  3754.                                                   wxDb *pDb,
  3755.                                                   const wxChar *ErrFile,
  3756.                                                   int ErrLine)
  3757. {
  3758.     static wxString msg;
  3759.     msg = userText;
  3760.  
  3761.     wxString tStr;
  3762.  
  3763.     if (ErrFile || ErrLine)
  3764.     {
  3765.         msg += wxT("File: ");
  3766.         msg += ErrFile;
  3767.         msg += wxT("   Line: ");
  3768.         tStr.Printf(wxT("%d"),ErrLine);
  3769.         msg += tStr.c_str();
  3770.         msg += wxT("\n");
  3771.     }
  3772.  
  3773.     msg.Append (wxT("\nODBC errors:\n"));
  3774.     msg += wxT("\n");
  3775.  
  3776.     // Display errors for this connection
  3777.     int i;
  3778.     for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
  3779.     {
  3780.         if (pDb->errorList[i])
  3781.         {
  3782.             msg.Append(pDb->errorList[i]);
  3783.             if (wxStrcmp(pDb->errorList[i],wxT("")) != 0)
  3784.                 msg.Append(wxT("\n"));
  3785.             // Clear the errmsg buffer so the next error will not
  3786.             // end up showing the previous error that have occurred
  3787.             wxStrcpy(pDb->errorList[i],wxT(""));
  3788.         }
  3789.     }
  3790.     msg += wxT("\n");
  3791.  
  3792.     wxLogDebug(msg.c_str());
  3793.  
  3794.     return msg.c_str();
  3795. }  // wxDbLogExtendedErrorMsg()
  3796.  
  3797.  
  3798. /********** wxDbSqlLog() **********/
  3799. bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
  3800. {
  3801.     bool append = FALSE;
  3802.     wxDbList *pList;
  3803.  
  3804.     for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
  3805.     {
  3806.         if (!pList->PtrDb->SetSqlLogging(state,filename,append))
  3807.             return(FALSE);
  3808.         append = TRUE;
  3809.     }
  3810.  
  3811.     SQLLOGstate = state;
  3812.     SQLLOGfn = filename;
  3813.  
  3814.     return(TRUE);
  3815.  
  3816. }  // wxDbSqlLog()
  3817.  
  3818.  
  3819. #if 0
  3820. /********** wxDbCreateDataSource() **********/
  3821. int wxDbCreateDataSource(const wxString &driverName, const wxString &dsn, const wxString &description,
  3822.                          bool sysDSN, const wxString &defDir, wxWindow *parent)
  3823. /*
  3824.  * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
  3825.  * Very rudimentary creation of an ODBC data source.
  3826.  *
  3827.  * ODBC driver must be ODBC 3.0 compliant to use this function
  3828.  */
  3829. {
  3830.     int result = FALSE;
  3831.  
  3832. //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
  3833. #ifdef __VISUALC__
  3834.     int       dsnLocation;
  3835.     wxString  setupStr;
  3836.  
  3837.     if (sysDSN)
  3838.         dsnLocation = ODBC_ADD_SYS_DSN;
  3839.     else
  3840.         dsnLocation = ODBC_ADD_DSN;
  3841.  
  3842.     // NOTE: The decimal 2 is an invalid character in all keyword pairs
  3843.     // so that is why I used it, as wxString does not deal well with
  3844.     // embedded nulls in strings
  3845.     setupStr.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn,2,description,2,defDir,2);
  3846.  
  3847.     // Replace the separator from above with the '\0' seperator needed
  3848.     // by the SQLConfigDataSource() function
  3849.     int k;
  3850.     do
  3851.     {
  3852.         k = setupStr.Find((wxChar)2,TRUE);
  3853.         if (k != wxNOT_FOUND)
  3854.             setupStr[(UINT)k] = wxT('\0');
  3855.     }
  3856.     while (k != wxNOT_FOUND);
  3857.  
  3858.     result = SQLConfigDataSource((HWND)parent->GetHWND(), dsnLocation,
  3859.                                  driverName, setupStr.c_str());
  3860.  
  3861.     if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
  3862.     {
  3863.         // check for errors caused by ConfigDSN based functions
  3864.         DWORD retcode = 0;
  3865.         WORD cb;
  3866.         wxChar errMsg[SQL_MAX_MESSAGE_LENGTH];
  3867.         errMsg[0] = wxT('\0');
  3868.  
  3869.         // This function is only supported in ODBC drivers v3.0 compliant and above
  3870.         SQLInstallerError(1,&retcode,errMsg,SQL_MAX_MESSAGE_LENGTH-1,&cb);
  3871.         if (retcode)
  3872.         {
  3873. #ifdef DBDEBUG_CONSOLE
  3874.                // When run in console mode, use standard out to display errors.
  3875.                cout << errMsg << endl;
  3876.                cout << wxT("Press any key to continue...") << endl;
  3877.                getchar();
  3878. #endif  // DBDEBUG_CONSOLE
  3879.  
  3880. #ifdef __WXDEBUG__
  3881.                wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
  3882. #endif  // __WXDEBUG__
  3883.         }
  3884.     }
  3885.     else
  3886.        result = TRUE;
  3887. #else
  3888.     // Using iODBC/unixODBC or some other compiler which does not support the APIs
  3889.     // necessary to use this function, so this function is not supported
  3890. #ifdef __WXDEBUG__
  3891.     wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
  3892. #endif
  3893.     result = FALSE;
  3894. #endif  // __VISUALC__
  3895.  
  3896.     return result;
  3897.  
  3898. }  // wxDbCreateDataSource()
  3899. #endif
  3900.  
  3901.  
  3902. /********** wxDbGetDataSource() **********/
  3903. bool wxDbGetDataSource(HENV henv, wxChar *Dsn, SWORD DsnMax, wxChar *DsDesc,
  3904.                        SWORD DsDescMax, UWORD direction)
  3905. /*
  3906.  * Dsn and DsDesc will contain the data source name and data source
  3907.  * description upon return
  3908.  */
  3909. {
  3910.     SWORD cb1,cb2;
  3911.  
  3912.     if (SQLDataSources(henv, direction, (UCHAR FAR *) Dsn, DsnMax, &cb1,
  3913.                              (UCHAR FAR *) DsDesc, DsDescMax, &cb2) == SQL_SUCCESS)
  3914.         return(TRUE);
  3915.     else
  3916.         return(FALSE);
  3917.  
  3918. }  // wxDbGetDataSource()
  3919.  
  3920.  
  3921. // Change this to 0 to remove use of all deprecated functions
  3922. #if wxODBC_BACKWARD_COMPATABILITY
  3923. /********************************************************************
  3924.  ********************************************************************
  3925.  *
  3926.  * The following functions are all DEPRECATED and are included for
  3927.  * backward compatability reasons only
  3928.  *
  3929.  ********************************************************************
  3930.  ********************************************************************/
  3931. bool SqlLog(sqlLog state, const wxChar *filename)
  3932. {
  3933.     return wxDbSqlLog((enum wxDbSqlLogState)state, filename);
  3934. }
  3935. /***** DEPRECATED: use wxGetDataSource() *****/
  3936. bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
  3937.                          UWORD direction)
  3938. {
  3939.     return wxDbGetDataSource(henv, Dsn, DsnMax, DsDesc, DsDescMax, direction);
  3940. }
  3941. /***** DEPRECATED: use wxDbGetConnection() *****/
  3942. wxDb WXDLLEXPORT *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
  3943. {
  3944.     return wxDbGetConnection((wxDbConnectInf *)pDbStuff, FwdOnlyCursors);
  3945. }
  3946. /***** DEPRECATED: use wxDbFreeConnection() *****/
  3947. bool WXDLLEXPORT FreeDbConnection(wxDb *pDb)
  3948. {
  3949.     return wxDbFreeConnection(pDb);
  3950. }
  3951. /***** DEPRECATED: use wxDbCloseConnections() *****/
  3952. void WXDLLEXPORT CloseDbConnections(void)
  3953. {
  3954.     wxDbCloseConnections();
  3955. }
  3956. /***** DEPRECATED: use wxDbConnectionsInUse() *****/
  3957. int WXDLLEXPORT NumberDbConnectionsInUse(void)
  3958. {
  3959.     return wxDbConnectionsInUse();
  3960. }
  3961. #endif
  3962.  
  3963.  
  3964. #endif
  3965.  // wxUSE_ODBC
  3966.