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
;
600 wxUnusedVar( retcode
);
604 // Connect to the data source
605 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
606 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
607 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
609 if ((retcode
!= SQL_SUCCESS
) &&
610 (retcode
!= SQL_SUCCESS_WITH_INFO
))
611 return(DispAllErrors(henv
, hdbc
));
614 If using Intersolv branded ODBC drivers, this is the place where you would substitute
615 your branded driver license information
617 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
618 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
621 // Mark database as open
624 // Allocate a statement handle for the database connection
625 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
626 return(DispAllErrors(henv
, hdbc
));
628 // Set Connection Options
629 if (!setConnectionOptions())
632 // Query the data source for inf. about itself
633 if (!getDbInfo(failOnDataTypeUnsupported
))
636 // Query the data source regarding data type information
639 // The way it was determined which SQL data types to use was by calling SQLGetInfo
640 // for all of the possible SQL data types to see which ones were supported. If
641 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
642 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
643 // types I've selected below will not alway's be what we want. These are just
644 // what happened to work against an Oracle 7/Intersolv combination. The following is
645 // a complete list of the results I got back against the Oracle 7 database:
647 // SQL_BIGINT SQL_NO_DATA_FOUND
648 // SQL_BINARY SQL_NO_DATA_FOUND
649 // SQL_BIT SQL_NO_DATA_FOUND
650 // SQL_CHAR type name = 'CHAR', Precision = 255
651 // SQL_DATE SQL_NO_DATA_FOUND
652 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
653 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
654 // SQL_FLOAT SQL_NO_DATA_FOUND
655 // SQL_INTEGER SQL_NO_DATA_FOUND
656 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
657 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
658 // SQL_NUMERIC SQL_NO_DATA_FOUND
659 // SQL_REAL SQL_NO_DATA_FOUND
660 // SQL_SMALLINT SQL_NO_DATA_FOUND
661 // SQL_TIME SQL_NO_DATA_FOUND
662 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
663 // SQL_VARBINARY type name = 'RAW', Precision = 255
664 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
665 // =====================================================================
666 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
668 // SQL_VARCHAR type name = 'TEXT', Precision = 255
669 // SQL_TIMESTAMP type name = 'DATETIME'
670 // SQL_DECIMAL SQL_NO_DATA_FOUND
671 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
672 // SQL_FLOAT SQL_NO_DATA_FOUND
673 // SQL_REAL type name = 'SINGLE', Precision = 7
674 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
675 // SQL_INTEGER type name = 'LONG', Precision = 10
677 // VARCHAR = Variable length character string
678 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
679 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
682 typeInfVarchar
.FsqlType
= SQL_CHAR
;
684 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
687 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
688 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
689 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
690 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
691 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
693 if (failOnDataTypeUnsupported
)
697 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
699 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
701 typeInfFloat
.FsqlType
= SQL_FLOAT
;
703 typeInfFloat
.FsqlType
= SQL_REAL
;
705 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
708 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
710 // If SQL_INTEGER is not supported, use the floating point
711 // data type to store integers as well as floats
712 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
714 if (failOnDataTypeUnsupported
)
718 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
721 typeInfInteger
.FsqlType
= SQL_INTEGER
;
724 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
726 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
729 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
731 typeInfDate
.FsqlType
= SQL_TIME
;
734 #endif // SQL_DATETIME defined
736 if (failOnDataTypeUnsupported
)
741 typeInfDate
.FsqlType
= SQL_DATE
;
744 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
747 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
749 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
751 if (failOnDataTypeUnsupported
)
755 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
758 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
760 //typeInfBlob.TypeName = "BLOB";
762 #ifdef DBDEBUG_CONSOLE
763 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
764 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
765 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
766 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
767 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
771 // Completed Successfully
777 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
779 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
780 dbConnectInf
->GetPassword());
784 bool wxDb::Open(wxDb
*copyDb
)
786 dsn
= copyDb
->GetDatasourceName();
787 uid
= copyDb
->GetUsername();
788 authStr
= copyDb
->GetPassword();
792 if (!FwdOnlyCursors())
794 // Specify that the ODBC cursor library be used, if needed. This must be
795 // specified before the connection is made.
796 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
798 #ifdef DBDEBUG_CONSOLE
799 if (retcode
== SQL_SUCCESS
)
800 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
802 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
804 wxUnusedVar( retcode
);
808 // Connect to the data source
809 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
810 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
811 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
813 if (retcode
== SQL_ERROR
)
814 return(DispAllErrors(henv
, hdbc
));
817 If using Intersolv branded ODBC drivers, this is the place where you would substitute
818 your branded driver license information
820 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
821 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
824 // Mark database as open
827 // Allocate a statement handle for the database connection
828 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
829 return(DispAllErrors(henv
, hdbc
));
831 // Set Connection Options
832 if (!setConnectionOptions())
835 // Instead of Querying the data source for info about itself, it can just be copied
836 // from the wxDb instance that was passed in (copyDb).
837 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
838 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
839 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
840 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
841 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
842 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
843 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
844 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
845 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
846 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
847 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
848 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
849 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
850 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
851 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
852 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
853 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
854 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
855 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
856 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
857 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
858 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
859 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
860 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
861 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
862 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
863 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
864 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
865 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
866 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
867 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
869 // VARCHAR = Variable length character string
870 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
871 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
872 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
873 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
874 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
877 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
878 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
879 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
880 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
881 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
884 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
885 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
886 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
887 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
888 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
891 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
892 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
893 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
894 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
895 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
898 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
899 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
900 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
901 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
902 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
904 #ifdef DBDEBUG_CONSOLE
905 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
906 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
907 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
908 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
909 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
913 // Completed Successfully
918 /********** wxDb::setConnectionOptions() **********/
919 bool wxDb::setConnectionOptions(void)
921 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
926 // I need to get the DBMS name here, because some of the connection options
927 // are database specific and need to call the Dbms() function.
928 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
929 return(DispAllErrors(henv
, hdbc
));
931 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
932 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
933 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
935 // By default, MS Sql Server closes cursors on commit and rollback. The following
936 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
937 // after a transaction. This is a driver specific option and is not part of the
938 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
939 // The database settings don't have any effect one way or the other.
940 if (Dbms() == dbmsMS_SQL_SERVER
)
942 const long SQL_PRESERVE_CURSORS
= 1204L;
943 const long SQL_PC_ON
= 1L;
944 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
947 // Display the connection options to verify them
948 #ifdef DBDEBUG_CONSOLE
950 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
952 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
953 return(DispAllErrors(henv
, hdbc
));
954 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
956 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
957 return(DispAllErrors(henv
, hdbc
));
958 cout
<< wxT("ODBC CURSORS: ");
961 case(SQL_CUR_USE_IF_NEEDED
):
962 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
964 case(SQL_CUR_USE_ODBC
):
965 cout
<< wxT("SQL_CUR_USE_ODBC");
967 case(SQL_CUR_USE_DRIVER
):
968 cout
<< wxT("SQL_CUR_USE_DRIVER");
973 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
974 return(DispAllErrors(henv
, hdbc
));
975 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
980 // Completed Successfully
983 } // wxDb::setConnectionOptions()
986 /********** wxDb::getDbInfo() **********/
987 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
992 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
994 DispAllErrors(henv
, hdbc
);
995 if (failOnDataTypeUnsupported
)
999 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
1001 DispAllErrors(henv
, hdbc
);
1002 if (failOnDataTypeUnsupported
)
1006 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1008 DispAllErrors(henv
, hdbc
);
1009 if (failOnDataTypeUnsupported
)
1014 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1015 // causing database connectivity to fail in some cases.
1016 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1018 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1020 DispAllErrors(henv
, hdbc
);
1021 if (failOnDataTypeUnsupported
)
1025 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1027 DispAllErrors(henv
, hdbc
);
1028 if (failOnDataTypeUnsupported
)
1032 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1034 DispAllErrors(henv
, hdbc
);
1035 if (failOnDataTypeUnsupported
)
1039 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1041 DispAllErrors(henv
, hdbc
);
1042 if (failOnDataTypeUnsupported
)
1046 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1048 DispAllErrors(henv
, hdbc
);
1049 if (failOnDataTypeUnsupported
)
1053 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1054 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1056 DispAllErrors(henv
, hdbc
);
1057 if (failOnDataTypeUnsupported
)
1061 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1063 DispAllErrors(henv
, hdbc
);
1064 if (failOnDataTypeUnsupported
)
1068 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1070 DispAllErrors(henv
, hdbc
);
1071 if (failOnDataTypeUnsupported
)
1075 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1077 // Not all drivers support this call - Nick Gorham(unixODBC)
1078 dbInf
.cliConfLvl
= 0;
1079 DispAllErrors(henv
, hdbc
);
1080 if (failOnDataTypeUnsupported
)
1084 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1086 DispAllErrors(henv
, hdbc
);
1087 if (failOnDataTypeUnsupported
)
1091 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1093 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1094 // TODO: dbInf.outerJoins[0]='N';
1095 // TODO: dbInf.outerJoins[1]='\x0';
1097 DispAllErrors(henv
, hdbc
);
1098 if (failOnDataTypeUnsupported
)
1102 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1104 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1105 // TODO: dbInf.procedureSupport[0]='N';
1106 // TODO: dbInf.procedureSupport[1]='\x0';
1108 DispAllErrors(henv
, hdbc
);
1109 if (failOnDataTypeUnsupported
)
1113 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1115 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1116 // TODO: dbInf.accessibleTables[0]='N';
1117 // TODO: dbInf.accessibleTables[1]='\x0';
1119 DispAllErrors(henv
, hdbc
);
1120 if (failOnDataTypeUnsupported
)
1124 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1126 DispAllErrors(henv
, hdbc
);
1127 if (failOnDataTypeUnsupported
)
1131 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1133 DispAllErrors(henv
, hdbc
);
1134 if (failOnDataTypeUnsupported
)
1138 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1140 DispAllErrors(henv
, hdbc
);
1141 if (failOnDataTypeUnsupported
)
1145 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1147 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1148 // TODO: dbInf.supportIEF[0]='N';
1149 // TODO: dbInf.supportIEF[1]='\x0';
1151 DispAllErrors(henv
, hdbc
);
1152 if (failOnDataTypeUnsupported
)
1156 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1158 DispAllErrors(henv
, hdbc
);
1159 if (failOnDataTypeUnsupported
)
1163 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1165 DispAllErrors(henv
, hdbc
);
1166 if (failOnDataTypeUnsupported
)
1170 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1172 DispAllErrors(henv
, hdbc
);
1173 if (failOnDataTypeUnsupported
)
1177 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1179 DispAllErrors(henv
, hdbc
);
1180 if (failOnDataTypeUnsupported
)
1184 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1186 DispAllErrors(henv
, hdbc
);
1187 if (failOnDataTypeUnsupported
)
1191 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1193 DispAllErrors(henv
, hdbc
);
1194 if (failOnDataTypeUnsupported
)
1198 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1200 DispAllErrors(henv
, hdbc
);
1201 if (failOnDataTypeUnsupported
)
1205 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1207 DispAllErrors(henv
, hdbc
);
1208 if (failOnDataTypeUnsupported
)
1212 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1214 DispAllErrors(henv
, hdbc
);
1215 if (failOnDataTypeUnsupported
)
1219 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1221 DispAllErrors(henv
, hdbc
);
1222 if (failOnDataTypeUnsupported
)
1226 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1228 DispAllErrors(henv
, hdbc
);
1229 if (failOnDataTypeUnsupported
)
1233 #ifdef DBDEBUG_CONSOLE
1234 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1235 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1236 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1237 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1239 cout
<< wxT("API Conf. Level: ");
1240 switch(dbInf
.apiConfLvl
)
1242 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1243 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1244 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1248 cout
<< wxT("SAG CLI Conf. Level: ");
1249 switch(dbInf
.cliConfLvl
)
1251 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1252 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1256 cout
<< wxT("SQL Conf. Level: ");
1257 switch(dbInf
.sqlConfLvl
)
1259 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1260 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1261 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1265 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1266 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1267 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1268 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1269 cout
<< wxT("Cursor COMMIT Behavior: ");
1270 switch(dbInf
.cursorCommitBehavior
)
1272 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1273 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1274 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1278 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1279 switch(dbInf
.cursorRollbackBehavior
)
1281 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1282 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1283 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1287 cout
<< wxT("Support NOT NULL clause: ");
1288 switch(dbInf
.supportNotNullClause
)
1290 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1291 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1295 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1296 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1298 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1301 cout
<< wxT("Default Transaction Isolation: ";
1302 switch(dbInf
.txnIsolation
)
1304 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1305 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1306 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1307 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1309 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1314 cout
<< wxT("Transaction Isolation Options: ");
1315 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1316 cout
<< wxT("Read Uncommitted, ");
1317 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1318 cout
<< wxT("Read Committed, ");
1319 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1320 cout
<< wxT("Repeatable Read, ");
1321 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1322 cout
<< wxT("Serializable, ");
1324 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1325 cout
<< wxT("Versioning");
1329 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1330 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1331 cout
<< wxT("Next, ");
1332 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1333 cout
<< wxT("Prev, ");
1334 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1335 cout
<< wxT("First, ");
1336 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1337 cout
<< wxT("Last, ");
1338 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1339 cout
<< wxT("Absolute, ");
1340 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1341 cout
<< wxT("Relative, ");
1343 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1344 cout
<< wxT("Resume, ");
1346 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1347 cout
<< wxT("Bookmark");
1350 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1351 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1352 cout
<< wxT("No Change, ");
1353 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1354 cout
<< wxT("Exclusive, ");
1355 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1356 cout
<< wxT("UnLock");
1359 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1360 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1361 cout
<< wxT("Position, ");
1362 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1363 cout
<< wxT("Refresh, ");
1364 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1365 cout
<< wxT("Upd, "));
1366 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1367 cout
<< wxT("Del, ");
1368 if (dbInf
.posOperations
& SQL_POS_ADD
)
1372 cout
<< wxT("Positioned Statements Supported: ");
1373 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1374 cout
<< wxT("Pos delete, ");
1375 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1376 cout
<< wxT("Pos update, ");
1377 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1378 cout
<< wxT("Select for update");
1381 cout
<< wxT("Scroll Concurrency: ");
1382 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1383 cout
<< wxT("Read Only, ");
1384 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1385 cout
<< wxT("Lock, ");
1386 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1387 cout
<< wxT("Opt. Rowver, ");
1388 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1389 cout
<< wxT("Opt. Values");
1392 cout
<< wxT("Scroll Options: ");
1393 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1394 cout
<< wxT("Fwd Only, ");
1395 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1396 cout
<< wxT("Static, ");
1397 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1398 cout
<< wxT("Keyset Driven, ");
1399 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1400 cout
<< wxT("Dynamic, ");
1401 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1402 cout
<< wxT("Mixed");
1405 cout
<< wxT("Static Sensitivity: ");
1406 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1407 cout
<< wxT("Additions, ");
1408 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1409 cout
<< wxT("Deletions, ");
1410 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1411 cout
<< wxT("Updates");
1414 cout
<< wxT("Transaction Capable?: ");
1415 switch(dbInf
.txnCapable
)
1417 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1418 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1419 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1420 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1421 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1428 // Completed Successfully
1431 } // wxDb::getDbInfo()
1434 /********** wxDb::getDataTypeInfo() **********/
1435 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1438 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1439 * the data type inf. is gathered for.
1441 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1446 // Get information about the data type specified
1447 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1448 return(DispAllErrors(henv
, hdbc
, hstmt
));
1451 retcode
= SQLFetch(hstmt
);
1452 if (retcode
!= SQL_SUCCESS
)
1454 #ifdef DBDEBUG_CONSOLE
1455 if (retcode
== SQL_NO_DATA_FOUND
)
1456 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1458 DispAllErrors(henv
, hdbc
, hstmt
);
1459 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1463 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1465 // Obtain columns from the record
1466 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1467 return(DispAllErrors(henv
, hdbc
, hstmt
));
1469 structSQLTypeInfo
.TypeName
= typeName
;
1471 // BJO 20000503: no more needed with new GetColumns...
1474 if (Dbms() == dbmsMY_SQL
)
1476 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1477 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1478 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1479 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1480 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1481 structSQLTypeInfo
.TypeName
= wxT("int");
1482 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1483 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1484 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1485 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1486 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1487 structSQLTypeInfo
.TypeName
= wxT("char");
1490 // BJO 20000427 : OpenLink driver
1491 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1492 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1494 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1495 structSQLTypeInfo
.TypeName
= wxT("real");
1499 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1500 return(DispAllErrors(henv
, hdbc
, hstmt
));
1501 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1502 return(DispAllErrors(henv
, hdbc
, hstmt
));
1503 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1504 // return(DispAllErrors(henv, hdbc, hstmt));
1506 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1507 return(DispAllErrors(henv
, hdbc
, hstmt
));
1509 if (structSQLTypeInfo
.MaximumScale
< 0)
1510 structSQLTypeInfo
.MaximumScale
= 0;
1512 // Close the statement handle which closes open cursors
1513 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1514 return(DispAllErrors(henv
, hdbc
, hstmt
));
1516 // Completed Successfully
1519 } // wxDb::getDataTypeInfo()
1522 /********** wxDb::Close() **********/
1523 void wxDb::Close(void)
1525 // Close the Sql Log file
1532 // Free statement handle
1535 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1536 DispAllErrors(henv
, hdbc
);
1539 // Disconnect from the datasource
1540 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1541 DispAllErrors(henv
, hdbc
);
1543 // Free the connection to the datasource
1544 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1545 DispAllErrors(henv
, hdbc
);
1547 // There should be zero Ctable objects still connected to this db object
1548 wxASSERT(nTables
== 0);
1553 pNode
= TablesInUse
.GetFirst();
1557 tiu
= (wxTablesInUse
*)pNode
->GetData();
1558 if (tiu
->pDb
== this)
1560 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1561 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1562 wxLogDebug (s
.c_str(),s2
.c_str());
1564 pNode
= pNode
->GetNext();
1568 // Copy the error messages to a global variable
1570 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1571 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1573 dbmsType
= dbmsUNIDENTIFIED
;
1579 /********** wxDb::CommitTrans() **********/
1580 bool wxDb::CommitTrans(void)
1584 // Commit the transaction
1585 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1586 return(DispAllErrors(henv
, hdbc
));
1589 // Completed successfully
1592 } // wxDb::CommitTrans()
1595 /********** wxDb::RollbackTrans() **********/
1596 bool wxDb::RollbackTrans(void)
1598 // Rollback the transaction
1599 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1600 return(DispAllErrors(henv
, hdbc
));
1602 // Completed successfully
1605 } // wxDb::RollbackTrans()
1608 /********** wxDb::DispAllErrors() **********/
1609 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1611 * This function is called internally whenever an error condition prevents the user's
1612 * request from being executed. This function will query the datasource as to the
1613 * actual error(s) that just occured on the previous request of the datasource.
1615 * The function will retrieve each error condition from the datasource and
1616 * Printf the codes/text values into a string which it then logs via logError().
1617 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1618 * window and program execution will be paused until the user presses a key.
1620 * This function always returns a FALSE, so that functions which call this function
1621 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1622 * of the users request, so that the calling code can then process the error msg log
1625 wxString odbcErrMsg
;
1627 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1629 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1630 logError(odbcErrMsg
, sqlState
);
1633 #ifdef DBDEBUG_CONSOLE
1634 // When run in console mode, use standard out to display errors.
1635 cout
<< odbcErrMsg
.c_str() << endl
;
1636 cout
<< wxT("Press any key to continue...") << endl
;
1641 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1646 return(FALSE
); // This function always returns FALSE.
1648 } // wxDb::DispAllErrors()
1651 /********** wxDb::GetNextError() **********/
1652 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1654 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1659 } // wxDb::GetNextError()
1662 /********** wxDb::DispNextError() **********/
1663 void wxDb::DispNextError(void)
1665 wxString odbcErrMsg
;
1667 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1668 logError(odbcErrMsg
, sqlState
);
1673 #ifdef DBDEBUG_CONSOLE
1674 // When run in console mode, use standard out to display errors.
1675 cout
<< odbcErrMsg
.c_str() << endl
;
1676 cout
<< wxT("Press any key to continue...") << endl
;
1681 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1682 #endif // __WXDEBUG__
1684 } // wxDb::DispNextError()
1687 /********** wxDb::logError() **********/
1688 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1690 wxASSERT(errMsg
.Length());
1692 static int pLast
= -1;
1695 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1698 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1699 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1703 wxStrcpy(errorList
[pLast
], errMsg
);
1705 if (SQLState
.Length())
1706 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1707 DB_STATUS
= dbStatus
;
1709 // Add the errmsg to the sql log
1710 WriteSqlLog(errMsg
);
1712 } // wxDb::logError()
1715 /**********wxDb::TranslateSqlState() **********/
1716 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1718 if (!wxStrcmp(SQLState
, wxT("01000")))
1719 return(DB_ERR_GENERAL_WARNING
);
1720 if (!wxStrcmp(SQLState
, wxT("01002")))
1721 return(DB_ERR_DISCONNECT_ERROR
);
1722 if (!wxStrcmp(SQLState
, wxT("01004")))
1723 return(DB_ERR_DATA_TRUNCATED
);
1724 if (!wxStrcmp(SQLState
, wxT("01006")))
1725 return(DB_ERR_PRIV_NOT_REVOKED
);
1726 if (!wxStrcmp(SQLState
, wxT("01S00")))
1727 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1728 if (!wxStrcmp(SQLState
, wxT("01S01")))
1729 return(DB_ERR_ERROR_IN_ROW
);
1730 if (!wxStrcmp(SQLState
, wxT("01S02")))
1731 return(DB_ERR_OPTION_VALUE_CHANGED
);
1732 if (!wxStrcmp(SQLState
, wxT("01S03")))
1733 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1734 if (!wxStrcmp(SQLState
, wxT("01S04")))
1735 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1736 if (!wxStrcmp(SQLState
, wxT("07001")))
1737 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1738 if (!wxStrcmp(SQLState
, wxT("07006")))
1739 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1740 if (!wxStrcmp(SQLState
, wxT("08001")))
1741 return(DB_ERR_UNABLE_TO_CONNECT
);
1742 if (!wxStrcmp(SQLState
, wxT("08002")))
1743 return(DB_ERR_CONNECTION_IN_USE
);
1744 if (!wxStrcmp(SQLState
, wxT("08003")))
1745 return(DB_ERR_CONNECTION_NOT_OPEN
);
1746 if (!wxStrcmp(SQLState
, wxT("08004")))
1747 return(DB_ERR_REJECTED_CONNECTION
);
1748 if (!wxStrcmp(SQLState
, wxT("08007")))
1749 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1750 if (!wxStrcmp(SQLState
, wxT("08S01")))
1751 return(DB_ERR_COMM_LINK_FAILURE
);
1752 if (!wxStrcmp(SQLState
, wxT("21S01")))
1753 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1754 if (!wxStrcmp(SQLState
, wxT("21S02")))
1755 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1756 if (!wxStrcmp(SQLState
, wxT("22001")))
1757 return(DB_ERR_STRING_RIGHT_TRUNC
);
1758 if (!wxStrcmp(SQLState
, wxT("22003")))
1759 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1760 if (!wxStrcmp(SQLState
, wxT("22005")))
1761 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1762 if (!wxStrcmp(SQLState
, wxT("22008")))
1763 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1764 if (!wxStrcmp(SQLState
, wxT("22012")))
1765 return(DB_ERR_DIVIDE_BY_ZERO
);
1766 if (!wxStrcmp(SQLState
, wxT("22026")))
1767 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1768 if (!wxStrcmp(SQLState
, wxT("23000")))
1769 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1770 if (!wxStrcmp(SQLState
, wxT("24000")))
1771 return(DB_ERR_INVALID_CURSOR_STATE
);
1772 if (!wxStrcmp(SQLState
, wxT("25000")))
1773 return(DB_ERR_INVALID_TRANS_STATE
);
1774 if (!wxStrcmp(SQLState
, wxT("28000")))
1775 return(DB_ERR_INVALID_AUTH_SPEC
);
1776 if (!wxStrcmp(SQLState
, wxT("34000")))
1777 return(DB_ERR_INVALID_CURSOR_NAME
);
1778 if (!wxStrcmp(SQLState
, wxT("37000")))
1779 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1780 if (!wxStrcmp(SQLState
, wxT("3C000")))
1781 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1782 if (!wxStrcmp(SQLState
, wxT("40001")))
1783 return(DB_ERR_SERIALIZATION_FAILURE
);
1784 if (!wxStrcmp(SQLState
, wxT("42000")))
1785 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1786 if (!wxStrcmp(SQLState
, wxT("70100")))
1787 return(DB_ERR_OPERATION_ABORTED
);
1788 if (!wxStrcmp(SQLState
, wxT("IM001")))
1789 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1790 if (!wxStrcmp(SQLState
, wxT("IM002")))
1791 return(DB_ERR_NO_DATA_SOURCE
);
1792 if (!wxStrcmp(SQLState
, wxT("IM003")))
1793 return(DB_ERR_DRIVER_LOAD_ERROR
);
1794 if (!wxStrcmp(SQLState
, wxT("IM004")))
1795 return(DB_ERR_SQLALLOCENV_FAILED
);
1796 if (!wxStrcmp(SQLState
, wxT("IM005")))
1797 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1798 if (!wxStrcmp(SQLState
, wxT("IM006")))
1799 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1800 if (!wxStrcmp(SQLState
, wxT("IM007")))
1801 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1802 if (!wxStrcmp(SQLState
, wxT("IM008")))
1803 return(DB_ERR_DIALOG_FAILED
);
1804 if (!wxStrcmp(SQLState
, wxT("IM009")))
1805 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1806 if (!wxStrcmp(SQLState
, wxT("IM010")))
1807 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1808 if (!wxStrcmp(SQLState
, wxT("IM011")))
1809 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1810 if (!wxStrcmp(SQLState
, wxT("IM012")))
1811 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1812 if (!wxStrcmp(SQLState
, wxT("IM013")))
1813 return(DB_ERR_TRACE_FILE_ERROR
);
1814 if (!wxStrcmp(SQLState
, wxT("S0001")))
1815 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1816 if (!wxStrcmp(SQLState
, wxT("S0002")))
1817 return(DB_ERR_TABLE_NOT_FOUND
);
1818 if (!wxStrcmp(SQLState
, wxT("S0011")))
1819 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1820 if (!wxStrcmp(SQLState
, wxT("S0012")))
1821 return(DB_ERR_INDEX_NOT_FOUND
);
1822 if (!wxStrcmp(SQLState
, wxT("S0021")))
1823 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1824 if (!wxStrcmp(SQLState
, wxT("S0022")))
1825 return(DB_ERR_COLUMN_NOT_FOUND
);
1826 if (!wxStrcmp(SQLState
, wxT("S0023")))
1827 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1828 if (!wxStrcmp(SQLState
, wxT("S1000")))
1829 return(DB_ERR_GENERAL_ERROR
);
1830 if (!wxStrcmp(SQLState
, wxT("S1001")))
1831 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1832 if (!wxStrcmp(SQLState
, wxT("S1002")))
1833 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1834 if (!wxStrcmp(SQLState
, wxT("S1003")))
1835 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1836 if (!wxStrcmp(SQLState
, wxT("S1004")))
1837 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1838 if (!wxStrcmp(SQLState
, wxT("S1008")))
1839 return(DB_ERR_OPERATION_CANCELLED
);
1840 if (!wxStrcmp(SQLState
, wxT("S1009")))
1841 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1842 if (!wxStrcmp(SQLState
, wxT("S1010")))
1843 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1844 if (!wxStrcmp(SQLState
, wxT("S1011")))
1845 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1846 if (!wxStrcmp(SQLState
, wxT("S1012")))
1847 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1848 if (!wxStrcmp(SQLState
, wxT("S1015")))
1849 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1850 if (!wxStrcmp(SQLState
, wxT("S1090")))
1851 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1852 if (!wxStrcmp(SQLState
, wxT("S1091")))
1853 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1854 if (!wxStrcmp(SQLState
, wxT("S1092")))
1855 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1856 if (!wxStrcmp(SQLState
, wxT("S1093")))
1857 return(DB_ERR_INVALID_PARAM_NO
);
1858 if (!wxStrcmp(SQLState
, wxT("S1094")))
1859 return(DB_ERR_INVALID_SCALE_VALUE
);
1860 if (!wxStrcmp(SQLState
, wxT("S1095")))
1861 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1862 if (!wxStrcmp(SQLState
, wxT("S1096")))
1863 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1864 if (!wxStrcmp(SQLState
, wxT("S1097")))
1865 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1866 if (!wxStrcmp(SQLState
, wxT("S1098")))
1867 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1868 if (!wxStrcmp(SQLState
, wxT("S1099")))
1869 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1870 if (!wxStrcmp(SQLState
, wxT("S1100")))
1871 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1872 if (!wxStrcmp(SQLState
, wxT("S1101")))
1873 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1874 if (!wxStrcmp(SQLState
, wxT("S1103")))
1875 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1876 if (!wxStrcmp(SQLState
, wxT("S1104")))
1877 return(DB_ERR_INVALID_PRECISION_VALUE
);
1878 if (!wxStrcmp(SQLState
, wxT("S1105")))
1879 return(DB_ERR_INVALID_PARAM_TYPE
);
1880 if (!wxStrcmp(SQLState
, wxT("S1106")))
1881 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1882 if (!wxStrcmp(SQLState
, wxT("S1107")))
1883 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1884 if (!wxStrcmp(SQLState
, wxT("S1108")))
1885 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1886 if (!wxStrcmp(SQLState
, wxT("S1109")))
1887 return(DB_ERR_INVALID_CURSOR_POSITION
);
1888 if (!wxStrcmp(SQLState
, wxT("S1110")))
1889 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1890 if (!wxStrcmp(SQLState
, wxT("S1111")))
1891 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1892 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1893 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1894 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1895 return(DB_ERR_TIMEOUT_EXPIRED
);
1900 } // wxDb::TranslateSqlState()
1903 /********** wxDb::Grant() **********/
1904 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1908 // Build the grant statement
1909 sqlStmt
= wxT("GRANT ");
1910 if (privileges
== DB_GRANT_ALL
)
1911 sqlStmt
+= wxT("ALL");
1915 if (privileges
& DB_GRANT_SELECT
)
1917 sqlStmt
+= wxT("SELECT");
1920 if (privileges
& DB_GRANT_INSERT
)
1923 sqlStmt
+= wxT(", ");
1924 sqlStmt
+= wxT("INSERT");
1926 if (privileges
& DB_GRANT_UPDATE
)
1929 sqlStmt
+= wxT(", ");
1930 sqlStmt
+= wxT("UPDATE");
1932 if (privileges
& DB_GRANT_DELETE
)
1935 sqlStmt
+= wxT(", ");
1936 sqlStmt
+= wxT("DELETE");
1940 sqlStmt
+= wxT(" ON ");
1941 sqlStmt
+= SQLTableName(tableName
);
1942 sqlStmt
+= wxT(" TO ");
1943 sqlStmt
+= userList
;
1945 #ifdef DBDEBUG_CONSOLE
1946 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1949 WriteSqlLog(sqlStmt
);
1951 return(ExecSql(sqlStmt
));
1956 /********** wxDb::CreateView() **********/
1957 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1958 const wxString
&pSqlStmt
, bool attemptDrop
)
1962 // Drop the view first
1963 if (attemptDrop
&& !DropView(viewName
))
1966 // Build the create view statement
1967 sqlStmt
= wxT("CREATE VIEW ");
1968 sqlStmt
+= viewName
;
1970 if (colList
.Length())
1972 sqlStmt
+= wxT(" (");
1974 sqlStmt
+= wxT(")");
1977 sqlStmt
+= wxT(" AS ");
1978 sqlStmt
+= pSqlStmt
;
1980 WriteSqlLog(sqlStmt
);
1982 #ifdef DBDEBUG_CONSOLE
1983 cout
<< sqlStmt
.c_str() << endl
;
1986 return(ExecSql(sqlStmt
));
1988 } // wxDb::CreateView()
1991 /********** wxDb::DropView() **********/
1992 bool wxDb::DropView(const wxString
&viewName
)
1995 * NOTE: This function returns TRUE if the View does not exist, but
1996 * only for identified databases. Code will need to be added
1997 * below for any other databases when those databases are defined
1998 * to handle this situation consistently
2002 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2004 WriteSqlLog(sqlStmt
);
2006 #ifdef DBDEBUG_CONSOLE
2007 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2010 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2012 // Check for "Base table not found" error and ignore
2013 GetNextError(henv
, hdbc
, hstmt
);
2014 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2016 // Check for product specific error codes
2017 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2020 DispAllErrors(henv
, hdbc
, hstmt
);
2027 // Commit the transaction
2033 } // wxDb::DropView()
2036 /********** wxDb::ExecSql() **********/
2037 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2041 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2043 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2044 if (retcode
== SQL_SUCCESS
||
2045 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2051 DispAllErrors(henv
, hdbc
, hstmt
);
2055 } // wxDb::ExecSql()
2058 /********** wxDb::GetNext() **********/
2059 bool wxDb::GetNext(void)
2061 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2065 DispAllErrors(henv
, hdbc
, hstmt
);
2069 } // wxDb::GetNext()
2072 /********** wxDb::GetData() **********/
2073 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2076 wxASSERT(cbReturned
);
2078 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2082 DispAllErrors(henv
, hdbc
, hstmt
);
2086 } // wxDb::GetData()
2089 /********** wxDb::GetKeyFields() **********/
2090 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2092 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2093 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2095 // SQLSMALLINT iKeySeq;
2096 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2097 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2103 * -----------------------------------------------------------------------
2104 * -- 19991224 : mj10777 : Create ------
2105 * -- : Three things are done and stored here : ------
2106 * -- : 1) which Column(s) is/are Primary Key(s) ------
2107 * -- : 2) which tables use this Key as a Foreign Key ------
2108 * -- : 3) which columns are Foreign Key and the name ------
2109 * -- : of the Table where the Key is the Primary Key -----
2110 * -- : Called from GetColumns(const wxString &tableName, ------
2111 * -- int *numCols,const wxChar *userID ) ------
2112 * -----------------------------------------------------------------------
2115 /*---------------------------------------------------------------------*/
2116 /* Get the names of the columns in the primary key. */
2117 /*---------------------------------------------------------------------*/
2118 retcode
= SQLPrimaryKeys(hstmt
,
2119 NULL
, 0, /* Catalog name */
2120 NULL
, 0, /* Schema name */
2121 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2123 /*---------------------------------------------------------------------*/
2124 /* Fetch and display the result set. This will be a list of the */
2125 /* columns in the primary key of the tableName table. */
2126 /*---------------------------------------------------------------------*/
2127 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2129 retcode
= SQLFetch(hstmt
);
2130 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2132 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2133 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2135 for (i
=0;i
<noCols
;i
++) // Find the Column name
2136 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2137 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2140 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2142 /*---------------------------------------------------------------------*/
2143 /* Get all the foreign keys that refer to tableName primary key. */
2144 /*---------------------------------------------------------------------*/
2145 retcode
= SQLForeignKeys(hstmt
,
2146 NULL
, 0, /* Primary catalog */
2147 NULL
, 0, /* Primary schema */
2148 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2149 NULL
, 0, /* Foreign catalog */
2150 NULL
, 0, /* Foreign schema */
2151 NULL
, 0); /* Foreign table */
2153 /*---------------------------------------------------------------------*/
2154 /* Fetch and display the result set. This will be all of the foreign */
2155 /* keys in other tables that refer to the tableName primary key. */
2156 /*---------------------------------------------------------------------*/
2159 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2161 retcode
= SQLFetch(hstmt
);
2162 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2164 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2165 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2166 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2167 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2168 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2169 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2173 tempStr
.Trim(); // Get rid of any unneeded blanks
2174 if (!tempStr
.IsEmpty())
2176 for (i
=0; i
<noCols
; i
++)
2177 { // Find the Column name
2178 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2179 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2183 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2185 /*---------------------------------------------------------------------*/
2186 /* Get all the foreign keys in the tablename table. */
2187 /*---------------------------------------------------------------------*/
2188 retcode
= SQLForeignKeys(hstmt
,
2189 NULL
, 0, /* Primary catalog */
2190 NULL
, 0, /* Primary schema */
2191 NULL
, 0, /* Primary table */
2192 NULL
, 0, /* Foreign catalog */
2193 NULL
, 0, /* Foreign schema */
2194 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2196 /*---------------------------------------------------------------------*/
2197 /* Fetch and display the result set. This will be all of the */
2198 /* primary keys in other tables that are referred to by foreign */
2199 /* keys in the tableName table. */
2200 /*---------------------------------------------------------------------*/
2201 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2203 retcode
= SQLFetch(hstmt
);
2204 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2206 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2207 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2208 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2210 for (i
=0; i
<noCols
; i
++) // Find the Column name
2212 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2214 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2215 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2220 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2224 } // wxDb::GetKeyFields()
2228 /********** wxDb::GetColumns() **********/
2229 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2231 * 1) The last array element of the tableName[] argument must be zero (null).
2232 * This is how the end of the array is detected.
2233 * 2) This function returns an array of wxDbColInf structures. If no columns
2234 * were found, or an error occured, this pointer will be zero (null). THE
2235 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2236 * IS FINISHED WITH IT. i.e.
2238 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2241 * // Use the column inf
2243 * // Destroy the memory
2247 * userID is evaluated in the following manner:
2248 * userID == NULL ... UserID is ignored
2249 * userID == "" ... UserID set equal to 'this->uid'
2250 * userID != "" ... UserID set equal to 'userID'
2252 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2253 * by this function. This function should use its own wxDb instance
2254 * to avoid undesired unbinding of columns.
2259 wxDbColInf
*colInf
= 0;
2267 convertUserID(userID
,UserID
);
2269 // Pass 1 - Determine how many columns there are.
2270 // Pass 2 - Allocate the wxDbColInf array and fill in
2271 // the array with the column information.
2273 for (pass
= 1; pass
<= 2; pass
++)
2277 if (noCols
== 0) // Probably a bogus table name(s)
2279 // Allocate n wxDbColInf objects to hold the column information
2280 colInf
= new wxDbColInf
[noCols
+1];
2283 // Mark the end of the array
2284 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2285 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2286 colInf
[noCols
].sqlDataType
= 0;
2288 // Loop through each table name
2290 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2292 TableName
= tableName
[tbl
];
2293 // Oracle and Interbase table names are uppercase only, so force
2294 // the name to uppercase just in case programmer forgot to do this
2295 if ((Dbms() == dbmsORACLE
) ||
2296 (Dbms() == dbmsINTERBASE
))
2297 TableName
= TableName
.Upper();
2299 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2301 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2302 // use the call below that leaves out the user name
2303 if (!UserID
.IsEmpty() &&
2304 Dbms() != dbmsMY_SQL
&&
2305 Dbms() != dbmsACCESS
&&
2306 Dbms() != dbmsMS_SQL_SERVER
)
2308 retcode
= SQLColumns(hstmt
,
2309 NULL
, 0, // All qualifiers
2310 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2311 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2312 NULL
, 0); // All columns
2316 retcode
= SQLColumns(hstmt
,
2317 NULL
, 0, // All qualifiers
2319 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2320 NULL
, 0); // All columns
2322 if (retcode
!= SQL_SUCCESS
)
2323 { // Error occured, abort
2324 DispAllErrors(henv
, hdbc
, hstmt
);
2327 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2331 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2333 if (pass
== 1) // First pass, just add up the number of columns
2335 else // Pass 2; Fill in the array of structures
2337 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2339 // NOTE: Only the ODBC 1.x fields are retrieved
2340 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2341 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2342 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2343 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2344 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2345 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2346 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2347 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2348 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2349 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2350 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2351 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2353 // Determine the wxDb data type that is used to represent the native data type of this data source
2354 colInf
[colNo
].dbDataType
= 0;
2355 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2358 // IODBC does not return a correct columnSize, so we set
2359 // columnSize = bufferLength if no column size was returned
2360 // IODBC returns the columnSize in bufferLength.. (bug)
2361 if (colInf
[colNo
].columnSize
< 1)
2363 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2366 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2368 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2369 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2370 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2371 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2372 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2373 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2374 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2375 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2380 if (retcode
!= SQL_NO_DATA_FOUND
)
2381 { // Error occured, abort
2382 DispAllErrors(henv
, hdbc
, hstmt
);
2385 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2391 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2394 } // wxDb::GetColumns()
2397 /********** wxDb::GetColumns() **********/
2399 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2401 // Same as the above GetColumns() function except this one gets columns
2402 // only for a single table, and if 'numCols' is not NULL, the number of
2403 // columns stored in the returned wxDbColInf is set in '*numCols'
2405 // userID is evaluated in the following manner:
2406 // userID == NULL ... UserID is ignored
2407 // userID == "" ... UserID set equal to 'this->uid'
2408 // userID != "" ... UserID set equal to 'userID'
2410 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2411 // by this function. This function should use its own wxDb instance
2412 // to avoid undesired unbinding of columns.
2417 wxDbColInf
*colInf
= 0;
2425 convertUserID(userID
,UserID
);
2427 // Pass 1 - Determine how many columns there are.
2428 // Pass 2 - Allocate the wxDbColInf array and fill in
2429 // the array with the column information.
2431 for (pass
= 1; pass
<= 2; pass
++)
2435 if (noCols
== 0) // Probably a bogus table name(s)
2437 // Allocate n wxDbColInf objects to hold the column information
2438 colInf
= new wxDbColInf
[noCols
+1];
2441 // Mark the end of the array
2442 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2443 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2444 colInf
[noCols
].sqlDataType
= 0;
2447 TableName
= tableName
;
2448 // Oracle and Interbase table names are uppercase only, so force
2449 // the name to uppercase just in case programmer forgot to do this
2450 if ((Dbms() == dbmsORACLE
) ||
2451 (Dbms() == dbmsINTERBASE
))
2452 TableName
= TableName
.Upper();
2454 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2456 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2457 // use the call below that leaves out the user name
2458 if (!UserID
.IsEmpty() &&
2459 Dbms() != dbmsMY_SQL
&&
2460 Dbms() != dbmsACCESS
&&
2461 Dbms() != dbmsMS_SQL_SERVER
)
2463 retcode
= SQLColumns(hstmt
,
2464 NULL
, 0, // All qualifiers
2465 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2466 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2467 NULL
, 0); // All columns
2471 retcode
= SQLColumns(hstmt
,
2472 NULL
, 0, // All qualifiers
2474 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2475 NULL
, 0); // All columns
2477 if (retcode
!= SQL_SUCCESS
)
2478 { // Error occured, abort
2479 DispAllErrors(henv
, hdbc
, hstmt
);
2482 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2488 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2490 if (pass
== 1) // First pass, just add up the number of columns
2492 else // Pass 2; Fill in the array of structures
2494 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2496 // NOTE: Only the ODBC 1.x fields are retrieved
2497 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2498 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2499 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2500 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2501 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2502 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2503 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2504 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2505 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2506 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2507 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2508 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2509 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2510 // Start Values for Primary/Foriegn Key (=No)
2511 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2512 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2513 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2514 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2516 // BJO 20000428 : Virtuoso returns type names with upper cases!
2517 if (Dbms() == dbmsVIRTUOSO
)
2519 wxString s
= colInf
[colNo
].typeName
;
2521 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2524 // Determine the wxDb data type that is used to represent the native data type of this data source
2525 colInf
[colNo
].dbDataType
= 0;
2526 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2529 // IODBC does not return a correct columnSize, so we set
2530 // columnSize = bufferLength if no column size was returned
2531 // IODBC returns the columnSize in bufferLength.. (bug)
2532 if (colInf
[colNo
].columnSize
< 1)
2534 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2538 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2540 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2541 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2542 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2543 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2544 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2545 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2546 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2547 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2553 if (retcode
!= SQL_NO_DATA_FOUND
)
2554 { // Error occured, abort
2555 DispAllErrors(henv
, hdbc
, hstmt
);
2558 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2565 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2567 // Store Primary and Foriegn Keys
2568 GetKeyFields(tableName
,colInf
,noCols
);
2574 } // wxDb::GetColumns()
2577 #else // New GetColumns
2582 These are tentative new GetColumns members which should be more database
2583 independant and which always returns the columns in the order they were
2586 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2587 wxChar* userID)) calls the second implementation for each separate table
2588 before merging the results. This makes the code easier to maintain as
2589 only one member (the second) makes the real work
2590 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2591 wxChar *userID) is a little bit improved
2592 - It doesn't anymore rely on the type-name to find out which database-type
2594 - It ends by sorting the columns, so that they are returned in the same
2595 order they were created
2605 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2608 // The last array element of the tableName[] argument must be zero (null).
2609 // This is how the end of the array is detected.
2613 // How many tables ?
2615 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2617 // Create a table to maintain the columns for each separate table
2618 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2621 for (i
= 0 ; i
< tbl
; i
++)
2624 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2625 if (TableColumns
[i
].colInf
== NULL
)
2627 noCols
+= TableColumns
[i
].noCols
;
2630 // Now merge all the separate table infos
2631 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2633 // Mark the end of the array
2634 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2635 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2636 colInf
[noCols
].sqlDataType
= 0;
2641 for (i
= 0 ; i
< tbl
; i
++)
2643 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2645 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2649 delete [] TableColumns
;
2652 } // wxDb::GetColumns() -- NEW
2655 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2657 // Same as the above GetColumns() function except this one gets columns
2658 // only for a single table, and if 'numCols' is not NULL, the number of
2659 // columns stored in the returned wxDbColInf is set in '*numCols'
2661 // userID is evaluated in the following manner:
2662 // userID == NULL ... UserID is ignored
2663 // userID == "" ... UserID set equal to 'this->uid'
2664 // userID != "" ... UserID set equal to 'userID'
2666 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2667 // by this function. This function should use its own wxDb instance
2668 // to avoid undesired unbinding of columns.
2672 wxDbColInf
*colInf
= 0;
2680 convertUserID(userID
,UserID
);
2682 // Pass 1 - Determine how many columns there are.
2683 // Pass 2 - Allocate the wxDbColInf array and fill in
2684 // the array with the column information.
2686 for (pass
= 1; pass
<= 2; pass
++)
2690 if (noCols
== 0) // Probably a bogus table name(s)
2692 // Allocate n wxDbColInf objects to hold the column information
2693 colInf
= new wxDbColInf
[noCols
+1];
2696 // Mark the end of the array
2697 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2698 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2699 colInf
[noCols
].sqlDataType
= 0;
2702 TableName
= tableName
;
2703 // Oracle and Interbase table names are uppercase only, so force
2704 // the name to uppercase just in case programmer forgot to do this
2705 if ((Dbms() == dbmsORACLE
) ||
2706 (Dbms() == dbmsINTERBASE
))
2707 TableName
= TableName
.Upper();
2709 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2711 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2712 // use the call below that leaves out the user name
2713 if (!UserID
.IsEmpty() &&
2714 Dbms() != dbmsMY_SQL
&&
2715 Dbms() != dbmsACCESS
&&
2716 Dbms() != dbmsMS_SQL_SERVER
)
2718 retcode
= SQLColumns(hstmt
,
2719 NULL
, 0, // All qualifiers
2720 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2721 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2722 NULL
, 0); // All columns
2726 retcode
= SQLColumns(hstmt
,
2727 NULL
, 0, // All qualifiers
2729 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2730 NULL
, 0); // All columns
2732 if (retcode
!= SQL_SUCCESS
)
2733 { // Error occured, abort
2734 DispAllErrors(henv
, hdbc
, hstmt
);
2737 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2743 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2745 if (pass
== 1) // First pass, just add up the number of columns
2747 else // Pass 2; Fill in the array of structures
2749 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2751 // NOTE: Only the ODBC 1.x fields are retrieved
2752 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2753 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2754 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2755 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2756 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2757 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2758 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2759 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2760 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2761 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2762 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2763 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2764 // Start Values for Primary/Foriegn Key (=No)
2765 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2766 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2767 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2768 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2771 // IODBC does not return a correct columnSize, so we set
2772 // columnSize = bufferLength if no column size was returned
2773 // IODBC returns the columnSize in bufferLength.. (bug)
2774 if (colInf
[colNo
].columnSize
< 1)
2776 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2780 // Determine the wxDb data type that is used to represent the native data type of this data source
2781 colInf
[colNo
].dbDataType
= 0;
2782 // Get the intern datatype
2783 switch (colInf
[colNo
].sqlDataType
)
2787 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2793 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2800 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2803 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2806 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2811 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2812 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2819 if (retcode
!= SQL_NO_DATA_FOUND
)
2820 { // Error occured, abort
2821 DispAllErrors(henv
, hdbc
, hstmt
);
2824 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2831 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2833 // Store Primary and Foreign Keys
2834 GetKeyFields(tableName
,colInf
,noCols
);
2836 ///////////////////////////////////////////////////////////////////////////
2837 // Now sort the the columns in order to make them appear in the right order
2838 ///////////////////////////////////////////////////////////////////////////
2840 // Build a generic SELECT statement which returns 0 rows
2843 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2846 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2848 DispAllErrors(henv
, hdbc
, hstmt
);
2852 // Get the number of result columns
2853 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2855 DispAllErrors(henv
, hdbc
, hstmt
);
2859 if (noCols
== 0) // Probably a bogus table name
2868 for (colNum
= 0; colNum
< noCols
; colNum
++)
2870 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2872 &Sword
, &Sdword
) != SQL_SUCCESS
)
2874 DispAllErrors(henv
, hdbc
, hstmt
);
2878 wxString Name1
= name
;
2879 Name1
= Name1
.Upper();
2881 // Where is this name in the array ?
2882 for (i
= colNum
; i
< noCols
; i
++)
2884 wxString Name2
= colInf
[i
].colName
;
2885 Name2
= Name2
.Upper();
2888 if (colNum
!= i
) // swap to sort
2890 wxDbColInf tmpColInf
= colInf
[colNum
];
2891 colInf
[colNum
] = colInf
[i
];
2892 colInf
[i
] = tmpColInf
;
2898 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2900 ///////////////////////////////////////////////////////////////////////////
2902 ///////////////////////////////////////////////////////////////////////////
2908 } // wxDb::GetColumns()
2911 #endif // #else OLD_GETCOLUMNS
2914 /********** wxDb::GetColumnCount() **********/
2915 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2917 * Returns a count of how many columns are in a table.
2918 * If an error occurs in computing the number of columns
2919 * this function will return a -1 for the count
2921 * userID is evaluated in the following manner:
2922 * userID == NULL ... UserID is ignored
2923 * userID == "" ... UserID set equal to 'this->uid'
2924 * userID != "" ... UserID set equal to 'userID'
2926 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2927 * by this function. This function should use its own wxDb instance
2928 * to avoid undesired unbinding of columns.
2938 convertUserID(userID
,UserID
);
2940 TableName
= tableName
;
2941 // Oracle and Interbase table names are uppercase only, so force
2942 // the name to uppercase just in case programmer forgot to do this
2943 if ((Dbms() == dbmsORACLE
) ||
2944 (Dbms() == dbmsINTERBASE
))
2945 TableName
= TableName
.Upper();
2947 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2949 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2950 // use the call below that leaves out the user name
2951 if (!UserID
.IsEmpty() &&
2952 Dbms() != dbmsMY_SQL
&&
2953 Dbms() != dbmsACCESS
&&
2954 Dbms() != dbmsMS_SQL_SERVER
)
2956 retcode
= SQLColumns(hstmt
,
2957 NULL
, 0, // All qualifiers
2958 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2959 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2960 NULL
, 0); // All columns
2964 retcode
= SQLColumns(hstmt
,
2965 NULL
, 0, // All qualifiers
2967 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2968 NULL
, 0); // All columns
2970 if (retcode
!= SQL_SUCCESS
)
2971 { // Error occured, abort
2972 DispAllErrors(henv
, hdbc
, hstmt
);
2973 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2977 // Count the columns
2978 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2981 if (retcode
!= SQL_NO_DATA_FOUND
)
2982 { // Error occured, abort
2983 DispAllErrors(henv
, hdbc
, hstmt
);
2984 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2988 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2991 } // wxDb::GetColumnCount()
2994 /********** wxDb::GetCatalog() *******/
2995 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2997 * ---------------------------------------------------------------------
2998 * -- 19991203 : mj10777 : Create ------
2999 * -- : Creates a wxDbInf with Tables / Cols Array ------
3000 * -- : uses SQLTables and fills pTableInf; ------
3001 * -- : pColInf is set to NULL and numCols to 0; ------
3002 * -- : returns pDbInf (wxDbInf) ------
3003 * -- - if unsuccesfull (pDbInf == NULL) ------
3004 * -- : pColInf can be filled with GetColumns(..); ------
3005 * -- : numCols can be filled with GetColumnCount(..); ------
3006 * ---------------------------------------------------------------------
3008 * userID is evaluated in the following manner:
3009 * userID == NULL ... UserID is ignored
3010 * userID == "" ... UserID set equal to 'this->uid'
3011 * userID != "" ... UserID set equal to 'userID'
3013 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3014 * by this function. This function should use its own wxDb instance
3015 * to avoid undesired unbinding of columns.
3018 int noTab
= 0; // Counter while filling table entries
3022 wxString tblNameSave
;
3025 convertUserID(userID
,UserID
);
3027 //-------------------------------------------------------------
3028 // Create the Database Array of catalog entries
3030 wxDbInf
*pDbInf
= new wxDbInf
;
3032 //-------------------------------------------------------------
3033 // Table Information
3034 // Pass 1 - Determine how many Tables there are.
3035 // Pass 2 - Create the Table array and fill it
3036 // - Create the Cols array = NULL
3037 //-------------------------------------------------------------
3039 for (pass
= 1; pass
<= 2; pass
++)
3041 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3042 tblNameSave
.Empty();
3044 if (!UserID
.IsEmpty() &&
3045 Dbms() != dbmsMY_SQL
&&
3046 Dbms() != dbmsACCESS
&&
3047 Dbms() != dbmsMS_SQL_SERVER
)
3049 retcode
= SQLTables(hstmt
,
3050 NULL
, 0, // All qualifiers
3051 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3052 NULL
, 0, // All tables
3053 NULL
, 0); // All columns
3057 retcode
= SQLTables(hstmt
,
3058 NULL
, 0, // All qualifiers
3059 NULL
, 0, // User specified
3060 NULL
, 0, // All tables
3061 NULL
, 0); // All columns
3064 if (retcode
!= SQL_SUCCESS
)
3066 DispAllErrors(henv
, hdbc
, hstmt
);
3068 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3072 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3074 if (pass
== 1) // First pass, just count the Tables
3076 if (pDbInf
->numTables
== 0)
3078 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3079 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3081 pDbInf
->numTables
++; // Counter for Tables
3083 if (pass
== 2) // Create and fill the Table entries
3085 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3086 { // no, then create the Array
3087 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3089 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3091 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3092 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3093 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3099 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3101 // Query how many columns are in each table
3102 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3104 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3109 } // wxDb::GetCatalog()
3112 /********** wxDb::Catalog() **********/
3113 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3115 * Creates the text file specified in 'filename' which will contain
3116 * a minimal data dictionary of all tables accessible by the user specified
3119 * userID is evaluated in the following manner:
3120 * userID == NULL ... UserID is ignored
3121 * userID == "" ... UserID set equal to 'this->uid'
3122 * userID != "" ... UserID set equal to 'userID'
3124 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3125 * by this function. This function should use its own wxDb instance
3126 * to avoid undesired unbinding of columns.
3129 wxASSERT(fileName
.Length());
3133 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3134 wxString tblNameSave
;
3135 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3137 wxChar typeName
[30+1];
3138 SDWORD precision
, length
;
3140 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3144 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3147 convertUserID(userID
,UserID
);
3149 if (!UserID
.IsEmpty() &&
3150 Dbms() != dbmsMY_SQL
&&
3151 Dbms() != dbmsACCESS
&&
3152 Dbms() != dbmsINTERBASE
&&
3153 Dbms() != dbmsMS_SQL_SERVER
)
3155 retcode
= SQLColumns(hstmt
,
3156 NULL
, 0, // All qualifiers
3157 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3158 NULL
, 0, // All tables
3159 NULL
, 0); // All columns
3163 retcode
= SQLColumns(hstmt
,
3164 NULL
, 0, // All qualifiers
3165 NULL
, 0, // User specified
3166 NULL
, 0, // All tables
3167 NULL
, 0); // All columns
3169 if (retcode
!= SQL_SUCCESS
)
3171 DispAllErrors(henv
, hdbc
, hstmt
);
3177 tblNameSave
.Empty();
3182 retcode
= SQLFetch(hstmt
);
3183 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3186 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3189 wxFputs(wxT("\n"), fp
);
3190 wxFputs(wxT("================================ "), fp
);
3191 wxFputs(wxT("================================ "), fp
);
3192 wxFputs(wxT("===================== "), fp
);
3193 wxFputs(wxT("========= "), fp
);
3194 wxFputs(wxT("=========\n"), fp
);
3195 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3196 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3197 wxFputs(outStr
.c_str(), fp
);
3198 wxFputs(wxT("================================ "), fp
);
3199 wxFputs(wxT("================================ "), fp
);
3200 wxFputs(wxT("===================== "), fp
);
3201 wxFputs(wxT("========= "), fp
);
3202 wxFputs(wxT("=========\n"), fp
);
3203 tblNameSave
= tblName
;
3206 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3207 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3208 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3209 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3210 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3211 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3213 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3214 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3215 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3217 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3224 if (retcode
!= SQL_NO_DATA_FOUND
)
3225 DispAllErrors(henv
, hdbc
, hstmt
);
3227 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3230 return(retcode
== SQL_NO_DATA_FOUND
);
3232 } // wxDb::Catalog()
3235 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3237 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3238 * if the object exists in the database. This function does not indicate
3239 * whether or not the user has privleges to query or perform other functions
3242 * userID is evaluated in the following manner:
3243 * userID == NULL ... UserID is ignored
3244 * userID == "" ... UserID set equal to 'this->uid'
3245 * userID != "" ... UserID set equal to 'userID'
3248 wxASSERT(tableName
.Length());
3252 if (Dbms() == dbmsDBASE
)
3255 if (tablePath
.Length())
3256 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3258 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3261 exists
= wxFileExists(dbName
);
3266 convertUserID(userID
,UserID
);
3268 TableName
= tableName
;
3269 // Oracle and Interbase table names are uppercase only, so force
3270 // the name to uppercase just in case programmer forgot to do this
3271 if ((Dbms() == dbmsORACLE
) ||
3272 (Dbms() == dbmsINTERBASE
))
3273 TableName
= TableName
.Upper();
3275 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3278 // Some databases cannot accept a user name when looking up table names,
3279 // so we use the call below that leaves out the user name
3280 if (!UserID
.IsEmpty() &&
3281 Dbms() != dbmsMY_SQL
&&
3282 Dbms() != dbmsACCESS
&&
3283 Dbms() != dbmsMS_SQL_SERVER
&&
3284 Dbms() != dbmsDB2
&&
3285 Dbms() != dbmsINTERBASE
&&
3286 Dbms() != dbmsPERVASIVE_SQL
)
3288 retcode
= SQLTables(hstmt
,
3289 NULL
, 0, // All qualifiers
3290 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3291 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3292 NULL
, 0); // All table types
3296 retcode
= SQLTables(hstmt
,
3297 NULL
, 0, // All qualifiers
3298 NULL
, 0, // All owners
3299 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3300 NULL
, 0); // All table types
3302 if (retcode
!= SQL_SUCCESS
)
3303 return(DispAllErrors(henv
, hdbc
, hstmt
));
3305 retcode
= SQLFetch(hstmt
);
3306 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3308 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3309 return(DispAllErrors(henv
, hdbc
, hstmt
));
3312 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3316 } // wxDb::TableExists()
3319 /********** wxDb::TablePrivileges() **********/
3320 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3321 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3323 wxASSERT(tableName
.Length());
3325 wxDbTablePrivilegeInfo result
;
3329 // We probably need to be able to dynamically set this based on
3330 // the driver type, and state.
3331 wxChar curRole
[]=wxT("public");
3335 wxString UserID
,Schema
;
3336 convertUserID(userID
,UserID
);
3337 convertUserID(schema
,Schema
);
3339 TableName
= tableName
;
3340 // Oracle and Interbase table names are uppercase only, so force
3341 // the name to uppercase just in case programmer forgot to do this
3342 if ((Dbms() == dbmsORACLE
) ||
3343 (Dbms() == dbmsINTERBASE
))
3344 TableName
= TableName
.Upper();
3346 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3348 // Some databases cannot accept a user name when looking up table names,
3349 // so we use the call below that leaves out the user name
3350 if (!Schema
.IsEmpty() &&
3351 Dbms() != dbmsMY_SQL
&&
3352 Dbms() != dbmsACCESS
&&
3353 Dbms() != dbmsMS_SQL_SERVER
)
3355 retcode
= SQLTablePrivileges(hstmt
,
3357 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3358 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3362 retcode
= SQLTablePrivileges(hstmt
,
3365 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3368 #ifdef DBDEBUG_CONSOLE
3369 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3372 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3373 return(DispAllErrors(henv
, hdbc
, hstmt
));
3375 bool failed
= FALSE
;
3376 retcode
= SQLFetch(hstmt
);
3377 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3379 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3382 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3385 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3388 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3391 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3394 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3397 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3402 return(DispAllErrors(henv
, hdbc
, hstmt
));
3404 #ifdef DBDEBUG_CONSOLE
3405 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3406 result
.privilege
,result
.tableOwner
,result
.tableName
,
3407 result
.grantor
, result
.grantee
);
3410 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3412 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3416 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3417 !wxStrcmp(result
.privilege
,priv
))
3419 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3423 if (!wxStrcmp(result
.grantee
,curRole
) &&
3424 !wxStrcmp(result
.privilege
,priv
))
3426 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3430 retcode
= SQLFetch(hstmt
);
3433 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3436 } // wxDb::TablePrivileges
3439 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3443 if (Dbms() == dbmsACCESS
)
3444 TableName
= _T("\"");
3445 TableName
+= tableName
;
3446 if (Dbms() == dbmsACCESS
)
3447 TableName
+= _T("\"");
3450 } // wxDb::SQLTableName()
3453 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3457 if (Dbms() == dbmsACCESS
)
3460 if (Dbms() == dbmsACCESS
)
3461 ColName
+= _T("\"");
3464 } // wxDb::SQLColumnName()
3467 /********** wxDb::SetSqlLogging() **********/
3468 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3470 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3471 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3473 if (state
== sqlLogON
)
3477 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3478 if (fpSqlLog
== NULL
)
3486 if (fclose(fpSqlLog
))
3492 sqlLogState
= state
;
3495 } // wxDb::SetSqlLogging()
3498 /********** wxDb::WriteSqlLog() **********/
3499 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3501 wxASSERT(logMsg
.Length());
3503 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3506 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3508 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3510 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3515 } // wxDb::WriteSqlLog()
3518 /********** wxDb::Dbms() **********/
3519 wxDBMS
wxDb::Dbms(void)
3521 * Be aware that not all database engines use the exact same syntax, and not
3522 * every ODBC compliant database is compliant to the same level of compliancy.
3523 * Some manufacturers support the minimum Level 1 compliancy, and others up
3524 * through Level 3. Others support subsets of features for levels above 1.
3526 * If you find an inconsistency between the wxDb class and a specific database
3527 * engine, and an identifier to this section, and special handle the database in
3528 * the area where behavior is non-conforming with the other databases.
3531 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3532 * ---------------------------------------------------
3535 * - Currently the only database supported by the class to support VIEWS
3538 * - Does not support the SQL_TIMESTAMP structure
3539 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3540 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3541 * is TRUE. The user must create ALL indexes from their program.
3542 * - Table names can only be 8 characters long
3543 * - Column names can only be 10 characters long
3546 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3547 * after every table name involved in the query/join if that tables matching record(s)
3549 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3551 * SYBASE (Enterprise)
3552 * - If a column is part of the Primary Key, the column cannot be NULL
3553 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3556 * - If a column is part of the Primary Key, the column cannot be NULL
3557 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3558 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3559 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3560 * column definition if it is not defined correctly, but it is experimental
3561 * - Does not support sub-queries in SQL statements
3564 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3565 * - Does not support sub-queries in SQL statements
3568 * - Primary keys must be declared as NOT NULL
3569 * - Table and index names must not be longer than 13 characters in length (technically
3570 * table names can be up to 18 characters, but the primary index is created using the
3571 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3576 * - Columns that are part of primary keys must be defined as being NOT NULL
3577 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3578 * column definition if it is not defined correctly, but it is experimental
3581 // Should only need to do this once for each new database connection
3582 // so return the value we already determined it to be to save time
3583 // and lots of string comparisons
3584 if (dbmsType
!= dbmsUNIDENTIFIED
)
3587 wxChar baseName
[25+1];
3588 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3591 // RGG 20001025 : add support for Interbase
3592 // GT : Integrated to base classes on 20001121
3593 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3594 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3596 // BJO 20000428 : add support for Virtuoso
3597 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3598 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3600 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3601 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3603 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3604 // connected through an OpenLink driver.
3605 // Is it also returned by Sybase Adapatitve server?
3606 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3607 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3609 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3610 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3611 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3613 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3616 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3617 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3620 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3621 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3624 if (!wxStricmp(baseName
,wxT("Pervasive")))
3625 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3628 if (!wxStricmp(baseName
,wxT("Informix")))
3629 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3632 if (!wxStricmp(baseName
,wxT("Oracle")))
3633 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3634 if (!wxStricmp(baseName
,wxT("ACCESS")))
3635 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3636 if (!wxStricmp(baseName
,wxT("Sybase")))
3637 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3640 if (!wxStricmp(baseName
,wxT("DBASE")))
3641 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3642 if (!wxStricmp(baseName
,wxT("xBase")))
3643 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3644 if (!wxStricmp(baseName
,wxT("MySQL")))
3645 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3648 if (!wxStricmp(baseName
,wxT("DB2")))
3649 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3651 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3656 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3657 int dataType
, ULONG columnLength
,
3658 const wxString
&optionalParam
)
3660 wxASSERT(tableName
.Length());
3661 wxASSERT(columnName
.Length());
3662 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3663 dataType
!= DB_DATA_TYPE_VARCHAR
);
3665 // Must specify a columnLength if modifying a VARCHAR type column
3666 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3669 wxString dataTypeName
;
3671 wxString alterSlashModify
;
3675 case DB_DATA_TYPE_VARCHAR
:
3676 dataTypeName
= typeInfVarchar
.TypeName
;
3678 case DB_DATA_TYPE_INTEGER
:
3679 dataTypeName
= typeInfInteger
.TypeName
;
3681 case DB_DATA_TYPE_FLOAT
:
3682 dataTypeName
= typeInfFloat
.TypeName
;
3684 case DB_DATA_TYPE_DATE
:
3685 dataTypeName
= typeInfDate
.TypeName
;
3687 case DB_DATA_TYPE_BLOB
:
3688 dataTypeName
= typeInfBlob
.TypeName
;
3694 // Set the modify or alter syntax depending on the type of database connected to
3698 alterSlashModify
= _T("MODIFY");
3700 case dbmsMS_SQL_SERVER
:
3701 alterSlashModify
= _T("ALTER COLUMN");
3703 case dbmsUNIDENTIFIED
:
3705 case dbmsSYBASE_ASA
:
3706 case dbmsSYBASE_ASE
:
3711 case dbmsXBASE_SEQUITER
:
3713 alterSlashModify
= _T("MODIFY");
3717 // create the SQL statement
3718 if ( Dbms() == dbmsMY_SQL
)
3720 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3721 columnName
.c_str(), dataTypeName
.c_str());
3725 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3726 columnName
.c_str(), dataTypeName
.c_str());
3729 // For varchars only, append the size of the column
3730 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3731 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3734 s
.Printf(wxT("(%lu)"), columnLength
);
3738 // for passing things like "NOT NULL"
3739 if (optionalParam
.Length())
3741 sqlStmt
+= wxT(" ");
3742 sqlStmt
+= optionalParam
;
3745 return ExecSql(sqlStmt
);
3747 } // wxDb::ModifyColumn()
3750 /********** wxDbGetConnection() **********/
3751 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3755 // Used to keep a pointer to a DB connection that matches the requested
3756 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3757 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3758 // rather than having to re-query the datasource to get all the values
3759 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3760 wxDb
*matchingDbConnection
= NULL
;
3762 // Scan the linked list searching for an available database connection
3763 // that's already been opened but is currently not in use.
3764 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3766 // The database connection must be for the same datasource
3767 // name and must currently not be in use.
3769 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3770 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3772 pList
->Free
= FALSE
;
3773 return(pList
->PtrDb
);
3776 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3777 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3778 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3779 matchingDbConnection
= pList
->PtrDb
;
3782 // No available connections. A new connection must be made and
3783 // appended to the end of the linked list.
3786 // Find the end of the list
3787 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3788 // Append a new list item
3789 pList
->PtrNext
= new wxDbList
;
3790 pList
->PtrNext
->PtrPrev
= pList
;
3791 pList
= pList
->PtrNext
;
3795 // Create the first node on the list
3796 pList
= PtrBegDbList
= new wxDbList
;
3800 // Initialize new node in the linked list
3802 pList
->Free
= FALSE
;
3803 pList
->Dsn
= pDbConfig
->GetDsn();
3804 pList
->Uid
= pDbConfig
->GetUserID();
3805 pList
->AuthStr
= pDbConfig
->GetPassword();
3807 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3811 if (!matchingDbConnection
)
3812 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3814 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3816 // Connect to the datasource
3819 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3820 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3821 return(pList
->PtrDb
);
3823 else // Unable to connect, destroy list item
3826 pList
->PtrPrev
->PtrNext
= 0;
3828 PtrBegDbList
= 0; // Empty list again
3829 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3830 pList
->PtrDb
->Close(); // Close the wxDb object
3831 delete pList
->PtrDb
; // Deletes the wxDb object
3832 delete pList
; // Deletes the linked list object
3836 } // wxDbGetConnection()
3839 /********** wxDbFreeConnection() **********/
3840 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3844 // Scan the linked list searching for the database connection
3845 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3847 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3848 return (pList
->Free
= TRUE
);
3851 // Never found the database object, return failure
3854 } // wxDbFreeConnection()
3857 /********** wxDbCloseConnections() **********/
3858 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3860 wxDbList
*pList
, *pNext
;
3862 // Traverse the linked list closing database connections and freeing memory as I go.
3863 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3865 pNext
= pList
->PtrNext
; // Save the pointer to next
3866 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3867 pList
->PtrDb
->Close(); // Close the wxDb object
3868 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3869 delete pList
->PtrDb
; // Deletes the wxDb object
3870 delete pList
; // Deletes the linked list object
3873 // Mark the list as empty
3876 } // wxDbCloseConnections()
3879 /********** wxDbConnectionsInUse() **********/
3880 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
3885 // Scan the linked list counting db connections that are currently in use
3886 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3888 if (pList
->Free
== FALSE
)
3894 } // wxDbConnectionsInUse()
3898 /********** wxDbLogExtendedErrorMsg() **********/
3899 // DEBUG ONLY function
3900 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3902 const wxChar
*ErrFile
,
3905 static wxString msg
;
3910 if (ErrFile
|| ErrLine
)
3912 msg
+= wxT("File: ");
3914 msg
+= wxT(" Line: ");
3915 tStr
.Printf(wxT("%d"),ErrLine
);
3916 msg
+= tStr
.c_str();
3920 msg
.Append (wxT("\nODBC errors:\n"));
3923 // Display errors for this connection
3925 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3927 if (pDb
->errorList
[i
])
3929 msg
.Append(pDb
->errorList
[i
]);
3930 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3931 msg
.Append(wxT("\n"));
3932 // Clear the errmsg buffer so the next error will not
3933 // end up showing the previous error that have occurred
3934 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3939 wxLogDebug(msg
.c_str());
3942 } // wxDbLogExtendedErrorMsg()
3945 /********** wxDbSqlLog() **********/
3946 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3948 bool append
= FALSE
;
3951 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3953 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3958 SQLLOGstate
= state
;
3959 SQLLOGfn
= filename
;
3967 /********** wxDbCreateDataSource() **********/
3968 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3969 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3971 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3972 * Very rudimentary creation of an ODBC data source.
3974 * ODBC driver must be ODBC 3.0 compliant to use this function
3979 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3985 dsnLocation
= ODBC_ADD_SYS_DSN
;
3987 dsnLocation
= ODBC_ADD_DSN
;
3989 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3990 // so that is why I used it, as wxString does not deal well with
3991 // embedded nulls in strings
3992 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3994 // Replace the separator from above with the '\0' seperator needed
3995 // by the SQLConfigDataSource() function
3999 k
= setupStr
.Find((wxChar
)2,TRUE
);
4000 if (k
!= wxNOT_FOUND
)
4001 setupStr
[(UINT
)k
] = wxT('\0');
4003 while (k
!= wxNOT_FOUND
);
4005 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4006 driverName
, setupStr
.c_str());
4008 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4010 // check for errors caused by ConfigDSN based functions
4013 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4014 errMsg
[0] = wxT('\0');
4016 // This function is only supported in ODBC drivers v3.0 compliant and above
4017 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4020 #ifdef DBDEBUG_CONSOLE
4021 // When run in console mode, use standard out to display errors.
4022 cout
<< errMsg
<< endl
;
4023 cout
<< wxT("Press any key to continue...") << endl
;
4025 #endif // DBDEBUG_CONSOLE
4028 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4029 #endif // __WXDEBUG__
4035 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4036 // necessary to use this function, so this function is not supported
4038 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4041 #endif // __VISUALC__
4045 } // wxDbCreateDataSource()
4049 /********** wxDbGetDataSource() **********/
4050 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4051 SWORD DsDescMax
, UWORD direction
)
4053 * Dsn and DsDesc will contain the data source name and data source
4054 * description upon return
4059 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4060 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4065 } // wxDbGetDataSource()
4068 // Change this to 0 to remove use of all deprecated functions
4069 #if wxODBC_BACKWARD_COMPATABILITY
4070 /********************************************************************
4071 ********************************************************************
4073 * The following functions are all DEPRECATED and are included for
4074 * backward compatability reasons only
4076 ********************************************************************
4077 ********************************************************************/
4078 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4080 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4082 /***** DEPRECATED: use wxGetDataSource() *****/
4083 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4086 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4088 /***** DEPRECATED: use wxDbGetConnection() *****/
4089 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4091 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4093 /***** DEPRECATED: use wxDbFreeConnection() *****/
4094 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4096 return wxDbFreeConnection(pDb
);
4098 /***** DEPRECATED: use wxDbCloseConnections() *****/
4099 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4101 wxDbCloseConnections();
4103 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4104 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4106 return wxDbConnectionsInUse();