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
;
761 #ifdef DBDEBUG_CONSOLE
762 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
763 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
764 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
765 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
766 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
770 // Completed Successfully
776 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
778 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
779 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
783 bool wxDb::Open(wxDb
*copyDb
)
785 dsn
= copyDb
->GetDatasourceName();
786 uid
= copyDb
->GetUsername();
787 authStr
= copyDb
->GetPassword();
791 if (!FwdOnlyCursors())
793 // Specify that the ODBC cursor library be used, if needed. This must be
794 // specified before the connection is made.
795 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
797 #ifdef DBDEBUG_CONSOLE
798 if (retcode
== SQL_SUCCESS
)
799 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
801 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
803 wxUnusedVar( retcode
);
807 // Connect to the data source
808 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
809 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
810 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
812 if (retcode
== SQL_ERROR
)
813 return(DispAllErrors(henv
, hdbc
));
816 If using Intersolv branded ODBC drivers, this is the place where you would substitute
817 your branded driver license information
819 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
820 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
823 // Mark database as open
826 // Allocate a statement handle for the database connection
827 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
828 return(DispAllErrors(henv
, hdbc
));
830 // Set Connection Options
831 if (!setConnectionOptions())
834 // Instead of Querying the data source for info about itself, it can just be copied
835 // from the wxDb instance that was passed in (copyDb).
836 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
837 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
838 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
839 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
840 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
841 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
842 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
843 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
844 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
845 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
846 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
847 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
848 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
849 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
850 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
851 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
852 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
853 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
854 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
855 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
856 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
857 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
858 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
859 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
860 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
861 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
862 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
863 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
864 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
865 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
866 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
868 // VARCHAR = Variable length character string
869 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
870 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
871 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
872 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
873 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
876 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
877 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
878 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
879 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
880 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
883 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
884 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
885 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
886 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
887 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
890 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
891 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
892 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
893 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
894 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
897 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
898 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
899 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
900 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
901 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
903 #ifdef DBDEBUG_CONSOLE
904 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
905 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
906 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
907 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
908 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
912 // Completed Successfully
917 /********** wxDb::setConnectionOptions() **********/
918 bool wxDb::setConnectionOptions(void)
920 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
925 // I need to get the DBMS name here, because some of the connection options
926 // are database specific and need to call the Dbms() function.
927 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
928 return(DispAllErrors(henv
, hdbc
));
930 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
931 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
932 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
934 // By default, MS Sql Server closes cursors on commit and rollback. The following
935 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
936 // after a transaction. This is a driver specific option and is not part of the
937 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
938 // The database settings don't have any effect one way or the other.
939 if (Dbms() == dbmsMS_SQL_SERVER
)
941 const long SQL_PRESERVE_CURSORS
= 1204L;
942 const long SQL_PC_ON
= 1L;
943 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
946 // Display the connection options to verify them
947 #ifdef DBDEBUG_CONSOLE
949 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
951 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
952 return(DispAllErrors(henv
, hdbc
));
953 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
955 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
956 return(DispAllErrors(henv
, hdbc
));
957 cout
<< wxT("ODBC CURSORS: ");
960 case(SQL_CUR_USE_IF_NEEDED
):
961 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
963 case(SQL_CUR_USE_ODBC
):
964 cout
<< wxT("SQL_CUR_USE_ODBC");
966 case(SQL_CUR_USE_DRIVER
):
967 cout
<< wxT("SQL_CUR_USE_DRIVER");
972 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
973 return(DispAllErrors(henv
, hdbc
));
974 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
979 // Completed Successfully
982 } // wxDb::setConnectionOptions()
985 /********** wxDb::getDbInfo() **********/
986 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
991 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
);
992 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
994 DispAllErrors(henv
, hdbc
);
995 if (failOnDataTypeUnsupported
)
999 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
);
1000 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1002 DispAllErrors(henv
, hdbc
);
1003 if (failOnDataTypeUnsupported
)
1007 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
);
1008 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1010 DispAllErrors(henv
, hdbc
);
1011 if (failOnDataTypeUnsupported
)
1016 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1017 // causing database connectivity to fail in some cases.
1018 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1019 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1021 DispAllErrors(henv
, hdbc
);
1022 if (failOnDataTypeUnsupported
)
1026 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1027 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1029 DispAllErrors(henv
, hdbc
);
1030 if (failOnDataTypeUnsupported
)
1034 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1035 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1037 DispAllErrors(henv
, hdbc
);
1038 if (failOnDataTypeUnsupported
)
1042 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
);
1043 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1045 DispAllErrors(henv
, hdbc
);
1046 if (failOnDataTypeUnsupported
)
1050 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
);
1051 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1053 DispAllErrors(henv
, hdbc
);
1054 if (failOnDataTypeUnsupported
)
1058 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1059 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1061 DispAllErrors(henv
, hdbc
);
1062 if (failOnDataTypeUnsupported
)
1066 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
);
1067 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1069 DispAllErrors(henv
, hdbc
);
1070 if (failOnDataTypeUnsupported
)
1074 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1075 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1077 DispAllErrors(henv
, hdbc
);
1078 if (failOnDataTypeUnsupported
)
1082 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1083 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1085 // Not all drivers support this call - Nick Gorham(unixODBC)
1086 dbInf
.cliConfLvl
= 0;
1087 DispAllErrors(henv
, hdbc
);
1088 if (failOnDataTypeUnsupported
)
1092 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1093 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1095 DispAllErrors(henv
, hdbc
);
1096 if (failOnDataTypeUnsupported
)
1100 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
);
1101 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1103 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1104 // TODO: dbInf.outerJoins[0]='N';
1105 // TODO: dbInf.outerJoins[1]='\x0';
1107 DispAllErrors(henv
, hdbc
);
1108 if (failOnDataTypeUnsupported
)
1112 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
);
1113 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1115 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1116 // TODO: dbInf.procedureSupport[0]='N';
1117 // TODO: dbInf.procedureSupport[1]='\x0';
1119 DispAllErrors(henv
, hdbc
);
1120 if (failOnDataTypeUnsupported
)
1124 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
);
1125 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1127 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1128 // TODO: dbInf.accessibleTables[0]='N';
1129 // TODO: dbInf.accessibleTables[1]='\x0';
1131 DispAllErrors(henv
, hdbc
);
1132 if (failOnDataTypeUnsupported
)
1136 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1137 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1139 DispAllErrors(henv
, hdbc
);
1140 if (failOnDataTypeUnsupported
)
1144 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1145 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1147 DispAllErrors(henv
, hdbc
);
1148 if (failOnDataTypeUnsupported
)
1152 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1153 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1155 DispAllErrors(henv
, hdbc
);
1156 if (failOnDataTypeUnsupported
)
1160 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
);
1161 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1163 // TODO: BugTracker# 785080 : fails with mysql 4 on linux - edr
1164 // TODO: dbInf.supportIEF[0]='N';
1165 // TODO: dbInf.supportIEF[1]='\x0';
1167 DispAllErrors(henv
, hdbc
);
1168 if (failOnDataTypeUnsupported
)
1172 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1173 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1175 DispAllErrors(henv
, hdbc
);
1176 if (failOnDataTypeUnsupported
)
1180 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1181 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1183 DispAllErrors(henv
, hdbc
);
1184 if (failOnDataTypeUnsupported
)
1188 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1189 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1191 DispAllErrors(henv
, hdbc
);
1192 if (failOnDataTypeUnsupported
)
1196 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1197 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1199 DispAllErrors(henv
, hdbc
);
1200 if (failOnDataTypeUnsupported
)
1204 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1205 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1207 DispAllErrors(henv
, hdbc
);
1208 if (failOnDataTypeUnsupported
)
1212 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1213 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1215 DispAllErrors(henv
, hdbc
);
1216 if (failOnDataTypeUnsupported
)
1220 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1221 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1223 DispAllErrors(henv
, hdbc
);
1224 if (failOnDataTypeUnsupported
)
1228 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1229 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1231 DispAllErrors(henv
, hdbc
);
1232 if (failOnDataTypeUnsupported
)
1236 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1237 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1239 DispAllErrors(henv
, hdbc
);
1240 if (failOnDataTypeUnsupported
)
1244 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1245 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1247 DispAllErrors(henv
, hdbc
);
1248 if (failOnDataTypeUnsupported
)
1252 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1253 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1255 DispAllErrors(henv
, hdbc
);
1256 if (failOnDataTypeUnsupported
)
1260 #ifdef DBDEBUG_CONSOLE
1261 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1262 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1263 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1264 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1266 cout
<< wxT("API Conf. Level: ");
1267 switch(dbInf
.apiConfLvl
)
1269 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1270 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1271 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1275 cout
<< wxT("SAG CLI Conf. Level: ");
1276 switch(dbInf
.cliConfLvl
)
1278 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1279 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1283 cout
<< wxT("SQL Conf. Level: ");
1284 switch(dbInf
.sqlConfLvl
)
1286 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1287 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1288 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1292 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1293 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1294 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1295 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1296 cout
<< wxT("Cursor COMMIT Behavior: ");
1297 switch(dbInf
.cursorCommitBehavior
)
1299 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1300 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1301 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1305 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1306 switch(dbInf
.cursorRollbackBehavior
)
1308 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1309 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1310 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1314 cout
<< wxT("Support NOT NULL clause: ");
1315 switch(dbInf
.supportNotNullClause
)
1317 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1318 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1322 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1323 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1325 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1328 cout
<< wxT("Default Transaction Isolation: ";
1329 switch(dbInf
.txnIsolation
)
1331 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1332 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1333 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1334 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1336 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1341 cout
<< wxT("Transaction Isolation Options: ");
1342 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1343 cout
<< wxT("Read Uncommitted, ");
1344 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1345 cout
<< wxT("Read Committed, ");
1346 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1347 cout
<< wxT("Repeatable Read, ");
1348 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1349 cout
<< wxT("Serializable, ");
1351 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1352 cout
<< wxT("Versioning");
1356 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1357 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1358 cout
<< wxT("Next, ");
1359 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1360 cout
<< wxT("Prev, ");
1361 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1362 cout
<< wxT("First, ");
1363 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1364 cout
<< wxT("Last, ");
1365 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1366 cout
<< wxT("Absolute, ");
1367 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1368 cout
<< wxT("Relative, ");
1370 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1371 cout
<< wxT("Resume, ");
1373 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1374 cout
<< wxT("Bookmark");
1377 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1378 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1379 cout
<< wxT("No Change, ");
1380 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1381 cout
<< wxT("Exclusive, ");
1382 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1383 cout
<< wxT("UnLock");
1386 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1387 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1388 cout
<< wxT("Position, ");
1389 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1390 cout
<< wxT("Refresh, ");
1391 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1392 cout
<< wxT("Upd, "));
1393 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1394 cout
<< wxT("Del, ");
1395 if (dbInf
.posOperations
& SQL_POS_ADD
)
1399 cout
<< wxT("Positioned Statements Supported: ");
1400 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1401 cout
<< wxT("Pos delete, ");
1402 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1403 cout
<< wxT("Pos update, ");
1404 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1405 cout
<< wxT("Select for update");
1408 cout
<< wxT("Scroll Concurrency: ");
1409 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1410 cout
<< wxT("Read Only, ");
1411 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1412 cout
<< wxT("Lock, ");
1413 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1414 cout
<< wxT("Opt. Rowver, ");
1415 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1416 cout
<< wxT("Opt. Values");
1419 cout
<< wxT("Scroll Options: ");
1420 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1421 cout
<< wxT("Fwd Only, ");
1422 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1423 cout
<< wxT("Static, ");
1424 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1425 cout
<< wxT("Keyset Driven, ");
1426 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1427 cout
<< wxT("Dynamic, ");
1428 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1429 cout
<< wxT("Mixed");
1432 cout
<< wxT("Static Sensitivity: ");
1433 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1434 cout
<< wxT("Additions, ");
1435 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1436 cout
<< wxT("Deletions, ");
1437 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1438 cout
<< wxT("Updates");
1441 cout
<< wxT("Transaction Capable?: ");
1442 switch(dbInf
.txnCapable
)
1444 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1445 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1446 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1447 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1448 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1455 // Completed Successfully
1458 } // wxDb::getDbInfo()
1461 /********** wxDb::getDataTypeInfo() **********/
1462 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1465 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1466 * the data type inf. is gathered for.
1468 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1473 // Get information about the data type specified
1474 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1475 return(DispAllErrors(henv
, hdbc
, hstmt
));
1478 retcode
= SQLFetch(hstmt
);
1479 if (retcode
!= SQL_SUCCESS
)
1481 #ifdef DBDEBUG_CONSOLE
1482 if (retcode
== SQL_NO_DATA_FOUND
)
1483 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1485 DispAllErrors(henv
, hdbc
, hstmt
);
1486 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1490 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1492 // Obtain columns from the record
1493 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1494 return(DispAllErrors(henv
, hdbc
, hstmt
));
1496 structSQLTypeInfo
.TypeName
= typeName
;
1498 // BJO 20000503: no more needed with new GetColumns...
1501 if (Dbms() == dbmsMY_SQL
)
1503 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1504 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1505 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1506 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1507 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1508 structSQLTypeInfo
.TypeName
= wxT("int");
1509 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1510 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1511 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1512 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1513 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1514 structSQLTypeInfo
.TypeName
= wxT("char");
1517 // BJO 20000427 : OpenLink driver
1518 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1519 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1521 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1522 structSQLTypeInfo
.TypeName
= wxT("real");
1526 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1527 return(DispAllErrors(henv
, hdbc
, hstmt
));
1528 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1529 return(DispAllErrors(henv
, hdbc
, hstmt
));
1530 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1531 // return(DispAllErrors(henv, hdbc, hstmt));
1533 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1534 return(DispAllErrors(henv
, hdbc
, hstmt
));
1536 if (structSQLTypeInfo
.MaximumScale
< 0)
1537 structSQLTypeInfo
.MaximumScale
= 0;
1539 // Close the statement handle which closes open cursors
1540 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1541 return(DispAllErrors(henv
, hdbc
, hstmt
));
1543 // Completed Successfully
1546 } // wxDb::getDataTypeInfo()
1549 /********** wxDb::Close() **********/
1550 void wxDb::Close(void)
1552 // Close the Sql Log file
1559 // Free statement handle
1562 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1563 DispAllErrors(henv
, hdbc
);
1566 // Disconnect from the datasource
1567 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1568 DispAllErrors(henv
, hdbc
);
1570 // Free the connection to the datasource
1571 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1572 DispAllErrors(henv
, hdbc
);
1574 // There should be zero Ctable objects still connected to this db object
1575 wxASSERT(nTables
== 0);
1580 pNode
= TablesInUse
.GetFirst();
1584 tiu
= (wxTablesInUse
*)pNode
->GetData();
1585 if (tiu
->pDb
== this)
1587 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1588 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1589 wxLogDebug(s
.c_str(),s2
.c_str());
1591 pNode
= pNode
->GetNext();
1595 // Copy the error messages to a global variable
1597 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1598 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1600 dbmsType
= dbmsUNIDENTIFIED
;
1606 /********** wxDb::CommitTrans() **********/
1607 bool wxDb::CommitTrans(void)
1611 // Commit the transaction
1612 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1613 return(DispAllErrors(henv
, hdbc
));
1616 // Completed successfully
1619 } // wxDb::CommitTrans()
1622 /********** wxDb::RollbackTrans() **********/
1623 bool wxDb::RollbackTrans(void)
1625 // Rollback the transaction
1626 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1627 return(DispAllErrors(henv
, hdbc
));
1629 // Completed successfully
1632 } // wxDb::RollbackTrans()
1635 /********** wxDb::DispAllErrors() **********/
1636 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1638 * This function is called internally whenever an error condition prevents the user's
1639 * request from being executed. This function will query the datasource as to the
1640 * actual error(s) that just occured on the previous request of the datasource.
1642 * The function will retrieve each error condition from the datasource and
1643 * Printf the codes/text values into a string which it then logs via logError().
1644 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1645 * window and program execution will be paused until the user presses a key.
1647 * This function always returns a FALSE, so that functions which call this function
1648 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1649 * of the users request, so that the calling code can then process the error msg log
1652 wxString odbcErrMsg
;
1654 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1656 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1657 logError(odbcErrMsg
, sqlState
);
1660 #ifdef DBDEBUG_CONSOLE
1661 // When run in console mode, use standard out to display errors.
1662 cout
<< odbcErrMsg
.c_str() << endl
;
1663 cout
<< wxT("Press any key to continue...") << endl
;
1668 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1673 return(FALSE
); // This function always returns FALSE.
1675 } // wxDb::DispAllErrors()
1678 /********** wxDb::GetNextError() **********/
1679 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1681 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1686 } // wxDb::GetNextError()
1689 /********** wxDb::DispNextError() **********/
1690 void wxDb::DispNextError(void)
1692 wxString odbcErrMsg
;
1694 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1695 logError(odbcErrMsg
, sqlState
);
1700 #ifdef DBDEBUG_CONSOLE
1701 // When run in console mode, use standard out to display errors.
1702 cout
<< odbcErrMsg
.c_str() << endl
;
1703 cout
<< wxT("Press any key to continue...") << endl
;
1708 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1709 #endif // __WXDEBUG__
1711 } // wxDb::DispNextError()
1714 /********** wxDb::logError() **********/
1715 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1717 wxASSERT(errMsg
.Length());
1719 static int pLast
= -1;
1722 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1725 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1726 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1730 wxStrcpy(errorList
[pLast
], errMsg
);
1732 if (SQLState
.Length())
1733 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1734 DB_STATUS
= dbStatus
;
1736 // Add the errmsg to the sql log
1737 WriteSqlLog(errMsg
);
1739 } // wxDb::logError()
1742 /**********wxDb::TranslateSqlState() **********/
1743 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1745 if (!wxStrcmp(SQLState
, wxT("01000")))
1746 return(DB_ERR_GENERAL_WARNING
);
1747 if (!wxStrcmp(SQLState
, wxT("01002")))
1748 return(DB_ERR_DISCONNECT_ERROR
);
1749 if (!wxStrcmp(SQLState
, wxT("01004")))
1750 return(DB_ERR_DATA_TRUNCATED
);
1751 if (!wxStrcmp(SQLState
, wxT("01006")))
1752 return(DB_ERR_PRIV_NOT_REVOKED
);
1753 if (!wxStrcmp(SQLState
, wxT("01S00")))
1754 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1755 if (!wxStrcmp(SQLState
, wxT("01S01")))
1756 return(DB_ERR_ERROR_IN_ROW
);
1757 if (!wxStrcmp(SQLState
, wxT("01S02")))
1758 return(DB_ERR_OPTION_VALUE_CHANGED
);
1759 if (!wxStrcmp(SQLState
, wxT("01S03")))
1760 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1761 if (!wxStrcmp(SQLState
, wxT("01S04")))
1762 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1763 if (!wxStrcmp(SQLState
, wxT("07001")))
1764 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1765 if (!wxStrcmp(SQLState
, wxT("07006")))
1766 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1767 if (!wxStrcmp(SQLState
, wxT("08001")))
1768 return(DB_ERR_UNABLE_TO_CONNECT
);
1769 if (!wxStrcmp(SQLState
, wxT("08002")))
1770 return(DB_ERR_CONNECTION_IN_USE
);
1771 if (!wxStrcmp(SQLState
, wxT("08003")))
1772 return(DB_ERR_CONNECTION_NOT_OPEN
);
1773 if (!wxStrcmp(SQLState
, wxT("08004")))
1774 return(DB_ERR_REJECTED_CONNECTION
);
1775 if (!wxStrcmp(SQLState
, wxT("08007")))
1776 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1777 if (!wxStrcmp(SQLState
, wxT("08S01")))
1778 return(DB_ERR_COMM_LINK_FAILURE
);
1779 if (!wxStrcmp(SQLState
, wxT("21S01")))
1780 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1781 if (!wxStrcmp(SQLState
, wxT("21S02")))
1782 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1783 if (!wxStrcmp(SQLState
, wxT("22001")))
1784 return(DB_ERR_STRING_RIGHT_TRUNC
);
1785 if (!wxStrcmp(SQLState
, wxT("22003")))
1786 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1787 if (!wxStrcmp(SQLState
, wxT("22005")))
1788 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1789 if (!wxStrcmp(SQLState
, wxT("22008")))
1790 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1791 if (!wxStrcmp(SQLState
, wxT("22012")))
1792 return(DB_ERR_DIVIDE_BY_ZERO
);
1793 if (!wxStrcmp(SQLState
, wxT("22026")))
1794 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1795 if (!wxStrcmp(SQLState
, wxT("23000")))
1796 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1797 if (!wxStrcmp(SQLState
, wxT("24000")))
1798 return(DB_ERR_INVALID_CURSOR_STATE
);
1799 if (!wxStrcmp(SQLState
, wxT("25000")))
1800 return(DB_ERR_INVALID_TRANS_STATE
);
1801 if (!wxStrcmp(SQLState
, wxT("28000")))
1802 return(DB_ERR_INVALID_AUTH_SPEC
);
1803 if (!wxStrcmp(SQLState
, wxT("34000")))
1804 return(DB_ERR_INVALID_CURSOR_NAME
);
1805 if (!wxStrcmp(SQLState
, wxT("37000")))
1806 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1807 if (!wxStrcmp(SQLState
, wxT("3C000")))
1808 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1809 if (!wxStrcmp(SQLState
, wxT("40001")))
1810 return(DB_ERR_SERIALIZATION_FAILURE
);
1811 if (!wxStrcmp(SQLState
, wxT("42000")))
1812 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1813 if (!wxStrcmp(SQLState
, wxT("70100")))
1814 return(DB_ERR_OPERATION_ABORTED
);
1815 if (!wxStrcmp(SQLState
, wxT("IM001")))
1816 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1817 if (!wxStrcmp(SQLState
, wxT("IM002")))
1818 return(DB_ERR_NO_DATA_SOURCE
);
1819 if (!wxStrcmp(SQLState
, wxT("IM003")))
1820 return(DB_ERR_DRIVER_LOAD_ERROR
);
1821 if (!wxStrcmp(SQLState
, wxT("IM004")))
1822 return(DB_ERR_SQLALLOCENV_FAILED
);
1823 if (!wxStrcmp(SQLState
, wxT("IM005")))
1824 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1825 if (!wxStrcmp(SQLState
, wxT("IM006")))
1826 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1827 if (!wxStrcmp(SQLState
, wxT("IM007")))
1828 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1829 if (!wxStrcmp(SQLState
, wxT("IM008")))
1830 return(DB_ERR_DIALOG_FAILED
);
1831 if (!wxStrcmp(SQLState
, wxT("IM009")))
1832 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1833 if (!wxStrcmp(SQLState
, wxT("IM010")))
1834 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1835 if (!wxStrcmp(SQLState
, wxT("IM011")))
1836 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1837 if (!wxStrcmp(SQLState
, wxT("IM012")))
1838 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1839 if (!wxStrcmp(SQLState
, wxT("IM013")))
1840 return(DB_ERR_TRACE_FILE_ERROR
);
1841 if (!wxStrcmp(SQLState
, wxT("S0001")))
1842 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1843 if (!wxStrcmp(SQLState
, wxT("S0002")))
1844 return(DB_ERR_TABLE_NOT_FOUND
);
1845 if (!wxStrcmp(SQLState
, wxT("S0011")))
1846 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1847 if (!wxStrcmp(SQLState
, wxT("S0012")))
1848 return(DB_ERR_INDEX_NOT_FOUND
);
1849 if (!wxStrcmp(SQLState
, wxT("S0021")))
1850 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1851 if (!wxStrcmp(SQLState
, wxT("S0022")))
1852 return(DB_ERR_COLUMN_NOT_FOUND
);
1853 if (!wxStrcmp(SQLState
, wxT("S0023")))
1854 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1855 if (!wxStrcmp(SQLState
, wxT("S1000")))
1856 return(DB_ERR_GENERAL_ERROR
);
1857 if (!wxStrcmp(SQLState
, wxT("S1001")))
1858 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1859 if (!wxStrcmp(SQLState
, wxT("S1002")))
1860 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1861 if (!wxStrcmp(SQLState
, wxT("S1003")))
1862 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1863 if (!wxStrcmp(SQLState
, wxT("S1004")))
1864 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1865 if (!wxStrcmp(SQLState
, wxT("S1008")))
1866 return(DB_ERR_OPERATION_CANCELLED
);
1867 if (!wxStrcmp(SQLState
, wxT("S1009")))
1868 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1869 if (!wxStrcmp(SQLState
, wxT("S1010")))
1870 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1871 if (!wxStrcmp(SQLState
, wxT("S1011")))
1872 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1873 if (!wxStrcmp(SQLState
, wxT("S1012")))
1874 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1875 if (!wxStrcmp(SQLState
, wxT("S1015")))
1876 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1877 if (!wxStrcmp(SQLState
, wxT("S1090")))
1878 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1879 if (!wxStrcmp(SQLState
, wxT("S1091")))
1880 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1881 if (!wxStrcmp(SQLState
, wxT("S1092")))
1882 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1883 if (!wxStrcmp(SQLState
, wxT("S1093")))
1884 return(DB_ERR_INVALID_PARAM_NO
);
1885 if (!wxStrcmp(SQLState
, wxT("S1094")))
1886 return(DB_ERR_INVALID_SCALE_VALUE
);
1887 if (!wxStrcmp(SQLState
, wxT("S1095")))
1888 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1889 if (!wxStrcmp(SQLState
, wxT("S1096")))
1890 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1891 if (!wxStrcmp(SQLState
, wxT("S1097")))
1892 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1893 if (!wxStrcmp(SQLState
, wxT("S1098")))
1894 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1895 if (!wxStrcmp(SQLState
, wxT("S1099")))
1896 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1897 if (!wxStrcmp(SQLState
, wxT("S1100")))
1898 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1899 if (!wxStrcmp(SQLState
, wxT("S1101")))
1900 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1901 if (!wxStrcmp(SQLState
, wxT("S1103")))
1902 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1903 if (!wxStrcmp(SQLState
, wxT("S1104")))
1904 return(DB_ERR_INVALID_PRECISION_VALUE
);
1905 if (!wxStrcmp(SQLState
, wxT("S1105")))
1906 return(DB_ERR_INVALID_PARAM_TYPE
);
1907 if (!wxStrcmp(SQLState
, wxT("S1106")))
1908 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1909 if (!wxStrcmp(SQLState
, wxT("S1107")))
1910 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1911 if (!wxStrcmp(SQLState
, wxT("S1108")))
1912 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1913 if (!wxStrcmp(SQLState
, wxT("S1109")))
1914 return(DB_ERR_INVALID_CURSOR_POSITION
);
1915 if (!wxStrcmp(SQLState
, wxT("S1110")))
1916 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1917 if (!wxStrcmp(SQLState
, wxT("S1111")))
1918 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1919 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1920 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1921 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1922 return(DB_ERR_TIMEOUT_EXPIRED
);
1927 } // wxDb::TranslateSqlState()
1930 /********** wxDb::Grant() **********/
1931 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1935 // Build the grant statement
1936 sqlStmt
= wxT("GRANT ");
1937 if (privileges
== DB_GRANT_ALL
)
1938 sqlStmt
+= wxT("ALL");
1942 if (privileges
& DB_GRANT_SELECT
)
1944 sqlStmt
+= wxT("SELECT");
1947 if (privileges
& DB_GRANT_INSERT
)
1950 sqlStmt
+= wxT(", ");
1951 sqlStmt
+= wxT("INSERT");
1953 if (privileges
& DB_GRANT_UPDATE
)
1956 sqlStmt
+= wxT(", ");
1957 sqlStmt
+= wxT("UPDATE");
1959 if (privileges
& DB_GRANT_DELETE
)
1962 sqlStmt
+= wxT(", ");
1963 sqlStmt
+= wxT("DELETE");
1967 sqlStmt
+= wxT(" ON ");
1968 sqlStmt
+= SQLTableName(tableName
);
1969 sqlStmt
+= wxT(" TO ");
1970 sqlStmt
+= userList
;
1972 #ifdef DBDEBUG_CONSOLE
1973 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1976 WriteSqlLog(sqlStmt
);
1978 return(ExecSql(sqlStmt
));
1983 /********** wxDb::CreateView() **********/
1984 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1985 const wxString
&pSqlStmt
, bool attemptDrop
)
1989 // Drop the view first
1990 if (attemptDrop
&& !DropView(viewName
))
1993 // Build the create view statement
1994 sqlStmt
= wxT("CREATE VIEW ");
1995 sqlStmt
+= viewName
;
1997 if (colList
.Length())
1999 sqlStmt
+= wxT(" (");
2001 sqlStmt
+= wxT(")");
2004 sqlStmt
+= wxT(" AS ");
2005 sqlStmt
+= pSqlStmt
;
2007 WriteSqlLog(sqlStmt
);
2009 #ifdef DBDEBUG_CONSOLE
2010 cout
<< sqlStmt
.c_str() << endl
;
2013 return(ExecSql(sqlStmt
));
2015 } // wxDb::CreateView()
2018 /********** wxDb::DropView() **********/
2019 bool wxDb::DropView(const wxString
&viewName
)
2022 * NOTE: This function returns TRUE if the View does not exist, but
2023 * only for identified databases. Code will need to be added
2024 * below for any other databases when those databases are defined
2025 * to handle this situation consistently
2029 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2031 WriteSqlLog(sqlStmt
);
2033 #ifdef DBDEBUG_CONSOLE
2034 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2037 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2039 // Check for "Base table not found" error and ignore
2040 GetNextError(henv
, hdbc
, hstmt
);
2041 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2043 // Check for product specific error codes
2044 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2047 DispAllErrors(henv
, hdbc
, hstmt
);
2054 // Commit the transaction
2060 } // wxDb::DropView()
2063 /********** wxDb::ExecSql() **********/
2064 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2068 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2070 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2071 if (retcode
== SQL_SUCCESS
||
2072 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2078 DispAllErrors(henv
, hdbc
, hstmt
);
2082 } // wxDb::ExecSql()
2085 /********** wxDb::GetNext() **********/
2086 bool wxDb::GetNext(void)
2088 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2092 DispAllErrors(henv
, hdbc
, hstmt
);
2096 } // wxDb::GetNext()
2099 /********** wxDb::GetData() **********/
2100 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2103 wxASSERT(cbReturned
);
2105 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2109 DispAllErrors(henv
, hdbc
, hstmt
);
2113 } // wxDb::GetData()
2116 /********** wxDb::GetKeyFields() **********/
2117 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2119 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2120 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2122 // SQLSMALLINT iKeySeq;
2123 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2124 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2130 * -----------------------------------------------------------------------
2131 * -- 19991224 : mj10777 : Create ------
2132 * -- : Three things are done and stored here : ------
2133 * -- : 1) which Column(s) is/are Primary Key(s) ------
2134 * -- : 2) which tables use this Key as a Foreign Key ------
2135 * -- : 3) which columns are Foreign Key and the name ------
2136 * -- : of the Table where the Key is the Primary Key -----
2137 * -- : Called from GetColumns(const wxString &tableName, ------
2138 * -- int *numCols,const wxChar *userID ) ------
2139 * -----------------------------------------------------------------------
2142 /*---------------------------------------------------------------------*/
2143 /* Get the names of the columns in the primary key. */
2144 /*---------------------------------------------------------------------*/
2145 retcode
= SQLPrimaryKeys(hstmt
,
2146 NULL
, 0, /* Catalog name */
2147 NULL
, 0, /* Schema name */
2148 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2150 /*---------------------------------------------------------------------*/
2151 /* Fetch and display the result set. This will be a list of the */
2152 /* columns in the primary key of the tableName table. */
2153 /*---------------------------------------------------------------------*/
2154 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2156 retcode
= SQLFetch(hstmt
);
2157 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2159 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2160 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2162 for (i
=0;i
<noCols
;i
++) // Find the Column name
2163 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2164 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2167 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2169 /*---------------------------------------------------------------------*/
2170 /* Get all the foreign keys that refer to tableName primary key. */
2171 /*---------------------------------------------------------------------*/
2172 retcode
= SQLForeignKeys(hstmt
,
2173 NULL
, 0, /* Primary catalog */
2174 NULL
, 0, /* Primary schema */
2175 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2176 NULL
, 0, /* Foreign catalog */
2177 NULL
, 0, /* Foreign schema */
2178 NULL
, 0); /* Foreign table */
2180 /*---------------------------------------------------------------------*/
2181 /* Fetch and display the result set. This will be all of the foreign */
2182 /* keys in other tables that refer to the tableName primary key. */
2183 /*---------------------------------------------------------------------*/
2186 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2188 retcode
= SQLFetch(hstmt
);
2189 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2191 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2192 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2193 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2194 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2195 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2196 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2200 tempStr
.Trim(); // Get rid of any unneeded blanks
2201 if (!tempStr
.IsEmpty())
2203 for (i
=0; i
<noCols
; i
++)
2204 { // Find the Column name
2205 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2206 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2210 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2212 /*---------------------------------------------------------------------*/
2213 /* Get all the foreign keys in the tablename table. */
2214 /*---------------------------------------------------------------------*/
2215 retcode
= SQLForeignKeys(hstmt
,
2216 NULL
, 0, /* Primary catalog */
2217 NULL
, 0, /* Primary schema */
2218 NULL
, 0, /* Primary table */
2219 NULL
, 0, /* Foreign catalog */
2220 NULL
, 0, /* Foreign schema */
2221 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2223 /*---------------------------------------------------------------------*/
2224 /* Fetch and display the result set. This will be all of the */
2225 /* primary keys in other tables that are referred to by foreign */
2226 /* keys in the tableName table. */
2227 /*---------------------------------------------------------------------*/
2228 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2230 retcode
= SQLFetch(hstmt
);
2231 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2233 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2234 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2235 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2237 for (i
=0; i
<noCols
; i
++) // Find the Column name
2239 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2241 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2242 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2247 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2251 } // wxDb::GetKeyFields()
2255 /********** wxDb::GetColumns() **********/
2256 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2258 * 1) The last array element of the tableName[] argument must be zero (null).
2259 * This is how the end of the array is detected.
2260 * 2) This function returns an array of wxDbColInf structures. If no columns
2261 * were found, or an error occured, this pointer will be zero (null). THE
2262 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2263 * IS FINISHED WITH IT. i.e.
2265 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2268 * // Use the column inf
2270 * // Destroy the memory
2274 * userID is evaluated in the following manner:
2275 * userID == NULL ... UserID is ignored
2276 * userID == "" ... UserID set equal to 'this->uid'
2277 * userID != "" ... UserID set equal to 'userID'
2279 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2280 * by this function. This function should use its own wxDb instance
2281 * to avoid undesired unbinding of columns.
2286 wxDbColInf
*colInf
= 0;
2294 convertUserID(userID
,UserID
);
2296 // Pass 1 - Determine how many columns there are.
2297 // Pass 2 - Allocate the wxDbColInf array and fill in
2298 // the array with the column information.
2300 for (pass
= 1; pass
<= 2; pass
++)
2304 if (noCols
== 0) // Probably a bogus table name(s)
2306 // Allocate n wxDbColInf objects to hold the column information
2307 colInf
= new wxDbColInf
[noCols
+1];
2310 // Mark the end of the array
2311 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2312 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2313 colInf
[noCols
].sqlDataType
= 0;
2315 // Loop through each table name
2317 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2319 TableName
= tableName
[tbl
];
2320 // Oracle and Interbase table names are uppercase only, so force
2321 // the name to uppercase just in case programmer forgot to do this
2322 if ((Dbms() == dbmsORACLE
) ||
2323 (Dbms() == dbmsINTERBASE
))
2324 TableName
= TableName
.Upper();
2326 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2328 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2329 // use the call below that leaves out the user name
2330 if (!UserID
.IsEmpty() &&
2331 Dbms() != dbmsMY_SQL
&&
2332 Dbms() != dbmsACCESS
&&
2333 Dbms() != dbmsMS_SQL_SERVER
)
2335 retcode
= SQLColumns(hstmt
,
2336 NULL
, 0, // All qualifiers
2337 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2338 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2339 NULL
, 0); // All columns
2343 retcode
= SQLColumns(hstmt
,
2344 NULL
, 0, // All qualifiers
2346 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2347 NULL
, 0); // All columns
2349 if (retcode
!= SQL_SUCCESS
)
2350 { // Error occured, abort
2351 DispAllErrors(henv
, hdbc
, hstmt
);
2354 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2358 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2360 if (pass
== 1) // First pass, just add up the number of columns
2362 else // Pass 2; Fill in the array of structures
2364 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2366 // NOTE: Only the ODBC 1.x fields are retrieved
2367 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2368 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2369 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2370 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2371 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2372 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2373 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2374 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2375 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2376 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2377 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2378 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2380 // Determine the wxDb data type that is used to represent the native data type of this data source
2381 colInf
[colNo
].dbDataType
= 0;
2382 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2385 // IODBC does not return a correct columnSize, so we set
2386 // columnSize = bufferLength if no column size was returned
2387 // IODBC returns the columnSize in bufferLength.. (bug)
2388 if (colInf
[colNo
].columnSize
< 1)
2390 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2393 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2395 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2396 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2397 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2398 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2399 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2400 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2401 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2402 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2407 if (retcode
!= SQL_NO_DATA_FOUND
)
2408 { // Error occured, abort
2409 DispAllErrors(henv
, hdbc
, hstmt
);
2412 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2418 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2421 } // wxDb::GetColumns()
2424 /********** wxDb::GetColumns() **********/
2426 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2428 // Same as the above GetColumns() function except this one gets columns
2429 // only for a single table, and if 'numCols' is not NULL, the number of
2430 // columns stored in the returned wxDbColInf is set in '*numCols'
2432 // userID is evaluated in the following manner:
2433 // userID == NULL ... UserID is ignored
2434 // userID == "" ... UserID set equal to 'this->uid'
2435 // userID != "" ... UserID set equal to 'userID'
2437 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2438 // by this function. This function should use its own wxDb instance
2439 // to avoid undesired unbinding of columns.
2444 wxDbColInf
*colInf
= 0;
2452 convertUserID(userID
,UserID
);
2454 // Pass 1 - Determine how many columns there are.
2455 // Pass 2 - Allocate the wxDbColInf array and fill in
2456 // the array with the column information.
2458 for (pass
= 1; pass
<= 2; pass
++)
2462 if (noCols
== 0) // Probably a bogus table name(s)
2464 // Allocate n wxDbColInf objects to hold the column information
2465 colInf
= new wxDbColInf
[noCols
+1];
2468 // Mark the end of the array
2469 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2470 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2471 colInf
[noCols
].sqlDataType
= 0;
2474 TableName
= tableName
;
2475 // Oracle and Interbase table names are uppercase only, so force
2476 // the name to uppercase just in case programmer forgot to do this
2477 if ((Dbms() == dbmsORACLE
) ||
2478 (Dbms() == dbmsINTERBASE
))
2479 TableName
= TableName
.Upper();
2481 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2483 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2484 // use the call below that leaves out the user name
2485 if (!UserID
.IsEmpty() &&
2486 Dbms() != dbmsMY_SQL
&&
2487 Dbms() != dbmsACCESS
&&
2488 Dbms() != dbmsMS_SQL_SERVER
)
2490 retcode
= SQLColumns(hstmt
,
2491 NULL
, 0, // All qualifiers
2492 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2493 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2494 NULL
, 0); // All columns
2498 retcode
= SQLColumns(hstmt
,
2499 NULL
, 0, // All qualifiers
2501 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2502 NULL
, 0); // All columns
2504 if (retcode
!= SQL_SUCCESS
)
2505 { // Error occured, abort
2506 DispAllErrors(henv
, hdbc
, hstmt
);
2509 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2515 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2517 if (pass
== 1) // First pass, just add up the number of columns
2519 else // Pass 2; Fill in the array of structures
2521 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2523 // NOTE: Only the ODBC 1.x fields are retrieved
2524 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2525 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2526 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2527 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2528 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2529 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2530 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2531 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2532 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2533 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2534 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2535 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2536 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2537 // Start Values for Primary/Foriegn Key (=No)
2538 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2539 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2540 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2541 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2543 // BJO 20000428 : Virtuoso returns type names with upper cases!
2544 if (Dbms() == dbmsVIRTUOSO
)
2546 wxString s
= colInf
[colNo
].typeName
;
2548 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2551 // Determine the wxDb data type that is used to represent the native data type of this data source
2552 colInf
[colNo
].dbDataType
= 0;
2553 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2556 // IODBC does not return a correct columnSize, so we set
2557 // columnSize = bufferLength if no column size was returned
2558 // IODBC returns the columnSize in bufferLength.. (bug)
2559 if (colInf
[colNo
].columnSize
< 1)
2561 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2565 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2567 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2568 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2569 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2570 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2571 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2572 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2573 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2574 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2580 if (retcode
!= SQL_NO_DATA_FOUND
)
2581 { // Error occured, abort
2582 DispAllErrors(henv
, hdbc
, hstmt
);
2585 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2592 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2594 // Store Primary and Foriegn Keys
2595 GetKeyFields(tableName
,colInf
,noCols
);
2601 } // wxDb::GetColumns()
2604 #else // New GetColumns
2609 These are tentative new GetColumns members which should be more database
2610 independant and which always returns the columns in the order they were
2613 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2614 wxChar* userID)) calls the second implementation for each separate table
2615 before merging the results. This makes the code easier to maintain as
2616 only one member (the second) makes the real work
2617 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2618 wxChar *userID) is a little bit improved
2619 - It doesn't anymore rely on the type-name to find out which database-type
2621 - It ends by sorting the columns, so that they are returned in the same
2622 order they were created
2632 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2635 // The last array element of the tableName[] argument must be zero (null).
2636 // This is how the end of the array is detected.
2640 // How many tables ?
2642 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2644 // Create a table to maintain the columns for each separate table
2645 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2648 for (i
= 0 ; i
< tbl
; i
++)
2651 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2652 if (TableColumns
[i
].colInf
== NULL
)
2654 noCols
+= TableColumns
[i
].noCols
;
2657 // Now merge all the separate table infos
2658 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2660 // Mark the end of the array
2661 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2662 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2663 colInf
[noCols
].sqlDataType
= 0;
2668 for (i
= 0 ; i
< tbl
; i
++)
2670 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2672 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2676 delete [] TableColumns
;
2679 } // wxDb::GetColumns() -- NEW
2682 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2684 // Same as the above GetColumns() function except this one gets columns
2685 // only for a single table, and if 'numCols' is not NULL, the number of
2686 // columns stored in the returned wxDbColInf is set in '*numCols'
2688 // userID is evaluated in the following manner:
2689 // userID == NULL ... UserID is ignored
2690 // userID == "" ... UserID set equal to 'this->uid'
2691 // userID != "" ... UserID set equal to 'userID'
2693 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2694 // by this function. This function should use its own wxDb instance
2695 // to avoid undesired unbinding of columns.
2699 wxDbColInf
*colInf
= 0;
2707 convertUserID(userID
,UserID
);
2709 // Pass 1 - Determine how many columns there are.
2710 // Pass 2 - Allocate the wxDbColInf array and fill in
2711 // the array with the column information.
2713 for (pass
= 1; pass
<= 2; pass
++)
2717 if (noCols
== 0) // Probably a bogus table name(s)
2719 // Allocate n wxDbColInf objects to hold the column information
2720 colInf
= new wxDbColInf
[noCols
+1];
2723 // Mark the end of the array
2724 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2725 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2726 colInf
[noCols
].sqlDataType
= 0;
2729 TableName
= tableName
;
2730 // Oracle and Interbase table names are uppercase only, so force
2731 // the name to uppercase just in case programmer forgot to do this
2732 if ((Dbms() == dbmsORACLE
) ||
2733 (Dbms() == dbmsINTERBASE
))
2734 TableName
= TableName
.Upper();
2736 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2738 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2739 // use the call below that leaves out the user name
2740 if (!UserID
.IsEmpty() &&
2741 Dbms() != dbmsMY_SQL
&&
2742 Dbms() != dbmsACCESS
&&
2743 Dbms() != dbmsMS_SQL_SERVER
)
2745 retcode
= SQLColumns(hstmt
,
2746 NULL
, 0, // All qualifiers
2747 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2748 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2749 NULL
, 0); // All columns
2753 retcode
= SQLColumns(hstmt
,
2754 NULL
, 0, // All qualifiers
2756 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2757 NULL
, 0); // All columns
2759 if (retcode
!= SQL_SUCCESS
)
2760 { // Error occured, abort
2761 DispAllErrors(henv
, hdbc
, hstmt
);
2764 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2770 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2772 if (pass
== 1) // First pass, just add up the number of columns
2774 else // Pass 2; Fill in the array of structures
2776 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2778 // NOTE: Only the ODBC 1.x fields are retrieved
2779 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2780 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2781 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2782 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2783 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2784 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2785 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2786 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2787 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2788 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2789 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2790 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2791 // Start Values for Primary/Foriegn Key (=No)
2792 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2793 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2794 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2795 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2798 // IODBC does not return a correct columnSize, so we set
2799 // columnSize = bufferLength if no column size was returned
2800 // IODBC returns the columnSize in bufferLength.. (bug)
2801 if (colInf
[colNo
].columnSize
< 1)
2803 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2807 // Determine the wxDb data type that is used to represent the native data type of this data source
2808 colInf
[colNo
].dbDataType
= 0;
2809 // Get the intern datatype
2810 switch (colInf
[colNo
].sqlDataType
)
2814 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2820 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2827 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2830 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2833 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2838 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2839 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2846 if (retcode
!= SQL_NO_DATA_FOUND
)
2847 { // Error occured, abort
2848 DispAllErrors(henv
, hdbc
, hstmt
);
2851 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2858 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2860 // Store Primary and Foreign Keys
2861 GetKeyFields(tableName
,colInf
,noCols
);
2863 ///////////////////////////////////////////////////////////////////////////
2864 // Now sort the the columns in order to make them appear in the right order
2865 ///////////////////////////////////////////////////////////////////////////
2867 // Build a generic SELECT statement which returns 0 rows
2870 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2873 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2875 DispAllErrors(henv
, hdbc
, hstmt
);
2879 // Get the number of result columns
2880 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2882 DispAllErrors(henv
, hdbc
, hstmt
);
2886 if (noCols
== 0) // Probably a bogus table name
2895 for (colNum
= 0; colNum
< noCols
; colNum
++)
2897 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2899 &Sword
, &Sdword
) != SQL_SUCCESS
)
2901 DispAllErrors(henv
, hdbc
, hstmt
);
2905 wxString Name1
= name
;
2906 Name1
= Name1
.Upper();
2908 // Where is this name in the array ?
2909 for (i
= colNum
; i
< noCols
; i
++)
2911 wxString Name2
= colInf
[i
].colName
;
2912 Name2
= Name2
.Upper();
2915 if (colNum
!= i
) // swap to sort
2917 wxDbColInf tmpColInf
= colInf
[colNum
];
2918 colInf
[colNum
] = colInf
[i
];
2919 colInf
[i
] = tmpColInf
;
2925 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2927 ///////////////////////////////////////////////////////////////////////////
2929 ///////////////////////////////////////////////////////////////////////////
2935 } // wxDb::GetColumns()
2938 #endif // #else OLD_GETCOLUMNS
2941 /********** wxDb::GetColumnCount() **********/
2942 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2944 * Returns a count of how many columns are in a table.
2945 * If an error occurs in computing the number of columns
2946 * this function will return a -1 for the count
2948 * userID is evaluated in the following manner:
2949 * userID == NULL ... UserID is ignored
2950 * userID == "" ... UserID set equal to 'this->uid'
2951 * userID != "" ... UserID set equal to 'userID'
2953 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2954 * by this function. This function should use its own wxDb instance
2955 * to avoid undesired unbinding of columns.
2965 convertUserID(userID
,UserID
);
2967 TableName
= tableName
;
2968 // Oracle and Interbase table names are uppercase only, so force
2969 // the name to uppercase just in case programmer forgot to do this
2970 if ((Dbms() == dbmsORACLE
) ||
2971 (Dbms() == dbmsINTERBASE
))
2972 TableName
= TableName
.Upper();
2974 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2976 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2977 // use the call below that leaves out the user name
2978 if (!UserID
.IsEmpty() &&
2979 Dbms() != dbmsMY_SQL
&&
2980 Dbms() != dbmsACCESS
&&
2981 Dbms() != dbmsMS_SQL_SERVER
)
2983 retcode
= SQLColumns(hstmt
,
2984 NULL
, 0, // All qualifiers
2985 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2986 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2987 NULL
, 0); // All columns
2991 retcode
= SQLColumns(hstmt
,
2992 NULL
, 0, // All qualifiers
2994 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2995 NULL
, 0); // All columns
2997 if (retcode
!= SQL_SUCCESS
)
2998 { // Error occured, abort
2999 DispAllErrors(henv
, hdbc
, hstmt
);
3000 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3004 // Count the columns
3005 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3008 if (retcode
!= SQL_NO_DATA_FOUND
)
3009 { // Error occured, abort
3010 DispAllErrors(henv
, hdbc
, hstmt
);
3011 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3015 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3018 } // wxDb::GetColumnCount()
3021 /********** wxDb::GetCatalog() *******/
3022 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3024 * ---------------------------------------------------------------------
3025 * -- 19991203 : mj10777 : Create ------
3026 * -- : Creates a wxDbInf with Tables / Cols Array ------
3027 * -- : uses SQLTables and fills pTableInf; ------
3028 * -- : pColInf is set to NULL and numCols to 0; ------
3029 * -- : returns pDbInf (wxDbInf) ------
3030 * -- - if unsuccesfull (pDbInf == NULL) ------
3031 * -- : pColInf can be filled with GetColumns(..); ------
3032 * -- : numCols can be filled with GetColumnCount(..); ------
3033 * ---------------------------------------------------------------------
3035 * userID is evaluated in the following manner:
3036 * userID == NULL ... UserID is ignored
3037 * userID == "" ... UserID set equal to 'this->uid'
3038 * userID != "" ... UserID set equal to 'userID'
3040 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3041 * by this function. This function should use its own wxDb instance
3042 * to avoid undesired unbinding of columns.
3045 int noTab
= 0; // Counter while filling table entries
3049 wxString tblNameSave
;
3052 convertUserID(userID
,UserID
);
3054 //-------------------------------------------------------------
3055 // Create the Database Array of catalog entries
3057 wxDbInf
*pDbInf
= new wxDbInf
;
3059 //-------------------------------------------------------------
3060 // Table Information
3061 // Pass 1 - Determine how many Tables there are.
3062 // Pass 2 - Create the Table array and fill it
3063 // - Create the Cols array = NULL
3064 //-------------------------------------------------------------
3066 for (pass
= 1; pass
<= 2; pass
++)
3068 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3069 tblNameSave
.Empty();
3071 if (!UserID
.IsEmpty() &&
3072 Dbms() != dbmsMY_SQL
&&
3073 Dbms() != dbmsACCESS
&&
3074 Dbms() != dbmsMS_SQL_SERVER
)
3076 retcode
= SQLTables(hstmt
,
3077 NULL
, 0, // All qualifiers
3078 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3079 NULL
, 0, // All tables
3080 NULL
, 0); // All columns
3084 retcode
= SQLTables(hstmt
,
3085 NULL
, 0, // All qualifiers
3086 NULL
, 0, // User specified
3087 NULL
, 0, // All tables
3088 NULL
, 0); // All columns
3091 if (retcode
!= SQL_SUCCESS
)
3093 DispAllErrors(henv
, hdbc
, hstmt
);
3095 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3099 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3101 if (pass
== 1) // First pass, just count the Tables
3103 if (pDbInf
->numTables
== 0)
3105 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3106 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3108 pDbInf
->numTables
++; // Counter for Tables
3110 if (pass
== 2) // Create and fill the Table entries
3112 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3113 { // no, then create the Array
3114 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3116 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3118 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3119 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3120 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3126 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3128 // Query how many columns are in each table
3129 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3131 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3136 } // wxDb::GetCatalog()
3139 /********** wxDb::Catalog() **********/
3140 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3142 * Creates the text file specified in 'filename' which will contain
3143 * a minimal data dictionary of all tables accessible by the user specified
3146 * userID is evaluated in the following manner:
3147 * userID == NULL ... UserID is ignored
3148 * userID == "" ... UserID set equal to 'this->uid'
3149 * userID != "" ... UserID set equal to 'userID'
3151 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3152 * by this function. This function should use its own wxDb instance
3153 * to avoid undesired unbinding of columns.
3156 wxASSERT(fileName
.Length());
3160 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3161 wxString tblNameSave
;
3162 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3164 wxChar typeName
[30+1];
3165 SDWORD precision
, length
;
3167 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3171 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3174 convertUserID(userID
,UserID
);
3176 if (!UserID
.IsEmpty() &&
3177 Dbms() != dbmsMY_SQL
&&
3178 Dbms() != dbmsACCESS
&&
3179 Dbms() != dbmsINTERBASE
&&
3180 Dbms() != dbmsMS_SQL_SERVER
)
3182 retcode
= SQLColumns(hstmt
,
3183 NULL
, 0, // All qualifiers
3184 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3185 NULL
, 0, // All tables
3186 NULL
, 0); // All columns
3190 retcode
= SQLColumns(hstmt
,
3191 NULL
, 0, // All qualifiers
3192 NULL
, 0, // User specified
3193 NULL
, 0, // All tables
3194 NULL
, 0); // All columns
3196 if (retcode
!= SQL_SUCCESS
)
3198 DispAllErrors(henv
, hdbc
, hstmt
);
3204 tblNameSave
.Empty();
3209 retcode
= SQLFetch(hstmt
);
3210 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3213 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3214 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3215 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3216 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3217 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3218 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3220 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3223 wxFputs(wxT("\n"), fp
);
3224 wxFputs(wxT("================================ "), fp
);
3225 wxFputs(wxT("================================ "), fp
);
3226 wxFputs(wxT("===================== "), fp
);
3227 wxFputs(wxT("========= "), fp
);
3228 wxFputs(wxT("=========\n"), fp
);
3229 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3230 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3231 wxFputs(outStr
.c_str(), fp
);
3232 wxFputs(wxT("================================ "), fp
);
3233 wxFputs(wxT("================================ "), fp
);
3234 wxFputs(wxT("===================== "), fp
);
3235 wxFputs(wxT("========= "), fp
);
3236 wxFputs(wxT("=========\n"), fp
);
3237 tblNameSave
= tblName
;
3240 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3241 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3242 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3244 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3251 if (retcode
!= SQL_NO_DATA_FOUND
)
3252 DispAllErrors(henv
, hdbc
, hstmt
);
3254 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3257 return(retcode
== SQL_NO_DATA_FOUND
);
3259 } // wxDb::Catalog()
3262 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3264 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3265 * if the object exists in the database. This function does not indicate
3266 * whether or not the user has privleges to query or perform other functions
3269 * userID is evaluated in the following manner:
3270 * userID == NULL ... UserID is ignored
3271 * userID == "" ... UserID set equal to 'this->uid'
3272 * userID != "" ... UserID set equal to 'userID'
3275 wxASSERT(tableName
.Length());
3279 if (Dbms() == dbmsDBASE
)
3282 if (tablePath
.Length())
3283 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3285 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3288 exists
= wxFileExists(dbName
);
3293 convertUserID(userID
,UserID
);
3295 TableName
= tableName
;
3296 // Oracle and Interbase table names are uppercase only, so force
3297 // the name to uppercase just in case programmer forgot to do this
3298 if ((Dbms() == dbmsORACLE
) ||
3299 (Dbms() == dbmsINTERBASE
))
3300 TableName
= TableName
.Upper();
3302 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3305 // Some databases cannot accept a user name when looking up table names,
3306 // so we use the call below that leaves out the user name
3307 if (!UserID
.IsEmpty() &&
3308 Dbms() != dbmsMY_SQL
&&
3309 Dbms() != dbmsACCESS
&&
3310 Dbms() != dbmsMS_SQL_SERVER
&&
3311 Dbms() != dbmsDB2
&&
3312 Dbms() != dbmsINTERBASE
&&
3313 Dbms() != dbmsPERVASIVE_SQL
)
3315 retcode
= SQLTables(hstmt
,
3316 NULL
, 0, // All qualifiers
3317 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3318 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3319 NULL
, 0); // All table types
3323 retcode
= SQLTables(hstmt
,
3324 NULL
, 0, // All qualifiers
3325 NULL
, 0, // All owners
3326 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3327 NULL
, 0); // All table types
3329 if (retcode
!= SQL_SUCCESS
)
3330 return(DispAllErrors(henv
, hdbc
, hstmt
));
3332 retcode
= SQLFetch(hstmt
);
3333 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3335 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3336 return(DispAllErrors(henv
, hdbc
, hstmt
));
3339 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3343 } // wxDb::TableExists()
3346 /********** wxDb::TablePrivileges() **********/
3347 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3348 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3350 wxASSERT(tableName
.Length());
3352 wxDbTablePrivilegeInfo result
;
3356 // We probably need to be able to dynamically set this based on
3357 // the driver type, and state.
3358 wxChar curRole
[]=wxT("public");
3362 wxString UserID
,Schema
;
3363 convertUserID(userID
,UserID
);
3364 convertUserID(schema
,Schema
);
3366 TableName
= tableName
;
3367 // Oracle and Interbase table names are uppercase only, so force
3368 // the name to uppercase just in case programmer forgot to do this
3369 if ((Dbms() == dbmsORACLE
) ||
3370 (Dbms() == dbmsINTERBASE
))
3371 TableName
= TableName
.Upper();
3373 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3375 // Some databases cannot accept a user name when looking up table names,
3376 // so we use the call below that leaves out the user name
3377 if (!Schema
.IsEmpty() &&
3378 Dbms() != dbmsMY_SQL
&&
3379 Dbms() != dbmsACCESS
&&
3380 Dbms() != dbmsMS_SQL_SERVER
)
3382 retcode
= SQLTablePrivileges(hstmt
,
3384 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3385 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3389 retcode
= SQLTablePrivileges(hstmt
,
3392 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3395 #ifdef DBDEBUG_CONSOLE
3396 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3399 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3400 return(DispAllErrors(henv
, hdbc
, hstmt
));
3402 bool failed
= FALSE
;
3403 retcode
= SQLFetch(hstmt
);
3404 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3406 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3409 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3412 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3415 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3418 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3421 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3424 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3429 return(DispAllErrors(henv
, hdbc
, hstmt
));
3431 #ifdef DBDEBUG_CONSOLE
3432 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3433 result
.privilege
,result
.tableOwner
,result
.tableName
,
3434 result
.grantor
, result
.grantee
);
3437 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3439 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3443 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3444 !wxStrcmp(result
.privilege
,priv
))
3446 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3450 if (!wxStrcmp(result
.grantee
,curRole
) &&
3451 !wxStrcmp(result
.privilege
,priv
))
3453 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3457 retcode
= SQLFetch(hstmt
);
3460 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3463 } // wxDb::TablePrivileges
3466 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3470 if (Dbms() == dbmsACCESS
)
3471 TableName
= _T("\"");
3472 TableName
+= tableName
;
3473 if (Dbms() == dbmsACCESS
)
3474 TableName
+= _T("\"");
3477 } // wxDb::SQLTableName()
3480 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3484 if (Dbms() == dbmsACCESS
)
3487 if (Dbms() == dbmsACCESS
)
3488 ColName
+= _T("\"");
3491 } // wxDb::SQLColumnName()
3494 /********** wxDb::SetSqlLogging() **********/
3495 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3497 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3498 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3500 if (state
== sqlLogON
)
3504 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3505 if (fpSqlLog
== NULL
)
3513 if (fclose(fpSqlLog
))
3519 sqlLogState
= state
;
3522 } // wxDb::SetSqlLogging()
3525 /********** wxDb::WriteSqlLog() **********/
3526 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3528 wxASSERT(logMsg
.Length());
3530 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3533 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3535 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3537 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3542 } // wxDb::WriteSqlLog()
3545 /********** wxDb::Dbms() **********/
3546 wxDBMS
wxDb::Dbms(void)
3548 * Be aware that not all database engines use the exact same syntax, and not
3549 * every ODBC compliant database is compliant to the same level of compliancy.
3550 * Some manufacturers support the minimum Level 1 compliancy, and others up
3551 * through Level 3. Others support subsets of features for levels above 1.
3553 * If you find an inconsistency between the wxDb class and a specific database
3554 * engine, and an identifier to this section, and special handle the database in
3555 * the area where behavior is non-conforming with the other databases.
3558 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3559 * ---------------------------------------------------
3562 * - Currently the only database supported by the class to support VIEWS
3565 * - Does not support the SQL_TIMESTAMP structure
3566 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3567 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3568 * is TRUE. The user must create ALL indexes from their program.
3569 * - Table names can only be 8 characters long
3570 * - Column names can only be 10 characters long
3573 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3574 * after every table name involved in the query/join if that tables matching record(s)
3576 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3578 * SYBASE (Enterprise)
3579 * - If a column is part of the Primary Key, the column cannot be NULL
3580 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3583 * - If a column is part of the Primary Key, the column cannot be NULL
3584 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3585 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3586 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3587 * column definition if it is not defined correctly, but it is experimental
3588 * - Does not support sub-queries in SQL statements
3591 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3592 * - Does not support sub-queries in SQL statements
3595 * - Primary keys must be declared as NOT NULL
3596 * - Table and index names must not be longer than 13 characters in length (technically
3597 * table names can be up to 18 characters, but the primary index is created using the
3598 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3603 * - Columns that are part of primary keys must be defined as being NOT NULL
3604 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3605 * column definition if it is not defined correctly, but it is experimental
3608 // Should only need to do this once for each new database connection
3609 // so return the value we already determined it to be to save time
3610 // and lots of string comparisons
3611 if (dbmsType
!= dbmsUNIDENTIFIED
)
3614 wxChar baseName
[25+1];
3615 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3618 // RGG 20001025 : add support for Interbase
3619 // GT : Integrated to base classes on 20001121
3620 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3621 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3623 // BJO 20000428 : add support for Virtuoso
3624 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3625 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3627 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3628 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3630 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3631 // connected through an OpenLink driver.
3632 // Is it also returned by Sybase Adapatitve server?
3633 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3634 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3636 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3637 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3638 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3640 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3643 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3644 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3647 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3648 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3651 if (!wxStricmp(baseName
,wxT("Pervasive")))
3652 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3655 if (!wxStricmp(baseName
,wxT("Informix")))
3656 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3659 if (!wxStricmp(baseName
,wxT("Oracle")))
3660 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3661 if (!wxStricmp(baseName
,wxT("ACCESS")))
3662 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3663 if (!wxStricmp(baseName
,wxT("Sybase")))
3664 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3667 if (!wxStricmp(baseName
,wxT("DBASE")))
3668 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3669 if (!wxStricmp(baseName
,wxT("xBase")))
3670 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3671 if (!wxStricmp(baseName
,wxT("MySQL")))
3672 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3675 if (!wxStricmp(baseName
,wxT("DB2")))
3676 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3678 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3683 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3684 int dataType
, ULONG columnLength
,
3685 const wxString
&optionalParam
)
3687 wxASSERT(tableName
.Length());
3688 wxASSERT(columnName
.Length());
3689 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3690 dataType
!= DB_DATA_TYPE_VARCHAR
);
3692 // Must specify a columnLength if modifying a VARCHAR type column
3693 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3696 wxString dataTypeName
;
3698 wxString alterSlashModify
;
3702 case DB_DATA_TYPE_VARCHAR
:
3703 dataTypeName
= typeInfVarchar
.TypeName
;
3705 case DB_DATA_TYPE_INTEGER
:
3706 dataTypeName
= typeInfInteger
.TypeName
;
3708 case DB_DATA_TYPE_FLOAT
:
3709 dataTypeName
= typeInfFloat
.TypeName
;
3711 case DB_DATA_TYPE_DATE
:
3712 dataTypeName
= typeInfDate
.TypeName
;
3714 case DB_DATA_TYPE_BLOB
:
3715 dataTypeName
= typeInfBlob
.TypeName
;
3721 // Set the modify or alter syntax depending on the type of database connected to
3725 alterSlashModify
= _T("MODIFY");
3727 case dbmsMS_SQL_SERVER
:
3728 alterSlashModify
= _T("ALTER COLUMN");
3730 case dbmsUNIDENTIFIED
:
3732 case dbmsSYBASE_ASA
:
3733 case dbmsSYBASE_ASE
:
3738 case dbmsXBASE_SEQUITER
:
3740 alterSlashModify
= _T("MODIFY");
3744 // create the SQL statement
3745 if ( Dbms() == dbmsMY_SQL
)
3747 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3748 columnName
.c_str(), dataTypeName
.c_str());
3752 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3753 columnName
.c_str(), dataTypeName
.c_str());
3756 // For varchars only, append the size of the column
3757 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3758 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3761 s
.Printf(wxT("(%lu)"), columnLength
);
3765 // for passing things like "NOT NULL"
3766 if (optionalParam
.Length())
3768 sqlStmt
+= wxT(" ");
3769 sqlStmt
+= optionalParam
;
3772 return ExecSql(sqlStmt
);
3774 } // wxDb::ModifyColumn()
3777 /********** wxDbGetConnection() **********/
3778 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3782 // Used to keep a pointer to a DB connection that matches the requested
3783 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3784 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3785 // rather than having to re-query the datasource to get all the values
3786 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3787 wxDb
*matchingDbConnection
= NULL
;
3789 // Scan the linked list searching for an available database connection
3790 // that's already been opened but is currently not in use.
3791 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3793 // The database connection must be for the same datasource
3794 // name and must currently not be in use.
3796 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3797 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3799 pList
->Free
= FALSE
;
3800 return(pList
->PtrDb
);
3803 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3804 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3805 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3806 matchingDbConnection
= pList
->PtrDb
;
3809 // No available connections. A new connection must be made and
3810 // appended to the end of the linked list.
3813 // Find the end of the list
3814 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3815 // Append a new list item
3816 pList
->PtrNext
= new wxDbList
;
3817 pList
->PtrNext
->PtrPrev
= pList
;
3818 pList
= pList
->PtrNext
;
3822 // Create the first node on the list
3823 pList
= PtrBegDbList
= new wxDbList
;
3827 // Initialize new node in the linked list
3829 pList
->Free
= FALSE
;
3830 pList
->Dsn
= pDbConfig
->GetDsn();
3831 pList
->Uid
= pDbConfig
->GetUserID();
3832 pList
->AuthStr
= pDbConfig
->GetPassword();
3834 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3838 if (!matchingDbConnection
)
3839 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3841 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3843 // Connect to the datasource
3846 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3847 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3848 return(pList
->PtrDb
);
3850 else // Unable to connect, destroy list item
3853 pList
->PtrPrev
->PtrNext
= 0;
3855 PtrBegDbList
= 0; // Empty list again
3856 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3857 pList
->PtrDb
->Close(); // Close the wxDb object
3858 delete pList
->PtrDb
; // Deletes the wxDb object
3859 delete pList
; // Deletes the linked list object
3863 } // wxDbGetConnection()
3866 /********** wxDbFreeConnection() **********/
3867 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3871 // Scan the linked list searching for the database connection
3872 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3874 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3875 return (pList
->Free
= TRUE
);
3878 // Never found the database object, return failure
3881 } // wxDbFreeConnection()
3884 /********** wxDbCloseConnections() **********/
3885 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3887 wxDbList
*pList
, *pNext
;
3889 // Traverse the linked list closing database connections and freeing memory as I go.
3890 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3892 pNext
= pList
->PtrNext
; // Save the pointer to next
3893 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3894 pList
->PtrDb
->Close(); // Close the wxDb object
3895 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3896 delete pList
->PtrDb
; // Deletes the wxDb object
3897 delete pList
; // Deletes the linked list object
3900 // Mark the list as empty
3903 } // wxDbCloseConnections()
3906 /********** wxDbConnectionsInUse() **********/
3907 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
3912 // Scan the linked list counting db connections that are currently in use
3913 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3915 if (pList
->Free
== FALSE
)
3921 } // wxDbConnectionsInUse()
3925 /********** wxDbLogExtendedErrorMsg() **********/
3926 // DEBUG ONLY function
3927 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3929 const wxChar
*ErrFile
,
3932 static wxString msg
;
3937 if (ErrFile
|| ErrLine
)
3939 msg
+= wxT("File: ");
3941 msg
+= wxT(" Line: ");
3942 tStr
.Printf(wxT("%d"),ErrLine
);
3943 msg
+= tStr
.c_str();
3947 msg
.Append (wxT("\nODBC errors:\n"));
3950 // Display errors for this connection
3952 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3954 if (pDb
->errorList
[i
])
3956 msg
.Append(pDb
->errorList
[i
]);
3957 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3958 msg
.Append(wxT("\n"));
3959 // Clear the errmsg buffer so the next error will not
3960 // end up showing the previous error that have occurred
3961 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3966 wxLogDebug(msg
.c_str());
3969 } // wxDbLogExtendedErrorMsg()
3972 /********** wxDbSqlLog() **********/
3973 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3975 bool append
= FALSE
;
3978 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3980 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3985 SQLLOGstate
= state
;
3986 SQLLOGfn
= filename
;
3994 /********** wxDbCreateDataSource() **********/
3995 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3996 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3998 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3999 * Very rudimentary creation of an ODBC data source.
4001 * ODBC driver must be ODBC 3.0 compliant to use this function
4006 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4012 dsnLocation
= ODBC_ADD_SYS_DSN
;
4014 dsnLocation
= ODBC_ADD_DSN
;
4016 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4017 // so that is why I used it, as wxString does not deal well with
4018 // embedded nulls in strings
4019 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4021 // Replace the separator from above with the '\0' seperator needed
4022 // by the SQLConfigDataSource() function
4026 k
= setupStr
.Find((wxChar
)2,TRUE
);
4027 if (k
!= wxNOT_FOUND
)
4028 setupStr
[(UINT
)k
] = wxT('\0');
4030 while (k
!= wxNOT_FOUND
);
4032 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4033 driverName
, setupStr
.c_str());
4035 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4037 // check for errors caused by ConfigDSN based functions
4040 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4041 errMsg
[0] = wxT('\0');
4043 // This function is only supported in ODBC drivers v3.0 compliant and above
4044 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4047 #ifdef DBDEBUG_CONSOLE
4048 // When run in console mode, use standard out to display errors.
4049 cout
<< errMsg
<< endl
;
4050 cout
<< wxT("Press any key to continue...") << endl
;
4052 #endif // DBDEBUG_CONSOLE
4055 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4056 #endif // __WXDEBUG__
4062 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4063 // necessary to use this function, so this function is not supported
4065 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4068 #endif // __VISUALC__
4072 } // wxDbCreateDataSource()
4076 /********** wxDbGetDataSource() **********/
4077 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4078 SWORD DsDescMax
, UWORD direction
)
4080 * Dsn and DsDesc will contain the data source name and data source
4081 * description upon return
4086 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4087 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4092 } // wxDbGetDataSource()
4095 // Change this to 0 to remove use of all deprecated functions
4096 #if wxODBC_BACKWARD_COMPATABILITY
4097 /********************************************************************
4098 ********************************************************************
4100 * The following functions are all DEPRECATED and are included for
4101 * backward compatability reasons only
4103 ********************************************************************
4104 ********************************************************************/
4105 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4107 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4109 /***** DEPRECATED: use wxGetDataSource() *****/
4110 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4113 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4115 /***** DEPRECATED: use wxDbGetConnection() *****/
4116 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4118 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4120 /***** DEPRECATED: use wxDbFreeConnection() *****/
4121 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4123 return wxDbFreeConnection(pDb
);
4125 /***** DEPRECATED: use wxDbCloseConnections() *****/
4126 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4128 wxDbCloseConnections();
4130 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4131 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4133 return wxDbConnectionsInUse();