1 /////////////////////////////////////////////////////////////////////////////// 
   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. 
   7 // Modified by: George Tasker 
   9 //                -Added support for SQL statement logging and database cataloging 
  11 //                -Added QUERY_ONLY mode support to reduce default number of cursors 
  12 //                -Added additional SQL logging code 
  13 //                -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections 
  14 //                -Set ODBC option to only read committed writes to the DB so all 
  15 //                   databases operate the same in that respect 
  18 // Copyright:   (c) 1996 Remstar International, Inc. 
  19 // Licence:     wxWindows licence, plus: 
  20 // Notice:      This class library and its intellectual design are free of charge for use, 
  21 //              modification, enhancement, debugging under the following conditions: 
  22 //              1) These classes may only be used as part of the implementation of a 
  23 //                 wxWindows-based application 
  24 //              2) All enhancements and bug fixes are to be submitted back to the wxWindows 
  25 //                 user groups free of all charges for use with the wxWindows library. 
  26 //              3) These classes may not be distributed as part of any other class library, 
  27 //                 DLL, text (written or electronic), other than a complete distribution of 
  28 //                 the wxWindows GUI development toolkit. 
  29 /////////////////////////////////////////////////////////////////////////////// 
  36 #include "wx/wxprec.h" 
  39 // Use this line for wxWindows v1.x 
  41 // Use this line for wxWindows v2.x 
  42 #include "wx/version.h" 
  44 #if wxMAJOR_VERSION == 2 
  46     #pragma implementation "db.h" 
  50 #ifdef DBDEBUG_CONSOLE 
  51     #include "wx/ioswrap.h" 
  58 #if wxMAJOR_VERSION == 2 
  60         #include "wx/string.h" 
  61         #include "wx/object.h" 
  64         #include "wx/msgdlg.h" 
  67     #include "wx/filefn.h" 
  68     #include "wx/wxchar.h" 
  72 #if wxMAJOR_VERSION == 1 
  73 #    if defined(wx_msw) || defined(wx_x) 
  91 #if   wxMAJOR_VERSION == 1 
  93 #elif wxMAJOR_VERSION == 2 
  97 WXDLLEXPORT_DATA(wxDbList
*) PtrBegDbList 
= 0; 
 100 char const *SQL_LOG_FILENAME         
= "sqllog.txt"; 
 101 char const *SQL_CATALOG_FILENAME     
= "catalog.txt"; 
 104     extern wxList TablesInUse
; 
 107 // SQL Log defaults to be used by GetDbConnection 
 108 wxDbSqlLogState SQLLOGstate 
= sqlLogOFF
; 
 110 //char SQLLOGfn[wxDB_PATH_MAX+1] = SQL_LOG_FILENAME; 
 111 wxChar 
*SQLLOGfn         
= (wxChar
*) SQL_LOG_FILENAME
; 
 113 // The wxDb::errorList is copied to this variable when the wxDb object 
 114 // is closed.  This way, the error list is still available after the 
 115 // database object is closed.  This is necessary if the database 
 116 // connection fails so the calling application can show the operator 
 117 // why the connection failed.  Note: as each wxDb object is closed, it 
 118 // will overwrite the errors of the previously destroyed wxDb object in 
 120 char DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
]; 
 123 /********** wxDbColFor Constructor **********/ 
 124 wxDbColFor::wxDbColFor() 
 126     i_Nation 
= 0;                // 0=EU, 1=UK, 2=International, 3=US 
 135     Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0);      // the Function that does the work 
 136 }  // wxDbColFor::wxDbColFor() 
 139 wxDbColFor::~wxDbColFor() 
 141 }  // wxDbColFor::~wxDbColFor() 
 144 int wxDbColFor::Format(int Nation
,int dbDataType
,SWORD sqlDataType
,short columnSize
,short decimalDigits
) 
 146     // ---------------------------------------------------------------------------------------- 
 147     // -- 19991224 : mj10777@gmx.net : Create 
 148     // There is still a lot of work to do here, but it is a start 
 149     // It handles all the basic data-types that I have run into up to now 
 150     // The main work will have be with Dates and float Formatting (US 1,000.00 ; EU 1.000,00) 
 151     // There are wxWindow plans for locale support and the new wxDateTime. 
 152     // - if they define some constants (wxEUROPEAN) that can be gloably used, 
 153     //    they should be used here. 
 154     // ---------------------------------------------------------------------------------------- 
 155     // There should also be a Function to scan in a string to fill the variable 
 156     // ---------------------------------------------------------------------------------------- 
 158     i_Nation      
= Nation
;                                 // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US 
 159     i_dbDataType  
= dbDataType
; 
 160     i_sqlDataType 
= sqlDataType
; 
 161     s_Field
.Printf(wxT("%s%d"),s_Menge
[1].c_str(),i_Menge
[1]);   // OK for VARCHAR, INTEGER and FLOAT 
 162     if (i_dbDataType 
== 0)                                  // Filter unsupported dbDataTypes 
 164         if ((i_sqlDataType 
== SQL_VARCHAR
) || (i_sqlDataType 
== SQL_LONGVARCHAR
)) 
 165             i_dbDataType 
= DB_DATA_TYPE_VARCHAR
; 
 166         if (i_sqlDataType 
== SQL_C_DATE
) 
 167             i_dbDataType 
= DB_DATA_TYPE_DATE
; 
 168         if (i_sqlDataType 
== SQL_C_BIT
) 
 169             i_dbDataType 
= DB_DATA_TYPE_INTEGER
; 
 170         if (i_sqlDataType 
== SQL_NUMERIC
) 
 171             i_dbDataType 
= DB_DATA_TYPE_VARCHAR
; 
 172         if (i_sqlDataType 
== SQL_REAL
) 
 173             i_dbDataType 
= DB_DATA_TYPE_FLOAT
; 
 175     if ((i_dbDataType 
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType 
== SQL_C_DOUBLE
)) 
 177         i_dbDataType 
= DB_DATA_TYPE_FLOAT
; 
 179     switch(i_dbDataType
)     // -A-> Still a lot of proper formatting to do 
 181         case DB_DATA_TYPE_VARCHAR
: 
 184         case DB_DATA_TYPE_INTEGER
: 
 187         case DB_DATA_TYPE_FLOAT
: 
 188             if (decimalDigits 
== 0) 
 191             Temp0
.Printf(wxT("%s%d.%d"),Temp0
.c_str(),columnSize
,decimalDigits
); 
 192             s_Field
.Printf(wxT("%sf"),Temp0
.c_str());        // 
 194         case DB_DATA_TYPE_DATE
: 
 195             if (i_Nation 
== 0)      // timestamp       YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE) 
 197                 s_Field 
= "%04d-%02d-%02d %02d:%02d:%02d.%03d"; 
 199             if (i_Nation 
== 1)      // European        DD.MM.YYYY HH:MM:SS.SSS 
 201                 s_Field 
= "%02d.%02d.%04d %02d:%02d:%02d.%03d"; 
 203             if (i_Nation 
== 2)      // UK              DD/MM/YYYY HH:MM:SS.SSS 
 205                 s_Field 
= "%02d/%02d/%04d %02d:%02d:%02d.%03d"; 
 207             if (i_Nation 
== 3)      // International   YYYY-MM-DD HH:MM:SS.SSS 
 209                 s_Field 
= "%04d-%02d-%02d %02d:%02d:%02d.%03d"; 
 211             if (i_Nation 
== 4)      // US              MM/DD/YYYY HH:MM:SS.SSS 
 213                 s_Field 
= "%02d/%02d/%04d %02d:%02d:%02d.%03d"; 
 217             s_Field
.Printf(wxT("-E-> unknown Format(%d)-sql(%d)"),dbDataType
,sqlDataType
);        // 
 221 }  // wxDbColFor::Format() 
 224 /********** wxDb Constructor **********/ 
 225 wxDb::wxDb(HENV 
&aHenv
, bool FwdOnlyCursors
) 
 229     fpSqlLog      
= 0;            // Sql Log file pointer 
 230     sqlLogState   
= sqlLogOFF
;    // By default, logging is turned off 
 233     wxStrcpy(sqlState
,wxT("")); 
 234     wxStrcpy(errorMsg
,wxT("")); 
 235     nativeError 
= cbErrorMsg 
= 0; 
 236     for (i 
= 0; i 
< DB_MAX_ERROR_HISTORY
; i
++) 
 237         wxStrcpy(errorList
[i
], wxT("")); 
 239     // Init typeInf structures 
 240     wxStrcpy(typeInfVarchar
.TypeName
,wxT("")); 
 241     typeInfVarchar
.FsqlType      
= 0; 
 242     typeInfVarchar
.Precision     
= 0; 
 243     typeInfVarchar
.CaseSensitive 
= 0; 
 244     typeInfVarchar
.MaximumScale  
= 0; 
 246     wxStrcpy(typeInfInteger
.TypeName
,wxT("")); 
 247     typeInfInteger
.FsqlType      
= 0; 
 248     typeInfInteger
.Precision     
= 0; 
 249     typeInfInteger
.CaseSensitive 
= 0; 
 250     typeInfInteger
.MaximumScale  
= 0; 
 252     wxStrcpy(typeInfFloat
.TypeName
,wxT("")); 
 253     typeInfFloat
.FsqlType      
= 0; 
 254     typeInfFloat
.Precision     
= 0; 
 255     typeInfFloat
.CaseSensitive 
= 0; 
 256     typeInfFloat
.MaximumScale  
= 0; 
 258     wxStrcpy(typeInfDate
.TypeName
,wxT("")); 
 259     typeInfDate
.FsqlType      
= 0; 
 260     typeInfDate
.Precision     
= 0; 
 261     typeInfDate
.CaseSensitive 
= 0; 
 262     typeInfDate
.MaximumScale  
= 0; 
 264     // Error reporting is turned OFF by default 
 267     // Copy the HENV into the db class 
 269     fwdOnlyCursors 
= FwdOnlyCursors
; 
 271     // Allocate a data source connection handle 
 272     if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
) 
 275     // Initialize the db status flag 
 278     // Mark database as not open as of yet 
 284 /********** wxDb::Open() **********/ 
 285 bool wxDb::Open(char *Dsn
, char *Uid
, char *AuthStr
) 
 287     assert(Dsn 
&& wxStrlen(Dsn
)); 
 294     if (!FwdOnlyCursors()) 
 296         // Specify that the ODBC cursor library be used, if needed.  This must be 
 297         // specified before the connection is made. 
 298         retcode 
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
); 
 300 #ifdef DBDEBUG_CONSOLE 
 301         if (retcode 
== SQL_SUCCESS
) 
 302             cout 
<< "SQLSetConnectOption(CURSOR_LIB) successful" << endl
; 
 304             cout 
<< "SQLSetConnectOption(CURSOR_LIB) failed" << endl
; 
 308     // Connect to the data source 
 309     retcode 
= SQLConnect(hdbc
, (UCHAR FAR 
*) Dsn
, SQL_NTS
, 
 310                          (UCHAR FAR 
*) Uid
, SQL_NTS
, 
 311                          (UCHAR FAR 
*) AuthStr
,SQL_NTS
); 
 313     if (retcode 
== SQL_SUCCESS_WITH_INFO
) 
 314         DispAllErrors(henv
, hdbc
); 
 315     else if (retcode 
!= SQL_SUCCESS
) 
 316         return(DispAllErrors(henv
, hdbc
)); 
 319     If using Intersolv branded ODBC drivers, this is the place where you would substitute 
 320     your branded driver license information 
 322     SQLSetConnectOption(hdbc, 1041, (UDWORD) ""); 
 323     SQLSetConnectOption(hdbc, 1042, (UDWORD) ""); 
 326     // Mark database as open 
 329     // Allocate a statement handle for the database connection 
 330     if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
) 
 331         return(DispAllErrors(henv
, hdbc
)); 
 333     // Set Connection Options 
 334     if (! setConnectionOptions()) 
 337     // Query the data source for inf. about itself 
 341     // Query the data source regarding data type information 
 344     // The way I determined which SQL data types to use was by calling SQLGetInfo 
 345     // for all of the possible SQL data types to see which ones were supported.  If 
 346     // a type is not supported, the SQLFetch() that's called from getDataTypeInfo() 
 347     // fails with SQL_NO_DATA_FOUND.  This is ugly because I'm sure the three SQL data 
 348     // types I've selected below will not alway's be what we want.  These are just 
 349     // what happened to work against an Oracle 7/Intersolv combination.  The following is 
 350     // a complete list of the results I got back against the Oracle 7 database: 
 352     // SQL_BIGINT             SQL_NO_DATA_FOUND 
 353     // SQL_BINARY             SQL_NO_DATA_FOUND 
 354     // SQL_BIT                SQL_NO_DATA_FOUND 
 355     // SQL_CHAR               type name = 'CHAR', Precision = 255 
 356     // SQL_DATE               SQL_NO_DATA_FOUND 
 357     // SQL_DECIMAL            type name = 'NUMBER', Precision = 38 
 358     // SQL_DOUBLE             type name = 'NUMBER', Precision = 15 
 359     // SQL_FLOAT              SQL_NO_DATA_FOUND 
 360     // SQL_INTEGER            SQL_NO_DATA_FOUND 
 361     // SQL_LONGVARBINARY      type name = 'LONG RAW', Precision = 2 billion 
 362     // SQL_LONGVARCHAR        type name = 'LONG', Precision = 2 billion 
 363     // SQL_NUMERIC            SQL_NO_DATA_FOUND 
 364     // SQL_REAL               SQL_NO_DATA_FOUND 
 365     // SQL_SMALLINT           SQL_NO_DATA_FOUND 
 366     // SQL_TIME               SQL_NO_DATA_FOUND 
 367     // SQL_TIMESTAMP          type name = 'DATE', Precision = 19 
 368     // SQL_VARBINARY          type name = 'RAW', Precision = 255 
 369     // SQL_VARCHAR            type name = 'VARCHAR2', Precision = 2000 
 370     // ===================================================================== 
 371     // Results from a Microsoft Access 7.0 db, using a driver from Microsoft 
 373     // SQL_VARCHAR            type name = 'TEXT', Precision = 255 
 374     // SQL_TIMESTAMP          type name = 'DATETIME' 
 375     // SQL_DECIMAL            SQL_NO_DATA_FOUND 
 376     // SQL_NUMERIC            type name = 'CURRENCY', Precision = 19 
 377     // SQL_FLOAT              SQL_NO_DATA_FOUND 
 378     // SQL_REAL               type name = 'SINGLE', Precision = 7 
 379     // SQL_DOUBLE             type name = 'DOUBLE', Precision = 15 
 380     // SQL_INTEGER            type name = 'LONG', Precision = 10 
 382     // VARCHAR = Variable length character string 
 383     if (! getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
)) 
 384         if (! getDataTypeInfo(SQL_CHAR
, typeInfVarchar
)) 
 387             typeInfVarchar
.FsqlType 
= SQL_CHAR
; 
 389         typeInfVarchar
.FsqlType 
= SQL_VARCHAR
; 
 392     if (! getDataTypeInfo(SQL_DOUBLE
, typeInfFloat
)) 
 394         if (! getDataTypeInfo(SQL_REAL
, typeInfFloat
)) 
 395             if (! getDataTypeInfo(SQL_FLOAT
, typeInfFloat
)) 
 396                 if (! getDataTypeInfo(SQL_DECIMAL
, typeInfFloat
)) 
 397                     if (! getDataTypeInfo(SQL_NUMERIC
, typeInfFloat
)) 
 400                         typeInfFloat
.FsqlType 
= SQL_NUMERIC
; 
 402                     typeInfFloat
.FsqlType 
= SQL_DECIMAL
; 
 404                 typeInfFloat
.FsqlType 
= SQL_FLOAT
; 
 406             typeInfFloat
.FsqlType 
= SQL_REAL
; 
 408         typeInfFloat
.FsqlType 
= SQL_DOUBLE
; 
 412     if (! getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))      
 413           // If SQL_INTEGER is not supported, use the floating point 
 414           // data type to store integers as well as floats 
 415           if (! getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
)) 
 418             typeInfInteger
.FsqlType 
= typeInfFloat
.FsqlType
; 
 420         typeInfInteger
.FsqlType 
= SQL_INTEGER
; 
 423     if (Dbms() != dbmsDBASE
) 
 425         if (! getDataTypeInfo(SQL_TIMESTAMP
, typeInfDate
)) 
 428             typeInfDate
.FsqlType 
= SQL_TIMESTAMP
; 
 432         if (! getDataTypeInfo(SQL_DATE
, typeInfDate
)) 
 435             typeInfDate
.FsqlType 
= SQL_DATE
; 
 438 #ifdef DBDEBUG_CONSOLE 
 439     cout 
<< "VARCHAR DATA TYPE: " << typeInfVarchar
.TypeName 
<< endl
; 
 440     cout 
<< "INTEGER DATA TYPE: " << typeInfInteger
.TypeName 
<< endl
; 
 441     cout 
<< "FLOAT   DATA TYPE: " << typeInfFloat
.TypeName 
<< endl
; 
 442     cout 
<< "DATE    DATA TYPE: " << typeInfDate
.TypeName 
<< endl
; 
 446     // Completed Successfully 
 452 /********** wxDb::setConnectionOptions() **********/ 
 453 bool wxDb::setConnectionOptions(void) 
 455  * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout. 
 458     SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
); 
 459     SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
); 
 461     // Display the connection options to verify them 
 462 #ifdef DBDEBUG_CONSOLE 
 464     cout 
<< "****** CONNECTION OPTIONS ******" << endl
; 
 466     if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
) 
 467         return(DispAllErrors(henv
, hdbc
)); 
 468     cout 
<< "AUTOCOMMIT: " << (l 
== SQL_AUTOCOMMIT_OFF 
? "OFF" : "ON") << endl
; 
 470     if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
) 
 471         return(DispAllErrors(henv
, hdbc
)); 
 472     cout 
<< "ODBC CURSORS: "; 
 475         case(SQL_CUR_USE_IF_NEEDED
): 
 476             cout 
<< "SQL_CUR_USE_IF_NEEDED"; 
 478         case(SQL_CUR_USE_ODBC
): 
 479             cout 
<< "SQL_CUR_USE_ODBC"; 
 481         case(SQL_CUR_USE_DRIVER
): 
 482             cout 
<< "SQL_CUR_USE_DRIVER"; 
 487     if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
) 
 488         return(DispAllErrors(henv
, hdbc
)); 
 489     cout 
<< "TRACING: " << (l 
== SQL_OPT_TRACE_OFF 
? "OFF" : "ON") << endl
; 
 494     // Completed Successfully 
 497 } // wxDb::setConnectionOptions() 
 500 /********** wxDb::getDbInfo() **********/ 
 501 bool wxDb::getDbInfo(void) 
 506     if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
) 
 507         return(DispAllErrors(henv
, hdbc
)); 
 509     if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
) 
 510         return(DispAllErrors(henv
, hdbc
)); 
 512     if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
) 
 513         return(DispAllErrors(henv
, hdbc
)); 
 516     // After upgrading to MSVC6, the original 20 char buffer below was insufficient, 
 517     // causing database connectivity to fail in some cases. 
 518     retcode 
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
); 
 520     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO 
) 
 521         return(DispAllErrors(henv
, hdbc
)); 
 523     if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
) 
 524         return(DispAllErrors(henv
, hdbc
)); 
 526     if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
) 
 527         return(DispAllErrors(henv
, hdbc
)); 
 529     if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
) 
 530         return(DispAllErrors(henv
, hdbc
)); 
 532     if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
) 
 533         return(DispAllErrors(henv
, hdbc
)); 
 535     retcode 
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
); 
 536     if (retcode 
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
 537         return(DispAllErrors(henv
, hdbc
)); 
 539     if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
) 
 540         return(DispAllErrors(henv
, hdbc
)); 
 542     if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
) 
 543         return(DispAllErrors(henv
, hdbc
)); 
 545     if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
) 
 546         return(DispAllErrors(henv
, hdbc
)); 
 548     if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
) 
 549         return(DispAllErrors(henv
, hdbc
)); 
 551     if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
) 
 552         return(DispAllErrors(henv
, hdbc
)); 
 554     if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
) 
 555         return(DispAllErrors(henv
, hdbc
)); 
 557     if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
) 
 558         return(DispAllErrors(henv
, hdbc
)); 
 560     if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
) 
 561         return(DispAllErrors(henv
, hdbc
)); 
 563     if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
) 
 564         return(DispAllErrors(henv
, hdbc
)); 
 566     if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
) 
 567         return(DispAllErrors(henv
, hdbc
)); 
 569     if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
) 
 570         return(DispAllErrors(henv
, hdbc
)); 
 572     if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
) 
 573         return(DispAllErrors(henv
, hdbc
)); 
 575     if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
) 
 576         return(DispAllErrors(henv
, hdbc
)); 
 578     if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
) 
 579         return(DispAllErrors(henv
, hdbc
)); 
 581     if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
) 
 582         return(DispAllErrors(henv
, hdbc
)); 
 584     if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
) 
 585         return(DispAllErrors(henv
, hdbc
)); 
 587     if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
) 
 588         return(DispAllErrors(henv
, hdbc
)); 
 590     if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
) 
 591         return(DispAllErrors(henv
, hdbc
)); 
 593     if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
) 
 594         return(DispAllErrors(henv
, hdbc
)); 
 596     if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
) 
 597         return(DispAllErrors(henv
, hdbc
)); 
 599     if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
) 
 600         return(DispAllErrors(henv
, hdbc
)); 
 602 #ifdef DBDEBUG_CONSOLE 
 603     cout 
<< "***** DATA SOURCE INFORMATION *****" << endl
; 
 604     cout 
<< "SERVER Name: " << dbInf
.serverName 
<< endl
; 
 605     cout 
<< "DBMS Name: " << dbInf
.dbmsName 
<< "; DBMS Version: " << dbInf
.dbmsVer 
<< endl
; 
 606     cout 
<< "ODBC Version: " << dbInf
.odbcVer 
<< "; Driver Version: " << dbInf
.driverVer 
<< endl
; 
 608     cout 
<< "API Conf. Level: "; 
 609     switch(dbInf
.apiConfLvl
) 
 611         case SQL_OAC_NONE
:      cout 
<< "None";       break; 
 612         case SQL_OAC_LEVEL1
:    cout 
<< "Level 1";    break; 
 613         case SQL_OAC_LEVEL2
:    cout 
<< "Level 2";    break; 
 617     cout 
<< "SAG CLI Conf. Level: "; 
 618     switch(dbInf
.cliConfLvl
) 
 620         case SQL_OSCC_NOT_COMPLIANT
:    cout 
<< "Not Compliant";    break; 
 621         case SQL_OSCC_COMPLIANT
:        cout 
<< "Compliant";        break; 
 625     cout 
<< "SQL Conf. Level: "; 
 626     switch(dbInf
.sqlConfLvl
) 
 628         case SQL_OSC_MINIMUM
:     cout 
<< "Minimum Grammer";     break; 
 629         case SQL_OSC_CORE
:        cout 
<< "Core Grammer";        break; 
 630         case SQL_OSC_EXTENDED
:    cout 
<< "Extended Grammer";    break; 
 634     cout 
<< "Max. Connections: "       << dbInf
.maxConnections   
<< endl
; 
 635     cout 
<< "Outer Joins: "            << dbInf
.outerJoins       
<< endl
; 
 636     cout 
<< "Support for Procedures: " << dbInf
.procedureSupport 
<< endl
; 
 638     cout 
<< "Cursor COMMIT Behavior: "; 
 639     switch(dbInf
.cursorCommitBehavior
) 
 641         case SQL_CB_DELETE
:        cout 
<< "Delete cursors";      break; 
 642         case SQL_CB_CLOSE
:         cout 
<< "Close cursors";       break; 
 643         case SQL_CB_PRESERVE
:      cout 
<< "Preserve cursors";    break; 
 647     cout 
<< "Cursor ROLLBACK Behavior: "; 
 648     switch(dbInf
.cursorRollbackBehavior
) 
 650         case SQL_CB_DELETE
:      cout 
<< "Delete cursors";      break; 
 651         case SQL_CB_CLOSE
:       cout 
<< "Close cursors";       break; 
 652         case SQL_CB_PRESERVE
:    cout 
<< "Preserve cursors";    break; 
 656     cout 
<< "Support NOT NULL clause: "; 
 657     switch(dbInf
.supportNotNullClause
) 
 659         case SQL_NNC_NULL
:        cout 
<< "No";        break; 
 660         case SQL_NNC_NON_NULL
:    cout 
<< "Yes";       break; 
 664     cout 
<< "Support IEF (Ref. Integrity): " << dbInf
.supportIEF   
<< endl
; 
 665     cout 
<< "Login Timeout: "                << dbInf
.loginTimeout 
<< endl
; 
 667     cout 
<< endl 
<< endl 
<< "more ..." << endl
; 
 670     cout 
<< "Default Transaction Isolation: "; 
 671     switch(dbInf
.txnIsolation
) 
 673         case SQL_TXN_READ_UNCOMMITTED
:  cout 
<< "Read Uncommitted";    break; 
 674         case SQL_TXN_READ_COMMITTED
:    cout 
<< "Read Committed";      break; 
 675         case SQL_TXN_REPEATABLE_READ
:   cout 
<< "Repeatable Read";     break; 
 676         case SQL_TXN_SERIALIZABLE
:      cout 
<< "Serializable";        break; 
 678         case SQL_TXN_VERSIONING
:        cout 
<< "Versioning";          break; 
 683     cout 
<< "Transaction Isolation Options: "; 
 684     if (dbInf
.txnIsolationOptions 
& SQL_TXN_READ_UNCOMMITTED
) 
 685         cout 
<< "Read Uncommitted, "; 
 686     if (dbInf
.txnIsolationOptions 
& SQL_TXN_READ_COMMITTED
) 
 687         cout 
<< "Read Committed, "; 
 688     if (dbInf
.txnIsolationOptions 
& SQL_TXN_REPEATABLE_READ
) 
 689         cout 
<< "Repeatable Read, "; 
 690     if (dbInf
.txnIsolationOptions 
& SQL_TXN_SERIALIZABLE
) 
 691         cout 
<< "Serializable, "; 
 693     if (dbInf
.txnIsolationOptions 
& SQL_TXN_VERSIONING
) 
 694         cout 
<< "Versioning"; 
 698     cout 
<< "Fetch Directions Supported:" << endl 
<< "   "; 
 699     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_NEXT
) 
 701     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_PRIOR
) 
 703     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_FIRST
) 
 705     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_LAST
) 
 707     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_ABSOLUTE
) 
 708         cout 
<< "Absolute, "; 
 709     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_RELATIVE
) 
 710         cout 
<< "Relative, "; 
 712     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_RESUME
) 
 715     if (dbInf
.fetchDirections 
& SQL_FD_FETCH_BOOKMARK
) 
 719     cout 
<< "Lock Types Supported (SQLSetPos): "; 
 720     if (dbInf
.lockTypes 
& SQL_LCK_NO_CHANGE
) 
 721         cout 
<< "No Change, "; 
 722     if (dbInf
.lockTypes 
& SQL_LCK_EXCLUSIVE
) 
 723         cout 
<< "Exclusive, "; 
 724     if (dbInf
.lockTypes 
& SQL_LCK_UNLOCK
) 
 728     cout 
<< "Position Operations Supported (SQLSetPos): "; 
 729     if (dbInf
.posOperations 
& SQL_POS_POSITION
) 
 730         cout 
<< "Position, "; 
 731     if (dbInf
.posOperations 
& SQL_POS_REFRESH
) 
 733     if (dbInf
.posOperations 
& SQL_POS_UPDATE
) 
 735     if (dbInf
.posOperations 
& SQL_POS_DELETE
) 
 737     if (dbInf
.posOperations 
& SQL_POS_ADD
) 
 741     cout 
<< "Positioned Statements Supported: "; 
 742     if (dbInf
.posStmts 
& SQL_PS_POSITIONED_DELETE
) 
 743         cout 
<< "Pos delete, "; 
 744     if (dbInf
.posStmts 
& SQL_PS_POSITIONED_UPDATE
) 
 745         cout 
<< "Pos update, "; 
 746     if (dbInf
.posStmts 
& SQL_PS_SELECT_FOR_UPDATE
) 
 747         cout 
<< "Select for update"; 
 750     cout 
<< "Scroll Concurrency: "; 
 751     if (dbInf
.scrollConcurrency 
& SQL_SCCO_READ_ONLY
) 
 752         cout 
<< "Read Only, "; 
 753     if (dbInf
.scrollConcurrency 
& SQL_SCCO_LOCK
) 
 755     if (dbInf
.scrollConcurrency 
& SQL_SCCO_OPT_ROWVER
) 
 756         cout 
<< "Opt. Rowver, "; 
 757     if (dbInf
.scrollConcurrency 
& SQL_SCCO_OPT_VALUES
) 
 758         cout 
<< "Opt. Values"; 
 761     cout 
<< "Scroll Options: "; 
 762     if (dbInf
.scrollOptions 
& SQL_SO_FORWARD_ONLY
) 
 763         cout 
<< "Fwd Only, "; 
 764     if (dbInf
.scrollOptions 
& SQL_SO_STATIC
) 
 766     if (dbInf
.scrollOptions 
& SQL_SO_KEYSET_DRIVEN
) 
 767         cout 
<< "Keyset Driven, "; 
 768     if (dbInf
.scrollOptions 
& SQL_SO_DYNAMIC
) 
 770     if (dbInf
.scrollOptions 
& SQL_SO_MIXED
) 
 774     cout 
<< "Static Sensitivity: "; 
 775     if (dbInf
.staticSensitivity 
& SQL_SS_ADDITIONS
) 
 776         cout 
<< "Additions, "; 
 777     if (dbInf
.staticSensitivity 
& SQL_SS_DELETIONS
) 
 778         cout 
<< "Deletions, "; 
 779     if (dbInf
.staticSensitivity 
& SQL_SS_UPDATES
) 
 783     cout 
<< "Transaction Capable?: "; 
 784     switch(dbInf
.txnCapable
) 
 786         case SQL_TC_NONE
:          cout 
<< "No";            break; 
 787         case SQL_TC_DML
:           cout 
<< "DML Only";      break; 
 788         case SQL_TC_DDL_COMMIT
:    cout 
<< "DDL Commit";    break; 
 789         case SQL_TC_DDL_IGNORE
:    cout 
<< "DDL Ignore";    break; 
 790         case SQL_TC_ALL
:           cout 
<< "DDL & DML";     break; 
 797     // Completed Successfully 
 800 } // wxDb::getDbInfo() 
 803 /********** wxDb::getDataTypeInfo() **********/ 
 804 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo 
&structSQLTypeInfo
) 
 807  * fSqlType will be something like SQL_VARCHAR.  This parameter determines 
 808  * the data type inf. is gathered for. 
 810  * wxDbSqlTypeInfo is a structure that is filled in with data type information, 
 815     // Get information about the data type specified 
 816     if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
) 
 817         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 819     if ((retcode 
= SQLFetch(hstmt
)) != SQL_SUCCESS
) 
 821 #ifdef DBDEBUG_CONSOLE 
 822         if (retcode 
== SQL_NO_DATA_FOUND
) 
 823           cout 
<< "SQL_NO_DATA_FOUND fetching inf. about data type." << endl
; 
 825         DispAllErrors(henv
, hdbc
, hstmt
); 
 826         SQLFreeStmt(hstmt
, SQL_CLOSE
); 
 829     // Obtain columns from the record 
 830     if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) structSQLTypeInfo
.TypeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
) 
 831         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 834     // BJO 20000503: no more needed with new GetColumns... 
 837     if (Dbms() == dbmsMY_SQL
) 
 839         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint"); 
 840         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint unsigned"); 
 841         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int"); 
 842         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int unsigned"); 
 843         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint"); 
 844         if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "varchar")) wxStrcpy(structSQLTypeInfo
.TypeName
, "char"); 
 847     // BJO 20000427 : OpenLink driver  
 848     if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) || 
 849         !wxStrncmp(dbInf
.driverName
, "OLOD", 4))     
 850       if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "double precision")) wxStrcpy(structSQLTypeInfo
.TypeName
, "real"); 
 853     if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
) 
 854         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 855     if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
) 
 856         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 857 //    if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS) 
 858 //        return(DispAllErrors(henv, hdbc, hstmt)); 
 860     if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*)  &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
) 
 861         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 863     if (structSQLTypeInfo
.MaximumScale 
< 0) 
 864         structSQLTypeInfo
.MaximumScale 
= 0; 
 866     // Close the statement handle which closes open cursors 
 867     if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
) 
 868         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
 870     // Completed Successfully 
 873 } // wxDb::getDataTypeInfo() 
 876 /********** wxDb::Close() **********/ 
 877 void wxDb::Close(void) 
 879     // Close the Sql Log file 
 886     // Free statement handle 
 889         if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
) 
 890             DispAllErrors(henv
, hdbc
); 
 893     // Disconnect from the datasource 
 894     if (SQLDisconnect(hdbc
) != SQL_SUCCESS
) 
 895         DispAllErrors(henv
, hdbc
); 
 897     // Free the connection to the datasource 
 898     if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
) 
 899         DispAllErrors(henv
, hdbc
); 
 901     // There should be zero Ctable objects still connected to this db object 
 902     assert(nTables 
== 0); 
 907     pNode 
= TablesInUse
.First(); 
 911         tiu 
= (wxTablesInUse 
*)pNode
->Data(); 
 912         if (tiu
->pDb 
== this) 
 914             s
.sprintf(wxT("(%-20s)     tableID:[%6lu]     pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
); 
 915             s2
.sprintf(wxT("Orphaned found using pDb:[%p]"),this); 
 916             wxLogDebug (s
.c_str(),s2
.c_str()); 
 918         pNode 
= pNode
->Next(); 
 922     // Copy the error messages to a global variable 
 924     for (i 
= 0; i 
< DB_MAX_ERROR_HISTORY
; i
++) 
 925         wxStrcpy(DBerrorList
[i
],errorList
[i
]); 
 930 /********** wxDb::CommitTrans() **********/ 
 931 bool wxDb::CommitTrans(void) 
 935         // Commit the transaction 
 936         if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
) 
 937             return(DispAllErrors(henv
, hdbc
)); 
 940     // Completed successfully 
 943 } // wxDb::CommitTrans() 
 946 /********** wxDb::RollbackTrans() **********/ 
 947 bool wxDb::RollbackTrans(void) 
 949     // Rollback the transaction 
 950     if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
) 
 951         return(DispAllErrors(henv
, hdbc
)); 
 953     // Completed successfully 
 956 } // wxDb::RollbackTrans() 
 959 /********** wxDb::DispAllErrors() **********/ 
 960 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
) 
 962 //    char odbcErrMsg[DB_MAX_ERROR_MSG_LEN]; 
 965     while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR 
*) sqlState
, &nativeError
, (UCHAR FAR 
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH 
- 1, &cbErrorMsg
) == SQL_SUCCESS
) 
 967         odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
); 
 968         logError(odbcErrMsg
.c_str(), sqlState
); 
 971 #ifdef DBDEBUG_CONSOLE 
 972             // When run in console mode, use standard out to display errors. 
 973             cout 
<< odbcErrMsg
.c_str() << endl
; 
 974             cout 
<< "Press any key to continue..." << endl
; 
 979             wxLogDebug(odbcErrMsg
.c_str(),wxT("ODBC DEBUG MESSAGE from DispAllErrors()")); 
 984     return(FALSE
);  // This function always returns false. 
 986 } // wxDb::DispAllErrors() 
 989 /********** wxDb::GetNextError() **********/ 
 990 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
) 
 992     if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR 
*) sqlState
, &nativeError
, (UCHAR FAR 
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH 
- 1, &cbErrorMsg
) == SQL_SUCCESS
) 
 997 } // wxDb::GetNextError() 
1000 /********** wxDb::DispNextError() **********/ 
1001 void wxDb::DispNextError(void) 
1003 //    char odbcErrMsg[DB_MAX_ERROR_MSG_LEN]; 
1004     wxString odbcErrMsg
; 
1006     odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
); 
1007     logError(odbcErrMsg
.c_str(), sqlState
); 
1012 #ifdef DBDEBUG_CONSOLE 
1013     // When run in console mode, use standard out to display errors. 
1014     cout 
<< odbcErrMsg
.c_str() << endl
; 
1015     cout 
<< "Press any key to continue..."  << endl
; 
1020     wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE")); 
1021 #endif  // __WXDEBUG__ 
1024 } // wxDb::DispNextError() 
1027 /********** wxDb::logError() **********/ 
1028 void wxDb::logError(const char *errMsg
, const char *SQLState
) 
1030     assert(errMsg 
&& wxStrlen(errMsg
)); 
1032     static int pLast 
= -1; 
1035     if (++pLast 
== DB_MAX_ERROR_HISTORY
) 
1038         for (i 
= 0; i 
< DB_MAX_ERROR_HISTORY
; i
++) 
1039             wxStrcpy(errorList
[i
], errorList
[i
+1]); 
1043     wxStrcpy(errorList
[pLast
], errMsg
); 
1045     if (SQLState 
&& wxStrlen(SQLState
)) 
1046         if ((dbStatus 
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
) 
1047             DB_STATUS 
= dbStatus
; 
1049     // Add the errmsg to the sql log 
1050     WriteSqlLog(errMsg
); 
1052 }  // wxDb::logError() 
1055 /**********wxDb::TranslateSqlState()  **********/ 
1056 int wxDb::TranslateSqlState(const wxChar 
*SQLState
) 
1058     if (!wxStrcmp(SQLState
, wxT("01000"))) 
1059         return(DB_ERR_GENERAL_WARNING
); 
1060     if (!wxStrcmp(SQLState
, wxT("01002"))) 
1061         return(DB_ERR_DISCONNECT_ERROR
); 
1062     if (!wxStrcmp(SQLState
, wxT("01004"))) 
1063         return(DB_ERR_DATA_TRUNCATED
); 
1064     if (!wxStrcmp(SQLState
, wxT("01006"))) 
1065         return(DB_ERR_PRIV_NOT_REVOKED
); 
1066     if (!wxStrcmp(SQLState
, wxT("01S00"))) 
1067         return(DB_ERR_INVALID_CONN_STR_ATTR
); 
1068     if (!wxStrcmp(SQLState
, wxT("01S01"))) 
1069         return(DB_ERR_ERROR_IN_ROW
); 
1070     if (!wxStrcmp(SQLState
, wxT("01S02"))) 
1071         return(DB_ERR_OPTION_VALUE_CHANGED
); 
1072     if (!wxStrcmp(SQLState
, wxT("01S03"))) 
1073         return(DB_ERR_NO_ROWS_UPD_OR_DEL
); 
1074     if (!wxStrcmp(SQLState
, wxT("01S04"))) 
1075         return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
); 
1076     if (!wxStrcmp(SQLState
, wxT("07001"))) 
1077         return(DB_ERR_WRONG_NO_OF_PARAMS
); 
1078     if (!wxStrcmp(SQLState
, wxT("07006"))) 
1079         return(DB_ERR_DATA_TYPE_ATTR_VIOL
); 
1080     if (!wxStrcmp(SQLState
, wxT("08001"))) 
1081         return(DB_ERR_UNABLE_TO_CONNECT
); 
1082     if (!wxStrcmp(SQLState
, wxT("08002"))) 
1083         return(DB_ERR_CONNECTION_IN_USE
); 
1084     if (!wxStrcmp(SQLState
, wxT("08003"))) 
1085         return(DB_ERR_CONNECTION_NOT_OPEN
); 
1086     if (!wxStrcmp(SQLState
, wxT("08004"))) 
1087         return(DB_ERR_REJECTED_CONNECTION
); 
1088     if (!wxStrcmp(SQLState
, wxT("08007"))) 
1089         return(DB_ERR_CONN_FAIL_IN_TRANS
); 
1090     if (!wxStrcmp(SQLState
, wxT("08S01"))) 
1091         return(DB_ERR_COMM_LINK_FAILURE
); 
1092     if (!wxStrcmp(SQLState
, wxT("21S01"))) 
1093         return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
); 
1094     if (!wxStrcmp(SQLState
, wxT("21S02"))) 
1095         return(DB_ERR_DERIVED_TABLE_MISMATCH
); 
1096     if (!wxStrcmp(SQLState
, wxT("22001"))) 
1097         return(DB_ERR_STRING_RIGHT_TRUNC
); 
1098     if (!wxStrcmp(SQLState
, wxT("22003"))) 
1099         return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
); 
1100     if (!wxStrcmp(SQLState
, wxT("22005"))) 
1101         return(DB_ERR_ERROR_IN_ASSIGNMENT
); 
1102     if (!wxStrcmp(SQLState
, wxT("22008"))) 
1103         return(DB_ERR_DATETIME_FLD_OVERFLOW
); 
1104     if (!wxStrcmp(SQLState
, wxT("22012"))) 
1105         return(DB_ERR_DIVIDE_BY_ZERO
); 
1106     if (!wxStrcmp(SQLState
, wxT("22026"))) 
1107         return(DB_ERR_STR_DATA_LENGTH_MISMATCH
); 
1108     if (!wxStrcmp(SQLState
, wxT("23000"))) 
1109         return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
); 
1110     if (!wxStrcmp(SQLState
, wxT("24000"))) 
1111         return(DB_ERR_INVALID_CURSOR_STATE
); 
1112     if (!wxStrcmp(SQLState
, wxT("25000"))) 
1113         return(DB_ERR_INVALID_TRANS_STATE
); 
1114     if (!wxStrcmp(SQLState
, wxT("28000"))) 
1115         return(DB_ERR_INVALID_AUTH_SPEC
); 
1116     if (!wxStrcmp(SQLState
, wxT("34000"))) 
1117         return(DB_ERR_INVALID_CURSOR_NAME
); 
1118     if (!wxStrcmp(SQLState
, wxT("37000"))) 
1119         return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
); 
1120     if (!wxStrcmp(SQLState
, wxT("3C000"))) 
1121         return(DB_ERR_DUPLICATE_CURSOR_NAME
); 
1122     if (!wxStrcmp(SQLState
, wxT("40001"))) 
1123         return(DB_ERR_SERIALIZATION_FAILURE
); 
1124     if (!wxStrcmp(SQLState
, wxT("42000"))) 
1125         return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
); 
1126     if (!wxStrcmp(SQLState
, wxT("70100"))) 
1127         return(DB_ERR_OPERATION_ABORTED
); 
1128     if (!wxStrcmp(SQLState
, wxT("IM001"))) 
1129         return(DB_ERR_UNSUPPORTED_FUNCTION
); 
1130     if (!wxStrcmp(SQLState
, wxT("IM002"))) 
1131         return(DB_ERR_NO_DATA_SOURCE
); 
1132     if (!wxStrcmp(SQLState
, wxT("IM003"))) 
1133         return(DB_ERR_DRIVER_LOAD_ERROR
); 
1134     if (!wxStrcmp(SQLState
, wxT("IM004"))) 
1135         return(DB_ERR_SQLALLOCENV_FAILED
); 
1136     if (!wxStrcmp(SQLState
, wxT("IM005"))) 
1137         return(DB_ERR_SQLALLOCCONNECT_FAILED
); 
1138     if (!wxStrcmp(SQLState
, wxT("IM006"))) 
1139         return(DB_ERR_SQLSETCONNECTOPTION_FAILED
); 
1140     if (!wxStrcmp(SQLState
, wxT("IM007"))) 
1141         return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
); 
1142     if (!wxStrcmp(SQLState
, wxT("IM008"))) 
1143         return(DB_ERR_DIALOG_FAILED
); 
1144     if (!wxStrcmp(SQLState
, wxT("IM009"))) 
1145         return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
); 
1146     if (!wxStrcmp(SQLState
, wxT("IM010"))) 
1147         return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
); 
1148     if (!wxStrcmp(SQLState
, wxT("IM011"))) 
1149         return(DB_ERR_DRIVER_NAME_TOO_LONG
); 
1150     if (!wxStrcmp(SQLState
, wxT("IM012"))) 
1151         return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
); 
1152     if (!wxStrcmp(SQLState
, wxT("IM013"))) 
1153         return(DB_ERR_TRACE_FILE_ERROR
); 
1154     if (!wxStrcmp(SQLState
, wxT("S0001"))) 
1155         return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
); 
1156     if (!wxStrcmp(SQLState
, wxT("S0002"))) 
1157         return(DB_ERR_TABLE_NOT_FOUND
); 
1158     if (!wxStrcmp(SQLState
, wxT("S0011"))) 
1159         return(DB_ERR_INDEX_ALREADY_EXISTS
); 
1160     if (!wxStrcmp(SQLState
, wxT("S0012"))) 
1161         return(DB_ERR_INDEX_NOT_FOUND
); 
1162     if (!wxStrcmp(SQLState
, wxT("S0021"))) 
1163         return(DB_ERR_COLUMN_ALREADY_EXISTS
); 
1164     if (!wxStrcmp(SQLState
, wxT("S0022"))) 
1165         return(DB_ERR_COLUMN_NOT_FOUND
); 
1166     if (!wxStrcmp(SQLState
, wxT("S0023"))) 
1167         return(DB_ERR_NO_DEFAULT_FOR_COLUMN
); 
1168     if (!wxStrcmp(SQLState
, wxT("S1000"))) 
1169         return(DB_ERR_GENERAL_ERROR
); 
1170     if (!wxStrcmp(SQLState
, wxT("S1001"))) 
1171         return(DB_ERR_MEMORY_ALLOCATION_FAILURE
); 
1172     if (!wxStrcmp(SQLState
, wxT("S1002"))) 
1173         return(DB_ERR_INVALID_COLUMN_NUMBER
); 
1174     if (!wxStrcmp(SQLState
, wxT("S1003"))) 
1175         return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
); 
1176     if (!wxStrcmp(SQLState
, wxT("S1004"))) 
1177         return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
); 
1178     if (!wxStrcmp(SQLState
, wxT("S1008"))) 
1179         return(DB_ERR_OPERATION_CANCELLED
); 
1180     if (!wxStrcmp(SQLState
, wxT("S1009"))) 
1181         return(DB_ERR_INVALID_ARGUMENT_VALUE
); 
1182     if (!wxStrcmp(SQLState
, wxT("S1010"))) 
1183         return(DB_ERR_FUNCTION_SEQUENCE_ERROR
); 
1184     if (!wxStrcmp(SQLState
, wxT("S1011"))) 
1185         return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
); 
1186     if (!wxStrcmp(SQLState
, wxT("S1012"))) 
1187         return(DB_ERR_INVALID_TRANS_OPERATION_CODE
); 
1188     if (!wxStrcmp(SQLState
, wxT("S1015"))) 
1189         return(DB_ERR_NO_CURSOR_NAME_AVAIL
); 
1190     if (!wxStrcmp(SQLState
, wxT("S1090"))) 
1191         return(DB_ERR_INVALID_STR_OR_BUF_LEN
); 
1192     if (!wxStrcmp(SQLState
, wxT("S1091"))) 
1193         return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
); 
1194     if (!wxStrcmp(SQLState
, wxT("S1092"))) 
1195         return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
); 
1196     if (!wxStrcmp(SQLState
, wxT("S1093"))) 
1197         return(DB_ERR_INVALID_PARAM_NO
); 
1198     if (!wxStrcmp(SQLState
, wxT("S1094"))) 
1199         return(DB_ERR_INVALID_SCALE_VALUE
); 
1200     if (!wxStrcmp(SQLState
, wxT("S1095"))) 
1201         return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
); 
1202     if (!wxStrcmp(SQLState
, wxT("S1096"))) 
1203         return(DB_ERR_INF_TYPE_OUT_OF_RANGE
); 
1204     if (!wxStrcmp(SQLState
, wxT("S1097"))) 
1205         return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
); 
1206     if (!wxStrcmp(SQLState
, wxT("S1098"))) 
1207         return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
); 
1208     if (!wxStrcmp(SQLState
, wxT("S1099"))) 
1209         return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
); 
1210     if (!wxStrcmp(SQLState
, wxT("S1100"))) 
1211         return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
); 
1212     if (!wxStrcmp(SQLState
, wxT("S1101"))) 
1213         return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
); 
1214     if (!wxStrcmp(SQLState
, wxT("S1103"))) 
1215         return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
); 
1216     if (!wxStrcmp(SQLState
, wxT("S1104"))) 
1217         return(DB_ERR_INVALID_PRECISION_VALUE
); 
1218     if (!wxStrcmp(SQLState
, wxT("S1105"))) 
1219         return(DB_ERR_INVALID_PARAM_TYPE
); 
1220     if (!wxStrcmp(SQLState
, wxT("S1106"))) 
1221         return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
); 
1222     if (!wxStrcmp(SQLState
, wxT("S1107"))) 
1223         return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
); 
1224     if (!wxStrcmp(SQLState
, wxT("S1108"))) 
1225         return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
); 
1226     if (!wxStrcmp(SQLState
, wxT("S1109"))) 
1227         return(DB_ERR_INVALID_CURSOR_POSITION
); 
1228     if (!wxStrcmp(SQLState
, wxT("S1110"))) 
1229         return(DB_ERR_INVALID_DRIVER_COMPLETION
); 
1230     if (!wxStrcmp(SQLState
, wxT("S1111"))) 
1231         return(DB_ERR_INVALID_BOOKMARK_VALUE
); 
1232     if (!wxStrcmp(SQLState
, wxT("S1C00"))) 
1233         return(DB_ERR_DRIVER_NOT_CAPABLE
); 
1234     if (!wxStrcmp(SQLState
, wxT("S1T00"))) 
1235         return(DB_ERR_TIMEOUT_EXPIRED
); 
1240 }  // wxDb::TranslateSqlState() 
1243 /**********  wxDb::Grant() **********/ 
1244 bool wxDb::Grant(int privileges
, const char *tableName
, const char *userList
) 
1246 //    char sqlStmt[DB_MAX_STATEMENT_LEN]; 
1249     // Build the grant statement 
1251     if (privileges 
== DB_GRANT_ALL
) 
1256         if (privileges 
& DB_GRANT_SELECT
) 
1258             sqlStmt 
+= "SELECT"; 
1261         if (privileges 
& DB_GRANT_INSERT
) 
1265             sqlStmt 
+= "INSERT"; 
1267         if (privileges 
& DB_GRANT_UPDATE
) 
1271             sqlStmt 
+= "UPDATE"; 
1273         if (privileges 
& DB_GRANT_DELETE
) 
1277             sqlStmt 
+= "DELETE"; 
1282     sqlStmt 
+= tableName
; 
1284     sqlStmt 
+= userList
; 
1286 #ifdef DBDEBUG_CONSOLE 
1287     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1290     WriteSqlLog(sqlStmt
.c_str()); 
1292     return(ExecSql(sqlStmt
.c_str())); 
1297 /********** wxDb::CreateView() **********/ 
1298 bool wxDb::CreateView(const char *viewName
, const char *colList
, const char *pSqlStmt
, bool attemptDrop
) 
1300 //    char sqlStmt[DB_MAX_STATEMENT_LEN]; 
1303     // Drop the view first 
1304     if (attemptDrop 
&& !DropView(viewName
)) 
1307     // Build the create view statement 
1308     sqlStmt  
= "CREATE VIEW "; 
1309     sqlStmt 
+= viewName
; 
1311     if (wxStrlen(colList
)) 
1319     sqlStmt 
+= pSqlStmt
; 
1321     WriteSqlLog(sqlStmt
.c_str()); 
1323 #ifdef DBDEBUG_CONSOLE 
1324     cout 
<< sqlStmt
.c_str() << endl
; 
1327     return(ExecSql(sqlStmt
.c_str())); 
1329 }  // wxDb::CreateView() 
1332 /********** wxDb::DropView()  **********/ 
1333 bool wxDb::DropView(const char *viewName
) 
1336  * NOTE: This function returns TRUE if the View does not exist, but 
1337  *       only for identified databases.  Code will need to be added 
1338  *            below for any other databases when those databases are defined 
1339  *       to handle this situation consistently 
1341 //    char sqlStmt[DB_MAX_STATEMENT_LEN]; 
1344     sqlStmt
.sprintf("DROP VIEW %s", viewName
); 
1346     WriteSqlLog(sqlStmt
.c_str()); 
1348 #ifdef DBDEBUG_CONSOLE 
1349     cout 
<< endl 
<< sqlStmt
.c_str() << endl
; 
1352     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
1354         // Check for "Base table not found" error and ignore 
1355         GetNextError(henv
, hdbc
, hstmt
); 
1356         if (wxStrcmp(sqlState
,wxT("S0002")))  // "Base table not found" 
1358             // Check for product specific error codes 
1359             if (!((Dbms() == dbmsSYBASE_ASA    
&& !wxStrcmp(sqlState
,wxT("42000")))))  // 5.x (and lower?) 
1362                 DispAllErrors(henv
, hdbc
, hstmt
); 
1369     // Commit the transaction 
1370     if (! CommitTrans()) 
1375 }  // wxDb::DropView() 
1378 /********** wxDb::ExecSql()  **********/ 
1379 bool wxDb::ExecSql(const char *pSqlStmt
) 
1381     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1382     if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) pSqlStmt
, SQL_NTS
) == SQL_SUCCESS
) 
1386         DispAllErrors(henv
, hdbc
, hstmt
); 
1390 }  // wxDb::ExecSql() 
1393 /********** wxDb::GetNext()  **********/ 
1394 bool wxDb::GetNext(void) 
1396     if (SQLFetch(hstmt
) == SQL_SUCCESS
) 
1400         DispAllErrors(henv
, hdbc
, hstmt
); 
1404 }  // wxDb::GetNext() 
1407 /********** wxDb::GetData()  **********/ 
1408 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR 
*cbReturned
) 
1413     if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
) 
1417         DispAllErrors(henv
, hdbc
, hstmt
); 
1421 }  // wxDb::GetData() 
1424 /********** wxDb::GetKeyFields() **********/ 
1425 int wxDb::GetKeyFields(char *tableName
, wxDbColInf
* colInf
, int noCols
) 
1427     char         szPkTable
[DB_MAX_TABLE_NAME_LEN
+1];  /* Primary key table name */ 
1428     char         szFkTable
[DB_MAX_TABLE_NAME_LEN
+1];  /* Foreign key table name */ 
1430 //    SQLSMALLINT  iKeySeq; 
1431     char         szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1];   /* Primary key column     */ 
1432     char         szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1];   /* Foreign key column     */ 
1438      * --------------------------------------------------------------------- 
1439      * -- 19991224 : mj10777@gmx.net : Create                         ------ 
1440      * --          : Three things are done and stored here :          ------ 
1441      * --          : 1) which Column(s) is/are Primary Key(s)         ------ 
1442      * --          : 2) which tables use this Key as a Foreign Key    ------ 
1443      * --          : 3) which columns are Foreign Key and the name    ------ 
1444      * --          :     of the Table where the Key is the Primary Key ----- 
1445      * --          : Called from GetColumns(char *tableName,          ------ 
1446      * --                           int *numCols,const char *userID ) ------ 
1447      * --------------------------------------------------------------------- 
1450     /*---------------------------------------------------------------------*/ 
1451     /* Get the names of the columns in the primary key.                    */ 
1452     /*---------------------------------------------------------------------*/ 
1453     retcode 
= SQLPrimaryKeys(hstmt
, 
1454                              NULL
, 0,                       /* Catalog name  */ 
1455                              NULL
, 0,                       /* Schema name   */ 
1456                              (UCHAR 
*) tableName
, SQL_NTS
); /* Table name    */ 
1458     /*---------------------------------------------------------------------*/ 
1459     /* Fetch and display the result set. This will be a list of the        */ 
1460     /* columns in the primary key of the tableName table.                  */ 
1461     /*---------------------------------------------------------------------*/ 
1462     while ((retcode 
== SQL_SUCCESS
) || (retcode 
== SQL_SUCCESS_WITH_INFO
)) 
1464         retcode 
= SQLFetch(hstmt
); 
1465         if (retcode 
== SQL_SUCCESS 
|| retcode 
== SQL_SUCCESS_WITH_INFO
) 
1467             GetData( 4, SQL_C_CHAR
,   szPkCol
,     DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1468             GetData( 5, SQL_C_SSHORT
, &iKeySeq
,    0,                        &cb
); 
1470             for (i
=0;i
<noCols
;i
++)                          // Find the Column name 
1471                 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
))   // We have found the Column 
1472                     colInf
[i
].PkCol 
= iKeySeq
;              // Which Primary Key is this (first, second usw.) ? 
1473         }   // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
1474     }    // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) 
1475     SQLFreeStmt(hstmt
, SQL_CLOSE
);  /* Close the cursor (the hstmt is still allocated).      */ 
1477     /*---------------------------------------------------------------------*/ 
1478     /* Get all the foreign keys that refer to tableName primary key.       */ 
1479     /*---------------------------------------------------------------------*/ 
1480     retcode 
= SQLForeignKeys(hstmt
, 
1481                              NULL
, 0,                       /* Primary catalog */ 
1482                              NULL
, 0,                       /* Primary schema  */ 
1483                              (UCHAR 
*)tableName
, SQL_NTS
,   /* Primary table   */ 
1484                              NULL
, 0,                       /* Foreign catalog */ 
1485                              NULL
, 0,                       /* Foreign schema  */ 
1486                              NULL
, 0);                      /* Foreign table   */ 
1488     /*---------------------------------------------------------------------*/ 
1489     /* Fetch and display the result set. This will be all of the foreign   */ 
1490     /* keys in other tables that refer to the tableName  primary key.      */ 
1491     /*---------------------------------------------------------------------*/ 
1494     while ((retcode 
== SQL_SUCCESS
) || (retcode 
== SQL_SUCCESS_WITH_INFO
)) 
1496         retcode 
= SQLFetch(hstmt
); 
1497         if (retcode 
== SQL_SUCCESS 
|| retcode 
== SQL_SUCCESS_WITH_INFO
) 
1499             GetData( 3, SQL_C_CHAR
,   szPkTable
,   DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
1500             GetData( 4, SQL_C_CHAR
,   szPkCol
,     DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1501             GetData( 5, SQL_C_SSHORT
, &iKeySeq
,    0,                        &cb
); 
1502             GetData( 7, SQL_C_CHAR
,   szFkTable
,   DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
1503             GetData( 8, SQL_C_CHAR
,   szFkCol
,     DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1504             Temp0
.Printf(wxT("%s[%s] "),Temp0
.c_str(),szFkTable
);  // [ ] in case there is a blank in the Table name 
1505         }  // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
1506     }   // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) 
1507     Temp0
.Trim();     // Get rid of any unneeded blanks 
1508     if (Temp0 
!= wxT("")) 
1510         for (i
=0;i
<noCols
;i
++)                          // Find the Column name 
1511             if (!wxStrcmp(colInf
[i
].colName
,szPkCol
))   // We have found the Column, store the Information 
1512                 wxStrcpy(colInf
[i
].PkTableName
,Temp0
.c_str());    // Name of the Tables where this Primary Key is used as a Foreign Key 
1513     }  // if (Temp0 != "") 
1514     SQLFreeStmt(hstmt
, SQL_CLOSE
);  /* Close the cursor (the hstmt is still allocated). */ 
1516     /*---------------------------------------------------------------------*/ 
1517     /* Get all the foreign keys in the tablename table.                    */ 
1518     /*---------------------------------------------------------------------*/ 
1519     retcode 
= SQLForeignKeys(hstmt
, 
1520                              NULL
, 0,                        /* Primary catalog   */ 
1521                              NULL
, 0,                        /* Primary schema    */ 
1522                              NULL
, 0,                        /* Primary table     */ 
1523                              NULL
, 0,                        /* Foreign catalog   */ 
1524                              NULL
, 0,                        /* Foreign schema    */ 
1525                              (UCHAR 
*)tableName
, SQL_NTS
);   /* Foreign table     */ 
1527     /*---------------------------------------------------------------------*/ 
1528     /*  Fetch and display the result set. This will be all of the          */ 
1529     /*  primary keys in other tables that are referred to by foreign       */ 
1530     /*  keys in the tableName table.                                       */ 
1531     /*---------------------------------------------------------------------*/ 
1533     while ((retcode 
== SQL_SUCCESS
) || (retcode 
== SQL_SUCCESS_WITH_INFO
)) 
1535         retcode 
= SQLFetch(hstmt
); 
1536         if (retcode 
== SQL_SUCCESS 
|| retcode 
== SQL_SUCCESS_WITH_INFO
) 
1538             GetData( 3, SQL_C_CHAR
,   szPkTable
,   DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
1539             GetData( 5, SQL_C_SSHORT
, &iKeySeq
,    0,                        &cb
); 
1540             GetData( 8, SQL_C_CHAR
,   szFkCol
,     DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1542             for (i
=0;i
<noCols
;i
++)                              // Find the Column name 
1544                 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
))       // We have found the (Foreign Key) Column 
1546                     colInf
[i
].FkCol 
= iKeySeq
;                  // Which Foreign Key is this (first, second usw.) ? 
1547                     wxStrcpy(colInf
[i
].FkTableName
,szPkTable
);    // Name of the Table where this Foriegn is the Primary Key 
1548                 } // if (!wxStrcmp(colInf[i].colName,szFkCol)) 
1549             }  // for (i=0;i<noCols;i++) 
1550         }   // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
1551     }    // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) 
1552     SQLFreeStmt(hstmt
, SQL_CLOSE
);  /* Close the cursor (the hstmt is still allocated). */ 
1554     /*---------------------------------------------------------------------*/ 
1556 }  // wxDb::GetKeyFields() 
1560 /********** wxDb::GetColumns() **********/ 
1561 wxDbColInf 
*wxDb::GetColumns(char *tableName
[], const char *userID
) 
1563  *        1) The last array element of the tableName[] argument must be zero (null). 
1564  *            This is how the end of the array is detected. 
1565  *        2) This function returns an array of wxDbColInf structures.  If no columns 
1566  *            were found, or an error occured, this pointer will be zero (null).  THE 
1567  *            CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT 
1568  *            IS FINISHED WITH IT.  i.e. 
1570  *            wxDbColInf *colInf = pDb->GetColumns(tableList, userID); 
1573  *                // Use the column inf 
1575  *                // Destroy the memory 
1579  * userID is evaluated in the following manner: 
1580  *        userID == NULL  ... UserID is ignored 
1581  *        userID == ""    ... UserID set equal to 'this->uid' 
1582  *        userID != ""    ... UserID set equal to 'userID' 
1584  * NOTE: ALL column bindings associated with this wxDb instance are unbound 
1585  *       by this function.  This function should use its own wxDb instance 
1586  *       to avoid undesired unbinding of columns. 
1591     wxDbColInf 
*colInf 
= 0; 
1601         if (!wxStrlen(userID
)) 
1609     // dBase does not use user names, and some drivers fail if you try to pass one 
1610     if (Dbms() == dbmsDBASE
) 
1613     // Oracle user names may only be in uppercase, so force 
1614     // the name to uppercase 
1615     if (Dbms() == dbmsORACLE
) 
1616         UserID 
= UserID
.Upper(); 
1618     // Pass 1 - Determine how many columns there are. 
1619     // Pass 2 - Allocate the wxDbColInf array and fill in 
1620     //                the array with the column information. 
1622     for (pass 
= 1; pass 
<= 2; pass
++) 
1626             if (noCols 
== 0)  // Probably a bogus table name(s) 
1628             // Allocate n wxDbColInf objects to hold the column information 
1629             colInf 
= new wxDbColInf
[noCols
+1]; 
1632             // Mark the end of the array 
1633             wxStrcpy(colInf
[noCols
].tableName
, wxT("")); 
1634             wxStrcpy(colInf
[noCols
].colName
, wxT("")); 
1635             colInf
[noCols
].sqlDataType 
= 0; 
1637         // Loop through each table name 
1639         for (tbl 
= 0; tableName
[tbl
]; tbl
++) 
1641             TableName 
= tableName
[tbl
]; 
1642             // Oracle table names are uppercase only, so force 
1643             // the name to uppercase just in case programmer forgot to do this 
1644             if (Dbms() == dbmsORACLE
) 
1645                 TableName 
= TableName
.Upper(); 
1647             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1649             // MySQL and Access cannot accept a user name when looking up column names, so we 
1650             // use the call below that leaves out the user name 
1651             if (wxStrcmp(UserID
.c_str(),wxT("")) && 
1652                  Dbms() != dbmsMY_SQL 
&& 
1653                  Dbms() != dbmsACCESS
) 
1655                 retcode 
= SQLColumns(hstmt
, 
1656                                      NULL
, 0,                                // All qualifiers 
1657                                      (UCHAR 
*) UserID
.c_str(), SQL_NTS
,    // Owner 
1658                                      (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
1659                                      NULL
, 0);                               // All columns 
1663                 retcode 
= SQLColumns(hstmt
, 
1664                                      NULL
, 0,                                // All qualifiers 
1666                                      (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
1667                                      NULL
, 0);                               // All columns 
1669             if (retcode 
!= SQL_SUCCESS
) 
1670             {  // Error occured, abort 
1671                 DispAllErrors(henv
, hdbc
, hstmt
); 
1674                 SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1678             while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
) 
1680                 if (pass 
== 1)  // First pass, just add up the number of columns 
1682                 else  // Pass 2; Fill in the array of structures 
1684                     if (colNo 
< noCols
)  // Some extra error checking to prevent memory overwrites 
1686                         // NOTE: Only the ODBC 1.x fields are retrieved 
1687                         GetData( 1, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].catalog
,      128+1,                    &cb
); 
1688                         GetData( 2, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].schema
,       128+1,                    &cb
); 
1689                         GetData( 3, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].tableName
,    DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
1690                         GetData( 4, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].colName
,      DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1691                         GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
,  0,                        &cb
); 
1692                         GetData( 6, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].typeName
,     128+1,                    &cb
); 
1693                         GetData( 7, SQL_C_SLONG
,  (UCHAR
*) &colInf
[colNo
].columnSize
,   0,                        &cb
); 
1694                         GetData( 8, SQL_C_SLONG
,  (UCHAR
*) &colInf
[colNo
].bufferLength
, 0,                        &cb
); 
1695                         GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0,                        &cb
); 
1696                         GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0,                        &cb
); 
1697                         GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
,     0,                        &cb
); 
1698                         GetData(12, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].remarks
,      254+1,                    &cb
); 
1700                         // Determine the wxDb data type that is used to represent the native data type of this data source 
1701                         colInf
[colNo
].dbDataType 
= 0; 
1702                         if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
)) 
1704                             if (colInf
[colNo
].columnSize 
< 1) 
1706                                // IODBC does not return a correct columnSize, so we set 
1707                                // columnSize = bufferLength if no column size was returned 
1708                                colInf
[colNo
].columnSize 
= colInf
[colNo
].bufferLength
; 
1710                             colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_VARCHAR
; 
1712                         else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
)) 
1713                             colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_INTEGER
; 
1714                         else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
)) 
1715                             colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_FLOAT
; 
1716                         else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
)) 
1717                             colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_DATE
; 
1723             if (retcode 
!= SQL_NO_DATA_FOUND
) 
1724             {  // Error occured, abort 
1725                 DispAllErrors(henv
, hdbc
, hstmt
); 
1728                 SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1734     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1737 }  // wxDb::GetColumns() 
1740 /********** wxDb::GetColumns() **********/ 
1742 wxDbColInf 
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
) 
1744 // Same as the above GetColumns() function except this one gets columns 
1745 // only for a single table, and if 'numCols' is not NULL, the number of 
1746 // columns stored in the returned wxDbColInf is set in '*numCols' 
1748 // userID is evaluated in the following manner: 
1749 //        userID == NULL  ... UserID is ignored 
1750 //        userID == ""    ... UserID set equal to 'this->uid' 
1751 //        userID != ""    ... UserID set equal to 'userID' 
1753 // NOTE: ALL column bindings associated with this wxDb instance are unbound 
1754 //       by this function.  This function should use its own wxDb instance 
1755 //       to avoid undesired unbinding of columns. 
1760     wxDbColInf 
*colInf 
= 0; 
1770         if (!wxStrlen(userID
)) 
1778     // dBase does not use user names, and some drivers fail if you try to pass one 
1779     if (Dbms() == dbmsDBASE
) 
1782     // Oracle user names may only be in uppercase, so force 
1783     // the name to uppercase 
1784     if (Dbms() == dbmsORACLE
) 
1785         UserID 
= UserID
.Upper(); 
1787     // Pass 1 - Determine how many columns there are. 
1788     // Pass 2 - Allocate the wxDbColInf array and fill in 
1789     //                the array with the column information. 
1791     for (pass 
= 1; pass 
<= 2; pass
++) 
1795             if (noCols 
== 0)  // Probably a bogus table name(s) 
1797             // Allocate n wxDbColInf objects to hold the column information 
1798             colInf 
= new wxDbColInf
[noCols
+1]; 
1801             // Mark the end of the array 
1802             wxStrcpy(colInf
[noCols
].tableName
, wxT("")); 
1803             wxStrcpy(colInf
[noCols
].colName
, wxT("")); 
1804             colInf
[noCols
].sqlDataType 
= 0; 
1807         TableName 
= tableName
; 
1808         // Oracle table names are uppercase only, so force 
1809         // the name to uppercase just in case programmer forgot to do this 
1810         if (Dbms() == dbmsORACLE
) 
1811             TableName 
= TableName
.Upper(); 
1813         SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1815         // MySQL and Access cannot accept a user name when looking up column names, so we 
1816         // use the call below that leaves out the user name 
1817         if (wxStrcmp(UserID
.c_str(),wxT("")) && 
1818              Dbms() != dbmsMY_SQL 
&& 
1819              Dbms() != dbmsACCESS
) 
1821             retcode 
= SQLColumns(hstmt
, 
1822                                  NULL
, 0,                                // All qualifiers 
1823                                  (UCHAR 
*) UserID
.c_str(), SQL_NTS
,    // Owner 
1824                                  (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
1825                                  NULL
, 0);                               // All columns 
1829             retcode 
= SQLColumns(hstmt
, 
1830                                  NULL
, 0,                                 // All qualifiers 
1832                                  (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
1833                                  NULL
, 0);                                // All columns 
1835         if (retcode 
!= SQL_SUCCESS
) 
1836         {  // Error occured, abort 
1837             DispAllErrors(henv
, hdbc
, hstmt
); 
1840             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1846         while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
) 
1848             if (pass 
== 1)  // First pass, just add up the number of columns 
1850             else  // Pass 2; Fill in the array of structures 
1852                 if (colNo 
< noCols
)  // Some extra error checking to prevent memory overwrites 
1854                     // NOTE: Only the ODBC 1.x fields are retrieved 
1855                     GetData( 1, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].catalog
,      128+1,                    &cb
); 
1856                     GetData( 2, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].schema
,       128+1,                    &cb
); 
1857                     GetData( 3, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].tableName
,    DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
1858                     GetData( 4, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].colName
,      DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
1859                     GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
,  0,                        &cb
); 
1860                     GetData( 6, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].typeName
,     128+1,                    &cb
); 
1861                     GetData( 7, SQL_C_SLONG
,  (UCHAR
*) &colInf
[colNo
].columnSize
,   0,                        &cb
); 
1862                     // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures) 
1863                     GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0,                        &cb
); 
1864                     GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0,                        &cb
); 
1865                     GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0,                        &cb
); 
1866                     GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
,     0,                        &cb
); 
1867                     GetData(12, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].remarks
,      254+1,                    &cb
); 
1868                     // Start Values for Primary/Foriegn Key (=No) 
1869                     colInf
[colNo
].PkCol 
= 0;           // Primary key column   0=No; 1= First Key, 2 = Second Key etc. 
1870                     colInf
[colNo
].PkTableName
[0] = 0;  // Tablenames where Primary Key is used as a Foreign Key 
1871                     colInf
[colNo
].FkCol 
= 0;           // Foreign key column   0=No; 1= First Key, 2 = Second Key etc. 
1872                     colInf
[colNo
].FkTableName
[0] = 0;  // Foreign key table name 
1874                     // BJO 20000428 : Virtuoso returns type names with upper cases! 
1875                     if (Dbms() == dbmsVIRTUOSO
) 
1877                         wxString s 
=  colInf
[colNo
].typeName
; 
1879                         wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());             
1882                     // Determine the wxDb data type that is used to represent the native data type of this data source 
1883                     colInf
[colNo
].dbDataType 
= 0; 
1884                     if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
)) 
1886                         if (colInf
[colNo
].columnSize 
< 1) 
1888                                 // IODBC does not return a correct columnSize, so we set 
1889                                 // columnSize = bufferLength if no column size was returned 
1890                             colInf
[colNo
].columnSize 
= colInf
[colNo
].bufferLength
; 
1892                         colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_VARCHAR
; 
1894                     else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
)) 
1895                       colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_INTEGER
; 
1896                     else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
)) 
1897                       colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_FLOAT
; 
1898                     else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
)) 
1899                       colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_DATE
;              
1905         if (retcode 
!= SQL_NO_DATA_FOUND
) 
1906           {  // Error occured, abort 
1907             DispAllErrors(henv
, hdbc
, hstmt
); 
1910             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1917     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
1919     // Store Primary and Foriegn Keys 
1920     GetKeyFields(tableName
,colInf
,noCols
); 
1926 }  // wxDb::GetColumns() 
1928 #else  // New GetColumns 
1936 These are tentative new GetColumns members: 
1938 - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)) calls 
1939   the second implementation for each separate table before merging the results. This makes the 
1940   code easier to maintain as only one member (the second) makes the real work 
1941 - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID) is a little bit improved 
1942      - It doesn't anymore rely on the type-name to find out which database-type each column has 
1943      - It ends by sorting the columns, so that they are returned in the same order they were created 
1953 wxDbColInf 
*wxDb::GetColumns(char *tableName
[], const char* userID
) 
1956   // The last array element of the tableName[] argument must be zero (null). 
1957   // This is how the end of the array is detected. 
1961   // How many tables ? 
1963   for (tbl 
= 0 ;  tableName
[tbl
]; tbl
++);  
1965   // Create a table to maintain the columns for each separate table 
1966   _TableColumns 
*TableColumns 
= new _TableColumns
[tbl
]; 
1969   for (i 
= 0 ; i 
< tbl 
; i
++) 
1972       TableColumns
[i
].colInf 
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);     
1973       if (TableColumns
[i
].colInf 
== NULL
) return NULL
; 
1974       noCols 
+= TableColumns
[i
].noCols
; 
1977   // Now merge all the separate table infos 
1978   wxDbColInf 
*colInf 
= new wxDbColInf
[noCols
+1]; 
1980   // Mark the end of the array 
1981   wxStrcpy(colInf
[noCols
].tableName
, wxT("")); 
1982   wxStrcpy(colInf
[noCols
].colName
, wxT("")); 
1983   colInf
[noCols
].sqlDataType 
= 0; 
1988   for (i 
= 0 ; i 
< tbl 
; i
++) 
1990     for (j 
= 0 ; j 
< TableColumns
[i
].noCols 
; j
++) 
1992            colInf
[offset
++] = TableColumns
[i
].colInf
[j
];             
1996   delete [] TableColumns
; 
2002 wxDbColInf 
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
) 
2004   // Same as the above GetColumns() function except this one gets columns 
2005   // only for a single table, and if 'numCols' is not NULL, the number of 
2006   // columns stored in the returned wxDbColInf is set in '*numCols' 
2008   // userID is evaluated in the following manner: 
2009   //        userID == NULL  ... UserID is ignored 
2010   //        userID == ""    ... UserID set equal to 'this->uid' 
2011   //        userID != ""    ... UserID set equal to 'userID' 
2013   // NOTE: ALL column bindings associated with this wxDb instance are unbound 
2014   //       by this function.  This function should use its own wxDb instance 
2015   //       to avoid undesired unbinding of columns. 
2020   wxDbColInf 
*colInf 
= 0; 
2030       if (!wxStrlen(userID
)) 
2038   // dBase does not use user names, and some drivers fail if you try to pass one 
2039   if (Dbms() == dbmsDBASE
) 
2042   // Oracle user names may only be in uppercase, so force 
2043   // the name to uppercase 
2044   if (Dbms() == dbmsORACLE
) 
2045     UserID 
= UserID
.Upper(); 
2047   // Pass 1 - Determine how many columns there are. 
2048   // Pass 2 - Allocate the wxDbColInf array and fill in 
2049   //                the array with the column information. 
2051   for (pass 
= 1; pass 
<= 2; pass
++) 
2055           if (noCols 
== 0)  // Probably a bogus table name(s) 
2057           // Allocate n wxDbColInf objects to hold the column information 
2058           colInf 
= new wxDbColInf
[noCols
+1]; 
2061           // Mark the end of the array 
2062           wxStrcpy(colInf
[noCols
].tableName
, wxT("")); 
2063           wxStrcpy(colInf
[noCols
].colName
, wxT("")); 
2064           colInf
[noCols
].sqlDataType 
= 0; 
2067       TableName 
= tableName
; 
2068       // Oracle table names are uppercase only, so force 
2069       // the name to uppercase just in case programmer forgot to do this 
2070       if (Dbms() == dbmsORACLE
) 
2071         TableName 
= TableName
.Upper(); 
2073       SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2075       // MySQL and Access cannot accept a user name when looking up column names, so we 
2076       // use the call below that leaves out the user name 
2077       if (wxStrcmp(UserID
.c_str(),wxT("")) && 
2078           Dbms() != dbmsMY_SQL 
&& 
2079           Dbms() != dbmsACCESS
) 
2081           retcode 
= SQLColumns(hstmt
, 
2082                                NULL
, 0,                                // All qualifiers 
2083                                (UCHAR 
*) UserID
.c_str(), SQL_NTS
,    // Owner 
2084                                (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
2085                                NULL
, 0);                               // All columns 
2089           retcode 
= SQLColumns(hstmt
, 
2090                                NULL
, 0,                                 // All qualifiers 
2092                                (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
2093                                NULL
, 0);                                // All columns 
2095       if (retcode 
!= SQL_SUCCESS
) 
2096         {  // Error occured, abort 
2097           DispAllErrors(henv
, hdbc
, hstmt
); 
2100           SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2106         while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
) 
2108             if (pass 
== 1)  // First pass, just add up the number of columns 
2110             else  // Pass 2; Fill in the array of structures 
2112                 if (colNo 
< noCols
)  // Some extra error checking to prevent memory overwrites 
2114                     // NOTE: Only the ODBC 1.x fields are retrieved 
2115                     GetData( 1, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].catalog
,      128+1,                    &cb
); 
2116                     GetData( 2, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].schema
,       128+1,                    &cb
); 
2117                     GetData( 3, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].tableName
,    DB_MAX_TABLE_NAME_LEN
+1,  &cb
); 
2118                     GetData( 4, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].colName
,      DB_MAX_COLUMN_NAME_LEN
+1, &cb
); 
2119                     GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
,  0,                        &cb
); 
2120                     GetData( 6, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].typeName
,     128+1,                    &cb
); 
2121                     GetData( 7, SQL_C_SLONG
,  (UCHAR
*) &colInf
[colNo
].columnSize
,   0,                        &cb
);                 
2122                     GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0,                        &cb
); 
2123                     GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0,                        &cb
); 
2124                     GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0,                        &cb
); 
2125                     GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
,     0,                        &cb
); 
2126                     GetData(12, SQL_C_CHAR
,   (UCHAR
*)  colInf
[colNo
].remarks
,      254+1,                    &cb
); 
2127                     // Start Values for Primary/Foriegn Key (=No) 
2128                     colInf
[colNo
].PkCol 
= 0;           // Primary key column   0=No; 1= First Key, 2 = Second Key etc. 
2129                     colInf
[colNo
].PkTableName
[0] = 0;  // Tablenames where Primary Key is used as a Foreign Key 
2130                     colInf
[colNo
].FkCol 
= 0;           // Foreign key column   0=No; 1= First Key, 2 = Second Key etc. 
2131                     colInf
[colNo
].FkTableName
[0] = 0;  // Foreign key table name 
2134                     // IODBC returns the columnSize in bufferLength.. (bug) 
2135                     colInf
[colNo
].columnSize 
= colInf
[colNo
].bufferLength
;                   
2138                     // Determine the wxDb data type that is used to represent the native data type of this data source 
2139                     colInf
[colNo
].dbDataType 
= 0; 
2140                     // Get the intern datatype 
2141                     switch (colInf
[colNo
].sqlDataType
) 
2145                         colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_VARCHAR
;                          
2151                         colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_INTEGER
;          
2158                         colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_FLOAT
;    
2161                         colInf
[colNo
].dbDataType 
= DB_DATA_TYPE_DATE
;    
2168                         errMsg
.sprintf("SQL Data type %d currently not supported by wxWindows", colInf
[colNo
].sqlDataType
); 
2169                         wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE")); 
2179         if (retcode 
!= SQL_NO_DATA_FOUND
) 
2180           {  // Error occured, abort 
2181             DispAllErrors(henv
, hdbc
, hstmt
); 
2184             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2191   SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2193   // Store Primary and Foreign Keys 
2194   GetKeyFields(tableName
,colInf
,noCols
); 
2198   /////////////////////////////////////////////////////////////////////////// 
2199   // Now sort the the columns in order to make them appear in the right order 
2200   /////////////////////////////////////////////////////////////////////////// 
2202   // Build a generic SELECT statement which returns 0 rows 
2205   Stmt
.sprintf("select * from %s where 0=1", tableName
); 
2208   if (SQLExecDirect(hstmt
, (UCHAR FAR 
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
) 
2210       DispAllErrors(henv
, hdbc
, hstmt
); 
2214   // Get the number of result columns 
2215   if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
) 
2217       DispAllErrors(henv
, hdbc
, hstmt
); 
2221   if (noCols 
== 0) // Probably a bogus table name 
2230   for (colNum 
= 0; colNum 
< noCols
; colNum
++) 
2232       if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,  
2234                            &Sword
, &Sdword
) != SQL_SUCCESS
) 
2236           DispAllErrors(henv
, hdbc
, hstmt
); 
2240       wxString Name1 
= name
; 
2241       Name1 
= Name1
.Upper(); 
2243       // Where is this name in the array ? 
2244       for (i 
= colNum 
; i 
< noCols 
; i
++) 
2246           wxString Name2 
=  colInf
[i
].colName
; 
2247           Name2 
= Name2
.Upper(); 
2250               if (colNum 
!= i
) // swap to sort 
2252                   wxDbColInf tmpColInf 
= colInf
[colNum
]; 
2253                   colInf
[colNum
] =  colInf
[i
]; 
2254                   colInf
[i
] = tmpColInf
; 
2260   SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2262   /////////////////////////////////////////////////////////////////////////// 
2264   /////////////////////////////////////////////////////////////////////////// 
2273 }  // wxDb::GetColumns() 
2279 /********** wxDb::GetColumnCount() **********/ 
2280 int wxDb::GetColumnCount(char *tableName
, const char *userID
) 
2282  * Returns a count of how many columns are in a table. 
2283  * If an error occurs in computing the number of columns 
2284  * this function will return a -1 for the count 
2286  * userID is evaluated in the following manner: 
2287  *        userID == NULL  ... UserID is ignored 
2288  *        userID == ""    ... UserID set equal to 'this->uid' 
2289  *        userID != ""    ... UserID set equal to 'userID' 
2291  * NOTE: ALL column bindings associated with this wxDb instance are unbound 
2292  *       by this function.  This function should use its own wxDb instance 
2293  *       to avoid undesired unbinding of columns. 
2305         if (!wxStrlen(userID
)) 
2313     // dBase does not use user names, and some drivers fail if you try to pass one 
2314     if (Dbms() == dbmsDBASE
) 
2317     // Oracle user names may only be in uppercase, so force 
2318     // the name to uppercase 
2319     if (Dbms() == dbmsORACLE
) 
2320         UserID 
= UserID
.Upper(); 
2323         // Loop through each table name 
2325             TableName 
= tableName
; 
2326             // Oracle table names are uppercase only, so force 
2327             // the name to uppercase just in case programmer forgot to do this 
2328             if (Dbms() == dbmsORACLE
) 
2329                 TableName 
= TableName
.Upper(); 
2331             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2333             // MySQL and Access cannot accept a user name when looking up column names, so we 
2334             // use the call below that leaves out the user name 
2335             if (wxStrcmp(UserID
.c_str(),wxT("")) && 
2336                  Dbms() != dbmsMY_SQL 
&& 
2337                  Dbms() != dbmsACCESS
) 
2339                 retcode 
= SQLColumns(hstmt
, 
2340                                      NULL
, 0,                                // All qualifiers 
2341                                      (UCHAR 
*) UserID
.c_str(), SQL_NTS
,    // Owner 
2342                                      (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
2343                                      NULL
, 0);                               // All columns 
2347                 retcode 
= SQLColumns(hstmt
, 
2348                                      NULL
, 0,                                // All qualifiers 
2350                                      (UCHAR 
*) TableName
.c_str(), SQL_NTS
, 
2351                                      NULL
, 0);                               // All columns 
2353             if (retcode 
!= SQL_SUCCESS
) 
2354             {  // Error occured, abort 
2355                 DispAllErrors(henv
, hdbc
, hstmt
); 
2356                 SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2360             // Count the columns 
2361             while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
) 
2364             if (retcode 
!= SQL_NO_DATA_FOUND
) 
2365             {  // Error occured, abort 
2366                 DispAllErrors(henv
, hdbc
, hstmt
); 
2367                 SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2373     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2376 }  // wxDb::GetColumnCount() 
2379 /********** wxDb::GetCatalog() *******/ 
2380 wxDbInf 
*wxDb::GetCatalog(char *userID
) 
2382  * --------------------------------------------------------------------- 
2383  * -- 19991203 : mj10777@gmx.net : Create                         ------ 
2384  * --          : Creates a wxDbInf with Tables / Cols Array       ------ 
2385  * --          : uses SQLTables and fills pTableInf;              ------ 
2386  * --          : pColInf is set to NULL and numCols to 0;         ------ 
2387  * --          : returns pDbInf (wxDbInf)                         ------ 
2388  * --            - if unsuccesfull (pDbInf == NULL)               ------ 
2389  * --          : pColInf can be filled with GetColumns(..);       ------ 
2390  * --          : numCols   can be filled with GetColumnCount(..); ------ 
2391  * --------------------------------------------------------------------- 
2393  * userID is evaluated in the following manner: 
2394  *        userID == NULL  ... UserID is ignored 
2395  *        userID == ""    ... UserID set equal to 'this->uid' 
2396  *        userID != ""    ... UserID set equal to 'userID' 
2398  * NOTE: ALL column bindings associated with this wxDb instance are unbound 
2399  *       by this function.  This function should use its own wxDb instance 
2400  *       to avoid undesired unbinding of columns. 
2403     wxDbInf 
*pDbInf 
= NULL
; // Array of catalog entries 
2404     int      noTab 
= 0;     // Counter while filling table entries 
2408 //    char     tblNameSave[DB_MAX_TABLE_NAME_LEN+1]; 
2409     wxString tblNameSave
; 
2415         if (!wxStrlen(userID
)) 
2423     // dBase does not use user names, and some drivers fail if you try to pass one 
2424     if (Dbms() == dbmsDBASE
) 
2427     // Oracle user names may only be in uppercase, so force 
2428     // the name to uppercase 
2429     if (Dbms() == dbmsORACLE
) 
2430         UserID 
= UserID
.Upper(); 
2432     //------------------------------------------------------------- 
2433     pDbInf 
= new wxDbInf
;          // Create the Database Arrray 
2434     pDbInf
->catalog
[0]     = 0; 
2435     pDbInf
->schema
[0]      = 0; 
2436     pDbInf
->numTables      
= 0;    // Counter for Tables 
2437     pDbInf
->pTableInf      
= NULL
; // Array of Tables 
2438     //------------------------------------------------------------- 
2439     // Table Information 
2440     // Pass 1 - Determine how many Tables there are. 
2441     // Pass 2 - Create the Table array and fill it 
2442     //        - Create the Cols array = NULL 
2443     //------------------------------------------------------------- 
2446     for (pass 
= 1; pass 
<= 2; pass
++) 
2448         SQLFreeStmt(hstmt
, SQL_CLOSE
);   // Close if Open 
2449         tblNameSave 
= wxT(""); 
2451         if (wxStrcmp(UserID
.c_str(),wxT("")) && 
2452              Dbms() != dbmsMY_SQL 
&& 
2453              Dbms() != dbmsACCESS
) 
2456             retcode 
= SQLTables(hstmt
, 
2457                                 NULL
, 0,                             // All qualifiers 
2458                                 (UCHAR 
*) UserID
.c_str(), SQL_NTS
, // User specified 
2459                                 NULL
, 0,                             // All tables 
2460                                 NULL
, 0);                            // All columns 
2465             retcode 
= SQLTables(hstmt
, 
2466                                 NULL
, 0,           // All qualifiers 
2467                                 NULL
, 0,           // User specified 
2468                                 NULL
, 0,           // All tables 
2469                                 NULL
, 0);          // All columns 
2472         if (retcode 
!= SQL_SUCCESS
) 
2475             DispAllErrors(henv
, hdbc
, hstmt
); 
2477             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2481         while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
)   // Table Information 
2484             if (pass 
== 1)  // First pass, just count the Tables 
2486                 if (pDbInf
->numTables 
== 0) 
2488                     GetData( 1, SQL_C_CHAR
,   (UCHAR
*)  pDbInf
->catalog
,  128+1, &cb
); 
2489                     GetData( 2, SQL_C_CHAR
,   (UCHAR
*)  pDbInf
->schema
,   128+1, &cb
); 
2491                  pDbInf
->numTables
++;      // Counter for Tables 
2493             if (pass 
== 2) // Create and fill the Table entries 
2495                 if (pDbInf
->pTableInf 
== NULL
)   // Has the Table Array been created 
2496                 {  // no, then create the Array 
2497                     pDbInf
->pTableInf 
= new wxDbTableInf
[pDbInf
->numTables
]; 
2498                     for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++) 
2500                         (pDbInf
->pTableInf
+noTab
)->tableName
[0]    = 0; 
2501                         (pDbInf
->pTableInf
+noTab
)->tableType
[0]    = 0; 
2502                         (pDbInf
->pTableInf
+noTab
)->tableRemarks
[0] = 0; 
2503                         (pDbInf
->pTableInf
+noTab
)->numCols         
= 0; 
2504                         (pDbInf
->pTableInf
+noTab
)->pColInf         
= NULL
; 
2507                 } // if (pDbInf->pTableInf == NULL)   // Has the Table Array been created        
2509                 GetData( 3, SQL_C_CHAR
,   (UCHAR
*)  (pDbInf
->pTableInf
+noTab
)->tableName
,    DB_MAX_TABLE_NAME_LEN
+1, &cb
); 
2510                 GetData( 4, SQL_C_CHAR
,   (UCHAR
*)  (pDbInf
->pTableInf
+noTab
)->tableType
,    30+1,                    &cb
); 
2511                 GetData( 5, SQL_C_CHAR
,   (UCHAR
*)  (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1,                   &cb
); 
2514             }  // if (pass == 2)  We now know the amount of Tables 
2515         }   // while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS) 
2516     }    // for (pass = 1; pass <= 2; pass++) 
2517     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2519     // Query how many columns are in each table 
2520     for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++) 
2522         (pDbInf
->pTableInf
+noTab
)->numCols 
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
); 
2525 }  // wxDb::GetCatalog() 
2528 /********** wxDb::Catalog() **********/ 
2529 bool wxDb::Catalog(const char *userID
, const char *fileName
) 
2531  * Creates the text file specified in 'filename' which will contain 
2532  * a minimal data dictionary of all tables accessible by the user specified 
2535  * userID is evaluated in the following manner: 
2536  *        userID == NULL  ... UserID is ignored 
2537  *        userID == ""    ... UserID set equal to 'this->uid' 
2538  *        userID != ""    ... UserID set equal to 'userID' 
2540  * NOTE: ALL column bindings associated with this wxDb instance are unbound 
2541  *       by this function.  This function should use its own wxDb instance 
2542  *       to avoid undesired unbinding of columns. 
2545     assert(fileName 
&& wxStrlen(fileName
)); 
2549     char      tblName
[DB_MAX_TABLE_NAME_LEN
+1]; 
2550     wxString  tblNameSave
; 
2551     char      colName
[DB_MAX_COLUMN_NAME_LEN
+1]; 
2553     char      typeName
[30+1]; 
2554     SWORD     precision
, length
; 
2558     FILE *fp 
= fopen(fileName
,"wt"); 
2562     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2566         if (!wxStrlen(userID
)) 
2574     // dBase does not use user names, and some drivers fail if you try to pass one 
2575     if (Dbms() == dbmsDBASE
) 
2578     // Oracle user names may only be in uppercase, so force 
2579     // the name to uppercase 
2580     if (Dbms() == dbmsORACLE
) 
2581         UserID 
= UserID
.Upper(); 
2583     if (wxStrcmp(UserID
.c_str(),wxT("")) && 
2584          Dbms() != dbmsMY_SQL 
&& 
2585          Dbms() != dbmsACCESS
) 
2587         retcode 
= SQLColumns(hstmt
, 
2588                              NULL
, 0,                                // All qualifiers 
2589                              (UCHAR 
*) UserID
.c_str(), SQL_NTS
,    // User specified 
2590                              NULL
, 0,                                // All tables 
2591                              NULL
, 0);                               // All columns 
2595         retcode 
= SQLColumns(hstmt
, 
2596                              NULL
, 0,    // All qualifiers 
2597                              NULL
, 0,    // User specified 
2598                              NULL
, 0,    // All tables 
2599                              NULL
, 0);   // All columns 
2601     if (retcode 
!= SQL_SUCCESS
) 
2603         DispAllErrors(henv
, hdbc
, hstmt
); 
2609     tblNameSave 
= wxT(""); 
2612     while ((retcode 
= SQLFetch(hstmt
)) == SQL_SUCCESS
) 
2614         if (wxStrcmp(tblName
,tblNameSave
.c_str())) 
2618             fputs("================================ ", fp
); 
2619             fputs("================================ ", fp
); 
2620             fputs("===================== ", fp
); 
2621             fputs("========= ", fp
); 
2622             fputs("=========\n", fp
); 
2623             outStr
.sprintf(wxT("%-32s %-32s %-21s %9s %9s\n"), 
2624                 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH")); 
2625             fputs(outStr
.c_str(), fp
); 
2626             fputs("================================ ", fp
); 
2627             fputs("================================ ", fp
); 
2628             fputs("===================== ", fp
); 
2629             fputs("========= ", fp
); 
2630             fputs("=========\n", fp
); 
2631             tblNameSave 
= tblName
; 
2634       GetData(3,SQL_C_CHAR
,  (UCHAR 
*)tblName
,     DB_MAX_TABLE_NAME_LEN
+1, &cb
); 
2635       GetData(4,SQL_C_CHAR
,  (UCHAR 
*)colName
,     DB_MAX_COLUMN_NAME_LEN
+1,&cb
); 
2636       GetData(5,SQL_C_SSHORT
,(UCHAR 
*)&sqlDataType
,0,                       &cb
); 
2637       GetData(6,SQL_C_CHAR
,  (UCHAR 
*)typeName
,    sizeof(typeName
),        &cb
); 
2638       GetData(7,SQL_C_SSHORT
,(UCHAR 
*)&precision
,  0,                       &cb
); 
2639       GetData(8,SQL_C_SSHORT
,(UCHAR 
*)&length
,     0,                       &cb
); 
2641         outStr
.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n", 
2642             tblName
, colName
, sqlDataType
, typeName
, precision
, length
); 
2643         if (fputs(outStr
.c_str(), fp
) == EOF
) 
2645             SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2652     if (retcode 
!= SQL_NO_DATA_FOUND
) 
2653         DispAllErrors(henv
, hdbc
, hstmt
); 
2655     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2658     return(retcode 
== SQL_NO_DATA_FOUND
); 
2660 }  // wxDb::Catalog() 
2663 bool wxDb::TableExists(const char *tableName
, const char *userID
, const char *tablePath
) 
2665  * Table name can refer to a table, view, alias or synonym.  Returns true 
2666  * if the object exists in the database.  This function does not indicate 
2667  * whether or not the user has privleges to query or perform other functions 
2670  * userID is evaluated in the following manner: 
2671  *        userID == NULL  ... UserID is ignored 
2672  *        userID == ""    ... UserID set equal to 'this->uid' 
2673  *        userID != ""    ... UserID set equal to 'userID' 
2679     assert(tableName 
&& wxStrlen(tableName
)); 
2681     if (Dbms() == dbmsDBASE
)  
2685       if (tablePath 
&& wxStrlen(tablePath
)) 
2686         dbName
.sprintf("%s\\%s.dbf",tablePath
,tableName
);       
2688         dbName
.sprintf("%s.dbf",tableName
); 
2691       exists 
= wxFileExists(dbName
.c_str());     
2697         if (!wxStrlen(userID
)) 
2705     // Oracle user names may only be in uppercase, so force 
2706     // the name to uppercase 
2707     if (Dbms() == dbmsORACLE
) 
2708         UserID 
= UserID
.Upper(); 
2710     TableName 
= tableName
; 
2711     // Oracle table names are uppercase only, so force 
2712     // the name to uppercase just in case programmer forgot to do this 
2713     if (Dbms() == dbmsORACLE
) 
2714         TableName 
= TableName
.Upper(); 
2716     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2719     // MySQL and Access cannot accept a user name when looking up table names, so we 
2720     // use the call below that leaves out the user name 
2721     if (wxStrcmp(UserID
,"") && 
2722          Dbms() != dbmsMY_SQL 
&& 
2723          Dbms() != dbmsACCESS
) 
2725         retcode 
= SQLTables(hstmt
, 
2726                             NULL
, 0,                                    // All qualifiers 
2727                             (UCHAR 
*) UserID
.c_str(), SQL_NTS
,        // All owners 
2728                             (UCHAR FAR 
*)TableName
.c_str(), SQL_NTS
, 
2729                             NULL
, 0);                                   // All table types 
2733         retcode 
= SQLTables(hstmt
, 
2734                             NULL
, 0,                                    // All qualifiers 
2735                             NULL
, 0,                                    // All owners 
2736                             (UCHAR FAR 
*)TableName
.c_str(), SQL_NTS
, 
2737                             NULL
, 0);                                   // All table types 
2739     if (retcode 
!= SQL_SUCCESS
) 
2740         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
2742     retcode 
= SQLFetch(hstmt
); 
2743     if (retcode  
!= SQL_SUCCESS 
&& retcode 
!= SQL_SUCCESS_WITH_INFO
) 
2745         SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2746         return(DispAllErrors(henv
, hdbc
, hstmt
)); 
2749     SQLFreeStmt(hstmt
, SQL_CLOSE
); 
2752 }  // wxDb::TableExists() 
2755 /********** wxDb::SetSqlLogging() **********/ 
2756 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const char *filename
, bool append
) 
2758     assert(state 
== sqlLogON  
|| state 
== sqlLogOFF
); 
2759     assert(state 
== sqlLogOFF 
|| filename
); 
2761     if (state 
== sqlLogON
) 
2765             fpSqlLog 
= fopen(filename
, (append 
? "at" : "wt")); 
2766             if (fpSqlLog 
== NULL
) 
2774             if (fclose(fpSqlLog
)) 
2780     sqlLogState 
= state
; 
2783 }  // wxDb::SetSqlLogging() 
2786 /********** wxDb::WriteSqlLog() **********/ 
2787 bool wxDb::WriteSqlLog(const wxChar 
*logMsg
) 
2791     if (fpSqlLog 
== 0 || sqlLogState 
== sqlLogOFF
) 
2794     if (fputs("\n",   fpSqlLog
) == EOF
) return(FALSE
); 
2795     if (fputs(logMsg
, fpSqlLog
) == EOF
) return(FALSE
); 
2796     if (fputs("\n",   fpSqlLog
) == EOF
) return(FALSE
); 
2800 }  // wxDb::WriteSqlLog() 
2803 /********** wxDb::Dbms() **********/ 
2804 wxDBMS 
wxDb::Dbms(void) 
2806  * Be aware that not all database engines use the exact same syntax, and not 
2807  * every ODBC compliant database is compliant to the same level of compliancy. 
2808  * Some manufacturers support the minimum Level 1 compliancy, and others up 
2809  * through Level 3.  Others support subsets of features for levels above 1. 
2811  * If you find an inconsistency between the wxDb class and a specific database 
2812  * engine, and an identifier to this section, and special handle the database in 
2813  * the area where behavior is non-conforming with the other databases. 
2816  * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE 
2817  * --------------------------------------------------- 
2820  *        - Currently the only database supported by the class to support VIEWS 
2823  *        - Does not support the SQL_TIMESTAMP structure 
2824  *        - Supports only one cursor and one connect (apparently? with Microsoft driver only?) 
2825  *        - Does not automatically create the primary index if the 'keyField' param of SetColDef 
2826  *            is TRUE.  The user must create ALL indexes from their program. 
2827  *        - Table names can only be 8 characters long 
2828  *        - Column names can only be 10 characters long 
2831  *        - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added 
2832  *            after every table name involved in the query/join if that tables matching record(s) 
2834  *        - Ignores the keywords 'FOR UPDATE'.  Use the HOLDLOCK functionality described above 
2836  * SYBASE (Enterprise) 
2837  *        - If a column is part of the Primary Key, the column cannot be NULL 
2840  *        - If a column is part of the Primary Key, the column cannot be NULL 
2841  *        - Cannot support selecting for update [::CanSelectForUpdate()].  Always returns FALSE 
2844  *        - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0 
2849     wxChar baseName
[25+1]; 
2850     wxStrncpy(baseName
,dbInf
.dbmsName
,25); 
2852     // BJO 20000428 : add support for Virtuoso 
2853     if (!wxStricmp(dbInf
.dbmsName
,"OpenLink Virtuoso VDBMS")) 
2854       return(dbmsVIRTUOSO
); 
2857     if (!wxStricmp(dbInf
.dbmsName
,"Adaptive Server Anywhere")) 
2858         return(dbmsSYBASE_ASA
); 
2860     // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when 
2861     // connected through an OpenLink driver. 
2862     // Is it also returned by Sybase Adapatitve server?     
2863     // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix      
2864     if (!wxStricmp(dbInf
.dbmsName
,"SQL Server"))        
2865       if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) || 
2866           !wxStrncmp(dbInf
.driverName
, "OLOD", 4)) 
2867         return dbmsMS_SQL_SERVER
; else return dbmsSYBASE_ASE
; 
2870     if (!wxStricmp(dbInf
.dbmsName
,"Microsoft SQL Server")) 
2871         return(dbmsMS_SQL_SERVER
); 
2872     if (!wxStricmp(dbInf
.dbmsName
,"MySQL")) 
2874     if (!wxStricmp(dbInf
.dbmsName
,"PostgreSQL"))  // v6.5.0 
2875         return(dbmsPOSTGRES
); 
2878     if (!wxStricmp(baseName
,"Informix")) 
2879         return(dbmsINFORMIX
); 
2883     if (!wxStricmp(baseName
,"Oracle")) 
2885     if (!wxStricmp(dbInf
.dbmsName
,"ACCESS")) 
2887     if (!wxStricmp(dbInf
.dbmsName
,"MySQL")) 
2889     if (!wxStricmp(baseName
,"Sybase"))              
2890       return(dbmsSYBASE_ASE
); 
2893     if (!wxStricmp(baseName
,"DBASE")) 
2896     return(dbmsUNIDENTIFIED
); 
2900 /********** wxDbGetConnection() **********/ 
2901 wxDb WXDLLEXPORT 
*wxDbGetConnection(wxDbConnectInf 
*pDbConfig
, bool FwdOnlyCursors
) 
2905     // Scan the linked list searching for an available database connection 
2906     // that's already been opened but is currently not in use. 
2907     for (pList 
= PtrBegDbList
; pList
; pList 
= pList
->PtrNext
) 
2909         // The database connection must be for the same datasource 
2910         // name and must currently not be in use. 
2911         if (pList
->Free 
&& (! wxStrcmp(pDbConfig
->Dsn
, pList
->Dsn
)))  // Found a free connection 
2913             pList
->Free 
= FALSE
; 
2914             return(pList
->PtrDb
); 
2918     // No available connections.  A new connection must be made and 
2919     // appended to the end of the linked list. 
2922         // Find the end of the list 
2923         for (pList 
= PtrBegDbList
; pList
->PtrNext
; pList 
= pList
->PtrNext
); 
2924         // Append a new list item 
2925         pList
->PtrNext 
= new wxDbList
; 
2926         pList
->PtrNext
->PtrPrev 
= pList
; 
2927         pList 
= pList
->PtrNext
; 
2931         // Create the first node on the list 
2932         pList 
= PtrBegDbList 
= new wxDbList
; 
2936     // Initialize new node in the linked list 
2938     pList
->Free 
= FALSE
; 
2939     wxStrcpy(pList
->Dsn
, pDbConfig
->Dsn
); 
2940     pList
->PtrDb 
= new wxDb(pDbConfig
->Henv
,FwdOnlyCursors
); 
2942     // Connect to the datasource 
2943     if (pList
->PtrDb
->Open(pDbConfig
->Dsn
, pDbConfig
->Uid
, pDbConfig
->AuthStr
)) 
2945         pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
); 
2946         return(pList
->PtrDb
); 
2948     else  // Unable to connect, destroy list item 
2951             pList
->PtrPrev
->PtrNext 
= 0; 
2953             PtrBegDbList 
= 0;                // Empty list again 
2954         pList
->PtrDb
->CommitTrans();    // Commit any open transactions on wxDb object 
2955         pList
->PtrDb
->Close();            // Close the wxDb object 
2956         delete pList
->PtrDb
;                // Deletes the wxDb object 
2957         delete pList
;                        // Deletes the linked list object 
2961 }  // wxDbGetConnection() 
2964 /********** wxDbFreeConnection() **********/ 
2965 bool WXDLLEXPORT 
wxDbFreeConnection(wxDb 
*pDb
) 
2969     // Scan the linked list searching for the database connection 
2970     for (pList 
= PtrBegDbList
; pList
; pList 
= pList
->PtrNext
) 
2972         if (pList
->PtrDb 
== pDb
)        // Found it!!! 
2973             return(pList
->Free 
= TRUE
); 
2976     // Never found the database object, return failure 
2979 }  // wxDbFreeConnection() 
2982 /********** wxDbCloseConnections() **********/ 
2983 void WXDLLEXPORT 
wxDbCloseConnections(void) 
2985     wxDbList 
*pList
, *pNext
; 
2987     // Traverse the linked list closing database connections and freeing memory as I go. 
2988     for (pList 
= PtrBegDbList
; pList
; pList 
= pNext
) 
2990         pNext 
= pList
->PtrNext
;       // Save the pointer to next 
2991         pList
->PtrDb
->CommitTrans();  // Commit any open transactions on wxDb object 
2992         pList
->PtrDb
->Close();        // Close the wxDb object 
2993         delete pList
->PtrDb
;          // Deletes the wxDb object 
2994         delete pList
;                 // Deletes the linked list object 
2997     // Mark the list as empty 
3000 }  // wxDbCloseConnections() 
3003 /********** wxDbNumberConnectionsInUse() **********/ 
3004 int WXDLLEXPORT 
wxDbConnectionsInUse(void) 
3009     // Scan the linked list counting db connections that are currently in use 
3010     for (pList 
= PtrBegDbList
; pList
; pList 
= pList
->PtrNext
) 
3012         if (pList
->Free 
== FALSE
) 
3018 }  // wxDbConnectionsInUse() 
3021 /********** wxDbSqlLog() **********/ 
3022 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar 
*filename
) 
3024     bool append 
= FALSE
; 
3027     for (pList 
= PtrBegDbList
; pList
; pList 
= pList
->PtrNext
) 
3029         if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
)) 
3034     SQLLOGstate 
= state
; 
3035     wxStrcpy(SQLLOGfn
,filename
); 
3043 /********** wxDbCreateDataSource() **********/ 
3044 int wxDbCreateDataSource(const char *driverName
, const char *dsn
, const char *description
, 
3045                          bool sysDSN
, const char *defDir
, wxWindow 
*parent
) 
3047  * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!! 
3048  * Very rudimentary creation of an ODBC data source.   
3058         dsnLocation 
= ODBC_ADD_SYS_DSN
; 
3060         dsnLocation 
= ODBC_ADD_DSN
; 
3062     // NOTE: The decimal 2 is an invalid character in all keyword pairs 
3063     // so that is why I used it, as wxString does not deal well with  
3064     // embedded nulls in strings 
3065     setupStr
.sprintf("DSN=%s%cDescription=%s%cDefaultDir=%s%c",dsn
,2,description
,2,defDir
,2); 
3067     // Replace the separator from above with the '\0' seperator needed 
3068     // by the SQLConfigDataSource() function 
3072         k 
= setupStr
.Find((wxChar
)2,TRUE
); 
3073         if (k 
!= wxNOT_FOUND
) 
3074             setupStr
[(UINT
)k
] = '\0'; 
3076     while (k 
!= wxNOT_FOUND
); 
3078     result 
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
, 
3079                                  driverName
, setupStr
.c_str()); 
3083         // check for errors caused by ConfigDSN based functions 
3086         wxChar errMsg
[500+1]; 
3089         SQLInstallerError(1,&retcode
,errMsg
,500,&cb
); 
3094 #ifdef DBDEBUG_CONSOLE 
3095                // When run in console mode, use standard out to display errors. 
3096                cout 
<< errMsg 
<< endl
; 
3097                cout 
<< "Press any key to continue..." << endl
; 
3099 #endif  // DBDEBUG_CONSOLE 
3102                wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE")); 
3103 #endif  // __WXDEBUG__ 
3112     wxLogDebug("wxDbCreateDataSource() not available except under MSW","DEBUG MESSAGE"); 
3118 }  // wxDbCreateDataSource() 
3122 /********** wxDbGetDataSource() **********/ 
3123 bool wxDbGetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
, 
3126  * Dsn and DsDesc will contain the data source name and data source 
3127  * description upon return 
3132     if (SQLDataSources(henv
, direction
, (UCHAR FAR 
*) Dsn
, DsnMax
, &cb1
, 
3133                              (UCHAR FAR 
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
) 
3138 }  // wxDbGetDataSource() 
3141 // Change this to 0 to remove use of all deprecated functions 
3142 #if wxODBC_BACKWARD_COMPATABILITY 
3143 /******************************************************************** 
3144  ******************************************************************** 
3146  * The following functions are all DEPRECATED and are included for 
3147  * backward compatability reasons only 
3149  ******************************************************************** 
3150  ********************************************************************/ 
3151 bool SqlLog(sqlLog state
, const wxChar 
*filename
) 
3153     return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
); 
3155 /***** DEPRECATED: use wxGetDataSource() *****/ 
3156 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
, 
3159     return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
); 
3161 /***** DEPRECATED: use wxDbGetConnection() *****/ 
3162 wxDb WXDLLEXPORT 
*GetDbConnection(DbStuff 
*pDbStuff
, bool FwdOnlyCursors
) 
3164     return wxDbGetConnection((wxDbConnectInf 
*)pDbStuff
, FwdOnlyCursors
); 
3166 /***** DEPRECATED: use wxDbFreeConnection() *****/ 
3167 bool WXDLLEXPORT 
FreeDbConnection(wxDb 
*pDb
) 
3169     return wxDbFreeConnection(pDb
); 
3171 /***** DEPRECATED: use wxDbCloseConnections() *****/ 
3172 void WXDLLEXPORT 
CloseDbConnections(void) 
3174     wxDbCloseConnections(); 
3176 /***** DEPRECATED: use wxDbConnectionsInUse() *****/ 
3177 int WXDLLEXPORT 
NumberDbConnectionsInUse(void) 
3179     return wxDbConnectionsInUse();