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 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence, plus:
22 // Notice: This class library and its intellectual design are free of charge for use,
23 // modification, enhancement, debugging under the following conditions:
24 // 1) These classes may only be used as part of the implementation of a
25 // wxWindows-based application
26 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
27 // user groups free of all charges for use with the wxWindows library.
28 // 3) These classes may not be distributed as part of any other class library,
29 // DLL, text (written or electronic), other than a complete distribution of
30 // the wxWindows GUI development toolkit.
31 ///////////////////////////////////////////////////////////////////////////////
37 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
38 #pragma implementation "db.h"
41 #include "wx/wxprec.h"
47 #ifdef DBDEBUG_CONSOLE
48 #include "wx/ioswrap.h"
52 #include "wx/string.h"
53 #include "wx/object.h"
58 #include "wx/filefn.h"
59 #include "wx/wxchar.h"
71 // DLL options compatibility check:
73 WX_CHECK_BUILD_OPTIONS("wxODBC")
75 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
77 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
78 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
81 extern wxList TablesInUse
;
84 // SQL Log defaults to be used by GetDbConnection
85 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
87 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
89 // The wxDb::errorList is copied to this variable when the wxDb object
90 // is closed. This way, the error list is still available after the
91 // database object is closed. This is necessary if the database
92 // connection fails so the calling application can show the operator
93 // why the connection failed. Note: as each wxDb object is closed, it
94 // will overwrite the errors of the previously destroyed wxDb object in
95 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
97 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
100 // This type defines the return row-struct form
101 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
104 wxChar tableQual
[128+1];
105 wxChar tableOwner
[128+1];
106 wxChar tableName
[128+1];
107 wxChar grantor
[128+1];
108 wxChar grantee
[128+1];
109 wxChar privilege
[128+1];
110 wxChar grantable
[3+1];
111 } wxDbTablePrivilegeInfo
;
114 /********** wxDbConnectInf Constructor - form 1 **********/
115 wxDbConnectInf::wxDbConnectInf()
118 freeHenvOnDestroy
= FALSE
;
124 /********** wxDbConnectInf Constructor - form 2 **********/
125 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
126 const wxString
&password
, const wxString
&defaultDir
,
127 const wxString
&fileType
, const wxString
&description
)
130 freeHenvOnDestroy
= FALSE
;
141 SetPassword(password
);
142 SetDescription(description
);
143 SetFileType(fileType
);
144 SetDefaultDir(defaultDir
);
145 } // wxDbConnectInf Constructor
148 wxDbConnectInf::~wxDbConnectInf()
150 if (freeHenvOnDestroy
)
154 } // wxDbConnectInf Destructor
158 /********** wxDbConnectInf::Initialize() **********/
159 bool wxDbConnectInf::Initialize()
161 freeHenvOnDestroy
= FALSE
;
163 if (freeHenvOnDestroy
&& Henv
)
175 } // wxDbConnectInf::Initialize()
178 /********** wxDbConnectInf::AllocHenv() **********/
179 bool wxDbConnectInf::AllocHenv()
181 // This is here to help trap if you are getting a new henv
182 // without releasing an existing henv
185 // Initialize the ODBC Environment for Database Operations
186 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
188 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
192 freeHenvOnDestroy
= TRUE
;
195 } // wxDbConnectInf::AllocHenv()
198 void wxDbConnectInf::FreeHenv()
206 freeHenvOnDestroy
= FALSE
;
208 } // wxDbConnectInf::FreeHenv()
211 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
213 wxASSERT(dsn
.Length() < sizeof(Dsn
));
216 } // wxDbConnectInf::SetDsn()
219 void wxDbConnectInf::SetUserID(const wxString
&uid
)
221 wxASSERT(uid
.Length() < sizeof(Uid
));
223 } // wxDbConnectInf::SetUserID()
226 void wxDbConnectInf::SetPassword(const wxString
&password
)
228 wxASSERT(password
.Length() < sizeof(AuthStr
));
230 wxStrcpy(AuthStr
,password
);
231 } // wxDbConnectInf::SetPassword()
235 /********** wxDbColFor Constructor **********/
236 wxDbColFor::wxDbColFor()
239 } // wxDbColFor::wxDbColFor()
242 wxDbColFor::~wxDbColFor()
244 } // wxDbColFor::~wxDbColFor()
247 /********** wxDbColFor::Initialize() **********/
248 void wxDbColFor::Initialize()
258 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
261 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
262 } // wxDbColFor::Initialize()
265 /********** wxDbColFor::Format() **********/
266 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
267 short columnSize
, short decimalDigits
)
269 // ----------------------------------------------------------------------------------------
270 // -- 19991224 : mj10777 : Create
271 // There is still a lot of work to do here, but it is a start
272 // It handles all the basic data-types that I have run into up to now
273 // The main work will have be with Dates and float Formatting
274 // (US 1,000.00 ; EU 1.000,00)
275 // There are wxWindow plans for locale support and the new wxDateTime. If
276 // they define some constants (wxEUROPEAN) that can be gloably used,
277 // they should be used here.
278 // ----------------------------------------------------------------------------------------
279 // There should also be a function to scan in a string to fill the variable
280 // ----------------------------------------------------------------------------------------
282 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
283 i_dbDataType
= dbDataType
;
284 i_sqlDataType
= sqlDataType
;
285 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
287 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
289 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
290 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
291 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
292 i_dbDataType
= DB_DATA_TYPE_DATE
;
293 if (i_sqlDataType
== SQL_C_BIT
)
294 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
295 if (i_sqlDataType
== SQL_NUMERIC
)
296 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
297 if (i_sqlDataType
== SQL_REAL
)
298 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
299 if (i_sqlDataType
== SQL_C_BINARY
)
300 i_dbDataType
= DB_DATA_TYPE_BLOB
;
303 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
305 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
308 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
310 case DB_DATA_TYPE_VARCHAR
:
313 case DB_DATA_TYPE_INTEGER
:
316 case DB_DATA_TYPE_FLOAT
:
317 if (decimalDigits
== 0)
320 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
321 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
323 case DB_DATA_TYPE_DATE
:
324 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
326 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
328 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
330 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
332 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
334 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
336 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
338 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
340 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
342 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
345 case DB_DATA_TYPE_BLOB
:
346 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
349 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
353 } // wxDbColFor::Format()
357 /********** wxDbColInf Constructor **********/
358 wxDbColInf::wxDbColInf()
361 } // wxDbColInf::wxDbColInf()
364 /********** wxDbColInf Destructor ********/
365 wxDbColInf::~wxDbColInf()
370 } // wxDbColInf::~wxDbColInf()
373 bool wxDbColInf::Initialize()
395 } // wxDbColInf::Initialize()
398 /********** wxDbTableInf Constructor ********/
399 wxDbTableInf::wxDbTableInf()
402 } // wxDbTableInf::wxDbTableInf()
405 /********** wxDbTableInf Constructor ********/
406 wxDbTableInf::~wxDbTableInf()
411 } // wxDbTableInf::~wxDbTableInf()
414 bool wxDbTableInf::Initialize()
423 } // wxDbTableInf::Initialize()
426 /********** wxDbInf Constructor *************/
430 } // wxDbInf::wxDbInf()
433 /********** wxDbInf Destructor *************/
439 } // wxDbInf::~wxDbInf()
442 /********** wxDbInf::Initialize() *************/
443 bool wxDbInf::Initialize()
451 } // wxDbInf::Initialize()
454 /********** wxDb Constructor **********/
455 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
457 // Copy the HENV into the db class
459 fwdOnlyCursors
= FwdOnlyCursors
;
465 /********** wxDb Destructor **********/
468 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
478 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
479 /********** wxDb::initialize() **********/
480 void wxDb::initialize()
482 * Private member function that sets all wxDb member variables to
483 * known values at creation of the wxDb
488 fpSqlLog
= 0; // Sql Log file pointer
489 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
491 dbmsType
= dbmsUNIDENTIFIED
;
493 wxStrcpy(sqlState
,wxEmptyString
);
494 wxStrcpy(errorMsg
,wxEmptyString
);
495 nativeError
= cbErrorMsg
= 0;
496 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
497 wxStrcpy(errorList
[i
], wxEmptyString
);
499 // Init typeInf structures
500 typeInfVarchar
.TypeName
.Empty();
501 typeInfVarchar
.FsqlType
= 0;
502 typeInfVarchar
.Precision
= 0;
503 typeInfVarchar
.CaseSensitive
= 0;
504 typeInfVarchar
.MaximumScale
= 0;
506 typeInfInteger
.TypeName
.Empty();
507 typeInfInteger
.FsqlType
= 0;
508 typeInfInteger
.Precision
= 0;
509 typeInfInteger
.CaseSensitive
= 0;
510 typeInfInteger
.MaximumScale
= 0;
512 typeInfFloat
.TypeName
.Empty();
513 typeInfFloat
.FsqlType
= 0;
514 typeInfFloat
.Precision
= 0;
515 typeInfFloat
.CaseSensitive
= 0;
516 typeInfFloat
.MaximumScale
= 0;
518 typeInfDate
.TypeName
.Empty();
519 typeInfDate
.FsqlType
= 0;
520 typeInfDate
.Precision
= 0;
521 typeInfDate
.CaseSensitive
= 0;
522 typeInfDate
.MaximumScale
= 0;
524 typeInfBlob
.TypeName
.Empty();
525 typeInfBlob
.FsqlType
= 0;
526 typeInfBlob
.Precision
= 0;
527 typeInfBlob
.CaseSensitive
= 0;
528 typeInfBlob
.MaximumScale
= 0;
530 // Error reporting is turned OFF by default
533 // Allocate a data source connection handle
534 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
537 // Initialize the db status flag
540 // Mark database as not open as of yet
543 } // wxDb::initialize()
546 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
548 // NOTE: Return value from this function MUST be copied
549 // immediately, as the value is not good after
550 // this function has left scope.
552 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
556 if (!wxStrlen(userID
))
564 // dBase does not use user names, and some drivers fail if you try to pass one
565 if ( Dbms() == dbmsDBASE
566 || Dbms() == dbmsXBASE_SEQUITER
)
569 // Oracle user names may only be in uppercase, so force
570 // the name to uppercase
571 if (Dbms() == dbmsORACLE
)
572 UserID
= UserID
.Upper();
574 return UserID
.c_str();
575 } // wxDb::convertUserID()
578 /********** wxDb::Open() **********/
579 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
581 wxASSERT(Dsn
.Length());
588 if (!FwdOnlyCursors())
590 // Specify that the ODBC cursor library be used, if needed. This must be
591 // specified before the connection is made.
592 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
594 #ifdef DBDEBUG_CONSOLE
595 if (retcode
== SQL_SUCCESS
)
596 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
598 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
602 // Connect to the data source
603 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
604 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
605 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
607 if ((retcode
!= SQL_SUCCESS
) &&
608 (retcode
!= SQL_SUCCESS_WITH_INFO
))
609 return(DispAllErrors(henv
, hdbc
));
612 If using Intersolv branded ODBC drivers, this is the place where you would substitute
613 your branded driver license information
615 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
616 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
619 // Mark database as open
622 // Allocate a statement handle for the database connection
623 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
624 return(DispAllErrors(henv
, hdbc
));
626 // Set Connection Options
627 if (!setConnectionOptions())
630 // Query the data source for inf. about itself
631 if (!getDbInfo(failOnDataTypeUnsupported
))
634 // Query the data source regarding data type information
637 // The way it was determined which SQL data types to use was by calling SQLGetInfo
638 // for all of the possible SQL data types to see which ones were supported. If
639 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
640 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
641 // types I've selected below will not alway's be what we want. These are just
642 // what happened to work against an Oracle 7/Intersolv combination. The following is
643 // a complete list of the results I got back against the Oracle 7 database:
645 // SQL_BIGINT SQL_NO_DATA_FOUND
646 // SQL_BINARY SQL_NO_DATA_FOUND
647 // SQL_BIT SQL_NO_DATA_FOUND
648 // SQL_CHAR type name = 'CHAR', Precision = 255
649 // SQL_DATE SQL_NO_DATA_FOUND
650 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
651 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
652 // SQL_FLOAT SQL_NO_DATA_FOUND
653 // SQL_INTEGER SQL_NO_DATA_FOUND
654 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
655 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
656 // SQL_NUMERIC SQL_NO_DATA_FOUND
657 // SQL_REAL SQL_NO_DATA_FOUND
658 // SQL_SMALLINT SQL_NO_DATA_FOUND
659 // SQL_TIME SQL_NO_DATA_FOUND
660 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
661 // SQL_VARBINARY type name = 'RAW', Precision = 255
662 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
663 // =====================================================================
664 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
666 // SQL_VARCHAR type name = 'TEXT', Precision = 255
667 // SQL_TIMESTAMP type name = 'DATETIME'
668 // SQL_DECIMAL SQL_NO_DATA_FOUND
669 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
670 // SQL_FLOAT SQL_NO_DATA_FOUND
671 // SQL_REAL type name = 'SINGLE', Precision = 7
672 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
673 // SQL_INTEGER type name = 'LONG', Precision = 10
675 // VARCHAR = Variable length character string
676 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
677 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
680 typeInfVarchar
.FsqlType
= SQL_CHAR
;
682 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
685 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
686 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
687 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
688 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
689 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
691 if (failOnDataTypeUnsupported
)
695 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
697 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
699 typeInfFloat
.FsqlType
= SQL_FLOAT
;
701 typeInfFloat
.FsqlType
= SQL_REAL
;
703 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
706 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
708 // If SQL_INTEGER is not supported, use the floating point
709 // data type to store integers as well as floats
710 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
712 if (failOnDataTypeUnsupported
)
716 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
719 typeInfInteger
.FsqlType
= SQL_INTEGER
;
722 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
724 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
727 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
729 typeInfDate
.FsqlType
= SQL_TIME
;
732 #endif // SQL_DATETIME defined
734 if (failOnDataTypeUnsupported
)
739 typeInfDate
.FsqlType
= SQL_DATE
;
742 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
745 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
747 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
749 if (failOnDataTypeUnsupported
)
753 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
756 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
758 //typeInfBlob.TypeName = "BLOB";
760 #ifdef DBDEBUG_CONSOLE
761 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
762 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
763 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
764 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
765 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
769 // Completed Successfully
775 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
777 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
778 dbConnectInf
->GetPassword());
782 bool wxDb::Open(wxDb
*copyDb
)
784 dsn
= copyDb
->GetDatasourceName();
785 uid
= copyDb
->GetUsername();
786 authStr
= copyDb
->GetPassword();
790 if (!FwdOnlyCursors())
792 // Specify that the ODBC cursor library be used, if needed. This must be
793 // specified before the connection is made.
794 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
796 #ifdef DBDEBUG_CONSOLE
797 if (retcode
== SQL_SUCCESS
)
798 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
800 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
804 // Connect to the data source
805 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
806 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
807 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
809 if (retcode
== SQL_ERROR
)
810 return(DispAllErrors(henv
, hdbc
));
813 If using Intersolv branded ODBC drivers, this is the place where you would substitute
814 your branded driver license information
816 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
817 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
820 // Mark database as open
823 // Allocate a statement handle for the database connection
824 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
825 return(DispAllErrors(henv
, hdbc
));
827 // Set Connection Options
828 if (!setConnectionOptions())
831 // Instead of Querying the data source for info about itself, it can just be copied
832 // from the wxDb instance that was passed in (copyDb).
833 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
834 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
835 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
836 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
837 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
838 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
839 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
840 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
841 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
842 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
843 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
844 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
845 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
846 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
847 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
848 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
849 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
850 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
851 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
852 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
853 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
854 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
855 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
856 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
857 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
858 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
859 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
860 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
861 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
862 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
863 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
865 // VARCHAR = Variable length character string
866 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
867 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
868 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
869 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
870 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
873 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
874 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
875 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
876 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
877 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
880 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
881 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
882 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
883 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
884 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
887 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
888 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
889 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
890 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
891 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
894 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
895 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
896 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
897 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
898 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
900 #ifdef DBDEBUG_CONSOLE
901 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
902 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
903 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
904 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
905 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
909 // Completed Successfully
914 /********** wxDb::setConnectionOptions() **********/
915 bool wxDb::setConnectionOptions(void)
917 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
922 // I need to get the DBMS name here, because some of the connection options
923 // are database specific and need to call the Dbms() function.
924 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
925 return(DispAllErrors(henv
, hdbc
));
927 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
928 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
929 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
931 // By default, MS Sql Server closes cursors on commit and rollback. The following
932 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
933 // after a transaction. This is a driver specific option and is not part of the
934 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
935 // The database settings don't have any effect one way or the other.
936 if (Dbms() == dbmsMS_SQL_SERVER
)
938 const long SQL_PRESERVE_CURSORS
= 1204L;
939 const long SQL_PC_ON
= 1L;
940 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
943 // Display the connection options to verify them
944 #ifdef DBDEBUG_CONSOLE
946 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
948 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
949 return(DispAllErrors(henv
, hdbc
));
950 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
952 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
953 return(DispAllErrors(henv
, hdbc
));
954 cout
<< wxT("ODBC CURSORS: ");
957 case(SQL_CUR_USE_IF_NEEDED
):
958 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
960 case(SQL_CUR_USE_ODBC
):
961 cout
<< wxT("SQL_CUR_USE_ODBC");
963 case(SQL_CUR_USE_DRIVER
):
964 cout
<< wxT("SQL_CUR_USE_DRIVER");
969 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
970 return(DispAllErrors(henv
, hdbc
));
971 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
976 // Completed Successfully
979 } // wxDb::setConnectionOptions()
982 /********** wxDb::getDbInfo() **********/
983 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
988 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
990 DispAllErrors(henv
, hdbc
);
991 if (failOnDataTypeUnsupported
)
995 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
997 DispAllErrors(henv
, hdbc
);
998 if (failOnDataTypeUnsupported
)
1002 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1004 DispAllErrors(henv
, hdbc
);
1005 if (failOnDataTypeUnsupported
)
1010 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1011 // causing database connectivity to fail in some cases.
1012 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1014 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1016 DispAllErrors(henv
, hdbc
);
1017 if (failOnDataTypeUnsupported
)
1021 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1023 DispAllErrors(henv
, hdbc
);
1024 if (failOnDataTypeUnsupported
)
1028 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1030 DispAllErrors(henv
, hdbc
);
1031 if (failOnDataTypeUnsupported
)
1035 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1037 DispAllErrors(henv
, hdbc
);
1038 if (failOnDataTypeUnsupported
)
1042 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1044 DispAllErrors(henv
, hdbc
);
1045 if (failOnDataTypeUnsupported
)
1049 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1050 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1052 DispAllErrors(henv
, hdbc
);
1053 if (failOnDataTypeUnsupported
)
1057 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1059 DispAllErrors(henv
, hdbc
);
1060 if (failOnDataTypeUnsupported
)
1064 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1066 DispAllErrors(henv
, hdbc
);
1067 if (failOnDataTypeUnsupported
)
1071 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1073 // Not all drivers support this call - Nick Gorham(unixODBC)
1074 dbInf
.cliConfLvl
= 0;
1075 DispAllErrors(henv
, hdbc
);
1076 if (failOnDataTypeUnsupported
)
1080 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1082 DispAllErrors(henv
, hdbc
);
1083 if (failOnDataTypeUnsupported
)
1087 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1089 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1090 // TODO: dbInf.outerJoins[0]='N';
1091 // TODO: dbInf.outerJoins[1]='\x0';
1093 DispAllErrors(henv
, hdbc
);
1094 if (failOnDataTypeUnsupported
)
1098 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1100 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1101 // TODO: dbInf.procedureSupport[0]='N';
1102 // TODO: dbInf.procedureSupport[1]='\x0';
1104 DispAllErrors(henv
, hdbc
);
1105 if (failOnDataTypeUnsupported
)
1109 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1111 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1112 // TODO: dbInf.accessibleTables[0]='N';
1113 // TODO: dbInf.accessibleTables[1]='\x0';
1115 DispAllErrors(henv
, hdbc
);
1116 if (failOnDataTypeUnsupported
)
1120 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1122 DispAllErrors(henv
, hdbc
);
1123 if (failOnDataTypeUnsupported
)
1127 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1129 DispAllErrors(henv
, hdbc
);
1130 if (failOnDataTypeUnsupported
)
1134 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1136 DispAllErrors(henv
, hdbc
);
1137 if (failOnDataTypeUnsupported
)
1141 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1143 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1144 // TODO: dbInf.supportIEF[0]='N';
1145 // TODO: dbInf.supportIEF[1]='\x0';
1147 DispAllErrors(henv
, hdbc
);
1148 if (failOnDataTypeUnsupported
)
1152 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1154 DispAllErrors(henv
, hdbc
);
1155 if (failOnDataTypeUnsupported
)
1159 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1161 DispAllErrors(henv
, hdbc
);
1162 if (failOnDataTypeUnsupported
)
1166 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1168 DispAllErrors(henv
, hdbc
);
1169 if (failOnDataTypeUnsupported
)
1173 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1175 DispAllErrors(henv
, hdbc
);
1176 if (failOnDataTypeUnsupported
)
1180 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1182 DispAllErrors(henv
, hdbc
);
1183 if (failOnDataTypeUnsupported
)
1187 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1189 DispAllErrors(henv
, hdbc
);
1190 if (failOnDataTypeUnsupported
)
1194 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1196 DispAllErrors(henv
, hdbc
);
1197 if (failOnDataTypeUnsupported
)
1201 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1203 DispAllErrors(henv
, hdbc
);
1204 if (failOnDataTypeUnsupported
)
1208 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1210 DispAllErrors(henv
, hdbc
);
1211 if (failOnDataTypeUnsupported
)
1215 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1217 DispAllErrors(henv
, hdbc
);
1218 if (failOnDataTypeUnsupported
)
1222 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1224 DispAllErrors(henv
, hdbc
);
1225 if (failOnDataTypeUnsupported
)
1229 #ifdef DBDEBUG_CONSOLE
1230 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1231 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1232 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1233 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1235 cout
<< wxT("API Conf. Level: ");
1236 switch(dbInf
.apiConfLvl
)
1238 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1239 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1240 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1244 cout
<< wxT("SAG CLI Conf. Level: ");
1245 switch(dbInf
.cliConfLvl
)
1247 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1248 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1252 cout
<< wxT("SQL Conf. Level: ");
1253 switch(dbInf
.sqlConfLvl
)
1255 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1256 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1257 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1261 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1262 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1263 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1264 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1265 cout
<< wxT("Cursor COMMIT Behavior: ");
1266 switch(dbInf
.cursorCommitBehavior
)
1268 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1269 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1270 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1274 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1275 switch(dbInf
.cursorRollbackBehavior
)
1277 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1278 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1279 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1283 cout
<< wxT("Support NOT NULL clause: ");
1284 switch(dbInf
.supportNotNullClause
)
1286 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1287 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1291 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1292 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1294 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1297 cout
<< wxT("Default Transaction Isolation: ";
1298 switch(dbInf
.txnIsolation
)
1300 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1301 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1302 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1303 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1305 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1310 cout
<< wxT("Transaction Isolation Options: ");
1311 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1312 cout
<< wxT("Read Uncommitted, ");
1313 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1314 cout
<< wxT("Read Committed, ");
1315 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1316 cout
<< wxT("Repeatable Read, ");
1317 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1318 cout
<< wxT("Serializable, ");
1320 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1321 cout
<< wxT("Versioning");
1325 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1326 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1327 cout
<< wxT("Next, ");
1328 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1329 cout
<< wxT("Prev, ");
1330 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1331 cout
<< wxT("First, ");
1332 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1333 cout
<< wxT("Last, ");
1334 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1335 cout
<< wxT("Absolute, ");
1336 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1337 cout
<< wxT("Relative, ");
1339 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1340 cout
<< wxT("Resume, ");
1342 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1343 cout
<< wxT("Bookmark");
1346 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1347 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1348 cout
<< wxT("No Change, ");
1349 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1350 cout
<< wxT("Exclusive, ");
1351 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1352 cout
<< wxT("UnLock");
1355 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1356 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1357 cout
<< wxT("Position, ");
1358 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1359 cout
<< wxT("Refresh, ");
1360 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1361 cout
<< wxT("Upd, "));
1362 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1363 cout
<< wxT("Del, ");
1364 if (dbInf
.posOperations
& SQL_POS_ADD
)
1368 cout
<< wxT("Positioned Statements Supported: ");
1369 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1370 cout
<< wxT("Pos delete, ");
1371 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1372 cout
<< wxT("Pos update, ");
1373 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1374 cout
<< wxT("Select for update");
1377 cout
<< wxT("Scroll Concurrency: ");
1378 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1379 cout
<< wxT("Read Only, ");
1380 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1381 cout
<< wxT("Lock, ");
1382 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1383 cout
<< wxT("Opt. Rowver, ");
1384 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1385 cout
<< wxT("Opt. Values");
1388 cout
<< wxT("Scroll Options: ");
1389 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1390 cout
<< wxT("Fwd Only, ");
1391 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1392 cout
<< wxT("Static, ");
1393 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1394 cout
<< wxT("Keyset Driven, ");
1395 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1396 cout
<< wxT("Dynamic, ");
1397 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1398 cout
<< wxT("Mixed");
1401 cout
<< wxT("Static Sensitivity: ");
1402 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1403 cout
<< wxT("Additions, ");
1404 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1405 cout
<< wxT("Deletions, ");
1406 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1407 cout
<< wxT("Updates");
1410 cout
<< wxT("Transaction Capable?: ");
1411 switch(dbInf
.txnCapable
)
1413 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1414 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1415 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1416 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1417 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1424 // Completed Successfully
1427 } // wxDb::getDbInfo()
1430 /********** wxDb::getDataTypeInfo() **********/
1431 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1434 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1435 * the data type inf. is gathered for.
1437 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1442 // Get information about the data type specified
1443 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1444 return(DispAllErrors(henv
, hdbc
, hstmt
));
1447 retcode
= SQLFetch(hstmt
);
1448 if (retcode
!= SQL_SUCCESS
)
1450 #ifdef DBDEBUG_CONSOLE
1451 if (retcode
== SQL_NO_DATA_FOUND
)
1452 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1454 DispAllErrors(henv
, hdbc
, hstmt
);
1455 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1459 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1461 // Obtain columns from the record
1462 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1463 return(DispAllErrors(henv
, hdbc
, hstmt
));
1465 structSQLTypeInfo
.TypeName
= typeName
;
1467 // BJO 20000503: no more needed with new GetColumns...
1470 if (Dbms() == dbmsMY_SQL
)
1472 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1473 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1474 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1475 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1476 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1477 structSQLTypeInfo
.TypeName
= wxT("int");
1478 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1479 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1480 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1481 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1482 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1483 structSQLTypeInfo
.TypeName
= wxT("char");
1486 // BJO 20000427 : OpenLink driver
1487 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1488 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1490 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1491 structSQLTypeInfo
.TypeName
= wxT("real");
1495 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1496 return(DispAllErrors(henv
, hdbc
, hstmt
));
1497 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1498 return(DispAllErrors(henv
, hdbc
, hstmt
));
1499 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1500 // return(DispAllErrors(henv, hdbc, hstmt));
1502 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1503 return(DispAllErrors(henv
, hdbc
, hstmt
));
1505 if (structSQLTypeInfo
.MaximumScale
< 0)
1506 structSQLTypeInfo
.MaximumScale
= 0;
1508 // Close the statement handle which closes open cursors
1509 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1510 return(DispAllErrors(henv
, hdbc
, hstmt
));
1512 // Completed Successfully
1515 } // wxDb::getDataTypeInfo()
1518 /********** wxDb::Close() **********/
1519 void wxDb::Close(void)
1521 // Close the Sql Log file
1528 // Free statement handle
1531 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1532 DispAllErrors(henv
, hdbc
);
1535 // Disconnect from the datasource
1536 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1537 DispAllErrors(henv
, hdbc
);
1539 // Free the connection to the datasource
1540 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1541 DispAllErrors(henv
, hdbc
);
1543 // There should be zero Ctable objects still connected to this db object
1544 wxASSERT(nTables
== 0);
1549 pNode
= TablesInUse
.GetFirst();
1553 tiu
= (wxTablesInUse
*)pNode
->GetData();
1554 if (tiu
->pDb
== this)
1556 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1557 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1558 wxLogDebug (s
.c_str(),s2
.c_str());
1560 pNode
= pNode
->GetNext();
1564 // Copy the error messages to a global variable
1566 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1567 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1569 dbmsType
= dbmsUNIDENTIFIED
;
1575 /********** wxDb::CommitTrans() **********/
1576 bool wxDb::CommitTrans(void)
1580 // Commit the transaction
1581 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1582 return(DispAllErrors(henv
, hdbc
));
1585 // Completed successfully
1588 } // wxDb::CommitTrans()
1591 /********** wxDb::RollbackTrans() **********/
1592 bool wxDb::RollbackTrans(void)
1594 // Rollback the transaction
1595 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1596 return(DispAllErrors(henv
, hdbc
));
1598 // Completed successfully
1601 } // wxDb::RollbackTrans()
1604 /********** wxDb::DispAllErrors() **********/
1605 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1607 * This function is called internally whenever an error condition prevents the user's
1608 * request from being executed. This function will query the datasource as to the
1609 * actual error(s) that just occured on the previous request of the datasource.
1611 * The function will retrieve each error condition from the datasource and
1612 * Printf the codes/text values into a string which it then logs via logError().
1613 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1614 * window and program execution will be paused until the user presses a key.
1616 * This function always returns a FALSE, so that functions which call this function
1617 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1618 * of the users request, so that the calling code can then process the error msg log
1621 wxString odbcErrMsg
;
1623 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1625 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1626 logError(odbcErrMsg
, sqlState
);
1629 #ifdef DBDEBUG_CONSOLE
1630 // When run in console mode, use standard out to display errors.
1631 cout
<< odbcErrMsg
.c_str() << endl
;
1632 cout
<< wxT("Press any key to continue...") << endl
;
1637 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1642 return(FALSE
); // This function always returns FALSE.
1644 } // wxDb::DispAllErrors()
1647 /********** wxDb::GetNextError() **********/
1648 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1650 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1655 } // wxDb::GetNextError()
1658 /********** wxDb::DispNextError() **********/
1659 void wxDb::DispNextError(void)
1661 wxString odbcErrMsg
;
1663 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1664 logError(odbcErrMsg
, sqlState
);
1669 #ifdef DBDEBUG_CONSOLE
1670 // When run in console mode, use standard out to display errors.
1671 cout
<< odbcErrMsg
.c_str() << endl
;
1672 cout
<< wxT("Press any key to continue...") << endl
;
1677 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1678 #endif // __WXDEBUG__
1680 } // wxDb::DispNextError()
1683 /********** wxDb::logError() **********/
1684 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1686 wxASSERT(errMsg
.Length());
1688 static int pLast
= -1;
1691 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1694 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1695 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1699 wxStrcpy(errorList
[pLast
], errMsg
);
1701 if (SQLState
.Length())
1702 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1703 DB_STATUS
= dbStatus
;
1705 // Add the errmsg to the sql log
1706 WriteSqlLog(errMsg
);
1708 } // wxDb::logError()
1711 /**********wxDb::TranslateSqlState() **********/
1712 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1714 if (!wxStrcmp(SQLState
, wxT("01000")))
1715 return(DB_ERR_GENERAL_WARNING
);
1716 if (!wxStrcmp(SQLState
, wxT("01002")))
1717 return(DB_ERR_DISCONNECT_ERROR
);
1718 if (!wxStrcmp(SQLState
, wxT("01004")))
1719 return(DB_ERR_DATA_TRUNCATED
);
1720 if (!wxStrcmp(SQLState
, wxT("01006")))
1721 return(DB_ERR_PRIV_NOT_REVOKED
);
1722 if (!wxStrcmp(SQLState
, wxT("01S00")))
1723 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1724 if (!wxStrcmp(SQLState
, wxT("01S01")))
1725 return(DB_ERR_ERROR_IN_ROW
);
1726 if (!wxStrcmp(SQLState
, wxT("01S02")))
1727 return(DB_ERR_OPTION_VALUE_CHANGED
);
1728 if (!wxStrcmp(SQLState
, wxT("01S03")))
1729 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1730 if (!wxStrcmp(SQLState
, wxT("01S04")))
1731 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1732 if (!wxStrcmp(SQLState
, wxT("07001")))
1733 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1734 if (!wxStrcmp(SQLState
, wxT("07006")))
1735 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1736 if (!wxStrcmp(SQLState
, wxT("08001")))
1737 return(DB_ERR_UNABLE_TO_CONNECT
);
1738 if (!wxStrcmp(SQLState
, wxT("08002")))
1739 return(DB_ERR_CONNECTION_IN_USE
);
1740 if (!wxStrcmp(SQLState
, wxT("08003")))
1741 return(DB_ERR_CONNECTION_NOT_OPEN
);
1742 if (!wxStrcmp(SQLState
, wxT("08004")))
1743 return(DB_ERR_REJECTED_CONNECTION
);
1744 if (!wxStrcmp(SQLState
, wxT("08007")))
1745 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1746 if (!wxStrcmp(SQLState
, wxT("08S01")))
1747 return(DB_ERR_COMM_LINK_FAILURE
);
1748 if (!wxStrcmp(SQLState
, wxT("21S01")))
1749 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1750 if (!wxStrcmp(SQLState
, wxT("21S02")))
1751 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1752 if (!wxStrcmp(SQLState
, wxT("22001")))
1753 return(DB_ERR_STRING_RIGHT_TRUNC
);
1754 if (!wxStrcmp(SQLState
, wxT("22003")))
1755 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1756 if (!wxStrcmp(SQLState
, wxT("22005")))
1757 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1758 if (!wxStrcmp(SQLState
, wxT("22008")))
1759 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1760 if (!wxStrcmp(SQLState
, wxT("22012")))
1761 return(DB_ERR_DIVIDE_BY_ZERO
);
1762 if (!wxStrcmp(SQLState
, wxT("22026")))
1763 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1764 if (!wxStrcmp(SQLState
, wxT("23000")))
1765 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1766 if (!wxStrcmp(SQLState
, wxT("24000")))
1767 return(DB_ERR_INVALID_CURSOR_STATE
);
1768 if (!wxStrcmp(SQLState
, wxT("25000")))
1769 return(DB_ERR_INVALID_TRANS_STATE
);
1770 if (!wxStrcmp(SQLState
, wxT("28000")))
1771 return(DB_ERR_INVALID_AUTH_SPEC
);
1772 if (!wxStrcmp(SQLState
, wxT("34000")))
1773 return(DB_ERR_INVALID_CURSOR_NAME
);
1774 if (!wxStrcmp(SQLState
, wxT("37000")))
1775 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1776 if (!wxStrcmp(SQLState
, wxT("3C000")))
1777 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1778 if (!wxStrcmp(SQLState
, wxT("40001")))
1779 return(DB_ERR_SERIALIZATION_FAILURE
);
1780 if (!wxStrcmp(SQLState
, wxT("42000")))
1781 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1782 if (!wxStrcmp(SQLState
, wxT("70100")))
1783 return(DB_ERR_OPERATION_ABORTED
);
1784 if (!wxStrcmp(SQLState
, wxT("IM001")))
1785 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1786 if (!wxStrcmp(SQLState
, wxT("IM002")))
1787 return(DB_ERR_NO_DATA_SOURCE
);
1788 if (!wxStrcmp(SQLState
, wxT("IM003")))
1789 return(DB_ERR_DRIVER_LOAD_ERROR
);
1790 if (!wxStrcmp(SQLState
, wxT("IM004")))
1791 return(DB_ERR_SQLALLOCENV_FAILED
);
1792 if (!wxStrcmp(SQLState
, wxT("IM005")))
1793 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1794 if (!wxStrcmp(SQLState
, wxT("IM006")))
1795 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1796 if (!wxStrcmp(SQLState
, wxT("IM007")))
1797 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1798 if (!wxStrcmp(SQLState
, wxT("IM008")))
1799 return(DB_ERR_DIALOG_FAILED
);
1800 if (!wxStrcmp(SQLState
, wxT("IM009")))
1801 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1802 if (!wxStrcmp(SQLState
, wxT("IM010")))
1803 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1804 if (!wxStrcmp(SQLState
, wxT("IM011")))
1805 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1806 if (!wxStrcmp(SQLState
, wxT("IM012")))
1807 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1808 if (!wxStrcmp(SQLState
, wxT("IM013")))
1809 return(DB_ERR_TRACE_FILE_ERROR
);
1810 if (!wxStrcmp(SQLState
, wxT("S0001")))
1811 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1812 if (!wxStrcmp(SQLState
, wxT("S0002")))
1813 return(DB_ERR_TABLE_NOT_FOUND
);
1814 if (!wxStrcmp(SQLState
, wxT("S0011")))
1815 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1816 if (!wxStrcmp(SQLState
, wxT("S0012")))
1817 return(DB_ERR_INDEX_NOT_FOUND
);
1818 if (!wxStrcmp(SQLState
, wxT("S0021")))
1819 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1820 if (!wxStrcmp(SQLState
, wxT("S0022")))
1821 return(DB_ERR_COLUMN_NOT_FOUND
);
1822 if (!wxStrcmp(SQLState
, wxT("S0023")))
1823 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1824 if (!wxStrcmp(SQLState
, wxT("S1000")))
1825 return(DB_ERR_GENERAL_ERROR
);
1826 if (!wxStrcmp(SQLState
, wxT("S1001")))
1827 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1828 if (!wxStrcmp(SQLState
, wxT("S1002")))
1829 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1830 if (!wxStrcmp(SQLState
, wxT("S1003")))
1831 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1832 if (!wxStrcmp(SQLState
, wxT("S1004")))
1833 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1834 if (!wxStrcmp(SQLState
, wxT("S1008")))
1835 return(DB_ERR_OPERATION_CANCELLED
);
1836 if (!wxStrcmp(SQLState
, wxT("S1009")))
1837 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1838 if (!wxStrcmp(SQLState
, wxT("S1010")))
1839 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1840 if (!wxStrcmp(SQLState
, wxT("S1011")))
1841 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1842 if (!wxStrcmp(SQLState
, wxT("S1012")))
1843 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1844 if (!wxStrcmp(SQLState
, wxT("S1015")))
1845 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1846 if (!wxStrcmp(SQLState
, wxT("S1090")))
1847 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1848 if (!wxStrcmp(SQLState
, wxT("S1091")))
1849 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1850 if (!wxStrcmp(SQLState
, wxT("S1092")))
1851 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1852 if (!wxStrcmp(SQLState
, wxT("S1093")))
1853 return(DB_ERR_INVALID_PARAM_NO
);
1854 if (!wxStrcmp(SQLState
, wxT("S1094")))
1855 return(DB_ERR_INVALID_SCALE_VALUE
);
1856 if (!wxStrcmp(SQLState
, wxT("S1095")))
1857 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1858 if (!wxStrcmp(SQLState
, wxT("S1096")))
1859 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1860 if (!wxStrcmp(SQLState
, wxT("S1097")))
1861 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1862 if (!wxStrcmp(SQLState
, wxT("S1098")))
1863 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1864 if (!wxStrcmp(SQLState
, wxT("S1099")))
1865 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1866 if (!wxStrcmp(SQLState
, wxT("S1100")))
1867 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1868 if (!wxStrcmp(SQLState
, wxT("S1101")))
1869 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1870 if (!wxStrcmp(SQLState
, wxT("S1103")))
1871 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1872 if (!wxStrcmp(SQLState
, wxT("S1104")))
1873 return(DB_ERR_INVALID_PRECISION_VALUE
);
1874 if (!wxStrcmp(SQLState
, wxT("S1105")))
1875 return(DB_ERR_INVALID_PARAM_TYPE
);
1876 if (!wxStrcmp(SQLState
, wxT("S1106")))
1877 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1878 if (!wxStrcmp(SQLState
, wxT("S1107")))
1879 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1880 if (!wxStrcmp(SQLState
, wxT("S1108")))
1881 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1882 if (!wxStrcmp(SQLState
, wxT("S1109")))
1883 return(DB_ERR_INVALID_CURSOR_POSITION
);
1884 if (!wxStrcmp(SQLState
, wxT("S1110")))
1885 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1886 if (!wxStrcmp(SQLState
, wxT("S1111")))
1887 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1888 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1889 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1890 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1891 return(DB_ERR_TIMEOUT_EXPIRED
);
1896 } // wxDb::TranslateSqlState()
1899 /********** wxDb::Grant() **********/
1900 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1904 // Build the grant statement
1905 sqlStmt
= wxT("GRANT ");
1906 if (privileges
== DB_GRANT_ALL
)
1907 sqlStmt
+= wxT("ALL");
1911 if (privileges
& DB_GRANT_SELECT
)
1913 sqlStmt
+= wxT("SELECT");
1916 if (privileges
& DB_GRANT_INSERT
)
1919 sqlStmt
+= wxT(", ");
1920 sqlStmt
+= wxT("INSERT");
1922 if (privileges
& DB_GRANT_UPDATE
)
1925 sqlStmt
+= wxT(", ");
1926 sqlStmt
+= wxT("UPDATE");
1928 if (privileges
& DB_GRANT_DELETE
)
1931 sqlStmt
+= wxT(", ");
1932 sqlStmt
+= wxT("DELETE");
1936 sqlStmt
+= wxT(" ON ");
1937 sqlStmt
+= SQLTableName(tableName
);
1938 sqlStmt
+= wxT(" TO ");
1939 sqlStmt
+= userList
;
1941 #ifdef DBDEBUG_CONSOLE
1942 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1945 WriteSqlLog(sqlStmt
);
1947 return(ExecSql(sqlStmt
));
1952 /********** wxDb::CreateView() **********/
1953 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1954 const wxString
&pSqlStmt
, bool attemptDrop
)
1958 // Drop the view first
1959 if (attemptDrop
&& !DropView(viewName
))
1962 // Build the create view statement
1963 sqlStmt
= wxT("CREATE VIEW ");
1964 sqlStmt
+= viewName
;
1966 if (colList
.Length())
1968 sqlStmt
+= wxT(" (");
1970 sqlStmt
+= wxT(")");
1973 sqlStmt
+= wxT(" AS ");
1974 sqlStmt
+= pSqlStmt
;
1976 WriteSqlLog(sqlStmt
);
1978 #ifdef DBDEBUG_CONSOLE
1979 cout
<< sqlStmt
.c_str() << endl
;
1982 return(ExecSql(sqlStmt
));
1984 } // wxDb::CreateView()
1987 /********** wxDb::DropView() **********/
1988 bool wxDb::DropView(const wxString
&viewName
)
1991 * NOTE: This function returns TRUE if the View does not exist, but
1992 * only for identified databases. Code will need to be added
1993 * below for any other databases when those databases are defined
1994 * to handle this situation consistently
1998 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2000 WriteSqlLog(sqlStmt
);
2002 #ifdef DBDEBUG_CONSOLE
2003 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2006 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2008 // Check for "Base table not found" error and ignore
2009 GetNextError(henv
, hdbc
, hstmt
);
2010 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2012 // Check for product specific error codes
2013 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2016 DispAllErrors(henv
, hdbc
, hstmt
);
2023 // Commit the transaction
2029 } // wxDb::DropView()
2032 /********** wxDb::ExecSql() **********/
2033 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2037 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2039 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2040 if (retcode
== SQL_SUCCESS
||
2041 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2047 DispAllErrors(henv
, hdbc
, hstmt
);
2051 } // wxDb::ExecSql()
2054 /********** wxDb::GetNext() **********/
2055 bool wxDb::GetNext(void)
2057 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2061 DispAllErrors(henv
, hdbc
, hstmt
);
2065 } // wxDb::GetNext()
2068 /********** wxDb::GetData() **********/
2069 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2072 wxASSERT(cbReturned
);
2074 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2078 DispAllErrors(henv
, hdbc
, hstmt
);
2082 } // wxDb::GetData()
2085 /********** wxDb::GetKeyFields() **********/
2086 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2088 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2089 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2091 // SQLSMALLINT iKeySeq;
2092 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2093 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2099 * -----------------------------------------------------------------------
2100 * -- 19991224 : mj10777 : Create ------
2101 * -- : Three things are done and stored here : ------
2102 * -- : 1) which Column(s) is/are Primary Key(s) ------
2103 * -- : 2) which tables use this Key as a Foreign Key ------
2104 * -- : 3) which columns are Foreign Key and the name ------
2105 * -- : of the Table where the Key is the Primary Key -----
2106 * -- : Called from GetColumns(const wxString &tableName, ------
2107 * -- int *numCols,const wxChar *userID ) ------
2108 * -----------------------------------------------------------------------
2111 /*---------------------------------------------------------------------*/
2112 /* Get the names of the columns in the primary key. */
2113 /*---------------------------------------------------------------------*/
2114 retcode
= SQLPrimaryKeys(hstmt
,
2115 NULL
, 0, /* Catalog name */
2116 NULL
, 0, /* Schema name */
2117 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2119 /*---------------------------------------------------------------------*/
2120 /* Fetch and display the result set. This will be a list of the */
2121 /* columns in the primary key of the tableName table. */
2122 /*---------------------------------------------------------------------*/
2123 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2125 retcode
= SQLFetch(hstmt
);
2126 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2128 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2129 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2131 for (i
=0;i
<noCols
;i
++) // Find the Column name
2132 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2133 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2136 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2138 /*---------------------------------------------------------------------*/
2139 /* Get all the foreign keys that refer to tableName primary key. */
2140 /*---------------------------------------------------------------------*/
2141 retcode
= SQLForeignKeys(hstmt
,
2142 NULL
, 0, /* Primary catalog */
2143 NULL
, 0, /* Primary schema */
2144 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2145 NULL
, 0, /* Foreign catalog */
2146 NULL
, 0, /* Foreign schema */
2147 NULL
, 0); /* Foreign table */
2149 /*---------------------------------------------------------------------*/
2150 /* Fetch and display the result set. This will be all of the foreign */
2151 /* keys in other tables that refer to the tableName primary key. */
2152 /*---------------------------------------------------------------------*/
2155 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2157 retcode
= SQLFetch(hstmt
);
2158 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2160 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2161 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2162 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2163 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2164 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2165 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2169 tempStr
.Trim(); // Get rid of any unneeded blanks
2170 if (!tempStr
.IsEmpty())
2172 for (i
=0; i
<noCols
; i
++)
2173 { // Find the Column name
2174 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2175 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2179 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2181 /*---------------------------------------------------------------------*/
2182 /* Get all the foreign keys in the tablename table. */
2183 /*---------------------------------------------------------------------*/
2184 retcode
= SQLForeignKeys(hstmt
,
2185 NULL
, 0, /* Primary catalog */
2186 NULL
, 0, /* Primary schema */
2187 NULL
, 0, /* Primary table */
2188 NULL
, 0, /* Foreign catalog */
2189 NULL
, 0, /* Foreign schema */
2190 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2192 /*---------------------------------------------------------------------*/
2193 /* Fetch and display the result set. This will be all of the */
2194 /* primary keys in other tables that are referred to by foreign */
2195 /* keys in the tableName table. */
2196 /*---------------------------------------------------------------------*/
2198 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2200 retcode
= SQLFetch(hstmt
);
2201 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2203 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2204 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2205 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2207 for (i
=0; i
<noCols
; i
++) // Find the Column name
2209 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2211 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2212 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2217 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2221 } // wxDb::GetKeyFields()
2225 /********** wxDb::GetColumns() **********/
2226 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2228 * 1) The last array element of the tableName[] argument must be zero (null).
2229 * This is how the end of the array is detected.
2230 * 2) This function returns an array of wxDbColInf structures. If no columns
2231 * were found, or an error occured, this pointer will be zero (null). THE
2232 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2233 * IS FINISHED WITH IT. i.e.
2235 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2238 * // Use the column inf
2240 * // Destroy the memory
2244 * userID is evaluated in the following manner:
2245 * userID == NULL ... UserID is ignored
2246 * userID == "" ... UserID set equal to 'this->uid'
2247 * userID != "" ... UserID set equal to 'userID'
2249 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2250 * by this function. This function should use its own wxDb instance
2251 * to avoid undesired unbinding of columns.
2256 wxDbColInf
*colInf
= 0;
2264 convertUserID(userID
,UserID
);
2266 // Pass 1 - Determine how many columns there are.
2267 // Pass 2 - Allocate the wxDbColInf array and fill in
2268 // the array with the column information.
2270 for (pass
= 1; pass
<= 2; pass
++)
2274 if (noCols
== 0) // Probably a bogus table name(s)
2276 // Allocate n wxDbColInf objects to hold the column information
2277 colInf
= new wxDbColInf
[noCols
+1];
2280 // Mark the end of the array
2281 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2282 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2283 colInf
[noCols
].sqlDataType
= 0;
2285 // Loop through each table name
2287 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2289 TableName
= tableName
[tbl
];
2290 // Oracle and Interbase table names are uppercase only, so force
2291 // the name to uppercase just in case programmer forgot to do this
2292 if ((Dbms() == dbmsORACLE
) ||
2293 (Dbms() == dbmsINTERBASE
))
2294 TableName
= TableName
.Upper();
2296 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2298 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2299 // use the call below that leaves out the user name
2300 if (!UserID
.IsEmpty() &&
2301 Dbms() != dbmsMY_SQL
&&
2302 Dbms() != dbmsACCESS
&&
2303 Dbms() != dbmsMS_SQL_SERVER
)
2305 retcode
= SQLColumns(hstmt
,
2306 NULL
, 0, // All qualifiers
2307 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2308 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2309 NULL
, 0); // All columns
2313 retcode
= SQLColumns(hstmt
,
2314 NULL
, 0, // All qualifiers
2316 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2317 NULL
, 0); // All columns
2319 if (retcode
!= SQL_SUCCESS
)
2320 { // Error occured, abort
2321 DispAllErrors(henv
, hdbc
, hstmt
);
2324 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2328 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2330 if (pass
== 1) // First pass, just add up the number of columns
2332 else // Pass 2; Fill in the array of structures
2334 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2336 // NOTE: Only the ODBC 1.x fields are retrieved
2337 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2338 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2339 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2340 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2341 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2342 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2343 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2344 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2345 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2346 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2347 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2348 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2350 // Determine the wxDb data type that is used to represent the native data type of this data source
2351 colInf
[colNo
].dbDataType
= 0;
2352 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2355 // IODBC does not return a correct columnSize, so we set
2356 // columnSize = bufferLength if no column size was returned
2357 // IODBC returns the columnSize in bufferLength.. (bug)
2358 if (colInf
[colNo
].columnSize
< 1)
2360 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2363 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2365 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2366 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2367 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2368 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2369 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2370 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2371 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2372 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2377 if (retcode
!= SQL_NO_DATA_FOUND
)
2378 { // Error occured, abort
2379 DispAllErrors(henv
, hdbc
, hstmt
);
2382 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2388 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2391 } // wxDb::GetColumns()
2394 /********** wxDb::GetColumns() **********/
2396 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2398 // Same as the above GetColumns() function except this one gets columns
2399 // only for a single table, and if 'numCols' is not NULL, the number of
2400 // columns stored in the returned wxDbColInf is set in '*numCols'
2402 // userID is evaluated in the following manner:
2403 // userID == NULL ... UserID is ignored
2404 // userID == "" ... UserID set equal to 'this->uid'
2405 // userID != "" ... UserID set equal to 'userID'
2407 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2408 // by this function. This function should use its own wxDb instance
2409 // to avoid undesired unbinding of columns.
2414 wxDbColInf
*colInf
= 0;
2422 convertUserID(userID
,UserID
);
2424 // Pass 1 - Determine how many columns there are.
2425 // Pass 2 - Allocate the wxDbColInf array and fill in
2426 // the array with the column information.
2428 for (pass
= 1; pass
<= 2; pass
++)
2432 if (noCols
== 0) // Probably a bogus table name(s)
2434 // Allocate n wxDbColInf objects to hold the column information
2435 colInf
= new wxDbColInf
[noCols
+1];
2438 // Mark the end of the array
2439 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2440 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2441 colInf
[noCols
].sqlDataType
= 0;
2444 TableName
= tableName
;
2445 // Oracle and Interbase table names are uppercase only, so force
2446 // the name to uppercase just in case programmer forgot to do this
2447 if ((Dbms() == dbmsORACLE
) ||
2448 (Dbms() == dbmsINTERBASE
))
2449 TableName
= TableName
.Upper();
2451 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2453 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2454 // use the call below that leaves out the user name
2455 if (!UserID
.IsEmpty() &&
2456 Dbms() != dbmsMY_SQL
&&
2457 Dbms() != dbmsACCESS
&&
2458 Dbms() != dbmsMS_SQL_SERVER
)
2460 retcode
= SQLColumns(hstmt
,
2461 NULL
, 0, // All qualifiers
2462 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2463 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2464 NULL
, 0); // All columns
2468 retcode
= SQLColumns(hstmt
,
2469 NULL
, 0, // All qualifiers
2471 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2472 NULL
, 0); // All columns
2474 if (retcode
!= SQL_SUCCESS
)
2475 { // Error occured, abort
2476 DispAllErrors(henv
, hdbc
, hstmt
);
2479 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2485 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2487 if (pass
== 1) // First pass, just add up the number of columns
2489 else // Pass 2; Fill in the array of structures
2491 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2493 // NOTE: Only the ODBC 1.x fields are retrieved
2494 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2495 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2496 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2497 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2498 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2499 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2500 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2501 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2502 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2503 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2504 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2505 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2506 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2507 // Start Values for Primary/Foriegn Key (=No)
2508 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2509 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2510 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2511 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2513 // BJO 20000428 : Virtuoso returns type names with upper cases!
2514 if (Dbms() == dbmsVIRTUOSO
)
2516 wxString s
= colInf
[colNo
].typeName
;
2518 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2521 // Determine the wxDb data type that is used to represent the native data type of this data source
2522 colInf
[colNo
].dbDataType
= 0;
2523 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2526 // IODBC does not return a correct columnSize, so we set
2527 // columnSize = bufferLength if no column size was returned
2528 // IODBC returns the columnSize in bufferLength.. (bug)
2529 if (colInf
[colNo
].columnSize
< 1)
2531 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2535 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2537 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2538 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2539 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2540 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2541 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2542 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2543 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2544 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2550 if (retcode
!= SQL_NO_DATA_FOUND
)
2551 { // Error occured, abort
2552 DispAllErrors(henv
, hdbc
, hstmt
);
2555 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2562 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2564 // Store Primary and Foriegn Keys
2565 GetKeyFields(tableName
,colInf
,noCols
);
2571 } // wxDb::GetColumns()
2574 #else // New GetColumns
2579 These are tentative new GetColumns members which should be more database
2580 independant and which always returns the columns in the order they were
2583 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2584 wxChar* userID)) calls the second implementation for each separate table
2585 before merging the results. This makes the code easier to maintain as
2586 only one member (the second) makes the real work
2587 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2588 wxChar *userID) is a little bit improved
2589 - It doesn't anymore rely on the type-name to find out which database-type
2591 - It ends by sorting the columns, so that they are returned in the same
2592 order they were created
2602 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2605 // The last array element of the tableName[] argument must be zero (null).
2606 // This is how the end of the array is detected.
2610 // How many tables ?
2612 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2614 // Create a table to maintain the columns for each separate table
2615 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2618 for (i
= 0 ; i
< tbl
; i
++)
2621 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2622 if (TableColumns
[i
].colInf
== NULL
)
2624 noCols
+= TableColumns
[i
].noCols
;
2627 // Now merge all the separate table infos
2628 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2630 // Mark the end of the array
2631 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2632 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2633 colInf
[noCols
].sqlDataType
= 0;
2638 for (i
= 0 ; i
< tbl
; i
++)
2640 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2642 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2646 delete [] TableColumns
;
2649 } // wxDb::GetColumns() -- NEW
2652 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2654 // Same as the above GetColumns() function except this one gets columns
2655 // only for a single table, and if 'numCols' is not NULL, the number of
2656 // columns stored in the returned wxDbColInf is set in '*numCols'
2658 // userID is evaluated in the following manner:
2659 // userID == NULL ... UserID is ignored
2660 // userID == "" ... UserID set equal to 'this->uid'
2661 // userID != "" ... UserID set equal to 'userID'
2663 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2664 // by this function. This function should use its own wxDb instance
2665 // to avoid undesired unbinding of columns.
2669 wxDbColInf
*colInf
= 0;
2677 convertUserID(userID
,UserID
);
2679 // Pass 1 - Determine how many columns there are.
2680 // Pass 2 - Allocate the wxDbColInf array and fill in
2681 // the array with the column information.
2683 for (pass
= 1; pass
<= 2; pass
++)
2687 if (noCols
== 0) // Probably a bogus table name(s)
2689 // Allocate n wxDbColInf objects to hold the column information
2690 colInf
= new wxDbColInf
[noCols
+1];
2693 // Mark the end of the array
2694 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2695 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2696 colInf
[noCols
].sqlDataType
= 0;
2699 TableName
= tableName
;
2700 // Oracle and Interbase table names are uppercase only, so force
2701 // the name to uppercase just in case programmer forgot to do this
2702 if ((Dbms() == dbmsORACLE
) ||
2703 (Dbms() == dbmsINTERBASE
))
2704 TableName
= TableName
.Upper();
2706 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2708 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2709 // use the call below that leaves out the user name
2710 if (!UserID
.IsEmpty() &&
2711 Dbms() != dbmsMY_SQL
&&
2712 Dbms() != dbmsACCESS
&&
2713 Dbms() != dbmsMS_SQL_SERVER
)
2715 retcode
= SQLColumns(hstmt
,
2716 NULL
, 0, // All qualifiers
2717 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2718 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2719 NULL
, 0); // All columns
2723 retcode
= SQLColumns(hstmt
,
2724 NULL
, 0, // All qualifiers
2726 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2727 NULL
, 0); // All columns
2729 if (retcode
!= SQL_SUCCESS
)
2730 { // Error occured, abort
2731 DispAllErrors(henv
, hdbc
, hstmt
);
2734 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2740 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2742 if (pass
== 1) // First pass, just add up the number of columns
2744 else // Pass 2; Fill in the array of structures
2746 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2748 // NOTE: Only the ODBC 1.x fields are retrieved
2749 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2750 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2751 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2752 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2753 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2754 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2755 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2756 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2757 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2758 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2759 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2760 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2761 // Start Values for Primary/Foriegn Key (=No)
2762 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2763 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2764 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2765 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2768 // IODBC does not return a correct columnSize, so we set
2769 // columnSize = bufferLength if no column size was returned
2770 // IODBC returns the columnSize in bufferLength.. (bug)
2771 if (colInf
[colNo
].columnSize
< 1)
2773 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2777 // Determine the wxDb data type that is used to represent the native data type of this data source
2778 colInf
[colNo
].dbDataType
= 0;
2779 // Get the intern datatype
2780 switch (colInf
[colNo
].sqlDataType
)
2784 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2790 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2797 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2800 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2803 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2808 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2809 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2816 if (retcode
!= SQL_NO_DATA_FOUND
)
2817 { // Error occured, abort
2818 DispAllErrors(henv
, hdbc
, hstmt
);
2821 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2828 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2830 // Store Primary and Foreign Keys
2831 GetKeyFields(tableName
,colInf
,noCols
);
2833 ///////////////////////////////////////////////////////////////////////////
2834 // Now sort the the columns in order to make them appear in the right order
2835 ///////////////////////////////////////////////////////////////////////////
2837 // Build a generic SELECT statement which returns 0 rows
2840 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2843 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2845 DispAllErrors(henv
, hdbc
, hstmt
);
2849 // Get the number of result columns
2850 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2852 DispAllErrors(henv
, hdbc
, hstmt
);
2856 if (noCols
== 0) // Probably a bogus table name
2865 for (colNum
= 0; colNum
< noCols
; colNum
++)
2867 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2869 &Sword
, &Sdword
) != SQL_SUCCESS
)
2871 DispAllErrors(henv
, hdbc
, hstmt
);
2875 wxString Name1
= name
;
2876 Name1
= Name1
.Upper();
2878 // Where is this name in the array ?
2879 for (i
= colNum
; i
< noCols
; i
++)
2881 wxString Name2
= colInf
[i
].colName
;
2882 Name2
= Name2
.Upper();
2885 if (colNum
!= i
) // swap to sort
2887 wxDbColInf tmpColInf
= colInf
[colNum
];
2888 colInf
[colNum
] = colInf
[i
];
2889 colInf
[i
] = tmpColInf
;
2895 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2897 ///////////////////////////////////////////////////////////////////////////
2899 ///////////////////////////////////////////////////////////////////////////
2905 } // wxDb::GetColumns()
2908 #endif // #else OLD_GETCOLUMNS
2911 /********** wxDb::GetColumnCount() **********/
2912 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2914 * Returns a count of how many columns are in a table.
2915 * If an error occurs in computing the number of columns
2916 * this function will return a -1 for the count
2918 * userID is evaluated in the following manner:
2919 * userID == NULL ... UserID is ignored
2920 * userID == "" ... UserID set equal to 'this->uid'
2921 * userID != "" ... UserID set equal to 'userID'
2923 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2924 * by this function. This function should use its own wxDb instance
2925 * to avoid undesired unbinding of columns.
2935 convertUserID(userID
,UserID
);
2937 TableName
= tableName
;
2938 // Oracle and Interbase table names are uppercase only, so force
2939 // the name to uppercase just in case programmer forgot to do this
2940 if ((Dbms() == dbmsORACLE
) ||
2941 (Dbms() == dbmsINTERBASE
))
2942 TableName
= TableName
.Upper();
2944 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2946 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2947 // use the call below that leaves out the user name
2948 if (!UserID
.IsEmpty() &&
2949 Dbms() != dbmsMY_SQL
&&
2950 Dbms() != dbmsACCESS
&&
2951 Dbms() != dbmsMS_SQL_SERVER
)
2953 retcode
= SQLColumns(hstmt
,
2954 NULL
, 0, // All qualifiers
2955 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2956 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2957 NULL
, 0); // All columns
2961 retcode
= SQLColumns(hstmt
,
2962 NULL
, 0, // All qualifiers
2964 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2965 NULL
, 0); // All columns
2967 if (retcode
!= SQL_SUCCESS
)
2968 { // Error occured, abort
2969 DispAllErrors(henv
, hdbc
, hstmt
);
2970 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2974 // Count the columns
2975 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2978 if (retcode
!= SQL_NO_DATA_FOUND
)
2979 { // Error occured, abort
2980 DispAllErrors(henv
, hdbc
, hstmt
);
2981 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2985 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2988 } // wxDb::GetColumnCount()
2991 /********** wxDb::GetCatalog() *******/
2992 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2994 * ---------------------------------------------------------------------
2995 * -- 19991203 : mj10777 : Create ------
2996 * -- : Creates a wxDbInf with Tables / Cols Array ------
2997 * -- : uses SQLTables and fills pTableInf; ------
2998 * -- : pColInf is set to NULL and numCols to 0; ------
2999 * -- : returns pDbInf (wxDbInf) ------
3000 * -- - if unsuccesfull (pDbInf == NULL) ------
3001 * -- : pColInf can be filled with GetColumns(..); ------
3002 * -- : numCols can be filled with GetColumnCount(..); ------
3003 * ---------------------------------------------------------------------
3005 * userID is evaluated in the following manner:
3006 * userID == NULL ... UserID is ignored
3007 * userID == "" ... UserID set equal to 'this->uid'
3008 * userID != "" ... UserID set equal to 'userID'
3010 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3011 * by this function. This function should use its own wxDb instance
3012 * to avoid undesired unbinding of columns.
3015 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
3016 int noTab
= 0; // Counter while filling table entries
3020 wxString tblNameSave
;
3023 convertUserID(userID
,UserID
);
3025 //-------------------------------------------------------------
3026 pDbInf
= new wxDbInf
; // Create the Database Array
3027 //-------------------------------------------------------------
3028 // Table Information
3029 // Pass 1 - Determine how many Tables there are.
3030 // Pass 2 - Create the Table array and fill it
3031 // - Create the Cols array = NULL
3032 //-------------------------------------------------------------
3034 for (pass
= 1; pass
<= 2; pass
++)
3036 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3037 tblNameSave
.Empty();
3039 if (!UserID
.IsEmpty() &&
3040 Dbms() != dbmsMY_SQL
&&
3041 Dbms() != dbmsACCESS
&&
3042 Dbms() != dbmsMS_SQL_SERVER
)
3044 retcode
= SQLTables(hstmt
,
3045 NULL
, 0, // All qualifiers
3046 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3047 NULL
, 0, // All tables
3048 NULL
, 0); // All columns
3052 retcode
= SQLTables(hstmt
,
3053 NULL
, 0, // All qualifiers
3054 NULL
, 0, // User specified
3055 NULL
, 0, // All tables
3056 NULL
, 0); // All columns
3059 if (retcode
!= SQL_SUCCESS
)
3061 DispAllErrors(henv
, hdbc
, hstmt
);
3063 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3067 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3069 if (pass
== 1) // First pass, just count the Tables
3071 if (pDbInf
->numTables
== 0)
3073 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3074 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3076 pDbInf
->numTables
++; // Counter for Tables
3078 if (pass
== 2) // Create and fill the Table entries
3080 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3081 { // no, then create the Array
3082 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3084 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3086 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3087 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3088 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3094 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3096 // Query how many columns are in each table
3097 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3099 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3104 } // wxDb::GetCatalog()
3107 /********** wxDb::Catalog() **********/
3108 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3110 * Creates the text file specified in 'filename' which will contain
3111 * a minimal data dictionary of all tables accessible by the user specified
3114 * userID is evaluated in the following manner:
3115 * userID == NULL ... UserID is ignored
3116 * userID == "" ... UserID set equal to 'this->uid'
3117 * userID != "" ... UserID set equal to 'userID'
3119 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3120 * by this function. This function should use its own wxDb instance
3121 * to avoid undesired unbinding of columns.
3124 wxASSERT(fileName
.Length());
3128 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3129 wxString tblNameSave
;
3130 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3132 wxChar typeName
[30+1];
3133 SDWORD precision
, length
;
3135 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3139 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3142 convertUserID(userID
,UserID
);
3144 if (!UserID
.IsEmpty() &&
3145 Dbms() != dbmsMY_SQL
&&
3146 Dbms() != dbmsACCESS
&&
3147 Dbms() != dbmsINTERBASE
&&
3148 Dbms() != dbmsMS_SQL_SERVER
)
3150 retcode
= SQLColumns(hstmt
,
3151 NULL
, 0, // All qualifiers
3152 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3153 NULL
, 0, // All tables
3154 NULL
, 0); // All columns
3158 retcode
= SQLColumns(hstmt
,
3159 NULL
, 0, // All qualifiers
3160 NULL
, 0, // User specified
3161 NULL
, 0, // All tables
3162 NULL
, 0); // All columns
3164 if (retcode
!= SQL_SUCCESS
)
3166 DispAllErrors(henv
, hdbc
, hstmt
);
3172 tblNameSave
.Empty();
3177 retcode
= SQLFetch(hstmt
);
3178 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3181 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3184 wxFputs(wxT("\n"), fp
);
3185 wxFputs(wxT("================================ "), fp
);
3186 wxFputs(wxT("================================ "), fp
);
3187 wxFputs(wxT("===================== "), fp
);
3188 wxFputs(wxT("========= "), fp
);
3189 wxFputs(wxT("=========\n"), fp
);
3190 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3191 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3192 wxFputs(outStr
.c_str(), fp
);
3193 wxFputs(wxT("================================ "), fp
);
3194 wxFputs(wxT("================================ "), fp
);
3195 wxFputs(wxT("===================== "), fp
);
3196 wxFputs(wxT("========= "), fp
);
3197 wxFputs(wxT("=========\n"), fp
);
3198 tblNameSave
= tblName
;
3201 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3202 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3203 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3204 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3205 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3206 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3208 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3209 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3210 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3212 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3219 if (retcode
!= SQL_NO_DATA_FOUND
)
3220 DispAllErrors(henv
, hdbc
, hstmt
);
3222 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3225 return(retcode
== SQL_NO_DATA_FOUND
);
3227 } // wxDb::Catalog()
3230 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3232 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3233 * if the object exists in the database. This function does not indicate
3234 * whether or not the user has privleges to query or perform other functions
3237 * userID is evaluated in the following manner:
3238 * userID == NULL ... UserID is ignored
3239 * userID == "" ... UserID set equal to 'this->uid'
3240 * userID != "" ... UserID set equal to 'userID'
3243 wxASSERT(tableName
.Length());
3247 if (Dbms() == dbmsDBASE
)
3250 if (tablePath
.Length())
3251 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3253 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3256 exists
= wxFileExists(dbName
);
3261 convertUserID(userID
,UserID
);
3263 TableName
= tableName
;
3264 // Oracle and Interbase table names are uppercase only, so force
3265 // the name to uppercase just in case programmer forgot to do this
3266 if ((Dbms() == dbmsORACLE
) ||
3267 (Dbms() == dbmsINTERBASE
))
3268 TableName
= TableName
.Upper();
3270 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3273 // Some databases cannot accept a user name when looking up table names,
3274 // so we use the call below that leaves out the user name
3275 if (!UserID
.IsEmpty() &&
3276 Dbms() != dbmsMY_SQL
&&
3277 Dbms() != dbmsACCESS
&&
3278 Dbms() != dbmsMS_SQL_SERVER
&&
3279 Dbms() != dbmsDB2
&&
3280 Dbms() != dbmsINTERBASE
&&
3281 Dbms() != dbmsPERVASIVE_SQL
)
3283 retcode
= SQLTables(hstmt
,
3284 NULL
, 0, // All qualifiers
3285 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3286 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3287 NULL
, 0); // All table types
3291 retcode
= SQLTables(hstmt
,
3292 NULL
, 0, // All qualifiers
3293 NULL
, 0, // All owners
3294 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3295 NULL
, 0); // All table types
3297 if (retcode
!= SQL_SUCCESS
)
3298 return(DispAllErrors(henv
, hdbc
, hstmt
));
3300 retcode
= SQLFetch(hstmt
);
3301 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3303 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3304 return(DispAllErrors(henv
, hdbc
, hstmt
));
3307 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3311 } // wxDb::TableExists()
3314 /********** wxDb::TablePrivileges() **********/
3315 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3316 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3318 wxASSERT(tableName
.Length());
3320 wxDbTablePrivilegeInfo result
;
3324 // We probably need to be able to dynamically set this based on
3325 // the driver type, and state.
3326 wxChar curRole
[]=wxT("public");
3330 wxString UserID
,Schema
;
3331 convertUserID(userID
,UserID
);
3332 convertUserID(schema
,Schema
);
3334 TableName
= tableName
;
3335 // Oracle and Interbase table names are uppercase only, so force
3336 // the name to uppercase just in case programmer forgot to do this
3337 if ((Dbms() == dbmsORACLE
) ||
3338 (Dbms() == dbmsINTERBASE
))
3339 TableName
= TableName
.Upper();
3341 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3343 // Some databases cannot accept a user name when looking up table names,
3344 // so we use the call below that leaves out the user name
3345 if (!Schema
.IsEmpty() &&
3346 Dbms() != dbmsMY_SQL
&&
3347 Dbms() != dbmsACCESS
&&
3348 Dbms() != dbmsMS_SQL_SERVER
)
3350 retcode
= SQLTablePrivileges(hstmt
,
3352 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3353 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3357 retcode
= SQLTablePrivileges(hstmt
,
3360 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3363 #ifdef DBDEBUG_CONSOLE
3364 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3367 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3368 return(DispAllErrors(henv
, hdbc
, hstmt
));
3370 bool failed
= FALSE
;
3371 retcode
= SQLFetch(hstmt
);
3372 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3374 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3377 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3380 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3383 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3386 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3389 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3392 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3397 return(DispAllErrors(henv
, hdbc
, hstmt
));
3399 #ifdef DBDEBUG_CONSOLE
3400 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3401 result
.privilege
,result
.tableOwner
,result
.tableName
,
3402 result
.grantor
, result
.grantee
);
3405 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3407 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3411 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3412 !wxStrcmp(result
.privilege
,priv
))
3414 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3418 if (!wxStrcmp(result
.grantee
,curRole
) &&
3419 !wxStrcmp(result
.privilege
,priv
))
3421 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3425 retcode
= SQLFetch(hstmt
);
3428 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3431 } // wxDb::TablePrivileges
3434 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3438 if (Dbms() == dbmsACCESS
)
3439 TableName
= _T("\"");
3440 TableName
+= tableName
;
3441 if (Dbms() == dbmsACCESS
)
3442 TableName
+= _T("\"");
3445 } // wxDb::SQLTableName()
3448 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3452 if (Dbms() == dbmsACCESS
)
3455 if (Dbms() == dbmsACCESS
)
3456 ColName
+= _T("\"");
3459 } // wxDb::SQLColumnName()
3462 /********** wxDb::SetSqlLogging() **********/
3463 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3465 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3466 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3468 if (state
== sqlLogON
)
3472 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3473 if (fpSqlLog
== NULL
)
3481 if (fclose(fpSqlLog
))
3487 sqlLogState
= state
;
3490 } // wxDb::SetSqlLogging()
3493 /********** wxDb::WriteSqlLog() **********/
3494 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3496 wxASSERT(logMsg
.Length());
3498 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3501 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3503 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3505 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3510 } // wxDb::WriteSqlLog()
3513 /********** wxDb::Dbms() **********/
3514 wxDBMS
wxDb::Dbms(void)
3516 * Be aware that not all database engines use the exact same syntax, and not
3517 * every ODBC compliant database is compliant to the same level of compliancy.
3518 * Some manufacturers support the minimum Level 1 compliancy, and others up
3519 * through Level 3. Others support subsets of features for levels above 1.
3521 * If you find an inconsistency between the wxDb class and a specific database
3522 * engine, and an identifier to this section, and special handle the database in
3523 * the area where behavior is non-conforming with the other databases.
3526 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3527 * ---------------------------------------------------
3530 * - Currently the only database supported by the class to support VIEWS
3533 * - Does not support the SQL_TIMESTAMP structure
3534 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3535 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3536 * is TRUE. The user must create ALL indexes from their program.
3537 * - Table names can only be 8 characters long
3538 * - Column names can only be 10 characters long
3541 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3542 * after every table name involved in the query/join if that tables matching record(s)
3544 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3546 * SYBASE (Enterprise)
3547 * - If a column is part of the Primary Key, the column cannot be NULL
3548 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3551 * - If a column is part of the Primary Key, the column cannot be NULL
3552 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3553 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3554 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3555 * column definition if it is not defined correctly, but it is experimental
3556 * - Does not support sub-queries in SQL statements
3559 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3560 * - Does not support sub-queries in SQL statements
3563 * - Primary keys must be declared as NOT NULL
3564 * - Table and index names must not be longer than 13 characters in length (technically
3565 * table names can be up to 18 characters, but the primary index is created using the
3566 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3571 * - Columns that are part of primary keys must be defined as being NOT NULL
3572 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3573 * column definition if it is not defined correctly, but it is experimental
3576 // Should only need to do this once for each new database connection
3577 // so return the value we already determined it to be to save time
3578 // and lots of string comparisons
3579 if (dbmsType
!= dbmsUNIDENTIFIED
)
3582 wxChar baseName
[25+1];
3583 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3586 // RGG 20001025 : add support for Interbase
3587 // GT : Integrated to base classes on 20001121
3588 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3589 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3591 // BJO 20000428 : add support for Virtuoso
3592 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3593 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3595 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3596 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3598 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3599 // connected through an OpenLink driver.
3600 // Is it also returned by Sybase Adapatitve server?
3601 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3602 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3604 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3605 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3606 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3608 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3611 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3612 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3615 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3616 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3619 if (!wxStricmp(baseName
,wxT("Pervasive")))
3620 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3623 if (!wxStricmp(baseName
,wxT("Informix")))
3624 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3627 if (!wxStricmp(baseName
,wxT("Oracle")))
3628 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3629 if (!wxStricmp(baseName
,wxT("ACCESS")))
3630 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3631 if (!wxStricmp(baseName
,wxT("Sybase")))
3632 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3635 if (!wxStricmp(baseName
,wxT("DBASE")))
3636 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3637 if (!wxStricmp(baseName
,wxT("xBase")))
3638 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3639 if (!wxStricmp(baseName
,wxT("MySQL")))
3640 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3643 if (!wxStricmp(baseName
,wxT("DB2")))
3644 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3646 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3651 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3652 int dataType
, ULONG columnLength
,
3653 const wxString
&optionalParam
)
3655 wxASSERT(tableName
.Length());
3656 wxASSERT(columnName
.Length());
3657 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3658 dataType
!= DB_DATA_TYPE_VARCHAR
);
3660 // Must specify a columnLength if modifying a VARCHAR type column
3661 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3664 wxString dataTypeName
;
3666 wxString alterSlashModify
;
3670 case DB_DATA_TYPE_VARCHAR
:
3671 dataTypeName
= typeInfVarchar
.TypeName
;
3673 case DB_DATA_TYPE_INTEGER
:
3674 dataTypeName
= typeInfInteger
.TypeName
;
3676 case DB_DATA_TYPE_FLOAT
:
3677 dataTypeName
= typeInfFloat
.TypeName
;
3679 case DB_DATA_TYPE_DATE
:
3680 dataTypeName
= typeInfDate
.TypeName
;
3682 case DB_DATA_TYPE_BLOB
:
3683 dataTypeName
= typeInfBlob
.TypeName
;
3689 // Set the modify or alter syntax depending on the type of database connected to
3693 alterSlashModify
= _T("MODIFY");
3695 case dbmsMS_SQL_SERVER
:
3696 alterSlashModify
= _T("ALTER COLUMN");
3698 case dbmsUNIDENTIFIED
:
3700 case dbmsSYBASE_ASA
:
3701 case dbmsSYBASE_ASE
:
3706 case dbmsXBASE_SEQUITER
:
3708 alterSlashModify
= _T("MODIFY");
3712 // create the SQL statement
3713 if ( Dbms() == dbmsMY_SQL
)
3715 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3716 columnName
.c_str(), dataTypeName
.c_str());
3720 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3721 columnName
.c_str(), dataTypeName
.c_str());
3724 // For varchars only, append the size of the column
3725 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3726 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3729 s
.Printf(wxT("(%lu)"), columnLength
);
3733 // for passing things like "NOT NULL"
3734 if (optionalParam
.Length())
3736 sqlStmt
+= wxT(" ");
3737 sqlStmt
+= optionalParam
;
3740 return ExecSql(sqlStmt
);
3742 } // wxDb::ModifyColumn()
3745 /********** wxDbGetConnection() **********/
3746 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3750 // Used to keep a pointer to a DB connection that matches the requested
3751 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3752 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3753 // rather than having to re-query the datasource to get all the values
3754 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3755 wxDb
*matchingDbConnection
= NULL
;
3757 // Scan the linked list searching for an available database connection
3758 // that's already been opened but is currently not in use.
3759 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3761 // The database connection must be for the same datasource
3762 // name and must currently not be in use.
3764 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3765 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3767 pList
->Free
= FALSE
;
3768 return(pList
->PtrDb
);
3771 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3772 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3773 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3774 matchingDbConnection
= pList
->PtrDb
;
3777 // No available connections. A new connection must be made and
3778 // appended to the end of the linked list.
3781 // Find the end of the list
3782 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3783 // Append a new list item
3784 pList
->PtrNext
= new wxDbList
;
3785 pList
->PtrNext
->PtrPrev
= pList
;
3786 pList
= pList
->PtrNext
;
3790 // Create the first node on the list
3791 pList
= PtrBegDbList
= new wxDbList
;
3795 // Initialize new node in the linked list
3797 pList
->Free
= FALSE
;
3798 pList
->Dsn
= pDbConfig
->GetDsn();
3799 pList
->Uid
= pDbConfig
->GetUserID();
3800 pList
->AuthStr
= pDbConfig
->GetPassword();
3802 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3804 bool opened
= FALSE
;
3806 if (!matchingDbConnection
)
3807 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3809 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3811 // Connect to the datasource
3814 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3815 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3816 return(pList
->PtrDb
);
3818 else // Unable to connect, destroy list item
3821 pList
->PtrPrev
->PtrNext
= 0;
3823 PtrBegDbList
= 0; // Empty list again
3824 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3825 pList
->PtrDb
->Close(); // Close the wxDb object
3826 delete pList
->PtrDb
; // Deletes the wxDb object
3827 delete pList
; // Deletes the linked list object
3831 } // wxDbGetConnection()
3834 /********** wxDbFreeConnection() **********/
3835 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3839 // Scan the linked list searching for the database connection
3840 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3842 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3843 return (pList
->Free
= TRUE
);
3846 // Never found the database object, return failure
3849 } // wxDbFreeConnection()
3852 /********** wxDbCloseConnections() **********/
3853 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3855 wxDbList
*pList
, *pNext
;
3857 // Traverse the linked list closing database connections and freeing memory as I go.
3858 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3860 pNext
= pList
->PtrNext
; // Save the pointer to next
3861 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3862 pList
->PtrDb
->Close(); // Close the wxDb object
3863 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3864 delete pList
->PtrDb
; // Deletes the wxDb object
3865 delete pList
; // Deletes the linked list object
3868 // Mark the list as empty
3871 } // wxDbCloseConnections()
3874 /********** wxDbConnectionsInUse() **********/
3875 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
3880 // Scan the linked list counting db connections that are currently in use
3881 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3883 if (pList
->Free
== FALSE
)
3889 } // wxDbConnectionsInUse()
3893 /********** wxDbLogExtendedErrorMsg() **********/
3894 // DEBUG ONLY function
3895 const wxChar
* WXDLLIMPEXP_ODBC
wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3897 const wxChar
*ErrFile
,
3900 static wxString msg
;
3905 if (ErrFile
|| ErrLine
)
3907 msg
+= wxT("File: ");
3909 msg
+= wxT(" Line: ");
3910 tStr
.Printf(wxT("%d"),ErrLine
);
3911 msg
+= tStr
.c_str();
3915 msg
.Append (wxT("\nODBC errors:\n"));
3918 // Display errors for this connection
3920 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3922 if (pDb
->errorList
[i
])
3924 msg
.Append(pDb
->errorList
[i
]);
3925 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3926 msg
.Append(wxT("\n"));
3927 // Clear the errmsg buffer so the next error will not
3928 // end up showing the previous error that have occurred
3929 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3934 wxLogDebug(msg
.c_str());
3937 } // wxDbLogExtendedErrorMsg()
3940 /********** wxDbSqlLog() **********/
3941 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3943 bool append
= FALSE
;
3946 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3948 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3953 SQLLOGstate
= state
;
3954 SQLLOGfn
= filename
;
3962 /********** wxDbCreateDataSource() **********/
3963 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3964 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3966 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3967 * Very rudimentary creation of an ODBC data source.
3969 * ODBC driver must be ODBC 3.0 compliant to use this function
3974 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3980 dsnLocation
= ODBC_ADD_SYS_DSN
;
3982 dsnLocation
= ODBC_ADD_DSN
;
3984 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3985 // so that is why I used it, as wxString does not deal well with
3986 // embedded nulls in strings
3987 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3989 // Replace the separator from above with the '\0' seperator needed
3990 // by the SQLConfigDataSource() function
3994 k
= setupStr
.Find((wxChar
)2,TRUE
);
3995 if (k
!= wxNOT_FOUND
)
3996 setupStr
[(UINT
)k
] = wxT('\0');
3998 while (k
!= wxNOT_FOUND
);
4000 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4001 driverName
, setupStr
.c_str());
4003 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4005 // check for errors caused by ConfigDSN based functions
4008 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4009 errMsg
[0] = wxT('\0');
4011 // This function is only supported in ODBC drivers v3.0 compliant and above
4012 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4015 #ifdef DBDEBUG_CONSOLE
4016 // When run in console mode, use standard out to display errors.
4017 cout
<< errMsg
<< endl
;
4018 cout
<< wxT("Press any key to continue...") << endl
;
4020 #endif // DBDEBUG_CONSOLE
4023 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4024 #endif // __WXDEBUG__
4030 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4031 // necessary to use this function, so this function is not supported
4033 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4036 #endif // __VISUALC__
4040 } // wxDbCreateDataSource()
4044 /********** wxDbGetDataSource() **********/
4045 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4046 SWORD DsDescMax
, UWORD direction
)
4048 * Dsn and DsDesc will contain the data source name and data source
4049 * description upon return
4054 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4055 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4060 } // wxDbGetDataSource()
4063 // Change this to 0 to remove use of all deprecated functions
4064 #if wxODBC_BACKWARD_COMPATABILITY
4065 /********************************************************************
4066 ********************************************************************
4068 * The following functions are all DEPRECATED and are included for
4069 * backward compatability reasons only
4071 ********************************************************************
4072 ********************************************************************/
4073 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4075 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4077 /***** DEPRECATED: use wxGetDataSource() *****/
4078 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4081 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4083 /***** DEPRECATED: use wxDbGetConnection() *****/
4084 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4086 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4088 /***** DEPRECATED: use wxDbFreeConnection() *****/
4089 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4091 return wxDbFreeConnection(pDb
);
4093 /***** DEPRECATED: use wxDbCloseConnections() *****/
4094 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4096 wxDbCloseConnections();
4098 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4099 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4101 return wxDbConnectionsInUse();