]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/db.cpp
fixed wxSplitPath() bug and added tests for it
[wxWidgets.git] / src / common / db.cpp
index 361b7b873d584c27625de00305c2441a03e37e88..71f017622614af12f269bd968f76eaa2cebdd165 100644 (file)
@@ -123,20 +123,18 @@ static wxString SQLLOGfn = SQL_LOG_FILENAME;
 // connection
 char DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
 
-#if EXPERIMENTAL_WXDB_FUNCTIONS  // will be added in 2.4
-// This type defines the return row-struct form 
+// This type defines the return row-struct form
 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
-typedef struct 
+typedef struct
 {
-   char        tableQual[129];
-   char        tableOwner[129];
-   char        tableName[129];
-   char        grantor[129];
-   char        grantee[129];
-   char        privilege[129];
-   char        grantable[4];
+   wxChar        tableQual[128+1];
+   wxChar        tableOwner[128+1];
+   wxChar        tableName[128+1];
+   wxChar        grantor[128+1];
+   wxChar        grantee[128+1];
+   wxChar        privilege[128+1];
+   wxChar        grantable[3+1];
 } wxDbTablePrivilegeInfo;
-#endif
 
 
 /********** wxDbColFor Constructor **********/
@@ -217,7 +215,7 @@ wxDbTableInf::~wxDbTableInf()
 /********** wxDbInf Constructor *************/
 wxDbInf::wxDbInf()
 {
-    catalog[0]      = 0; 
+    catalog[0]      = 0;
     schema[0]       = 0;
     numTables       = 0;
     pTableInf       = NULL;
@@ -242,7 +240,7 @@ int wxDbColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnS
     // -- 19991224 : mj10777 : Create
     // There is still a lot of work to do here, but it is a start
     // It handles all the basic data-types that I have run into up to now
-    // The main work will have be with Dates and float Formatting 
+    // The main work will have be with Dates and float Formatting
     //    (US 1,000.00 ; EU 1.000,00)
     // There are wxWindow plans for locale support and the new wxDateTime.  If
     //    they define some constants (wxEUROPEAN) that can be gloably used,
@@ -259,7 +257,7 @@ int wxDbColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnS
     {
         if ((i_sqlDataType == SQL_VARCHAR) || (i_sqlDataType == SQL_LONGVARCHAR))
             i_dbDataType = DB_DATA_TYPE_VARCHAR;
-        if (i_sqlDataType == SQL_C_DATE)
+        if ((i_sqlDataType == SQL_C_DATE) || (i_sqlDataType == SQL_C_TIMESTAMP))
             i_dbDataType = DB_DATA_TYPE_DATE;
         if (i_sqlDataType == SQL_C_BIT)
             i_dbDataType = DB_DATA_TYPE_INTEGER;
@@ -317,14 +315,28 @@ int wxDbColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnS
 }  // wxDbColFor::Format()
 
 
-/********** wxDb Constructor **********/
+/********** wxDb Constructors **********/
 wxDb::wxDb(HENV &aHenv, bool FwdOnlyCursors)
+{
+    // Copy the HENV into the db class
+    henv = aHenv;
+    fwdOnlyCursors = FwdOnlyCursors;
+    initialize();
+} // wxDb::wxDb()
+
+
+void wxDb::initialize()
+/*
+ * Private member function that sets all wxDb member variables to
+ * known values at creation of the wxDb
+ */
 {
     int i;
 
     fpSqlLog      = 0;            // Sql Log file pointer
     sqlLogState   = sqlLogOFF;    // By default, logging is turned off
     nTables       = 0;
+    dbmsType      = dbmsUNIDENTIFIED;
 
     wxStrcpy(sqlState,wxT(""));
     wxStrcpy(errorMsg,wxT(""));
@@ -360,10 +372,6 @@ wxDb::wxDb(HENV &aHenv, bool FwdOnlyCursors)
     // Error reporting is turned OFF by default
     silent = TRUE;
 
-    // Copy the HENV into the db class
-    henv = aHenv;
-    fwdOnlyCursors = FwdOnlyCursors;
-
     // Allocate a data source connection handle
     if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
         DispAllErrors(henv);
@@ -373,8 +381,7 @@ wxDb::wxDb(HENV &aHenv, bool FwdOnlyCursors)
 
     // Mark database as not open as of yet
     dbIsOpen = FALSE;
-
-} // wxDb::wxDb()
+}  // wxDb::initialize()
 
 
 /********** wxDb::Open() **********/
@@ -402,15 +409,23 @@ bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
     }
 
     // Connect to the data source
-    retcode = SQLConnect(hdbc, (UCHAR FAR *) Dsn, SQL_NTS,
-                         (UCHAR FAR *) Uid, SQL_NTS,
-                         (UCHAR FAR *) AuthStr,SQL_NTS);
+    retcode = SQLConnect(hdbc, (UCHAR FAR *) dsn, SQL_NTS,
+                         (UCHAR FAR *) uid, SQL_NTS,
+                         (UCHAR FAR *) authStr,SQL_NTS);
 
+/*
     if (retcode == SQL_SUCCESS_WITH_INFO)
         DispAllErrors(henv, hdbc);
     else if (retcode != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc));
 
+       if (retcode == SQL_ERROR)
+               return(DispAllErrors(henv, hdbc));
+*/
+       if ((retcode != SQL_SUCCESS) &&
+        (retcode != SQL_SUCCESS_WITH_INFO))
+               return(DispAllErrors(henv, hdbc));
+
 /*
     If using Intersolv branded ODBC drivers, this is the place where you would substitute
     your branded driver license information
@@ -427,11 +442,11 @@ bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
         return(DispAllErrors(henv, hdbc));
 
     // Set Connection Options
-    if (! setConnectionOptions())
+    if (!setConnectionOptions())
         return(FALSE);
 
     // Query the data source for inf. about itself
-    if (! getDbInfo())
+    if (!getDbInfo())
         return(FALSE);
 
     // Query the data source regarding data type information
@@ -503,9 +518,8 @@ bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
     else
         typeInfFloat.FsqlType = SQL_DOUBLE;
 
-
     // Integer
-    if (!getDataTypeInfo(SQL_INTEGER, typeInfInteger))     
+    if (!getDataTypeInfo(SQL_INTEGER, typeInfInteger))
     {
         // If SQL_INTEGER is not supported, use the floating point
         // data type to store integers as well as floats
@@ -547,14 +561,158 @@ bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
 } // wxDb::Open()
 
 
+bool wxDb::Open(wxDb *copyDb)
+{
+    dsn        = (char *)copyDb->GetDatasourceName();
+    uid        = (char *)copyDb->GetUsername();
+    authStr    = (char *)copyDb->GetPassword();
+
+    RETCODE retcode;
+
+    if (!FwdOnlyCursors())
+    {
+        // Specify that the ODBC cursor library be used, if needed.  This must be
+        // specified before the connection is made.
+        retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
+
+#ifdef DBDEBUG_CONSOLE
+        if (retcode == SQL_SUCCESS)
+            cout << "SQLSetConnectOption(CURSOR_LIB) successful" << endl;
+        else
+            cout << "SQLSetConnectOption(CURSOR_LIB) failed" << endl;
+#endif
+    }
+
+    // Connect to the data source
+    retcode = SQLConnect(hdbc, (UCHAR FAR *) dsn, SQL_NTS,
+                         (UCHAR FAR *) uid, SQL_NTS,
+                         (UCHAR FAR *) authStr, SQL_NTS);
+
+       if (retcode == SQL_ERROR)
+               return(DispAllErrors(henv, hdbc));
+
+/*
+    If using Intersolv branded ODBC drivers, this is the place where you would substitute
+    your branded driver license information
+
+    SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
+    SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
+*/
+
+    // Mark database as open
+    dbIsOpen = TRUE;
+
+    // Allocate a statement handle for the database connection
+    if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
+        return(DispAllErrors(henv, hdbc));
+
+    // Set Connection Options
+    if (!setConnectionOptions())
+        return(FALSE);
+
+    // Instead of Querying the data source for info about itself, it can just be copied
+    // from the wxDb instance that was passed in (copyDb).
+    wxStrcpy(dbInf.serverName,copyDb->dbInf.serverName);
+    wxStrcpy(dbInf.databaseName,copyDb->dbInf.databaseName);
+    wxStrcpy(dbInf.dbmsName,copyDb->dbInf.dbmsName);
+    wxStrcpy(dbInf.dbmsVer,copyDb->dbInf.dbmsVer);
+    dbInf.maxConnections = copyDb->dbInf.maxConnections;
+    dbInf.maxStmts = copyDb->dbInf.maxStmts;
+    wxStrcpy(dbInf.driverName,copyDb->dbInf.driverName);
+    wxStrcpy(dbInf.odbcVer,copyDb->dbInf.odbcVer);
+    wxStrcpy(dbInf.drvMgrOdbcVer,copyDb->dbInf.drvMgrOdbcVer);
+    wxStrcpy(dbInf.driverVer,copyDb->dbInf.driverVer);
+    dbInf.apiConfLvl = copyDb->dbInf.apiConfLvl;
+    dbInf.cliConfLvl = copyDb->dbInf.cliConfLvl;
+    dbInf.sqlConfLvl = copyDb->dbInf.sqlConfLvl;
+    wxStrcpy(dbInf.outerJoins,copyDb->dbInf.outerJoins);
+    wxStrcpy(dbInf.procedureSupport,copyDb->dbInf.procedureSupport);
+    wxStrcpy(dbInf.accessibleTables,copyDb->dbInf.accessibleTables);
+    dbInf.cursorCommitBehavior = copyDb->dbInf.cursorCommitBehavior;
+    dbInf.cursorRollbackBehavior = copyDb->dbInf.cursorRollbackBehavior;
+    dbInf.supportNotNullClause = copyDb->dbInf.supportNotNullClause;
+    wxStrcpy(dbInf.supportIEF,copyDb->dbInf.supportIEF);
+    dbInf.txnIsolation = copyDb->dbInf.txnIsolation;
+    dbInf.txnIsolationOptions = copyDb->dbInf.txnIsolationOptions;
+    dbInf.fetchDirections = copyDb->dbInf.fetchDirections;
+    dbInf.lockTypes = copyDb->dbInf.lockTypes;
+    dbInf.posOperations = copyDb->dbInf.posOperations;
+    dbInf.posStmts = copyDb->dbInf.posStmts;
+    dbInf.scrollConcurrency = copyDb->dbInf.scrollConcurrency;
+    dbInf.scrollOptions = copyDb->dbInf.scrollOptions;
+    dbInf.staticSensitivity = copyDb->dbInf.staticSensitivity;
+    dbInf.txnCapable = copyDb->dbInf.txnCapable;
+    dbInf.loginTimeout = copyDb->dbInf.loginTimeout;
+
+    // VARCHAR = Variable length character string
+    typeInfVarchar.FsqlType = copyDb->typeInfVarchar.FsqlType;
+    wxStrcpy(typeInfVarchar.TypeName, copyDb->typeInfVarchar.TypeName);
+    typeInfVarchar.Precision        = copyDb->typeInfVarchar.Precision;
+    typeInfVarchar.CaseSensitive    = copyDb->typeInfVarchar.CaseSensitive;
+    typeInfVarchar.MaximumScale     = copyDb->typeInfVarchar.MaximumScale;
+
+    // Float
+    typeInfFloat.FsqlType = copyDb->typeInfFloat.FsqlType;
+    wxStrcpy(typeInfFloat.TypeName, copyDb->typeInfFloat.TypeName);
+    typeInfFloat.Precision        = copyDb->typeInfFloat.Precision;
+    typeInfFloat.CaseSensitive    = copyDb->typeInfFloat.CaseSensitive;
+    typeInfFloat.MaximumScale     = copyDb->typeInfFloat.MaximumScale;
+
+    // Integer
+    typeInfInteger.FsqlType = copyDb->typeInfInteger.FsqlType;
+    wxStrcpy(typeInfInteger.TypeName, copyDb->typeInfInteger.TypeName);
+    typeInfInteger.Precision        = copyDb->typeInfInteger.Precision;
+    typeInfInteger.CaseSensitive    = copyDb->typeInfInteger.CaseSensitive;
+    typeInfInteger.MaximumScale     = copyDb->typeInfInteger.MaximumScale;
+
+    // Date/Time
+    typeInfDate.FsqlType = copyDb->typeInfDate.FsqlType;
+    wxStrcpy(typeInfDate.TypeName, copyDb->typeInfDate.TypeName);
+    typeInfDate.Precision        = copyDb->typeInfDate.Precision;
+    typeInfDate.CaseSensitive    = copyDb->typeInfDate.CaseSensitive;
+    typeInfDate.MaximumScale     = copyDb->typeInfDate.MaximumScale;
+
+#ifdef DBDEBUG_CONSOLE
+    cout << "VARCHAR DATA TYPE: " << typeInfVarchar.TypeName << endl;
+    cout << "INTEGER DATA TYPE: " << typeInfInteger.TypeName << endl;
+    cout << "FLOAT   DATA TYPE: " << typeInfFloat.TypeName << endl;
+    cout << "DATE    DATA TYPE: " << typeInfDate.TypeName << endl;
+    cout << endl;
+#endif
+
+    // Completed Successfully
+    return(TRUE);
+} // wxDb::Open() 2
+
+
 /********** wxDb::setConnectionOptions() **********/
 bool wxDb::setConnectionOptions(void)
 /*
  * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
  */
 {
+    SWORD cb;
+
+    // I need to get the DBMS name here, because some of the connection options
+    // are database specific and need to call the Dbms() function.
+    if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
+        return(DispAllErrors(henv, hdbc));
+
     SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
     SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
+//     SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED);  // No dirty reads
+
+    // By default, MS Sql Server closes cursors on commit and rollback.  The following
+    // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
+    // after a transaction.  This is a driver specific option and is not part of the
+    // ODBC standard.  Note: this behavior is specific to the ODBC interface to SQL Server.
+    // The database settings don't have any effect one way or the other.
+    if (Dbms() == dbmsMS_SQL_SERVER)
+    {
+        const long SQL_PRESERVE_CURSORS = 1204L;
+        const long SQL_PC_ON = 1L;
+        SQLSetConnectOption(hdbc, SQL_PRESERVE_CURSORS, SQL_PC_ON);
+    }
 
     // Display the connection options to verify them
 #ifdef DBDEBUG_CONSOLE
@@ -641,7 +799,11 @@ bool wxDb::getDbInfo(void)
         return(DispAllErrors(henv, hdbc));
 
     if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
-        return(DispAllErrors(henv, hdbc));
+//        return(DispAllErrors(henv, hdbc));
+    {
+        // Not all drivers support this call - Nick Gorham(unixODBC)
+        dbInf.cliConfLvl = 0;
+    }
 
     if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc));
@@ -651,10 +813,10 @@ bool wxDb::getDbInfo(void)
 
     if (SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc));
-#if EXPERIMENTAL_WXDB_FUNCTIONS  // will be added in 2.4
+
     if (SQLGetInfo(hdbc, SQL_ACCESSIBLE_TABLES, (UCHAR*) dbInf.accessibleTables, 2, &cb) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc));
-#endif
+
     if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc));
 
@@ -735,9 +897,7 @@ bool wxDb::getDbInfo(void)
     cout << "Max. Connections: "       << dbInf.maxConnections   << endl;
     cout << "Outer Joins: "            << dbInf.outerJoins       << endl;
     cout << "Support for Procedures: " << dbInf.procedureSupport << endl;
-#if EXPERIMENTAL_WXDB_FUNCTIONS  // will be added in 2.4
     cout << "All tables accessible : " << dbInf.accessibleTables << endl;
-#endif
     cout << "Cursor COMMIT Behavior: ";
     switch(dbInf.cursorCommitBehavior)
     {
@@ -914,7 +1074,7 @@ bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
  */
     RETCODE retcode;
     SDWORD  cbRet;
-   
+
     // Get information about the data type specified
     if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc, hstmt));
@@ -932,7 +1092,6 @@ bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
     // Obtain columns from the record
     if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) structSQLTypeInfo.TypeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc, hstmt));
-   
 
     // BJO 20000503: no more needed with new GetColumns...
 #if  OLD_GETCOLUMNS
@@ -946,10 +1105,10 @@ bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
         if (!wxStrcmp(structSQLTypeInfo.TypeName, "middleint")) wxStrcpy(structSQLTypeInfo.TypeName, "mediumint");
         if (!wxStrcmp(structSQLTypeInfo.TypeName, "varchar")) wxStrcpy(structSQLTypeInfo.TypeName, "char");
     }
-    
-    // BJO 20000427 : OpenLink driver 
+
+    // BJO 20000427 : OpenLink driver
     if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
-        !wxStrncmp(dbInf.driverName, "OLOD", 4))    
+        !wxStrncmp(dbInf.driverName, "OLOD", 4))
     {
         if (!wxStrcmp(structSQLTypeInfo.TypeName, "double precision"))
             wxStrcpy(structSQLTypeInfo.TypeName, "real");
@@ -1030,6 +1189,9 @@ void wxDb::Close(void)
     for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
         wxStrcpy(DBerrorList[i],errorList[i]);
 
+    dbmsType   = dbmsUNIDENTIFIED;
+    dbIsOpen = FALSE;
+
 } // wxDb::Close()
 
 
@@ -1064,8 +1226,21 @@ bool wxDb::RollbackTrans(void)
 
 /********** wxDb::DispAllErrors() **********/
 bool wxDb::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
+/*
+ * This function is called internally whenever an error condition prevents the user's
+ * request from being executed.  This function will query the datasource as to the
+ * actual error(s) that just occured on the previous request of the datasource.
+ *
+ * The function will retrieve each error condition from the datasource and
+ * sprintf the codes/text values into a string which it then logs via logError().
+ * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
+ * window and program execution will be paused until the user presses a key.
+ *
+ * This function always returns a FALSE, so that functions which call this function
+ * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
+ * of the users request, so that the calling code can then process the error msg log
+ */
 {
-//    char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
     wxString odbcErrMsg;
 
     while (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
@@ -1714,7 +1889,7 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char *userID)
     if (Dbms() == dbmsDBASE)
         UserID = "";
 
-    // Oracle user names may only be in uppercase, so force
+    // Oracle and Interbase user names may only be in uppercase, so force
     // the name to uppercase
     if (Dbms() == dbmsORACLE)
         UserID = UserID.Upper();
@@ -1743,18 +1918,20 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char *userID)
         for (tbl = 0; tableName[tbl]; tbl++)
         {
             TableName = tableName[tbl];
-            // Oracle table names are uppercase only, so force
+            // Oracle and Interbase table names are uppercase only, so force
             // the name to uppercase just in case programmer forgot to do this
-            if (Dbms() == dbmsORACLE)
+            if ((Dbms() == dbmsORACLE) ||
+                (Dbms() == dbmsINTERBASE))
                 TableName = TableName.Upper();
 
             SQLFreeStmt(hstmt, SQL_CLOSE);
 
-            // MySQL and Access cannot accept a user name when looking up column names, so we
+            // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
             // use the call below that leaves out the user name
             if (wxStrcmp(UserID.c_str(),wxT("")) &&
                  Dbms() != dbmsMY_SQL &&
-                 Dbms() != dbmsACCESS)
+                 Dbms() != dbmsACCESS &&
+                 Dbms() != dbmsMS_SQL_SERVER)
             {
                 retcode = SQLColumns(hstmt,
                                      NULL, 0,                                // All qualifiers
@@ -1800,17 +1977,20 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char *userID)
                         GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0,                        &cb);
                         GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable,     0,                        &cb);
                         GetData(12, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].remarks,      254+1,                    &cb);
-        
+
                         // Determine the wxDb data type that is used to represent the native data type of this data source
                         colInf[colNo].dbDataType = 0;
                         if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
                         {
+#ifdef _IODBC_
+                            // IODBC does not return a correct columnSize, so we set
+                            // columnSize = bufferLength if no column size was returned
+                            // IODBC returns the columnSize in bufferLength.. (bug)
                             if (colInf[colNo].columnSize < 1)
                             {
-                               // IODBC does not return a correct columnSize, so we set
-                               // columnSize = bufferLength if no column size was returned
                                colInf[colNo].columnSize = colInf[colNo].bufferLength;
                             }
+#endif
                             colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
                         }
                         else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
@@ -1909,18 +2089,20 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
         }
 
         TableName = tableName;
-        // Oracle table names are uppercase only, so force
+        // Oracle and Interbase table names are uppercase only, so force
         // the name to uppercase just in case programmer forgot to do this
-        if (Dbms() == dbmsORACLE)
+        if ((Dbms() == dbmsORACLE) ||
+            (Dbms() == dbmsINTERBASE))
             TableName = TableName.Upper();
 
         SQLFreeStmt(hstmt, SQL_CLOSE);
 
-        // MySQL and Access cannot accept a user name when looking up column names, so we
+        // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
         // use the call below that leaves out the user name
         if (wxStrcmp(UserID.c_str(),wxT("")) &&
              Dbms() != dbmsMY_SQL &&
-             Dbms() != dbmsACCESS)
+             Dbms() != dbmsACCESS &&
+             Dbms() != dbmsMS_SQL_SERVER)
         {
             retcode = SQLColumns(hstmt,
                                  NULL, 0,                                // All qualifiers
@@ -1980,19 +2162,23 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     {
                         wxString s =  colInf[colNo].typeName;
                         s = s.MakeLower();
-                        wxStrcmp(colInf[colNo].typeName, s.c_str());        
+                        wxStrcmp(colInf[colNo].typeName, s.c_str());
                     }
-                       
+
                     // Determine the wxDb data type that is used to represent the native data type of this data source
                     colInf[colNo].dbDataType = 0;
                     if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
                     {
+#ifdef _IODBC_
+                        // IODBC does not return a correct columnSize, so we set
+                        // columnSize = bufferLength if no column size was returned
+                        // IODBC returns the columnSize in bufferLength.. (bug)
                         if (colInf[colNo].columnSize < 1)
                         {
-                            // IODBC does not return a correct columnSize, so we set
-                            // columnSize = bufferLength if no column size was returned
-                            colInf[colNo].columnSize = colInf[colNo].bufferLength;
+                           colInf[colNo].columnSize = colInf[colNo].bufferLength;
                         }
+#endif
+
                         colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
                     }
                     else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
@@ -2000,8 +2186,8 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     else if (!wxStricmp(typeInfFloat.TypeName,colInf[colNo].typeName))
                         colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
                     else if (!wxStricmp(typeInfDate.TypeName,colInf[colNo].typeName))
-                        colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;     
-            
+                        colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
+
                     colNo++;
                 }
             }
@@ -2036,18 +2222,18 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
 /*
     BJO 20000503
     These are tentative new GetColumns members which should be more database
-    independant and which always returns the columns in the order they were 
+    independant and which always returns the columns in the order they were
     created.
 
-    - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const 
+    - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const
       char* userID)) calls the second implementation for each separate table
-      before merging the results. This makes the code easier to maintain as 
+      before merging the results. This makes the code easier to maintain as
       only one member (the second) makes the real work
-    - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const 
-      char *userID) is a little bit improved 
-    - It doesn't anymore rely on the type-name to find out which database-type 
+    - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const
+      char *userID) is a little bit improved
+    - It doesn't anymore rely on the type-name to find out which database-type
       each column has
-    - It ends by sorting the columns, so that they are returned in the same 
+    - It ends by sorting the columns, so that they are returned in the same
       order they were created
 */
 
@@ -2068,42 +2254,42 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)
 
     // How many tables ?
     int tbl;
-    for (tbl = 0 ; tableName[tbl]; tbl++); 
-    
+    for (tbl = 0 ; tableName[tbl]; tbl++);
+
     // Create a table to maintain the columns for each separate table
     _TableColumns *TableColumns = new _TableColumns[tbl];
-    
+
     // Fill the table
     for (i = 0 ; i < tbl ; i++)
-        
+
     {
         TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
-        if (TableColumns[i].colInf == NULL) 
+        if (TableColumns[i].colInf == NULL)
             return NULL;
         noCols += TableColumns[i].noCols;
     }
-    
+
     // Now merge all the separate table infos
     wxDbColInf *colInf = new wxDbColInf[noCols+1];
-    
+
     // Mark the end of the array
     wxStrcpy(colInf[noCols].tableName, wxT(""));
     wxStrcpy(colInf[noCols].colName, wxT(""));
     colInf[noCols].sqlDataType = 0;
-    
+
     // Merge ...
     int offset = 0;
-    
+
     for (i = 0 ; i < tbl ; i++)
     {
         for (j = 0 ; j < TableColumns[i].noCols ; j++)
         {
-            colInf[offset++] = TableColumns[i].colInf[j];        
+            colInf[offset++] = TableColumns[i].colInf[j];
         }
     }
-    
+
     delete [] TableColumns;
-    
+
     return colInf;
 }  // wxDb::GetColumns()  -- NEW
 
@@ -2126,13 +2312,13 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
     SWORD       noCols = 0;
     int         colNo  = 0;
     wxDbColInf *colInf = 0;
-    
+
     RETCODE  retcode;
     SDWORD   cb;
-    
+
     wxString UserID;
     wxString TableName;
-    
+
     if (userID)
     {
         if (!wxStrlen(userID))
@@ -2142,16 +2328,16 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
     }
     else
         UserID = "";
-    
+
     // dBase does not use user names, and some drivers fail if you try to pass one
     if (Dbms() == dbmsDBASE)
         UserID = "";
-    
+
     // Oracle user names may only be in uppercase, so force
     // the name to uppercase
     if (Dbms() == dbmsORACLE)
         UserID = UserID.Upper();
-    
+
     // Pass 1 - Determine how many columns there are.
     // Pass 2 - Allocate the wxDbColInf array and fill in
     //                the array with the column information.
@@ -2171,20 +2357,22 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
             wxStrcpy(colInf[noCols].colName, wxT(""));
             colInf[noCols].sqlDataType = 0;
         }
-        
+
         TableName = tableName;
-        // Oracle table names are uppercase only, so force
+        // Oracle and Interbase table names are uppercase only, so force
         // the name to uppercase just in case programmer forgot to do this
-        if (Dbms() == dbmsORACLE)
+        if ((Dbms() == dbmsORACLE) ||
+            (Dbms() == dbmsINTERBASE))
             TableName = TableName.Upper();
-        
+
         SQLFreeStmt(hstmt, SQL_CLOSE);
-        
-        // MySQL and Access cannot accept a user name when looking up column names, so we
+
+        // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
         // use the call below that leaves out the user name
         if (wxStrcmp(UserID.c_str(),wxT("")) &&
             Dbms() != dbmsMY_SQL &&
-            Dbms() != dbmsACCESS)
+            Dbms() != dbmsACCESS &&
+            Dbms() != dbmsMS_SQL_SERVER)
         {
             retcode = SQLColumns(hstmt,
                                  NULL, 0,                              // All qualifiers
@@ -2210,7 +2398,7 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                 *numCols = 0;
             return(0);
         }
-        
+
         while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
         {
             if (pass == 1)  // First pass, just add up the number of columns
@@ -2226,7 +2414,7 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     GetData( 4, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].colName,      DB_MAX_COLUMN_NAME_LEN+1, &cb);
                     GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType,  0,                        &cb);
                     GetData( 6, SQL_C_CHAR,   (UCHAR*)  colInf[colNo].typeName,     128+1,                    &cb);
-                    GetData( 7, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].columnSize,   0,                        &cb);                
+                    GetData( 7, SQL_C_SLONG,  (UCHAR*) &colInf[colNo].columnSize,   0,                        &cb);
                     GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0,                        &cb);
                     GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0,                        &cb);
                     GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0,                        &cb);
@@ -2237,12 +2425,17 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     colInf[colNo].PkTableName[0] = 0;  // Tablenames where Primary Key is used as a Foreign Key
                     colInf[colNo].FkCol = 0;           // Foreign key column   0=No; 1= First Key, 2 = Second Key etc.
                     colInf[colNo].FkTableName[0] = 0;  // Foreign key table name
-                    
-#ifdef _IODBC_          
+
+#ifdef _IODBC_
+                    // IODBC does not return a correct columnSize, so we set
+                    // columnSize = bufferLength if no column size was returned
                     // IODBC returns the columnSize in bufferLength.. (bug)
-                    colInf[colNo].columnSize = colInf[colNo].bufferLength;          
+                    if (colInf[colNo].columnSize < 1)
+                    {
+                       colInf[colNo].columnSize = colInf[colNo].bufferLength;
+                    }
 #endif
-                    
+
                     // Determine the wxDb data type that is used to represent the native data type of this data source
                     colInf[colNo].dbDataType = 0;
                     // Get the intern datatype
@@ -2250,30 +2443,30 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     {
                         case SQL_VARCHAR:
                         case SQL_CHAR:
-                            colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;             
+                            colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
                         break;
-                        
+
                         case SQL_TINYINT:
                         case SQL_SMALLINT:
                         case SQL_INTEGER:
-                            colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;     
+                            colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
                             break;
                         case SQL_DOUBLE:
                         case SQL_DECIMAL:
                         case SQL_NUMERIC:
                         case SQL_FLOAT:
                         case SQL_REAL:
-                            colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;   
+                            colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
                             break;
                         case SQL_DATE:
-                            colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;   
+                            colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
                             break;
 #ifdef __WXDEBUG__
                         default:
                             wxString errMsg;
                             errMsg.sprintf("SQL Data type %d currently not supported by wxWindows", colInf[colNo].sqlDataType);
                             wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
-#endif              
+#endif
                     }
                     colNo++;
                 }
@@ -2290,57 +2483,57 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
             return(0);
         }
     }
-    
+
     SQLFreeStmt(hstmt, SQL_CLOSE);
-    
+
     // Store Primary and Foreign Keys
     GetKeyFields(tableName,colInf,noCols);
 
     ///////////////////////////////////////////////////////////////////////////
     // Now sort the the columns in order to make them appear in the right order
     ///////////////////////////////////////////////////////////////////////////
-    
+
     // Build a generic SELECT statement which returns 0 rows
     wxString Stmt;
-    
+
     Stmt.sprintf("select * from %s where 0=1", tableName);
-    
-    // Execute query  
+
+    // Execute query
     if (SQLExecDirect(hstmt, (UCHAR FAR *) Stmt.c_str(), SQL_NTS) != SQL_SUCCESS)
     {
         DispAllErrors(henv, hdbc, hstmt);
         return NULL;
-    } 
-    
+    }
+
     // Get the number of result columns
     if (SQLNumResultCols (hstmt, &noCols) != SQL_SUCCESS)
     {
         DispAllErrors(henv, hdbc, hstmt);
         return NULL;
     }
-    
+
     if (noCols == 0) // Probably a bogus table name
         return NULL;
-    
-    //  Get the name 
+
+    //  Get the name
     int i;
     short colNum;
     UCHAR name[100];
     SWORD Sword;
     SDWORD Sdword;
     for (colNum = 0; colNum < noCols; colNum++)
-    { 
-        if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME, 
-            name, sizeof(name), 
+    {
+        if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
+            name, sizeof(name),
             &Sword, &Sdword) != SQL_SUCCESS)
         {
             DispAllErrors(henv, hdbc, hstmt);
             return NULL;
-        } 
-        
+        }
+
         wxString Name1 = name;
         Name1 = Name1.Upper();
-        
+
         // Where is this name in the array ?
         for (i = colNum ; i < noCols ; i++)
         {
@@ -2355,9 +2548,9 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
                     colInf[i] = tmpColInf;
                 }
                 break;
-            } 
-        }     
-    } 
+            }
+        }
+    }
     SQLFreeStmt(hstmt, SQL_CLOSE);
 
     ///////////////////////////////////////////////////////////////////////////
@@ -2367,7 +2560,7 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
     if (numCols)
         *numCols = noCols;
     return colInf;
-    
+
 }  // wxDb::GetColumns()
 
 
@@ -2421,18 +2614,20 @@ int wxDb::GetColumnCount(char *tableName, const char *userID)
         // Loop through each table name
         {
             TableName = tableName;
-            // Oracle table names are uppercase only, so force
+            // Oracle and Interbase table names are uppercase only, so force
             // the name to uppercase just in case programmer forgot to do this
-            if (Dbms() == dbmsORACLE)
+            if ((Dbms() == dbmsORACLE) ||
+                (Dbms() == dbmsINTERBASE))
                 TableName = TableName.Upper();
 
             SQLFreeStmt(hstmt, SQL_CLOSE);
 
-            // MySQL and Access cannot accept a user name when looking up column names, so we
+            // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
             // use the call below that leaves out the user name
             if (wxStrcmp(UserID.c_str(),wxT("")) &&
                  Dbms() != dbmsMY_SQL &&
-                 Dbms() != dbmsACCESS)
+                 Dbms() != dbmsACCESS &&
+                 Dbms() != dbmsMS_SQL_SERVER)
             {
                 retcode = SQLColumns(hstmt,
                                      NULL, 0,                                // All qualifiers
@@ -2566,7 +2761,7 @@ wxDbInf *wxDb::GetCatalog(char *userID)
             SQLFreeStmt(hstmt, SQL_CLOSE);
             return pDbInf;
         }
-    
+
         while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)   // Table Information
         {
             if (pass == 1)  // First pass, just count the Tables
@@ -2584,7 +2779,7 @@ wxDbInf *wxDb::GetCatalog(char *userID)
                 {  // no, then create the Array
                     pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
                     noTab = 0;
-                } // if (pDbInf->pTableInf == NULL)   // Has the Table Array been created   
+                } // if (pDbInf->pTableInf == NULL)   // Has the Table Array been created
 
                 GetData( 3, SQL_C_CHAR,   (UCHAR*)  (pDbInf->pTableInf+noTab)->tableName,    DB_MAX_TABLE_NAME_LEN+1, &cb);
                 GetData( 4, SQL_C_CHAR,   (UCHAR*)  (pDbInf->pTableInf+noTab)->tableType,    30+1,                    &cb);
@@ -2757,22 +2952,22 @@ bool wxDb::TableExists(const char *tableName, const char *userID, const char *ta
 {
     wxString UserID;
     wxString TableName;
-    
+
     assert(tableName && wxStrlen(tableName));
-    
-    if (Dbms() == dbmsDBASE) 
+
+    if (Dbms() == dbmsDBASE)
     {
         wxString dbName;
         if (tablePath && wxStrlen(tablePath))
-            dbName.sprintf("%s\\%s.dbf",tablePath,tableName);      
+            dbName.sprintf("%s\\%s.dbf",tablePath,tableName);
         else
             dbName.sprintf("%s.dbf",tableName);
-        
+
         bool exists;
-        exists = wxFileExists(dbName.c_str());    
+        exists = wxFileExists(dbName.c_str());
         return exists;
     }
-    
+
     if (userID)
     {
         if (!wxStrlen(userID))
@@ -2782,30 +2977,32 @@ bool wxDb::TableExists(const char *tableName, const char *userID, const char *ta
     }
     else
         UserID = "";
-    
+
     // Oracle user names may only be in uppercase, so force
     // the name to uppercase
     if (Dbms() == dbmsORACLE)
         UserID = UserID.Upper();
-    
+
     TableName = tableName;
-    // Oracle table names are uppercase only, so force
+    // Oracle and Interbase table names are uppercase only, so force
     // the name to uppercase just in case programmer forgot to do this
-    if (Dbms() == dbmsORACLE)
+    if ((Dbms() == dbmsORACLE) ||
+        (Dbms() == dbmsINTERBASE))
         TableName = TableName.Upper();
-    
+
     SQLFreeStmt(hstmt, SQL_CLOSE);
     RETCODE retcode;
-    
-    // MySQL and Access cannot accept a user name when looking up table names, so we
-    // use the call below that leaves out the user name
+
+    // Some databases cannot accept a user name when looking up table names,
+    // so we use the call below that leaves out the user name
     if (wxStrcmp(UserID,"") &&
         Dbms() != dbmsMY_SQL &&
-        Dbms() != dbmsACCESS)
+        Dbms() != dbmsACCESS &&
+        Dbms() != dbmsMS_SQL_SERVER)
     {
         retcode = SQLTables(hstmt,
                             NULL, 0,                                  // All qualifiers
-                            (UCHAR *) UserID.c_str(), SQL_NTS,        // All owners
+                            (UCHAR *) UserID.c_str(), SQL_NTS,        // Only tables owned by this user
                             (UCHAR FAR *)TableName.c_str(), SQL_NTS,
                             NULL, 0);                                 // All table types
     }
@@ -2819,40 +3016,40 @@ bool wxDb::TableExists(const char *tableName, const char *userID, const char *ta
     }
     if (retcode != SQL_SUCCESS)
         return(DispAllErrors(henv, hdbc, hstmt));
-    
+
     retcode = SQLFetch(hstmt);
-    if (retcode  != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
+    if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
     {
         SQLFreeStmt(hstmt, SQL_CLOSE);
         return(DispAllErrors(henv, hdbc, hstmt));
     }
-    
+
     SQLFreeStmt(hstmt, SQL_CLOSE);
 
     return(TRUE);
-    
+
 }  // wxDb::TableExists()
 
 
-#if EXPERIMENTAL_WXDB_FUNCTIONS  // will be added in 2.4
-/********** wxDB::TablePrivileges() **********/
-bool wxDB::TablePrivileges(const char *tableName, const char* priv,
-                      const char *userID, const char *tablePath)
+/********** wxDb::TablePrivileges() **********/
+bool wxDb::TablePrivileges(const char *tableName, const char* priv, const char *userID,
+                            const char *schema, const char *tablePath)
 {
     wxDbTablePrivilegeInfo  result;
     SDWORD  cbRetVal;
     RETCODE retcode;
-    
-    //We probably need to be able to dynamically set this based on 
+
+    //We probably need to be able to dynamically set this based on
     //the driver type, and state.
     char curRole[]="public";
-    
+
     //Prologue here similar to db::TableExists()
     wxString UserID;
     wxString TableName;
-    
+
+    assert(userID);
     assert(tableName && wxStrlen(tableName));
-    
+
     if (userID)
     {
         if (!wxStrlen(userID))
@@ -2862,68 +3059,100 @@ bool wxDB::TablePrivileges(const char *tableName, const char* priv,
     }
     else
         UserID = "";
-    
-    // Oracle user names may only be in uppercase, so force 
+
+    // Oracle user names may only be in uppercase, so force
     // the name to uppercase
     if (Dbms() == dbmsORACLE)
         UserID = UserID.Upper();
-    
+
     TableName = tableName;
-    // Oracle table names are uppercase only, so force 
+    // Oracle and Interbase table names are uppercase only, so force
     // the name to uppercase just in case programmer forgot to do this
-    if (Dbms() == dbmsORACLE)
+    if ((Dbms() == dbmsORACLE) ||
+        (Dbms() == dbmsINTERBASE))
         TableName = TableName.Upper();
-    
+
     SQLFreeStmt(hstmt, SQL_CLOSE);
-    
-    retcode = SQLTablePrivileges(hstmt,
-                                 NULL, 0,                                    // All qualifiers
-                                 NULL, 0,                                    // All owners
-                                 (UCHAR FAR *)TableName.GetData(), SQL_NTS);  
-    
-#ifdef DBDEBUG_CONSOLE 
+
+    if (!schema)
+    {
+        retcode = SQLTablePrivileges(hstmt,
+                                     NULL, 0,                                    // Catalog
+                                     NULL, 0,                                    // Schema
+                                     (UCHAR FAR *)TableName.c_str(), SQL_NTS);
+    }
+    else
+    {
+        retcode = SQLTablePrivileges(hstmt,
+                                     NULL, 0,                                    // Catalog
+                                     (UCHAR FAR *)schema, SQL_NTS,               // Schema
+                                     (UCHAR FAR *)TableName.c_str(), SQL_NTS);
+    }
+
+#ifdef DBDEBUG_CONSOLE
     fprintf(stderr ,"SQLTablePrivileges() returned %i \n",retcode);
 #endif
-    retcode = SQLBindCol (hstmt, 1, SQL_C_CHAR , &result.tableQual, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 2, SQL_C_CHAR , &result.tableOwner, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 3, SQL_C_CHAR , &result.tableName, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 4, SQL_C_CHAR , &result.grantor, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 5, SQL_C_CHAR , &result.grantee, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 6, SQL_C_CHAR , &result.privilege, 128, &cbRetVal);
-    
-    retcode = SQLBindCol (hstmt, 7, SQL_C_CHAR , &result.grantable, 3, &cbRetVal);
-    
+
+    if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
+        return(DispAllErrors(henv, hdbc, hstmt));
+
     retcode = SQLFetch(hstmt);
-    while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
+    while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
     {
+        if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) result.tableQual, sizeof(result.tableQual), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 2, SQL_C_CHAR, (UCHAR*) result.tableOwner, sizeof(result.tableOwner), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 3, SQL_C_CHAR, (UCHAR*) result.tableName, sizeof(result.tableName), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 4, SQL_C_CHAR, (UCHAR*) result.grantor, sizeof(result.grantor), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 5, SQL_C_CHAR, (UCHAR*) result.grantee, sizeof(result.grantee), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 6, SQL_C_CHAR, (UCHAR*) result.privilege, sizeof(result.privilege), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
+        if (SQLGetData(hstmt, 7, SQL_C_CHAR, (UCHAR*) result.grantable, sizeof(result.grantable), &cbRetVal) != SQL_SUCCESS)
+            return(DispAllErrors(henv, hdbc, hstmt));
+
 #ifdef DBDEBUG_CONSOLE
-        fprintf(stderr,"Scanning %s privilege  on table %s.%s  granted by %s to %s\n",
-                result.privilege,result.tabowner,result.tabname,
+        fprintf(stderr,"Scanning %s privilege on table %s.%s granted by %s to %s\n",
+                result.privilege,result.tableOwner,result.tableName,
                 result.grantor, result.grantee);
-#endif 
-        if (UserID.IsSameAs(result.tableOwner,false) )
+#endif
+
+        if (UserID.IsSameAs(result.tableOwner,FALSE))
+        {
+            SQLFreeStmt(hstmt, SQL_CLOSE);
             return TRUE;
-        
-        if (UserID.IsSameAs(result.grantee,false) &&
-            !strcmp(result.privilege,priv))
+        }
+
+        if (UserID.IsSameAs(result.grantee,FALSE) &&
+            !wxStrcmp(result.privilege,priv))
+        {
+            SQLFreeStmt(hstmt, SQL_CLOSE);
             return TRUE;
-        
-        if (!strcmp(result.grantee,curRole) &&
-            !strcmp(result.privilege,priv))
+        }
+
+        if (!wxStrcmp(result.grantee,curRole) &&
+            !wxStrcmp(result.privilege,priv))
+        {
+            SQLFreeStmt(hstmt, SQL_CLOSE);
             return TRUE;
-        
+        }
+
         retcode = SQLFetch(hstmt);
-    } 
-    
+    }
+
+    SQLFreeStmt(hstmt, SQL_CLOSE);
     return FALSE;
 
-}  // wxDB::TablePrivileges
-#endif
+}  // wxDb::TablePrivileges
 
 
 /********** wxDb::SetSqlLogging() **********/
@@ -3023,57 +3252,76 @@ wxDBMS wxDb::Dbms(void)
  *        - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
  *        - Does not support sub-queries in SQL statements
  *
+ * DB2
+ *        - Primary keys must be declared as NOT NULL
+ *
  */
 {
+    // Should only need to do this once for each new database connection
+    // so return the value we already determined it to be to save time
+    // and lots of string comparisons
+    if (dbmsType != dbmsUNIDENTIFIED)
+        return(dbmsType);
+
     wxChar baseName[25+1];
     wxStrncpy(baseName,dbInf.dbmsName,25);
     baseName[25] = 0;
 
+    // RGG 20001025 : add support for Interbase
+    // GT : Integrated to base classes on 20001121
+    if (!wxStricmp(dbInf.dbmsName,"Interbase"))
+        return((wxDBMS)(dbmsType = dbmsINTERBASE));
+
     // BJO 20000428 : add support for Virtuoso
     if (!wxStricmp(dbInf.dbmsName,"OpenLink Virtuoso VDBMS"))
-      return(dbmsVIRTUOSO);
-    
+      return((wxDBMS)(dbmsType = dbmsVIRTUOSO));
 
     if (!wxStricmp(dbInf.dbmsName,"Adaptive Server Anywhere"))
-        return(dbmsSYBASE_ASA);
-    
+        return((wxDBMS)(dbmsType = dbmsSYBASE_ASA));
+
     // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
     // connected through an OpenLink driver.
-    // Is it also returned by Sybase Adapatitve server?    
-    // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix 
-    if (!wxStricmp(dbInf.dbmsName,"SQL Server"))       
+    // Is it also returned by Sybase Adapatitve server?
+    // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
+    if (!wxStricmp(dbInf.dbmsName,"SQL Server"))
     {
       if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
           !wxStrncmp(dbInf.driverName, "OLOD", 4))
-            return dbmsMS_SQL_SERVER; else return dbmsSYBASE_ASE;
+                       return ((wxDBMS)(dbmsMS_SQL_SERVER));
+               else
+                       return ((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
     }
 
     if (!wxStricmp(dbInf.dbmsName,"Microsoft SQL Server"))
-        return(dbmsMS_SQL_SERVER);
+        return((wxDBMS)(dbmsType = dbmsMS_SQL_SERVER));
     if (!wxStricmp(dbInf.dbmsName,"MySQL"))
-        return(dbmsMY_SQL);
+        return((wxDBMS)(dbmsType = dbmsMY_SQL));
     if (!wxStricmp(dbInf.dbmsName,"PostgreSQL"))  // v6.5.0
-        return(dbmsPOSTGRES);
+        return((wxDBMS)(dbmsType = dbmsPOSTGRES));
 
     baseName[8] = 0;
     if (!wxStricmp(baseName,"Informix"))
-        return(dbmsINFORMIX);
+        return((wxDBMS)(dbmsType = dbmsINFORMIX));
 
     baseName[6] = 0;
     if (!wxStricmp(baseName,"Oracle"))
-        return(dbmsORACLE);
+        return((wxDBMS)(dbmsType = dbmsORACLE));
     if (!wxStricmp(dbInf.dbmsName,"ACCESS"))
-        return(dbmsACCESS);
+        return((wxDBMS)(dbmsType = dbmsACCESS));
     if (!wxStricmp(dbInf.dbmsName,"MySQL"))
-        return(dbmsMY_SQL);
-    if (!wxStricmp(baseName,"Sybase"))             
-      return(dbmsSYBASE_ASE);
+        return((wxDBMS)(dbmsType = dbmsMY_SQL));
+    if (!wxStricmp(baseName,"Sybase"))
+      return((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
 
     baseName[5] = 0;
     if (!wxStricmp(baseName,"DBASE"))
-        return(dbmsDBASE);
+        return((wxDBMS)(dbmsType = dbmsDBASE));
+
+    baseName[3] = 0;
+    if (!wxStricmp(baseName,"DB2"))
+        return((wxDBMS)(dbmsType = dbmsDBASE));
 
-    return(dbmsUNIDENTIFIED);
+    return((wxDBMS)(dbmsType = dbmsUNIDENTIFIED));
 
 }  // wxDb::Dbms()
 
@@ -3083,6 +3331,13 @@ wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCurso
 {
     wxDbList *pList;
 
+    // Used to keep a pointer to a DB connection that matches the requested
+    // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
+    // data types can be copied from it (using the wxDb::Open(wxDb *) function)
+    // rather than having to re-query the datasource to get all the values
+    // using the wxDb::Open(Dsn,Uid,AuthStr) function
+    wxDb *matchingDbConnection = NULL;
+
     // Scan the linked list searching for an available database connection
     // that's already been opened but is currently not in use.
     for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
@@ -3096,6 +3351,11 @@ wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCurso
             pList->Free = FALSE;
             return(pList->PtrDb);
         }
+
+        if (!wxStrcmp(pDbConfig->Dsn, pList->Dsn) &&
+            !wxStrcmp(pDbConfig->Uid, pList->Uid) &&
+            !wxStrcmp(pDbConfig->AuthStr, pList->AuthStr))
+            matchingDbConnection = pList->PtrDb;
     }
 
     // No available connections.  A new connection must be made and
@@ -3120,10 +3380,20 @@ wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCurso
     pList->PtrNext = 0;
     pList->Free = FALSE;
     wxStrcpy(pList->Dsn, pDbConfig->Dsn);
+    wxStrcpy(pList->Uid, pDbConfig->Uid);
+    wxStrcpy(pList->AuthStr, pDbConfig->AuthStr);
+
     pList->PtrDb = new wxDb(pDbConfig->Henv,FwdOnlyCursors);
 
+    bool opened = FALSE;
+
+    if (!matchingDbConnection)
+        opened = pList->PtrDb->Open(pDbConfig->Dsn, pDbConfig->Uid, pDbConfig->AuthStr);
+    else
+        opened = pList->PtrDb->Open(matchingDbConnection);
+
     // Connect to the datasource
-    if (pList->PtrDb->Open(pDbConfig->Dsn, pDbConfig->Uid, pDbConfig->AuthStr))
+    if (opened)
     {
         pList->PtrDb->SetSqlLogging(SQLLOGstate,SQLLOGfn,TRUE);
         return(pList->PtrDb);
@@ -3183,7 +3453,7 @@ void WXDLLEXPORT wxDbCloseConnections(void)
 }  // wxDbCloseConnections()
 
 
-/********** wxDbNumberConnectionsInUse() **********/
+/********** wxDbConnectionsInUse() **********/
 int WXDLLEXPORT wxDbConnectionsInUse(void)
 {
     wxDbList *pList;
@@ -3222,18 +3492,21 @@ bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
 }  // wxDbSqlLog()
 
 
-#if EXPERIMENTAL_WXDB_FUNCTIONS  // will be added in 2.4
+#if 0
 /********** wxDbCreateDataSource() **********/
 int wxDbCreateDataSource(const char *driverName, const char *dsn, const char *description,
                          bool sysDSN, const char *defDir, wxWindow *parent)
 /*
  * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
- * Very rudimentary creation of an ODBC data source.  
+ * Very rudimentary creation of an ODBC data source.
+ *
+ * ODBC driver must be ODBC 3.0 compliant to use this function
  */
 {
     int result = FALSE;
 
-#ifdef __WXMSW__
+//!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
+#ifdef __VISUALC__
     int       dsnLocation;
     wxString  setupStr;
 
@@ -3243,7 +3516,7 @@ int wxDbCreateDataSource(const char *driverName, const char *dsn, const char *de
         dsnLocation = ODBC_ADD_DSN;
 
     // NOTE: The decimal 2 is an invalid character in all keyword pairs
-    // so that is why I used it, as wxString does not deal well with 
+    // so that is why I used it, as wxString does not deal well with
     // embedded nulls in strings
     setupStr.sprintf("DSN=%s%cDescription=%s%cDefaultDir=%s%c",dsn,2,description,2,defDir,2);
 
@@ -3261,40 +3534,40 @@ int wxDbCreateDataSource(const char *driverName, const char *dsn, const char *de
     result = SQLConfigDataSource((HWND)parent->GetHWND(), dsnLocation,
                                  driverName, setupStr.c_str());
 
-    if (!result)
+    if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
     {
         // check for errors caused by ConfigDSN based functions
         DWORD retcode = 0;
         WORD cb;
-        wxChar errMsg[500+1];
+        wxChar errMsg[SQL_MAX_MESSAGE_LENGTH];
         errMsg[0] = '\0';
 
-        SQLInstallerError(1,&retcode,errMsg,500,&cb);
+        // This function is only supported in ODBC drivers v3.0 compliant and above
+        SQLInstallerError(1,&retcode,errMsg,SQL_MAX_MESSAGE_LENGTH-1,&cb);
         if (retcode)
         {
-            if (!silent)
-            {
 #ifdef DBDEBUG_CONSOLE
                // When run in console mode, use standard out to display errors.
                cout << errMsg << endl;
-               cout << "Press any key to continue..." << endl;
+               cout << wxT("Press any key to continue...") << endl;
                getchar();
 #endif  // DBDEBUG_CONSOLE
 
 #ifdef __WXDEBUG__
                wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
 #endif  // __WXDEBUG__
-            }
         }
     }
     else
        result = TRUE;
-
-#else  // using iODBC, so this function is not supported
+#else
+    // Using iODBC/unixODBC or some other compiler which does not support the APIs
+    // necessary to use this function, so this function is not supported
 #ifdef __WXDEBUG__
-    wxLogDebug("wxDbCreateDataSource() not available except under MSW","DEBUG MESSAGE");
+    wxLogDebug("wxDbCreateDataSource() not available except under VC++/MSW",wxT("ODBC DEBUG MESSAGE"));
 #endif
-#endif  // __WXMSW__
+    result = FALSE;
+#endif  // __VISUALC__
 
     return result;