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 ///////////////////////////////////////////////////////////////////////////////
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"
57 #include "wx/msgdlg.h"
61 #include "wx/filefn.h"
62 #include "wx/wxchar.h"
74 WXDLLEXPORT_DATA(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
)
568 // Oracle user names may only be in uppercase, so force
569 // the name to uppercase
570 if (Dbms() == dbmsORACLE
)
571 UserID
= UserID
.Upper();
573 return UserID
.c_str();
574 } // wxDb::convertUserID()
577 /********** wxDb::Open() **********/
578 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
)
580 wxASSERT(Dsn
.Length());
587 if (!FwdOnlyCursors())
589 // Specify that the ODBC cursor library be used, if needed. This must be
590 // specified before the connection is made.
591 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
593 #ifdef DBDEBUG_CONSOLE
594 if (retcode
== SQL_SUCCESS
)
595 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
597 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
601 // Connect to the data source
602 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
603 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
604 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
606 if ((retcode
!= SQL_SUCCESS
) &&
607 (retcode
!= SQL_SUCCESS_WITH_INFO
))
608 return(DispAllErrors(henv
, hdbc
));
611 If using Intersolv branded ODBC drivers, this is the place where you would substitute
612 your branded driver license information
614 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
615 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
618 // Mark database as open
621 // Allocate a statement handle for the database connection
622 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
623 return(DispAllErrors(henv
, hdbc
));
625 // Set Connection Options
626 if (!setConnectionOptions())
629 // Query the data source for inf. about itself
633 // Query the data source regarding data type information
636 // The way it was determined which SQL data types to use was by calling SQLGetInfo
637 // for all of the possible SQL data types to see which ones were supported. If
638 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
639 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
640 // types I've selected below will not alway's be what we want. These are just
641 // what happened to work against an Oracle 7/Intersolv combination. The following is
642 // a complete list of the results I got back against the Oracle 7 database:
644 // SQL_BIGINT SQL_NO_DATA_FOUND
645 // SQL_BINARY SQL_NO_DATA_FOUND
646 // SQL_BIT SQL_NO_DATA_FOUND
647 // SQL_CHAR type name = 'CHAR', Precision = 255
648 // SQL_DATE SQL_NO_DATA_FOUND
649 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
650 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
651 // SQL_FLOAT SQL_NO_DATA_FOUND
652 // SQL_INTEGER SQL_NO_DATA_FOUND
653 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
654 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
655 // SQL_NUMERIC SQL_NO_DATA_FOUND
656 // SQL_REAL SQL_NO_DATA_FOUND
657 // SQL_SMALLINT SQL_NO_DATA_FOUND
658 // SQL_TIME SQL_NO_DATA_FOUND
659 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
660 // SQL_VARBINARY type name = 'RAW', Precision = 255
661 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
662 // =====================================================================
663 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
665 // SQL_VARCHAR type name = 'TEXT', Precision = 255
666 // SQL_TIMESTAMP type name = 'DATETIME'
667 // SQL_DECIMAL SQL_NO_DATA_FOUND
668 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
669 // SQL_FLOAT SQL_NO_DATA_FOUND
670 // SQL_REAL type name = 'SINGLE', Precision = 7
671 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
672 // SQL_INTEGER type name = 'LONG', Precision = 10
674 // VARCHAR = Variable length character string
675 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
676 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
679 typeInfVarchar
.FsqlType
= SQL_CHAR
;
681 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
684 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
685 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
686 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
687 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
688 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
691 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
693 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
695 typeInfFloat
.FsqlType
= SQL_FLOAT
;
697 typeInfFloat
.FsqlType
= SQL_REAL
;
699 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
702 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
704 // If SQL_INTEGER is not supported, use the floating point
705 // data type to store integers as well as floats
706 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
709 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
712 typeInfInteger
.FsqlType
= SQL_INTEGER
;
715 if (Dbms() != dbmsDBASE
)
717 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
720 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
724 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
727 typeInfDate
.FsqlType
= SQL_DATE
;
730 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
732 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
735 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
738 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
740 //typeInfBlob.TypeName = "BLOB";
742 #ifdef DBDEBUG_CONSOLE
743 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
744 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
745 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
746 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
747 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
751 // Completed Successfully
757 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
759 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
760 dbConnectInf
->GetPassword());
764 bool wxDb::Open(wxDb
*copyDb
)
766 dsn
= copyDb
->GetDatasourceName();
767 uid
= copyDb
->GetUsername();
768 authStr
= copyDb
->GetPassword();
772 if (!FwdOnlyCursors())
774 // Specify that the ODBC cursor library be used, if needed. This must be
775 // specified before the connection is made.
776 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
778 #ifdef DBDEBUG_CONSOLE
779 if (retcode
== SQL_SUCCESS
)
780 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
782 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
786 // Connect to the data source
787 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
788 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
789 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
791 if (retcode
== SQL_ERROR
)
792 return(DispAllErrors(henv
, hdbc
));
795 If using Intersolv branded ODBC drivers, this is the place where you would substitute
796 your branded driver license information
798 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
799 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
802 // Mark database as open
805 // Allocate a statement handle for the database connection
806 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
807 return(DispAllErrors(henv
, hdbc
));
809 // Set Connection Options
810 if (!setConnectionOptions())
813 // Instead of Querying the data source for info about itself, it can just be copied
814 // from the wxDb instance that was passed in (copyDb).
815 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
816 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
817 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
818 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
819 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
820 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
821 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
822 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
823 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
824 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
825 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
826 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
827 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
828 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
829 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
830 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
831 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
832 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
833 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
834 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
835 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
836 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
837 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
838 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
839 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
840 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
841 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
842 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
843 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
844 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
845 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
847 // VARCHAR = Variable length character string
848 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
849 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
850 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
851 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
852 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
855 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
856 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
857 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
858 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
859 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
862 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
863 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
864 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
865 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
866 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
869 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
870 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
871 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
872 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
873 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
876 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
877 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
878 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
879 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
880 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
882 #ifdef DBDEBUG_CONSOLE
883 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
884 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
885 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
886 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
887 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
891 // Completed Successfully
896 /********** wxDb::setConnectionOptions() **********/
897 bool wxDb::setConnectionOptions(void)
899 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
904 // I need to get the DBMS name here, because some of the connection options
905 // are database specific and need to call the Dbms() function.
906 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
907 return(DispAllErrors(henv
, hdbc
));
909 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
910 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
911 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
913 // By default, MS Sql Server closes cursors on commit and rollback. The following
914 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
915 // after a transaction. This is a driver specific option and is not part of the
916 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
917 // The database settings don't have any effect one way or the other.
918 if (Dbms() == dbmsMS_SQL_SERVER
)
920 const long SQL_PRESERVE_CURSORS
= 1204L;
921 const long SQL_PC_ON
= 1L;
922 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
925 // Display the connection options to verify them
926 #ifdef DBDEBUG_CONSOLE
928 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
930 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
931 return(DispAllErrors(henv
, hdbc
));
932 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
934 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
935 return(DispAllErrors(henv
, hdbc
));
936 cout
<< wxT("ODBC CURSORS: ");
939 case(SQL_CUR_USE_IF_NEEDED
):
940 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
942 case(SQL_CUR_USE_ODBC
):
943 cout
<< wxT("SQL_CUR_USE_ODBC");
945 case(SQL_CUR_USE_DRIVER
):
946 cout
<< wxT("SQL_CUR_USE_DRIVER");
951 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
952 return(DispAllErrors(henv
, hdbc
));
953 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
958 // Completed Successfully
961 } // wxDb::setConnectionOptions()
964 /********** wxDb::getDbInfo() **********/
965 bool wxDb::getDbInfo(void)
970 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
971 return(DispAllErrors(henv
, hdbc
));
973 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
974 return(DispAllErrors(henv
, hdbc
));
976 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
977 return(DispAllErrors(henv
, hdbc
));
980 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
981 // causing database connectivity to fail in some cases.
982 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
984 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
985 return(DispAllErrors(henv
, hdbc
));
987 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
988 return(DispAllErrors(henv
, hdbc
));
990 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
991 return(DispAllErrors(henv
, hdbc
));
993 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
994 return(DispAllErrors(henv
, hdbc
));
996 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
997 return(DispAllErrors(henv
, hdbc
));
999 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1000 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1001 return(DispAllErrors(henv
, hdbc
));
1003 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1007 return(DispAllErrors(henv
, hdbc
));
1009 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1010 // return(DispAllErrors(henv, hdbc));
1012 // Not all drivers support this call - Nick Gorham(unixODBC)
1013 dbInf
.cliConfLvl
= 0;
1016 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1017 return(DispAllErrors(henv
, hdbc
));
1019 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1020 return(DispAllErrors(henv
, hdbc
));
1022 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1023 return(DispAllErrors(henv
, hdbc
));
1025 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1026 return(DispAllErrors(henv
, hdbc
));
1028 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1029 return(DispAllErrors(henv
, hdbc
));
1031 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1032 return(DispAllErrors(henv
, hdbc
));
1034 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1035 return(DispAllErrors(henv
, hdbc
));
1037 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1038 return(DispAllErrors(henv
, hdbc
));
1040 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1041 return(DispAllErrors(henv
, hdbc
));
1043 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1044 return(DispAllErrors(henv
, hdbc
));
1046 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1047 return(DispAllErrors(henv
, hdbc
));
1049 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1050 return(DispAllErrors(henv
, hdbc
));
1052 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1053 return(DispAllErrors(henv
, hdbc
));
1055 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1056 return(DispAllErrors(henv
, hdbc
));
1058 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1059 return(DispAllErrors(henv
, hdbc
));
1061 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1062 return(DispAllErrors(henv
, hdbc
));
1064 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1065 return(DispAllErrors(henv
, hdbc
));
1067 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1068 return(DispAllErrors(henv
, hdbc
));
1070 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1071 return(DispAllErrors(henv
, hdbc
));
1073 #ifdef DBDEBUG_CONSOLE
1074 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1075 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1076 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1077 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1079 cout
<< wxT("API Conf. Level: ");
1080 switch(dbInf
.apiConfLvl
)
1082 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1083 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1084 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1088 cout
<< wxT("SAG CLI Conf. Level: ");
1089 switch(dbInf
.cliConfLvl
)
1091 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1092 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1096 cout
<< wxT("SQL Conf. Level: ");
1097 switch(dbInf
.sqlConfLvl
)
1099 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1100 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1101 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1105 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1106 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1107 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1108 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1109 cout
<< wxT("Cursor COMMIT Behavior: ");
1110 switch(dbInf
.cursorCommitBehavior
)
1112 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1113 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1114 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1118 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1119 switch(dbInf
.cursorRollbackBehavior
)
1121 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1122 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1123 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1127 cout
<< wxT("Support NOT NULL clause: ");
1128 switch(dbInf
.supportNotNullClause
)
1130 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1131 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1135 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1136 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1138 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1141 cout
<< wxT("Default Transaction Isolation: ";
1142 switch(dbInf
.txnIsolation
)
1144 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1145 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1146 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1147 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1149 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1154 cout
<< wxT("Transaction Isolation Options: ");
1155 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1156 cout
<< wxT("Read Uncommitted, ");
1157 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1158 cout
<< wxT("Read Committed, ");
1159 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1160 cout
<< wxT("Repeatable Read, ");
1161 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1162 cout
<< wxT("Serializable, ");
1164 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1165 cout
<< wxT("Versioning");
1169 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1170 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1171 cout
<< wxT("Next, ");
1172 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1173 cout
<< wxT("Prev, ");
1174 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1175 cout
<< wxT("First, ");
1176 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1177 cout
<< wxT("Last, ");
1178 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1179 cout
<< wxT("Absolute, ");
1180 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1181 cout
<< wxT("Relative, ");
1183 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1184 cout
<< wxT("Resume, ");
1186 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1187 cout
<< wxT("Bookmark");
1190 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1191 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1192 cout
<< wxT("No Change, ");
1193 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1194 cout
<< wxT("Exclusive, ");
1195 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1196 cout
<< wxT("UnLock");
1199 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1200 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1201 cout
<< wxT("Position, ");
1202 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1203 cout
<< wxT("Refresh, ");
1204 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1205 cout
<< wxT("Upd, "));
1206 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1207 cout
<< wxT("Del, ");
1208 if (dbInf
.posOperations
& SQL_POS_ADD
)
1212 cout
<< wxT("Positioned Statements Supported: ");
1213 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1214 cout
<< wxT("Pos delete, ");
1215 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1216 cout
<< wxT("Pos update, ");
1217 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1218 cout
<< wxT("Select for update");
1221 cout
<< wxT("Scroll Concurrency: ");
1222 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1223 cout
<< wxT("Read Only, ");
1224 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1225 cout
<< wxT("Lock, ");
1226 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1227 cout
<< wxT("Opt. Rowver, ");
1228 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1229 cout
<< wxT("Opt. Values");
1232 cout
<< wxT("Scroll Options: ");
1233 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1234 cout
<< wxT("Fwd Only, ");
1235 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1236 cout
<< wxT("Static, ");
1237 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1238 cout
<< wxT("Keyset Driven, ");
1239 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1240 cout
<< wxT("Dynamic, ");
1241 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1242 cout
<< wxT("Mixed");
1245 cout
<< wxT("Static Sensitivity: ");
1246 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1247 cout
<< wxT("Additions, ");
1248 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1249 cout
<< wxT("Deletions, ");
1250 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1251 cout
<< wxT("Updates");
1254 cout
<< wxT("Transaction Capable?: ");
1255 switch(dbInf
.txnCapable
)
1257 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1258 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1259 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1260 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1261 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1268 // Completed Successfully
1271 } // wxDb::getDbInfo()
1274 /********** wxDb::getDataTypeInfo() **********/
1275 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1278 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1279 * the data type inf. is gathered for.
1281 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1286 // Get information about the data type specified
1287 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1288 return(DispAllErrors(henv
, hdbc
, hstmt
));
1290 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
1292 #ifdef DBDEBUG_CONSOLE
1293 if (retcode
== SQL_NO_DATA_FOUND
)
1294 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1296 DispAllErrors(henv
, hdbc
, hstmt
);
1297 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1301 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1302 // Obtain columns from the record
1303 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1304 return(DispAllErrors(henv
, hdbc
, hstmt
));
1306 structSQLTypeInfo
.TypeName
= typeName
;
1308 // BJO 20000503: no more needed with new GetColumns...
1311 if (Dbms() == dbmsMY_SQL
)
1313 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1314 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1315 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1316 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1317 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1318 structSQLTypeInfo
.TypeName
= wxT("int");
1319 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1320 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1321 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1322 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1323 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1324 structSQLTypeInfo
.TypeName
= wxT("char");
1327 // BJO 20000427 : OpenLink driver
1328 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1329 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1331 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1332 structSQLTypeInfo
.TypeName
= wxT("real");
1336 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1337 return(DispAllErrors(henv
, hdbc
, hstmt
));
1338 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1339 return(DispAllErrors(henv
, hdbc
, hstmt
));
1340 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1341 // return(DispAllErrors(henv, hdbc, hstmt));
1343 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1344 return(DispAllErrors(henv
, hdbc
, hstmt
));
1346 if (structSQLTypeInfo
.MaximumScale
< 0)
1347 structSQLTypeInfo
.MaximumScale
= 0;
1349 // Close the statement handle which closes open cursors
1350 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1351 return(DispAllErrors(henv
, hdbc
, hstmt
));
1353 // Completed Successfully
1356 } // wxDb::getDataTypeInfo()
1359 /********** wxDb::Close() **********/
1360 void wxDb::Close(void)
1362 // Close the Sql Log file
1369 // Free statement handle
1372 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1373 DispAllErrors(henv
, hdbc
);
1376 // Disconnect from the datasource
1377 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1378 DispAllErrors(henv
, hdbc
);
1380 // Free the connection to the datasource
1381 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1382 DispAllErrors(henv
, hdbc
);
1384 // There should be zero Ctable objects still connected to this db object
1385 wxASSERT(nTables
== 0);
1390 pNode
= TablesInUse
.First();
1394 tiu
= (wxTablesInUse
*)pNode
->Data();
1395 if (tiu
->pDb
== this)
1397 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1398 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1399 wxLogDebug (s
.c_str(),s2
.c_str());
1401 pNode
= pNode
->Next();
1405 // Copy the error messages to a global variable
1407 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1408 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1410 dbmsType
= dbmsUNIDENTIFIED
;
1416 /********** wxDb::CommitTrans() **********/
1417 bool wxDb::CommitTrans(void)
1421 // Commit the transaction
1422 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1423 return(DispAllErrors(henv
, hdbc
));
1426 // Completed successfully
1429 } // wxDb::CommitTrans()
1432 /********** wxDb::RollbackTrans() **********/
1433 bool wxDb::RollbackTrans(void)
1435 // Rollback the transaction
1436 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1437 return(DispAllErrors(henv
, hdbc
));
1439 // Completed successfully
1442 } // wxDb::RollbackTrans()
1445 /********** wxDb::DispAllErrors() **********/
1446 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1448 * This function is called internally whenever an error condition prevents the user's
1449 * request from being executed. This function will query the datasource as to the
1450 * actual error(s) that just occured on the previous request of the datasource.
1452 * The function will retrieve each error condition from the datasource and
1453 * Printf the codes/text values into a string which it then logs via logError().
1454 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1455 * window and program execution will be paused until the user presses a key.
1457 * This function always returns a FALSE, so that functions which call this function
1458 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1459 * of the users request, so that the calling code can then process the error msg log
1462 wxString odbcErrMsg
;
1464 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1466 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1467 logError(odbcErrMsg
, sqlState
);
1470 #ifdef DBDEBUG_CONSOLE
1471 // When run in console mode, use standard out to display errors.
1472 cout
<< odbcErrMsg
.c_str() << endl
;
1473 cout
<< wxT("Press any key to continue...") << endl
;
1478 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1483 return(FALSE
); // This function always returns FALSE.
1485 } // wxDb::DispAllErrors()
1488 /********** wxDb::GetNextError() **********/
1489 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1491 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1496 } // wxDb::GetNextError()
1499 /********** wxDb::DispNextError() **********/
1500 void wxDb::DispNextError(void)
1502 wxString odbcErrMsg
;
1504 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1505 logError(odbcErrMsg
, sqlState
);
1510 #ifdef DBDEBUG_CONSOLE
1511 // When run in console mode, use standard out to display errors.
1512 cout
<< odbcErrMsg
.c_str() << endl
;
1513 cout
<< wxT("Press any key to continue...") << endl
;
1518 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1519 #endif // __WXDEBUG__
1521 } // wxDb::DispNextError()
1524 /********** wxDb::logError() **********/
1525 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1527 wxASSERT(errMsg
.Length());
1529 static int pLast
= -1;
1532 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1535 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1536 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1540 wxStrcpy(errorList
[pLast
], errMsg
);
1542 if (SQLState
.Length())
1543 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1544 DB_STATUS
= dbStatus
;
1546 // Add the errmsg to the sql log
1547 WriteSqlLog(errMsg
);
1549 } // wxDb::logError()
1552 /**********wxDb::TranslateSqlState() **********/
1553 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1555 if (!wxStrcmp(SQLState
, wxT("01000")))
1556 return(DB_ERR_GENERAL_WARNING
);
1557 if (!wxStrcmp(SQLState
, wxT("01002")))
1558 return(DB_ERR_DISCONNECT_ERROR
);
1559 if (!wxStrcmp(SQLState
, wxT("01004")))
1560 return(DB_ERR_DATA_TRUNCATED
);
1561 if (!wxStrcmp(SQLState
, wxT("01006")))
1562 return(DB_ERR_PRIV_NOT_REVOKED
);
1563 if (!wxStrcmp(SQLState
, wxT("01S00")))
1564 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1565 if (!wxStrcmp(SQLState
, wxT("01S01")))
1566 return(DB_ERR_ERROR_IN_ROW
);
1567 if (!wxStrcmp(SQLState
, wxT("01S02")))
1568 return(DB_ERR_OPTION_VALUE_CHANGED
);
1569 if (!wxStrcmp(SQLState
, wxT("01S03")))
1570 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1571 if (!wxStrcmp(SQLState
, wxT("01S04")))
1572 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1573 if (!wxStrcmp(SQLState
, wxT("07001")))
1574 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1575 if (!wxStrcmp(SQLState
, wxT("07006")))
1576 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1577 if (!wxStrcmp(SQLState
, wxT("08001")))
1578 return(DB_ERR_UNABLE_TO_CONNECT
);
1579 if (!wxStrcmp(SQLState
, wxT("08002")))
1580 return(DB_ERR_CONNECTION_IN_USE
);
1581 if (!wxStrcmp(SQLState
, wxT("08003")))
1582 return(DB_ERR_CONNECTION_NOT_OPEN
);
1583 if (!wxStrcmp(SQLState
, wxT("08004")))
1584 return(DB_ERR_REJECTED_CONNECTION
);
1585 if (!wxStrcmp(SQLState
, wxT("08007")))
1586 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1587 if (!wxStrcmp(SQLState
, wxT("08S01")))
1588 return(DB_ERR_COMM_LINK_FAILURE
);
1589 if (!wxStrcmp(SQLState
, wxT("21S01")))
1590 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1591 if (!wxStrcmp(SQLState
, wxT("21S02")))
1592 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1593 if (!wxStrcmp(SQLState
, wxT("22001")))
1594 return(DB_ERR_STRING_RIGHT_TRUNC
);
1595 if (!wxStrcmp(SQLState
, wxT("22003")))
1596 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1597 if (!wxStrcmp(SQLState
, wxT("22005")))
1598 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1599 if (!wxStrcmp(SQLState
, wxT("22008")))
1600 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1601 if (!wxStrcmp(SQLState
, wxT("22012")))
1602 return(DB_ERR_DIVIDE_BY_ZERO
);
1603 if (!wxStrcmp(SQLState
, wxT("22026")))
1604 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1605 if (!wxStrcmp(SQLState
, wxT("23000")))
1606 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1607 if (!wxStrcmp(SQLState
, wxT("24000")))
1608 return(DB_ERR_INVALID_CURSOR_STATE
);
1609 if (!wxStrcmp(SQLState
, wxT("25000")))
1610 return(DB_ERR_INVALID_TRANS_STATE
);
1611 if (!wxStrcmp(SQLState
, wxT("28000")))
1612 return(DB_ERR_INVALID_AUTH_SPEC
);
1613 if (!wxStrcmp(SQLState
, wxT("34000")))
1614 return(DB_ERR_INVALID_CURSOR_NAME
);
1615 if (!wxStrcmp(SQLState
, wxT("37000")))
1616 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1617 if (!wxStrcmp(SQLState
, wxT("3C000")))
1618 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1619 if (!wxStrcmp(SQLState
, wxT("40001")))
1620 return(DB_ERR_SERIALIZATION_FAILURE
);
1621 if (!wxStrcmp(SQLState
, wxT("42000")))
1622 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1623 if (!wxStrcmp(SQLState
, wxT("70100")))
1624 return(DB_ERR_OPERATION_ABORTED
);
1625 if (!wxStrcmp(SQLState
, wxT("IM001")))
1626 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1627 if (!wxStrcmp(SQLState
, wxT("IM002")))
1628 return(DB_ERR_NO_DATA_SOURCE
);
1629 if (!wxStrcmp(SQLState
, wxT("IM003")))
1630 return(DB_ERR_DRIVER_LOAD_ERROR
);
1631 if (!wxStrcmp(SQLState
, wxT("IM004")))
1632 return(DB_ERR_SQLALLOCENV_FAILED
);
1633 if (!wxStrcmp(SQLState
, wxT("IM005")))
1634 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1635 if (!wxStrcmp(SQLState
, wxT("IM006")))
1636 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1637 if (!wxStrcmp(SQLState
, wxT("IM007")))
1638 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1639 if (!wxStrcmp(SQLState
, wxT("IM008")))
1640 return(DB_ERR_DIALOG_FAILED
);
1641 if (!wxStrcmp(SQLState
, wxT("IM009")))
1642 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1643 if (!wxStrcmp(SQLState
, wxT("IM010")))
1644 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1645 if (!wxStrcmp(SQLState
, wxT("IM011")))
1646 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1647 if (!wxStrcmp(SQLState
, wxT("IM012")))
1648 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1649 if (!wxStrcmp(SQLState
, wxT("IM013")))
1650 return(DB_ERR_TRACE_FILE_ERROR
);
1651 if (!wxStrcmp(SQLState
, wxT("S0001")))
1652 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1653 if (!wxStrcmp(SQLState
, wxT("S0002")))
1654 return(DB_ERR_TABLE_NOT_FOUND
);
1655 if (!wxStrcmp(SQLState
, wxT("S0011")))
1656 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1657 if (!wxStrcmp(SQLState
, wxT("S0012")))
1658 return(DB_ERR_INDEX_NOT_FOUND
);
1659 if (!wxStrcmp(SQLState
, wxT("S0021")))
1660 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1661 if (!wxStrcmp(SQLState
, wxT("S0022")))
1662 return(DB_ERR_COLUMN_NOT_FOUND
);
1663 if (!wxStrcmp(SQLState
, wxT("S0023")))
1664 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1665 if (!wxStrcmp(SQLState
, wxT("S1000")))
1666 return(DB_ERR_GENERAL_ERROR
);
1667 if (!wxStrcmp(SQLState
, wxT("S1001")))
1668 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1669 if (!wxStrcmp(SQLState
, wxT("S1002")))
1670 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1671 if (!wxStrcmp(SQLState
, wxT("S1003")))
1672 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1673 if (!wxStrcmp(SQLState
, wxT("S1004")))
1674 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1675 if (!wxStrcmp(SQLState
, wxT("S1008")))
1676 return(DB_ERR_OPERATION_CANCELLED
);
1677 if (!wxStrcmp(SQLState
, wxT("S1009")))
1678 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1679 if (!wxStrcmp(SQLState
, wxT("S1010")))
1680 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1681 if (!wxStrcmp(SQLState
, wxT("S1011")))
1682 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1683 if (!wxStrcmp(SQLState
, wxT("S1012")))
1684 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1685 if (!wxStrcmp(SQLState
, wxT("S1015")))
1686 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1687 if (!wxStrcmp(SQLState
, wxT("S1090")))
1688 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1689 if (!wxStrcmp(SQLState
, wxT("S1091")))
1690 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1691 if (!wxStrcmp(SQLState
, wxT("S1092")))
1692 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1693 if (!wxStrcmp(SQLState
, wxT("S1093")))
1694 return(DB_ERR_INVALID_PARAM_NO
);
1695 if (!wxStrcmp(SQLState
, wxT("S1094")))
1696 return(DB_ERR_INVALID_SCALE_VALUE
);
1697 if (!wxStrcmp(SQLState
, wxT("S1095")))
1698 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1699 if (!wxStrcmp(SQLState
, wxT("S1096")))
1700 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1701 if (!wxStrcmp(SQLState
, wxT("S1097")))
1702 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1703 if (!wxStrcmp(SQLState
, wxT("S1098")))
1704 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1705 if (!wxStrcmp(SQLState
, wxT("S1099")))
1706 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1707 if (!wxStrcmp(SQLState
, wxT("S1100")))
1708 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1709 if (!wxStrcmp(SQLState
, wxT("S1101")))
1710 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1711 if (!wxStrcmp(SQLState
, wxT("S1103")))
1712 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1713 if (!wxStrcmp(SQLState
, wxT("S1104")))
1714 return(DB_ERR_INVALID_PRECISION_VALUE
);
1715 if (!wxStrcmp(SQLState
, wxT("S1105")))
1716 return(DB_ERR_INVALID_PARAM_TYPE
);
1717 if (!wxStrcmp(SQLState
, wxT("S1106")))
1718 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1719 if (!wxStrcmp(SQLState
, wxT("S1107")))
1720 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1721 if (!wxStrcmp(SQLState
, wxT("S1108")))
1722 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1723 if (!wxStrcmp(SQLState
, wxT("S1109")))
1724 return(DB_ERR_INVALID_CURSOR_POSITION
);
1725 if (!wxStrcmp(SQLState
, wxT("S1110")))
1726 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1727 if (!wxStrcmp(SQLState
, wxT("S1111")))
1728 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1729 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1730 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1731 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1732 return(DB_ERR_TIMEOUT_EXPIRED
);
1737 } // wxDb::TranslateSqlState()
1740 /********** wxDb::Grant() **********/
1741 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1745 // Build the grant statement
1746 sqlStmt
= wxT("GRANT ");
1747 if (privileges
== DB_GRANT_ALL
)
1748 sqlStmt
+= wxT("ALL");
1752 if (privileges
& DB_GRANT_SELECT
)
1754 sqlStmt
+= wxT("SELECT");
1757 if (privileges
& DB_GRANT_INSERT
)
1760 sqlStmt
+= wxT(", ");
1761 sqlStmt
+= wxT("INSERT");
1763 if (privileges
& DB_GRANT_UPDATE
)
1766 sqlStmt
+= wxT(", ");
1767 sqlStmt
+= wxT("UPDATE");
1769 if (privileges
& DB_GRANT_DELETE
)
1772 sqlStmt
+= wxT(", ");
1773 sqlStmt
+= wxT("DELETE");
1777 sqlStmt
+= wxT(" ON ");
1778 sqlStmt
+= SQLTableName(tableName
);
1779 sqlStmt
+= wxT(" TO ");
1780 sqlStmt
+= userList
;
1782 #ifdef DBDEBUG_CONSOLE
1783 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1786 WriteSqlLog(sqlStmt
);
1788 return(ExecSql(sqlStmt
));
1793 /********** wxDb::CreateView() **********/
1794 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1795 const wxString
&pSqlStmt
, bool attemptDrop
)
1799 // Drop the view first
1800 if (attemptDrop
&& !DropView(viewName
))
1803 // Build the create view statement
1804 sqlStmt
= wxT("CREATE VIEW ");
1805 sqlStmt
+= viewName
;
1807 if (colList
.Length())
1809 sqlStmt
+= wxT(" (");
1811 sqlStmt
+= wxT(")");
1814 sqlStmt
+= wxT(" AS ");
1815 sqlStmt
+= pSqlStmt
;
1817 WriteSqlLog(sqlStmt
);
1819 #ifdef DBDEBUG_CONSOLE
1820 cout
<< sqlStmt
.c_str() << endl
;
1823 return(ExecSql(sqlStmt
));
1825 } // wxDb::CreateView()
1828 /********** wxDb::DropView() **********/
1829 bool wxDb::DropView(const wxString
&viewName
)
1832 * NOTE: This function returns TRUE if the View does not exist, but
1833 * only for identified databases. Code will need to be added
1834 * below for any other databases when those databases are defined
1835 * to handle this situation consistently
1839 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1841 WriteSqlLog(sqlStmt
);
1843 #ifdef DBDEBUG_CONSOLE
1844 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1847 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1849 // Check for "Base table not found" error and ignore
1850 GetNextError(henv
, hdbc
, hstmt
);
1851 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1853 // Check for product specific error codes
1854 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1857 DispAllErrors(henv
, hdbc
, hstmt
);
1864 // Commit the transaction
1870 } // wxDb::DropView()
1873 /********** wxDb::ExecSql() **********/
1874 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1878 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1880 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
1881 if (retcode
== SQL_SUCCESS
||
1882 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
1888 DispAllErrors(henv
, hdbc
, hstmt
);
1892 } // wxDb::ExecSql()
1895 /********** wxDb::GetNext() **********/
1896 bool wxDb::GetNext(void)
1898 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1902 DispAllErrors(henv
, hdbc
, hstmt
);
1906 } // wxDb::GetNext()
1909 /********** wxDb::GetData() **********/
1910 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1913 wxASSERT(cbReturned
);
1915 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1919 DispAllErrors(henv
, hdbc
, hstmt
);
1923 } // wxDb::GetData()
1926 /********** wxDb::GetKeyFields() **********/
1927 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1929 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1930 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1932 // SQLSMALLINT iKeySeq;
1933 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1934 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1940 * -----------------------------------------------------------------------
1941 * -- 19991224 : mj10777 : Create ------
1942 * -- : Three things are done and stored here : ------
1943 * -- : 1) which Column(s) is/are Primary Key(s) ------
1944 * -- : 2) which tables use this Key as a Foreign Key ------
1945 * -- : 3) which columns are Foreign Key and the name ------
1946 * -- : of the Table where the Key is the Primary Key -----
1947 * -- : Called from GetColumns(const wxString &tableName, ------
1948 * -- int *numCols,const wxChar *userID ) ------
1949 * -----------------------------------------------------------------------
1952 /*---------------------------------------------------------------------*/
1953 /* Get the names of the columns in the primary key. */
1954 /*---------------------------------------------------------------------*/
1955 retcode
= SQLPrimaryKeys(hstmt
,
1956 NULL
, 0, /* Catalog name */
1957 NULL
, 0, /* Schema name */
1958 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1960 /*---------------------------------------------------------------------*/
1961 /* Fetch and display the result set. This will be a list of the */
1962 /* columns in the primary key of the tableName table. */
1963 /*---------------------------------------------------------------------*/
1964 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1966 retcode
= SQLFetch(hstmt
);
1967 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1969 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1970 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1972 for (i
=0;i
<noCols
;i
++) // Find the Column name
1973 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1974 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1977 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1979 /*---------------------------------------------------------------------*/
1980 /* Get all the foreign keys that refer to tableName primary key. */
1981 /*---------------------------------------------------------------------*/
1982 retcode
= SQLForeignKeys(hstmt
,
1983 NULL
, 0, /* Primary catalog */
1984 NULL
, 0, /* Primary schema */
1985 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
1986 NULL
, 0, /* Foreign catalog */
1987 NULL
, 0, /* Foreign schema */
1988 NULL
, 0); /* Foreign table */
1990 /*---------------------------------------------------------------------*/
1991 /* Fetch and display the result set. This will be all of the foreign */
1992 /* keys in other tables that refer to the tableName primary key. */
1993 /*---------------------------------------------------------------------*/
1996 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1998 retcode
= SQLFetch(hstmt
);
1999 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2001 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2002 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2003 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2004 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2005 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2006 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2010 tempStr
.Trim(); // Get rid of any unneeded blanks
2011 if (!tempStr
.IsEmpty())
2013 for (i
=0; i
<noCols
; i
++)
2014 { // Find the Column name
2015 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2016 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2020 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2022 /*---------------------------------------------------------------------*/
2023 /* Get all the foreign keys in the tablename table. */
2024 /*---------------------------------------------------------------------*/
2025 retcode
= SQLForeignKeys(hstmt
,
2026 NULL
, 0, /* Primary catalog */
2027 NULL
, 0, /* Primary schema */
2028 NULL
, 0, /* Primary table */
2029 NULL
, 0, /* Foreign catalog */
2030 NULL
, 0, /* Foreign schema */
2031 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2033 /*---------------------------------------------------------------------*/
2034 /* Fetch and display the result set. This will be all of the */
2035 /* primary keys in other tables that are referred to by foreign */
2036 /* keys in the tableName table. */
2037 /*---------------------------------------------------------------------*/
2039 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2041 retcode
= SQLFetch(hstmt
);
2042 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2044 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2045 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2046 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2048 for (i
=0; i
<noCols
; i
++) // Find the Column name
2050 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2052 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2053 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2058 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2062 } // wxDb::GetKeyFields()
2066 /********** wxDb::GetColumns() **********/
2067 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2069 * 1) The last array element of the tableName[] argument must be zero (null).
2070 * This is how the end of the array is detected.
2071 * 2) This function returns an array of wxDbColInf structures. If no columns
2072 * were found, or an error occured, this pointer will be zero (null). THE
2073 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2074 * IS FINISHED WITH IT. i.e.
2076 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2079 * // Use the column inf
2081 * // Destroy the memory
2085 * userID is evaluated in the following manner:
2086 * userID == NULL ... UserID is ignored
2087 * userID == "" ... UserID set equal to 'this->uid'
2088 * userID != "" ... UserID set equal to 'userID'
2090 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2091 * by this function. This function should use its own wxDb instance
2092 * to avoid undesired unbinding of columns.
2097 wxDbColInf
*colInf
= 0;
2105 convertUserID(userID
,UserID
);
2107 // Pass 1 - Determine how many columns there are.
2108 // Pass 2 - Allocate the wxDbColInf array and fill in
2109 // the array with the column information.
2111 for (pass
= 1; pass
<= 2; pass
++)
2115 if (noCols
== 0) // Probably a bogus table name(s)
2117 // Allocate n wxDbColInf objects to hold the column information
2118 colInf
= new wxDbColInf
[noCols
+1];
2121 // Mark the end of the array
2122 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2123 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2124 colInf
[noCols
].sqlDataType
= 0;
2126 // Loop through each table name
2128 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2130 TableName
= tableName
[tbl
];
2131 // Oracle and Interbase table names are uppercase only, so force
2132 // the name to uppercase just in case programmer forgot to do this
2133 if ((Dbms() == dbmsORACLE
) ||
2134 (Dbms() == dbmsINTERBASE
))
2135 TableName
= TableName
.Upper();
2137 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2139 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2140 // use the call below that leaves out the user name
2141 if (!UserID
.IsEmpty() &&
2142 Dbms() != dbmsMY_SQL
&&
2143 Dbms() != dbmsACCESS
&&
2144 Dbms() != dbmsMS_SQL_SERVER
)
2146 retcode
= SQLColumns(hstmt
,
2147 NULL
, 0, // All qualifiers
2148 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2149 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2150 NULL
, 0); // All columns
2154 retcode
= SQLColumns(hstmt
,
2155 NULL
, 0, // All qualifiers
2157 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2158 NULL
, 0); // All columns
2160 if (retcode
!= SQL_SUCCESS
)
2161 { // Error occured, abort
2162 DispAllErrors(henv
, hdbc
, hstmt
);
2165 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2169 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2171 if (pass
== 1) // First pass, just add up the number of columns
2173 else // Pass 2; Fill in the array of structures
2175 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2177 // NOTE: Only the ODBC 1.x fields are retrieved
2178 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2179 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2180 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2181 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2182 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2183 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2184 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2185 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2186 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2187 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2188 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2189 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2191 // Determine the wxDb data type that is used to represent the native data type of this data source
2192 colInf
[colNo
].dbDataType
= 0;
2193 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2196 // IODBC does not return a correct columnSize, so we set
2197 // columnSize = bufferLength if no column size was returned
2198 // IODBC returns the columnSize in bufferLength.. (bug)
2199 if (colInf
[colNo
].columnSize
< 1)
2201 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2204 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2206 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2207 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2208 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2209 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2210 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2211 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2212 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2213 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2218 if (retcode
!= SQL_NO_DATA_FOUND
)
2219 { // Error occured, abort
2220 DispAllErrors(henv
, hdbc
, hstmt
);
2223 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2229 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2232 } // wxDb::GetColumns()
2235 /********** wxDb::GetColumns() **********/
2237 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2239 // Same as the above GetColumns() function except this one gets columns
2240 // only for a single table, and if 'numCols' is not NULL, the number of
2241 // columns stored in the returned wxDbColInf is set in '*numCols'
2243 // userID is evaluated in the following manner:
2244 // userID == NULL ... UserID is ignored
2245 // userID == "" ... UserID set equal to 'this->uid'
2246 // userID != "" ... UserID set equal to 'userID'
2248 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2249 // by this function. This function should use its own wxDb instance
2250 // to avoid undesired unbinding of columns.
2255 wxDbColInf
*colInf
= 0;
2263 convertUserID(userID
,UserID
);
2265 // Pass 1 - Determine how many columns there are.
2266 // Pass 2 - Allocate the wxDbColInf array and fill in
2267 // the array with the column information.
2269 for (pass
= 1; pass
<= 2; pass
++)
2273 if (noCols
== 0) // Probably a bogus table name(s)
2275 // Allocate n wxDbColInf objects to hold the column information
2276 colInf
= new wxDbColInf
[noCols
+1];
2279 // Mark the end of the array
2280 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2281 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2282 colInf
[noCols
].sqlDataType
= 0;
2285 TableName
= tableName
;
2286 // Oracle and Interbase table names are uppercase only, so force
2287 // the name to uppercase just in case programmer forgot to do this
2288 if ((Dbms() == dbmsORACLE
) ||
2289 (Dbms() == dbmsINTERBASE
))
2290 TableName
= TableName
.Upper();
2292 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2294 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2295 // use the call below that leaves out the user name
2296 if (!UserID
.IsEmpty() &&
2297 Dbms() != dbmsMY_SQL
&&
2298 Dbms() != dbmsACCESS
&&
2299 Dbms() != dbmsMS_SQL_SERVER
)
2301 retcode
= SQLColumns(hstmt
,
2302 NULL
, 0, // All qualifiers
2303 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2304 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2305 NULL
, 0); // All columns
2309 retcode
= SQLColumns(hstmt
,
2310 NULL
, 0, // All qualifiers
2312 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2313 NULL
, 0); // All columns
2315 if (retcode
!= SQL_SUCCESS
)
2316 { // Error occured, abort
2317 DispAllErrors(henv
, hdbc
, hstmt
);
2320 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2326 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2328 if (pass
== 1) // First pass, just add up the number of columns
2330 else // Pass 2; Fill in the array of structures
2332 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2334 // NOTE: Only the ODBC 1.x fields are retrieved
2335 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2336 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2337 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2338 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2339 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2340 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2341 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2342 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2343 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2344 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2345 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2346 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2347 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2348 // Start Values for Primary/Foriegn Key (=No)
2349 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2350 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2351 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2352 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2354 // BJO 20000428 : Virtuoso returns type names with upper cases!
2355 if (Dbms() == dbmsVIRTUOSO
)
2357 wxString s
= colInf
[colNo
].typeName
;
2359 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2362 // Determine the wxDb data type that is used to represent the native data type of this data source
2363 colInf
[colNo
].dbDataType
= 0;
2364 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2367 // IODBC does not return a correct columnSize, so we set
2368 // columnSize = bufferLength if no column size was returned
2369 // IODBC returns the columnSize in bufferLength.. (bug)
2370 if (colInf
[colNo
].columnSize
< 1)
2372 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2376 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2378 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2379 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2380 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2381 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2382 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2383 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2384 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2385 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2391 if (retcode
!= SQL_NO_DATA_FOUND
)
2392 { // Error occured, abort
2393 DispAllErrors(henv
, hdbc
, hstmt
);
2396 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2403 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2405 // Store Primary and Foriegn Keys
2406 GetKeyFields(tableName
,colInf
,noCols
);
2412 } // wxDb::GetColumns()
2415 #else // New GetColumns
2420 These are tentative new GetColumns members which should be more database
2421 independant and which always returns the columns in the order they were
2424 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2425 wxChar* userID)) calls the second implementation for each separate table
2426 before merging the results. This makes the code easier to maintain as
2427 only one member (the second) makes the real work
2428 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2429 wxChar *userID) is a little bit improved
2430 - It doesn't anymore rely on the type-name to find out which database-type
2432 - It ends by sorting the columns, so that they are returned in the same
2433 order they were created
2443 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2446 // The last array element of the tableName[] argument must be zero (null).
2447 // This is how the end of the array is detected.
2451 // How many tables ?
2453 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2455 // Create a table to maintain the columns for each separate table
2456 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2459 for (i
= 0 ; i
< tbl
; i
++)
2462 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2463 if (TableColumns
[i
].colInf
== NULL
)
2465 noCols
+= TableColumns
[i
].noCols
;
2468 // Now merge all the separate table infos
2469 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2471 // Mark the end of the array
2472 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2473 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2474 colInf
[noCols
].sqlDataType
= 0;
2479 for (i
= 0 ; i
< tbl
; i
++)
2481 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2483 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2487 delete [] TableColumns
;
2490 } // wxDb::GetColumns() -- NEW
2493 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2495 // Same as the above GetColumns() function except this one gets columns
2496 // only for a single table, and if 'numCols' is not NULL, the number of
2497 // columns stored in the returned wxDbColInf is set in '*numCols'
2499 // userID is evaluated in the following manner:
2500 // userID == NULL ... UserID is ignored
2501 // userID == "" ... UserID set equal to 'this->uid'
2502 // userID != "" ... UserID set equal to 'userID'
2504 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2505 // by this function. This function should use its own wxDb instance
2506 // to avoid undesired unbinding of columns.
2510 wxDbColInf
*colInf
= 0;
2518 convertUserID(userID
,UserID
);
2520 // Pass 1 - Determine how many columns there are.
2521 // Pass 2 - Allocate the wxDbColInf array and fill in
2522 // the array with the column information.
2524 for (pass
= 1; pass
<= 2; pass
++)
2528 if (noCols
== 0) // Probably a bogus table name(s)
2530 // Allocate n wxDbColInf objects to hold the column information
2531 colInf
= new wxDbColInf
[noCols
+1];
2534 // Mark the end of the array
2535 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2536 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2537 colInf
[noCols
].sqlDataType
= 0;
2540 TableName
= tableName
;
2541 // Oracle and Interbase table names are uppercase only, so force
2542 // the name to uppercase just in case programmer forgot to do this
2543 if ((Dbms() == dbmsORACLE
) ||
2544 (Dbms() == dbmsINTERBASE
))
2545 TableName
= TableName
.Upper();
2547 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2549 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2550 // use the call below that leaves out the user name
2551 if (!UserID
.IsEmpty() &&
2552 Dbms() != dbmsMY_SQL
&&
2553 Dbms() != dbmsACCESS
&&
2554 Dbms() != dbmsMS_SQL_SERVER
)
2556 retcode
= SQLColumns(hstmt
,
2557 NULL
, 0, // All qualifiers
2558 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2559 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2560 NULL
, 0); // All columns
2564 retcode
= SQLColumns(hstmt
,
2565 NULL
, 0, // All qualifiers
2567 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2568 NULL
, 0); // All columns
2570 if (retcode
!= SQL_SUCCESS
)
2571 { // Error occured, abort
2572 DispAllErrors(henv
, hdbc
, hstmt
);
2575 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2581 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2583 if (pass
== 1) // First pass, just add up the number of columns
2585 else // Pass 2; Fill in the array of structures
2587 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2589 // NOTE: Only the ODBC 1.x fields are retrieved
2590 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2591 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2592 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2593 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2594 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2595 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2596 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2597 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2598 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2599 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2600 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2601 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2602 // Start Values for Primary/Foriegn Key (=No)
2603 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2604 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2605 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2606 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2609 // IODBC does not return a correct columnSize, so we set
2610 // columnSize = bufferLength if no column size was returned
2611 // IODBC returns the columnSize in bufferLength.. (bug)
2612 if (colInf
[colNo
].columnSize
< 1)
2614 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2618 // Determine the wxDb data type that is used to represent the native data type of this data source
2619 colInf
[colNo
].dbDataType
= 0;
2620 // Get the intern datatype
2621 switch (colInf
[colNo
].sqlDataType
)
2625 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2631 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2638 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2641 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2644 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2649 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2650 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2657 if (retcode
!= SQL_NO_DATA_FOUND
)
2658 { // Error occured, abort
2659 DispAllErrors(henv
, hdbc
, hstmt
);
2662 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2669 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2671 // Store Primary and Foreign Keys
2672 GetKeyFields(tableName
,colInf
,noCols
);
2674 ///////////////////////////////////////////////////////////////////////////
2675 // Now sort the the columns in order to make them appear in the right order
2676 ///////////////////////////////////////////////////////////////////////////
2678 // Build a generic SELECT statement which returns 0 rows
2681 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2684 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2686 DispAllErrors(henv
, hdbc
, hstmt
);
2690 // Get the number of result columns
2691 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2693 DispAllErrors(henv
, hdbc
, hstmt
);
2697 if (noCols
== 0) // Probably a bogus table name
2706 for (colNum
= 0; colNum
< noCols
; colNum
++)
2708 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2710 &Sword
, &Sdword
) != SQL_SUCCESS
)
2712 DispAllErrors(henv
, hdbc
, hstmt
);
2716 wxString Name1
= name
;
2717 Name1
= Name1
.Upper();
2719 // Where is this name in the array ?
2720 for (i
= colNum
; i
< noCols
; i
++)
2722 wxString Name2
= colInf
[i
].colName
;
2723 Name2
= Name2
.Upper();
2726 if (colNum
!= i
) // swap to sort
2728 wxDbColInf tmpColInf
= colInf
[colNum
];
2729 colInf
[colNum
] = colInf
[i
];
2730 colInf
[i
] = tmpColInf
;
2736 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2738 ///////////////////////////////////////////////////////////////////////////
2740 ///////////////////////////////////////////////////////////////////////////
2746 } // wxDb::GetColumns()
2749 #endif // #else OLD_GETCOLUMNS
2752 /********** wxDb::GetColumnCount() **********/
2753 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2755 * Returns a count of how many columns are in a table.
2756 * If an error occurs in computing the number of columns
2757 * this function will return a -1 for the count
2759 * userID is evaluated in the following manner:
2760 * userID == NULL ... UserID is ignored
2761 * userID == "" ... UserID set equal to 'this->uid'
2762 * userID != "" ... UserID set equal to 'userID'
2764 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2765 * by this function. This function should use its own wxDb instance
2766 * to avoid undesired unbinding of columns.
2776 convertUserID(userID
,UserID
);
2778 TableName
= tableName
;
2779 // Oracle and Interbase table names are uppercase only, so force
2780 // the name to uppercase just in case programmer forgot to do this
2781 if ((Dbms() == dbmsORACLE
) ||
2782 (Dbms() == dbmsINTERBASE
))
2783 TableName
= TableName
.Upper();
2785 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2787 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2788 // use the call below that leaves out the user name
2789 if (!UserID
.IsEmpty() &&
2790 Dbms() != dbmsMY_SQL
&&
2791 Dbms() != dbmsACCESS
&&
2792 Dbms() != dbmsMS_SQL_SERVER
)
2794 retcode
= SQLColumns(hstmt
,
2795 NULL
, 0, // All qualifiers
2796 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2797 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2798 NULL
, 0); // All columns
2802 retcode
= SQLColumns(hstmt
,
2803 NULL
, 0, // All qualifiers
2805 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2806 NULL
, 0); // All columns
2808 if (retcode
!= SQL_SUCCESS
)
2809 { // Error occured, abort
2810 DispAllErrors(henv
, hdbc
, hstmt
);
2811 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2815 // Count the columns
2816 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2819 if (retcode
!= SQL_NO_DATA_FOUND
)
2820 { // Error occured, abort
2821 DispAllErrors(henv
, hdbc
, hstmt
);
2822 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2826 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2829 } // wxDb::GetColumnCount()
2832 /********** wxDb::GetCatalog() *******/
2833 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2835 * ---------------------------------------------------------------------
2836 * -- 19991203 : mj10777 : Create ------
2837 * -- : Creates a wxDbInf with Tables / Cols Array ------
2838 * -- : uses SQLTables and fills pTableInf; ------
2839 * -- : pColInf is set to NULL and numCols to 0; ------
2840 * -- : returns pDbInf (wxDbInf) ------
2841 * -- - if unsuccesfull (pDbInf == NULL) ------
2842 * -- : pColInf can be filled with GetColumns(..); ------
2843 * -- : numCols can be filled with GetColumnCount(..); ------
2844 * ---------------------------------------------------------------------
2846 * userID is evaluated in the following manner:
2847 * userID == NULL ... UserID is ignored
2848 * userID == "" ... UserID set equal to 'this->uid'
2849 * userID != "" ... UserID set equal to 'userID'
2851 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2852 * by this function. This function should use its own wxDb instance
2853 * to avoid undesired unbinding of columns.
2856 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2857 int noTab
= 0; // Counter while filling table entries
2861 wxString tblNameSave
;
2864 convertUserID(userID
,UserID
);
2866 //-------------------------------------------------------------
2867 pDbInf
= new wxDbInf
; // Create the Database Array
2868 //-------------------------------------------------------------
2869 // Table Information
2870 // Pass 1 - Determine how many Tables there are.
2871 // Pass 2 - Create the Table array and fill it
2872 // - Create the Cols array = NULL
2873 //-------------------------------------------------------------
2875 for (pass
= 1; pass
<= 2; pass
++)
2877 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2878 tblNameSave
.Empty();
2880 if (!UserID
.IsEmpty() &&
2881 Dbms() != dbmsMY_SQL
&&
2882 Dbms() != dbmsACCESS
&&
2883 Dbms() != dbmsMS_SQL_SERVER
)
2885 retcode
= SQLTables(hstmt
,
2886 NULL
, 0, // All qualifiers
2887 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2888 NULL
, 0, // All tables
2889 NULL
, 0); // All columns
2893 retcode
= SQLTables(hstmt
,
2894 NULL
, 0, // All qualifiers
2895 NULL
, 0, // User specified
2896 NULL
, 0, // All tables
2897 NULL
, 0); // All columns
2900 if (retcode
!= SQL_SUCCESS
)
2902 DispAllErrors(henv
, hdbc
, hstmt
);
2904 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2908 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2910 if (pass
== 1) // First pass, just count the Tables
2912 if (pDbInf
->numTables
== 0)
2914 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2915 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2917 pDbInf
->numTables
++; // Counter for Tables
2919 if (pass
== 2) // Create and fill the Table entries
2921 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2922 { // no, then create the Array
2923 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2925 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2927 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2928 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2929 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2935 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2937 // Query how many columns are in each table
2938 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2940 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2945 } // wxDb::GetCatalog()
2948 /********** wxDb::Catalog() **********/
2949 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2951 * Creates the text file specified in 'filename' which will contain
2952 * a minimal data dictionary of all tables accessible by the user specified
2955 * userID is evaluated in the following manner:
2956 * userID == NULL ... UserID is ignored
2957 * userID == "" ... UserID set equal to 'this->uid'
2958 * userID != "" ... UserID set equal to 'userID'
2960 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2961 * by this function. This function should use its own wxDb instance
2962 * to avoid undesired unbinding of columns.
2965 wxASSERT(fileName
.Length());
2969 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2970 wxString tblNameSave
;
2971 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2973 wxChar typeName
[30+1];
2974 SDWORD precision
, length
;
2976 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
2980 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2983 convertUserID(userID
,UserID
);
2985 if (!UserID
.IsEmpty() &&
2986 Dbms() != dbmsMY_SQL
&&
2987 Dbms() != dbmsACCESS
&&
2988 Dbms() != dbmsINTERBASE
&&
2989 Dbms() != dbmsMS_SQL_SERVER
)
2991 retcode
= SQLColumns(hstmt
,
2992 NULL
, 0, // All qualifiers
2993 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2994 NULL
, 0, // All tables
2995 NULL
, 0); // All columns
2999 retcode
= SQLColumns(hstmt
,
3000 NULL
, 0, // All qualifiers
3001 NULL
, 0, // User specified
3002 NULL
, 0, // All tables
3003 NULL
, 0); // All columns
3005 if (retcode
!= SQL_SUCCESS
)
3007 DispAllErrors(henv
, hdbc
, hstmt
);
3013 tblNameSave
.Empty();
3018 retcode
= SQLFetch(hstmt
);
3019 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3022 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3025 fputs(wxT("\n"), fp
);
3026 fputs(wxT("================================ "), fp
);
3027 fputs(wxT("================================ "), fp
);
3028 fputs(wxT("===================== "), fp
);
3029 fputs(wxT("========= "), fp
);
3030 fputs(wxT("=========\n"), fp
);
3031 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3032 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3033 fputs(outStr
.c_str(), fp
);
3034 fputs(wxT("================================ "), fp
);
3035 fputs(wxT("================================ "), fp
);
3036 fputs(wxT("===================== "), fp
);
3037 fputs(wxT("========= "), fp
);
3038 fputs(wxT("=========\n"), fp
);
3039 tblNameSave
= tblName
;
3042 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3043 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3044 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3045 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3046 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3047 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3049 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
3050 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3051 if (fputs(outStr
.c_str(), fp
) == EOF
)
3053 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3060 if (retcode
!= SQL_NO_DATA_FOUND
)
3061 DispAllErrors(henv
, hdbc
, hstmt
);
3063 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3066 return(retcode
== SQL_NO_DATA_FOUND
);
3068 } // wxDb::Catalog()
3071 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3073 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3074 * if the object exists in the database. This function does not indicate
3075 * whether or not the user has privleges to query or perform other functions
3078 * userID is evaluated in the following manner:
3079 * userID == NULL ... UserID is ignored
3080 * userID == "" ... UserID set equal to 'this->uid'
3081 * userID != "" ... UserID set equal to 'userID'
3084 wxASSERT(tableName
.Length());
3088 if (Dbms() == dbmsDBASE
)
3091 if (tablePath
.Length())
3092 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3094 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3097 exists
= wxFileExists(dbName
);
3102 convertUserID(userID
,UserID
);
3104 TableName
= tableName
;
3105 // Oracle and Interbase table names are uppercase only, so force
3106 // the name to uppercase just in case programmer forgot to do this
3107 if ((Dbms() == dbmsORACLE
) ||
3108 (Dbms() == dbmsINTERBASE
))
3109 TableName
= TableName
.Upper();
3111 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3114 // Some databases cannot accept a user name when looking up table names,
3115 // so we use the call below that leaves out the user name
3116 if (!UserID
.IsEmpty() &&
3117 Dbms() != dbmsMY_SQL
&&
3118 Dbms() != dbmsACCESS
&&
3119 Dbms() != dbmsMS_SQL_SERVER
&&
3120 Dbms() != dbmsDB2
&&
3121 Dbms() != dbmsINTERBASE
&&
3122 Dbms() != dbmsPERVASIVE_SQL
)
3124 retcode
= SQLTables(hstmt
,
3125 NULL
, 0, // All qualifiers
3126 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3127 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3128 NULL
, 0); // All table types
3132 retcode
= SQLTables(hstmt
,
3133 NULL
, 0, // All qualifiers
3134 NULL
, 0, // All owners
3135 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3136 NULL
, 0); // All table types
3138 if (retcode
!= SQL_SUCCESS
)
3139 return(DispAllErrors(henv
, hdbc
, hstmt
));
3141 retcode
= SQLFetch(hstmt
);
3142 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3144 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3145 return(DispAllErrors(henv
, hdbc
, hstmt
));
3148 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3152 } // wxDb::TableExists()
3155 /********** wxDb::TablePrivileges() **********/
3156 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3157 const wxChar
*schema
, const wxString
&tablePath
)
3159 wxASSERT(tableName
.Length());
3161 wxDbTablePrivilegeInfo result
;
3165 // We probably need to be able to dynamically set this based on
3166 // the driver type, and state.
3167 wxChar curRole
[]=wxT("public");
3171 wxString UserID
,Schema
;
3172 convertUserID(userID
,UserID
);
3173 convertUserID(schema
,Schema
);
3175 TableName
= tableName
;
3176 // Oracle and Interbase table names are uppercase only, so force
3177 // the name to uppercase just in case programmer forgot to do this
3178 if ((Dbms() == dbmsORACLE
) ||
3179 (Dbms() == dbmsINTERBASE
))
3180 TableName
= TableName
.Upper();
3182 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3184 // Some databases cannot accept a user name when looking up table names,
3185 // so we use the call below that leaves out the user name
3186 if (!Schema
.IsEmpty() &&
3187 Dbms() != dbmsMY_SQL
&&
3188 Dbms() != dbmsACCESS
&&
3189 Dbms() != dbmsMS_SQL_SERVER
)
3191 retcode
= SQLTablePrivileges(hstmt
,
3193 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3194 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3198 retcode
= SQLTablePrivileges(hstmt
,
3201 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3204 #ifdef DBDEBUG_CONSOLE
3205 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3208 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3209 return(DispAllErrors(henv
, hdbc
, hstmt
));
3211 bool failed
= FALSE
;
3212 retcode
= SQLFetch(hstmt
);
3213 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3215 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3218 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3221 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3224 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3227 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3230 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3233 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3238 return(DispAllErrors(henv
, hdbc
, hstmt
));
3240 #ifdef DBDEBUG_CONSOLE
3241 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3242 result
.privilege
,result
.tableOwner
,result
.tableName
,
3243 result
.grantor
, result
.grantee
);
3246 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3248 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3252 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3253 !wxStrcmp(result
.privilege
,priv
))
3255 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3259 if (!wxStrcmp(result
.grantee
,curRole
) &&
3260 !wxStrcmp(result
.privilege
,priv
))
3262 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3266 retcode
= SQLFetch(hstmt
);
3269 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3272 } // wxDb::TablePrivileges
3275 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3279 if (Dbms() == dbmsACCESS
)
3281 TableName
+= tableName
;
3282 if (Dbms() == dbmsACCESS
)
3286 } // wxDb::SQLTableName()
3289 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3293 if (Dbms() == dbmsACCESS
)
3296 if (Dbms() == dbmsACCESS
)
3300 } // wxDb::SQLColumnName()
3303 /********** wxDb::SetSqlLogging() **********/
3304 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3306 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3307 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3309 if (state
== sqlLogON
)
3313 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3314 if (fpSqlLog
== NULL
)
3322 if (fclose(fpSqlLog
))
3328 sqlLogState
= state
;
3331 } // wxDb::SetSqlLogging()
3334 /********** wxDb::WriteSqlLog() **********/
3335 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3337 wxASSERT(logMsg
.Length());
3339 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3342 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3344 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3346 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3351 } // wxDb::WriteSqlLog()
3354 /********** wxDb::Dbms() **********/
3355 wxDBMS
wxDb::Dbms(void)
3357 * Be aware that not all database engines use the exact same syntax, and not
3358 * every ODBC compliant database is compliant to the same level of compliancy.
3359 * Some manufacturers support the minimum Level 1 compliancy, and others up
3360 * through Level 3. Others support subsets of features for levels above 1.
3362 * If you find an inconsistency between the wxDb class and a specific database
3363 * engine, and an identifier to this section, and special handle the database in
3364 * the area where behavior is non-conforming with the other databases.
3367 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3368 * ---------------------------------------------------
3371 * - Currently the only database supported by the class to support VIEWS
3374 * - Does not support the SQL_TIMESTAMP structure
3375 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3376 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3377 * is TRUE. The user must create ALL indexes from their program.
3378 * - Table names can only be 8 characters long
3379 * - Column names can only be 10 characters long
3382 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3383 * after every table name involved in the query/join if that tables matching record(s)
3385 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3387 * SYBASE (Enterprise)
3388 * - If a column is part of the Primary Key, the column cannot be NULL
3389 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3392 * - If a column is part of the Primary Key, the column cannot be NULL
3393 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3394 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3395 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3396 * column definition if it is not defined correctly, but it is experimental
3397 * - Does not support sub-queries in SQL statements
3400 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3401 * - Does not support sub-queries in SQL statements
3404 * - Primary keys must be declared as NOT NULL
3405 * - Table and index names must not be longer than 13 characters in length (technically
3406 * table names can be up to 18 characters, but the primary index is created using the
3407 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3412 * - Columns that are part of primary keys must be defined as being NOT NULL
3413 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3414 * column definition if it is not defined correctly, but it is experimental
3417 // Should only need to do this once for each new database connection
3418 // so return the value we already determined it to be to save time
3419 // and lots of string comparisons
3420 if (dbmsType
!= dbmsUNIDENTIFIED
)
3423 wxChar baseName
[25+1];
3424 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3427 // RGG 20001025 : add support for Interbase
3428 // GT : Integrated to base classes on 20001121
3429 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3430 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3432 // BJO 20000428 : add support for Virtuoso
3433 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3434 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3436 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3437 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3439 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3440 // connected through an OpenLink driver.
3441 // Is it also returned by Sybase Adapatitve server?
3442 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3443 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3445 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3446 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3447 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3449 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3452 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3453 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3454 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3455 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3456 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3457 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3460 if (!wxStricmp(dbInf
.dbmsName
,wxT("Pervasive")))
3461 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3464 if (!wxStricmp(baseName
,wxT("Informix")))
3465 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3468 if (!wxStricmp(baseName
,wxT("Oracle")))
3469 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3470 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3471 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3472 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3473 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3474 if (!wxStricmp(baseName
,wxT("Sybase")))
3475 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3478 if (!wxStricmp(baseName
,wxT("DBASE")))
3479 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3482 if (!wxStricmp(baseName
,wxT("DB2")))
3483 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3485 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3490 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3491 int dataType
, ULONG columnLength
,
3492 const wxString
&optionalParam
)
3494 wxASSERT(tableName
.Length());
3495 wxASSERT(columnName
.Length());
3496 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3497 dataType
!= DB_DATA_TYPE_VARCHAR
);
3499 // Must specify a columnLength if modifying a VARCHAR type column
3500 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3503 wxString dataTypeName
;
3505 wxString alterSlashModify
;
3509 case DB_DATA_TYPE_VARCHAR
:
3510 dataTypeName
= typeInfVarchar
.TypeName
;
3512 case DB_DATA_TYPE_INTEGER
:
3513 dataTypeName
= typeInfInteger
.TypeName
;
3515 case DB_DATA_TYPE_FLOAT
:
3516 dataTypeName
= typeInfFloat
.TypeName
;
3518 case DB_DATA_TYPE_DATE
:
3519 dataTypeName
= typeInfDate
.TypeName
;
3521 case DB_DATA_TYPE_BLOB
:
3522 dataTypeName
= typeInfBlob
.TypeName
;
3528 // Set the modify or alter syntax depending on the type of database connected to
3532 alterSlashModify
= "MODIFY";
3534 case dbmsMS_SQL_SERVER
:
3535 alterSlashModify
= "ALTER COLUMN";
3537 case dbmsUNIDENTIFIED
:
3539 case dbmsSYBASE_ASA
:
3540 case dbmsSYBASE_ASE
:
3546 alterSlashModify
= "MODIFY";
3550 // create the SQL statement
3551 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3552 columnName
.c_str(), dataTypeName
.c_str());
3554 // For varchars only, append the size of the column
3555 if (dataType
== DB_DATA_TYPE_VARCHAR
)
3558 s
.Printf(wxT("(%d)"), columnLength
);
3562 // for passing things like "NOT NULL"
3563 if (optionalParam
.Length())
3565 sqlStmt
+= wxT(" ");
3566 sqlStmt
+= optionalParam
;
3569 return ExecSql(sqlStmt
);
3571 } // wxDb::ModifyColumn()
3574 /********** wxDbGetConnection() **********/
3575 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3579 // Used to keep a pointer to a DB connection that matches the requested
3580 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3581 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3582 // rather than having to re-query the datasource to get all the values
3583 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3584 wxDb
*matchingDbConnection
= NULL
;
3586 // Scan the linked list searching for an available database connection
3587 // that's already been opened but is currently not in use.
3588 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3590 // The database connection must be for the same datasource
3591 // name and must currently not be in use.
3593 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3594 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3596 pList
->Free
= FALSE
;
3597 return(pList
->PtrDb
);
3600 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3601 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3602 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3603 matchingDbConnection
= pList
->PtrDb
;
3606 // No available connections. A new connection must be made and
3607 // appended to the end of the linked list.
3610 // Find the end of the list
3611 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3612 // Append a new list item
3613 pList
->PtrNext
= new wxDbList
;
3614 pList
->PtrNext
->PtrPrev
= pList
;
3615 pList
= pList
->PtrNext
;
3619 // Create the first node on the list
3620 pList
= PtrBegDbList
= new wxDbList
;
3624 // Initialize new node in the linked list
3626 pList
->Free
= FALSE
;
3627 pList
->Dsn
= pDbConfig
->GetDsn();
3628 pList
->Uid
= pDbConfig
->GetUserID();
3629 pList
->AuthStr
= pDbConfig
->GetPassword();
3631 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3633 bool opened
= FALSE
;
3635 if (!matchingDbConnection
)
3636 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3638 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3640 // Connect to the datasource
3643 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3644 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3645 return(pList
->PtrDb
);
3647 else // Unable to connect, destroy list item
3650 pList
->PtrPrev
->PtrNext
= 0;
3652 PtrBegDbList
= 0; // Empty list again
3653 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3654 pList
->PtrDb
->Close(); // Close the wxDb object
3655 delete pList
->PtrDb
; // Deletes the wxDb object
3656 delete pList
; // Deletes the linked list object
3660 } // wxDbGetConnection()
3663 /********** wxDbFreeConnection() **********/
3664 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3668 // Scan the linked list searching for the database connection
3669 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3671 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3672 return (pList
->Free
= TRUE
);
3675 // Never found the database object, return failure
3678 } // wxDbFreeConnection()
3681 /********** wxDbCloseConnections() **********/
3682 void WXDLLEXPORT
wxDbCloseConnections(void)
3684 wxDbList
*pList
, *pNext
;
3686 // Traverse the linked list closing database connections and freeing memory as I go.
3687 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3689 pNext
= pList
->PtrNext
; // Save the pointer to next
3690 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3691 pList
->PtrDb
->Close(); // Close the wxDb object
3692 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3693 delete pList
->PtrDb
; // Deletes the wxDb object
3694 delete pList
; // Deletes the linked list object
3697 // Mark the list as empty
3700 } // wxDbCloseConnections()
3703 /********** wxDbConnectionsInUse() **********/
3704 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3709 // Scan the linked list counting db connections that are currently in use
3710 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3712 if (pList
->Free
== FALSE
)
3718 } // wxDbConnectionsInUse()
3722 /********** wxDbLogExtendedErrorMsg() **********/
3723 // DEBUG ONLY function
3724 const wxChar WXDLLEXPORT
*wxDbLogExtendedErrorMsg(const wxChar
*userText
, wxDb
*pDb
,
3725 wxChar
*ErrFile
, int ErrLine
)
3727 static wxString msg
;
3732 if (ErrFile
|| ErrLine
)
3734 msg
+= wxT("File: ");
3736 msg
+= wxT(" Line: ");
3737 tStr
.Printf(wxT("%d"),ErrLine
);
3738 msg
+= tStr
.c_str();
3742 msg
.Append (wxT("\nODBC errors:\n"));
3745 // Display errors for this connection
3747 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3749 if (pDb
->errorList
[i
])
3751 msg
.Append(pDb
->errorList
[i
]);
3752 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3753 msg
.Append(wxT("\n"));
3754 // Clear the errmsg buffer so the next error will not
3755 // end up showing the previous error that have occurred
3756 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3761 wxLogDebug(msg
.c_str());
3764 } // wxDbLogExtendedErrorMsg()
3767 /********** wxDbSqlLog() **********/
3768 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3770 bool append
= FALSE
;
3773 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3775 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3780 SQLLOGstate
= state
;
3781 SQLLOGfn
= filename
;
3789 /********** wxDbCreateDataSource() **********/
3790 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3791 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3793 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3794 * Very rudimentary creation of an ODBC data source.
3796 * ODBC driver must be ODBC 3.0 compliant to use this function
3801 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3807 dsnLocation
= ODBC_ADD_SYS_DSN
;
3809 dsnLocation
= ODBC_ADD_DSN
;
3811 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3812 // so that is why I used it, as wxString does not deal well with
3813 // embedded nulls in strings
3814 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3816 // Replace the separator from above with the '\0' seperator needed
3817 // by the SQLConfigDataSource() function
3821 k
= setupStr
.Find((wxChar
)2,TRUE
);
3822 if (k
!= wxNOT_FOUND
)
3823 setupStr
[(UINT
)k
] = wxT('\0');
3825 while (k
!= wxNOT_FOUND
);
3827 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3828 driverName
, setupStr
.c_str());
3830 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3832 // check for errors caused by ConfigDSN based functions
3835 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3836 errMsg
[0] = wxT('\0');
3838 // This function is only supported in ODBC drivers v3.0 compliant and above
3839 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3842 #ifdef DBDEBUG_CONSOLE
3843 // When run in console mode, use standard out to display errors.
3844 cout
<< errMsg
<< endl
;
3845 cout
<< wxT("Press any key to continue...") << endl
;
3847 #endif // DBDEBUG_CONSOLE
3850 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3851 #endif // __WXDEBUG__
3857 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3858 // necessary to use this function, so this function is not supported
3860 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3863 #endif // __VISUALC__
3867 } // wxDbCreateDataSource()
3871 /********** wxDbGetDataSource() **********/
3872 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3873 SWORD DsDescMax
, UWORD direction
)
3875 * Dsn and DsDesc will contain the data source name and data source
3876 * description upon return
3881 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3882 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3887 } // wxDbGetDataSource()
3890 // Change this to 0 to remove use of all deprecated functions
3891 #if wxODBC_BACKWARD_COMPATABILITY
3892 /********************************************************************
3893 ********************************************************************
3895 * The following functions are all DEPRECATED and are included for
3896 * backward compatability reasons only
3898 ********************************************************************
3899 ********************************************************************/
3900 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3902 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3904 /***** DEPRECATED: use wxGetDataSource() *****/
3905 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3908 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3910 /***** DEPRECATED: use wxDbGetConnection() *****/
3911 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3913 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3915 /***** DEPRECATED: use wxDbFreeConnection() *****/
3916 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3918 return wxDbFreeConnection(pDb
);
3920 /***** DEPRECATED: use wxDbCloseConnections() *****/
3921 void WXDLLEXPORT
CloseDbConnections(void)
3923 wxDbCloseConnections();
3925 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3926 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3928 return wxDbConnectionsInUse();