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
, bool failOnDataTypeUnsupported
)
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
))
690 if (failOnDataTypeUnsupported
)
694 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
696 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
698 typeInfFloat
.FsqlType
= SQL_FLOAT
;
700 typeInfFloat
.FsqlType
= SQL_REAL
;
702 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
705 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
707 // If SQL_INTEGER is not supported, use the floating point
708 // data type to store integers as well as floats
709 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
711 if (failOnDataTypeUnsupported
)
715 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
718 typeInfInteger
.FsqlType
= SQL_INTEGER
;
721 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
723 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
725 if (!getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
727 if (failOnDataTypeUnsupported
)
731 typeInfDate
.FsqlType
= SQL_TIME
;
734 typeInfDate
.FsqlType
= SQL_DATE
;
737 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
740 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
742 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
744 if (failOnDataTypeUnsupported
)
748 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
751 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
753 //typeInfBlob.TypeName = "BLOB";
755 #ifdef DBDEBUG_CONSOLE
756 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
757 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
758 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
759 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
760 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
764 // Completed Successfully
770 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
772 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
773 dbConnectInf
->GetPassword());
777 bool wxDb::Open(wxDb
*copyDb
)
779 dsn
= copyDb
->GetDatasourceName();
780 uid
= copyDb
->GetUsername();
781 authStr
= copyDb
->GetPassword();
785 if (!FwdOnlyCursors())
787 // Specify that the ODBC cursor library be used, if needed. This must be
788 // specified before the connection is made.
789 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
791 #ifdef DBDEBUG_CONSOLE
792 if (retcode
== SQL_SUCCESS
)
793 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
795 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
799 // Connect to the data source
800 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
801 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
802 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
804 if (retcode
== SQL_ERROR
)
805 return(DispAllErrors(henv
, hdbc
));
808 If using Intersolv branded ODBC drivers, this is the place where you would substitute
809 your branded driver license information
811 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
812 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
815 // Mark database as open
818 // Allocate a statement handle for the database connection
819 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
820 return(DispAllErrors(henv
, hdbc
));
822 // Set Connection Options
823 if (!setConnectionOptions())
826 // Instead of Querying the data source for info about itself, it can just be copied
827 // from the wxDb instance that was passed in (copyDb).
828 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
829 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
830 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
831 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
832 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
833 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
834 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
835 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
836 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
837 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
838 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
839 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
840 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
841 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
842 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
843 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
844 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
845 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
846 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
847 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
848 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
849 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
850 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
851 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
852 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
853 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
854 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
855 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
856 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
857 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
858 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
860 // VARCHAR = Variable length character string
861 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
862 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
863 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
864 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
865 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
868 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
869 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
870 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
871 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
872 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
875 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
876 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
877 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
878 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
879 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
882 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
883 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
884 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
885 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
886 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
889 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
890 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
891 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
892 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
893 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
895 #ifdef DBDEBUG_CONSOLE
896 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
897 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
898 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
899 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
900 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
904 // Completed Successfully
909 /********** wxDb::setConnectionOptions() **********/
910 bool wxDb::setConnectionOptions(void)
912 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
917 // I need to get the DBMS name here, because some of the connection options
918 // are database specific and need to call the Dbms() function.
919 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
920 return(DispAllErrors(henv
, hdbc
));
922 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
923 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
924 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
926 // By default, MS Sql Server closes cursors on commit and rollback. The following
927 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
928 // after a transaction. This is a driver specific option and is not part of the
929 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
930 // The database settings don't have any effect one way or the other.
931 if (Dbms() == dbmsMS_SQL_SERVER
)
933 const long SQL_PRESERVE_CURSORS
= 1204L;
934 const long SQL_PC_ON
= 1L;
935 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
938 // Display the connection options to verify them
939 #ifdef DBDEBUG_CONSOLE
941 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
943 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
944 return(DispAllErrors(henv
, hdbc
));
945 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
947 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
948 return(DispAllErrors(henv
, hdbc
));
949 cout
<< wxT("ODBC CURSORS: ");
952 case(SQL_CUR_USE_IF_NEEDED
):
953 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
955 case(SQL_CUR_USE_ODBC
):
956 cout
<< wxT("SQL_CUR_USE_ODBC");
958 case(SQL_CUR_USE_DRIVER
):
959 cout
<< wxT("SQL_CUR_USE_DRIVER");
964 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
965 return(DispAllErrors(henv
, hdbc
));
966 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
971 // Completed Successfully
974 } // wxDb::setConnectionOptions()
977 /********** wxDb::getDbInfo() **********/
978 bool wxDb::getDbInfo(void)
983 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
984 return(DispAllErrors(henv
, hdbc
));
986 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
987 return(DispAllErrors(henv
, hdbc
));
989 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
990 return(DispAllErrors(henv
, hdbc
));
993 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
994 // causing database connectivity to fail in some cases.
995 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
997 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
998 return(DispAllErrors(henv
, hdbc
));
1000 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1001 return(DispAllErrors(henv
, hdbc
));
1003 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1007 return(DispAllErrors(henv
, hdbc
));
1009 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1010 return(DispAllErrors(henv
, hdbc
));
1012 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1013 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1014 return(DispAllErrors(henv
, hdbc
));
1016 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1017 return(DispAllErrors(henv
, hdbc
));
1019 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1020 return(DispAllErrors(henv
, hdbc
));
1022 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1023 // return(DispAllErrors(henv, hdbc));
1025 // Not all drivers support this call - Nick Gorham(unixODBC)
1026 dbInf
.cliConfLvl
= 0;
1029 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1030 return(DispAllErrors(henv
, hdbc
));
1032 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1033 return(DispAllErrors(henv
, hdbc
));
1035 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1036 return(DispAllErrors(henv
, hdbc
));
1038 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1039 return(DispAllErrors(henv
, hdbc
));
1041 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1042 return(DispAllErrors(henv
, hdbc
));
1044 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1045 return(DispAllErrors(henv
, hdbc
));
1047 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1048 return(DispAllErrors(henv
, hdbc
));
1050 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1051 return(DispAllErrors(henv
, hdbc
));
1053 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1054 return(DispAllErrors(henv
, hdbc
));
1056 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1057 return(DispAllErrors(henv
, hdbc
));
1059 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1060 return(DispAllErrors(henv
, hdbc
));
1062 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1063 return(DispAllErrors(henv
, hdbc
));
1065 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1066 return(DispAllErrors(henv
, hdbc
));
1068 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1069 return(DispAllErrors(henv
, hdbc
));
1071 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1072 return(DispAllErrors(henv
, hdbc
));
1074 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1075 return(DispAllErrors(henv
, hdbc
));
1077 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1078 return(DispAllErrors(henv
, hdbc
));
1080 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1081 return(DispAllErrors(henv
, hdbc
));
1083 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1084 return(DispAllErrors(henv
, hdbc
));
1086 #ifdef DBDEBUG_CONSOLE
1087 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1088 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1089 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1090 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1092 cout
<< wxT("API Conf. Level: ");
1093 switch(dbInf
.apiConfLvl
)
1095 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1096 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1097 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1101 cout
<< wxT("SAG CLI Conf. Level: ");
1102 switch(dbInf
.cliConfLvl
)
1104 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1105 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1109 cout
<< wxT("SQL Conf. Level: ");
1110 switch(dbInf
.sqlConfLvl
)
1112 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1113 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1114 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1118 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1119 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1120 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1121 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1122 cout
<< wxT("Cursor COMMIT Behavior: ");
1123 switch(dbInf
.cursorCommitBehavior
)
1125 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1126 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1127 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1131 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1132 switch(dbInf
.cursorRollbackBehavior
)
1134 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1135 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1136 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1140 cout
<< wxT("Support NOT NULL clause: ");
1141 switch(dbInf
.supportNotNullClause
)
1143 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1144 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1148 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1149 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1151 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1154 cout
<< wxT("Default Transaction Isolation: ";
1155 switch(dbInf
.txnIsolation
)
1157 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1158 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1159 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1160 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1162 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1167 cout
<< wxT("Transaction Isolation Options: ");
1168 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1169 cout
<< wxT("Read Uncommitted, ");
1170 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1171 cout
<< wxT("Read Committed, ");
1172 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1173 cout
<< wxT("Repeatable Read, ");
1174 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1175 cout
<< wxT("Serializable, ");
1177 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1178 cout
<< wxT("Versioning");
1182 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1183 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1184 cout
<< wxT("Next, ");
1185 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1186 cout
<< wxT("Prev, ");
1187 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1188 cout
<< wxT("First, ");
1189 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1190 cout
<< wxT("Last, ");
1191 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1192 cout
<< wxT("Absolute, ");
1193 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1194 cout
<< wxT("Relative, ");
1196 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1197 cout
<< wxT("Resume, ");
1199 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1200 cout
<< wxT("Bookmark");
1203 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1204 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1205 cout
<< wxT("No Change, ");
1206 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1207 cout
<< wxT("Exclusive, ");
1208 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1209 cout
<< wxT("UnLock");
1212 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1213 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1214 cout
<< wxT("Position, ");
1215 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1216 cout
<< wxT("Refresh, ");
1217 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1218 cout
<< wxT("Upd, "));
1219 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1220 cout
<< wxT("Del, ");
1221 if (dbInf
.posOperations
& SQL_POS_ADD
)
1225 cout
<< wxT("Positioned Statements Supported: ");
1226 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1227 cout
<< wxT("Pos delete, ");
1228 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1229 cout
<< wxT("Pos update, ");
1230 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1231 cout
<< wxT("Select for update");
1234 cout
<< wxT("Scroll Concurrency: ");
1235 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1236 cout
<< wxT("Read Only, ");
1237 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1238 cout
<< wxT("Lock, ");
1239 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1240 cout
<< wxT("Opt. Rowver, ");
1241 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1242 cout
<< wxT("Opt. Values");
1245 cout
<< wxT("Scroll Options: ");
1246 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1247 cout
<< wxT("Fwd Only, ");
1248 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1249 cout
<< wxT("Static, ");
1250 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1251 cout
<< wxT("Keyset Driven, ");
1252 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1253 cout
<< wxT("Dynamic, ");
1254 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1255 cout
<< wxT("Mixed");
1258 cout
<< wxT("Static Sensitivity: ");
1259 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1260 cout
<< wxT("Additions, ");
1261 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1262 cout
<< wxT("Deletions, ");
1263 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1264 cout
<< wxT("Updates");
1267 cout
<< wxT("Transaction Capable?: ");
1268 switch(dbInf
.txnCapable
)
1270 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1271 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1272 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1273 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1274 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1281 // Completed Successfully
1284 } // wxDb::getDbInfo()
1287 /********** wxDb::getDataTypeInfo() **********/
1288 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1291 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1292 * the data type inf. is gathered for.
1294 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1299 // Get information about the data type specified
1300 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1301 return(DispAllErrors(henv
, hdbc
, hstmt
));
1304 retcode
= SQLFetch(hstmt
);
1305 if (retcode
!= SQL_SUCCESS
)
1307 #ifdef DBDEBUG_CONSOLE
1308 if (retcode
== SQL_NO_DATA_FOUND
)
1309 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1311 DispAllErrors(henv
, hdbc
, hstmt
);
1312 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1316 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1318 // Obtain columns from the record
1319 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1320 return(DispAllErrors(henv
, hdbc
, hstmt
));
1322 structSQLTypeInfo
.TypeName
= typeName
;
1324 // BJO 20000503: no more needed with new GetColumns...
1327 if (Dbms() == dbmsMY_SQL
)
1329 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1330 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1331 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1332 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1333 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1334 structSQLTypeInfo
.TypeName
= wxT("int");
1335 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1336 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1337 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1338 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1339 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1340 structSQLTypeInfo
.TypeName
= wxT("char");
1343 // BJO 20000427 : OpenLink driver
1344 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1345 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1347 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1348 structSQLTypeInfo
.TypeName
= wxT("real");
1352 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1353 return(DispAllErrors(henv
, hdbc
, hstmt
));
1354 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1355 return(DispAllErrors(henv
, hdbc
, hstmt
));
1356 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1357 // return(DispAllErrors(henv, hdbc, hstmt));
1359 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1360 return(DispAllErrors(henv
, hdbc
, hstmt
));
1362 if (structSQLTypeInfo
.MaximumScale
< 0)
1363 structSQLTypeInfo
.MaximumScale
= 0;
1365 // Close the statement handle which closes open cursors
1366 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1367 return(DispAllErrors(henv
, hdbc
, hstmt
));
1369 // Completed Successfully
1372 } // wxDb::getDataTypeInfo()
1375 /********** wxDb::Close() **********/
1376 void wxDb::Close(void)
1378 // Close the Sql Log file
1385 // Free statement handle
1388 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1389 DispAllErrors(henv
, hdbc
);
1392 // Disconnect from the datasource
1393 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1394 DispAllErrors(henv
, hdbc
);
1396 // Free the connection to the datasource
1397 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1398 DispAllErrors(henv
, hdbc
);
1400 // There should be zero Ctable objects still connected to this db object
1401 wxASSERT(nTables
== 0);
1406 pNode
= TablesInUse
.First();
1410 tiu
= (wxTablesInUse
*)pNode
->Data();
1411 if (tiu
->pDb
== this)
1413 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1414 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1415 wxLogDebug (s
.c_str(),s2
.c_str());
1417 pNode
= pNode
->Next();
1421 // Copy the error messages to a global variable
1423 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1424 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1426 dbmsType
= dbmsUNIDENTIFIED
;
1432 /********** wxDb::CommitTrans() **********/
1433 bool wxDb::CommitTrans(void)
1437 // Commit the transaction
1438 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1439 return(DispAllErrors(henv
, hdbc
));
1442 // Completed successfully
1445 } // wxDb::CommitTrans()
1448 /********** wxDb::RollbackTrans() **********/
1449 bool wxDb::RollbackTrans(void)
1451 // Rollback the transaction
1452 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1453 return(DispAllErrors(henv
, hdbc
));
1455 // Completed successfully
1458 } // wxDb::RollbackTrans()
1461 /********** wxDb::DispAllErrors() **********/
1462 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1464 * This function is called internally whenever an error condition prevents the user's
1465 * request from being executed. This function will query the datasource as to the
1466 * actual error(s) that just occured on the previous request of the datasource.
1468 * The function will retrieve each error condition from the datasource and
1469 * Printf the codes/text values into a string which it then logs via logError().
1470 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1471 * window and program execution will be paused until the user presses a key.
1473 * This function always returns a FALSE, so that functions which call this function
1474 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1475 * of the users request, so that the calling code can then process the error msg log
1478 wxString odbcErrMsg
;
1480 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1482 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1483 logError(odbcErrMsg
, sqlState
);
1486 #ifdef DBDEBUG_CONSOLE
1487 // When run in console mode, use standard out to display errors.
1488 cout
<< odbcErrMsg
.c_str() << endl
;
1489 cout
<< wxT("Press any key to continue...") << endl
;
1494 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1499 return(FALSE
); // This function always returns FALSE.
1501 } // wxDb::DispAllErrors()
1504 /********** wxDb::GetNextError() **********/
1505 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1507 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1512 } // wxDb::GetNextError()
1515 /********** wxDb::DispNextError() **********/
1516 void wxDb::DispNextError(void)
1518 wxString odbcErrMsg
;
1520 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1521 logError(odbcErrMsg
, sqlState
);
1526 #ifdef DBDEBUG_CONSOLE
1527 // When run in console mode, use standard out to display errors.
1528 cout
<< odbcErrMsg
.c_str() << endl
;
1529 cout
<< wxT("Press any key to continue...") << endl
;
1534 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1535 #endif // __WXDEBUG__
1537 } // wxDb::DispNextError()
1540 /********** wxDb::logError() **********/
1541 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1543 wxASSERT(errMsg
.Length());
1545 static int pLast
= -1;
1548 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1551 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1552 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1556 wxStrcpy(errorList
[pLast
], errMsg
);
1558 if (SQLState
.Length())
1559 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1560 DB_STATUS
= dbStatus
;
1562 // Add the errmsg to the sql log
1563 WriteSqlLog(errMsg
);
1565 } // wxDb::logError()
1568 /**********wxDb::TranslateSqlState() **********/
1569 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1571 if (!wxStrcmp(SQLState
, wxT("01000")))
1572 return(DB_ERR_GENERAL_WARNING
);
1573 if (!wxStrcmp(SQLState
, wxT("01002")))
1574 return(DB_ERR_DISCONNECT_ERROR
);
1575 if (!wxStrcmp(SQLState
, wxT("01004")))
1576 return(DB_ERR_DATA_TRUNCATED
);
1577 if (!wxStrcmp(SQLState
, wxT("01006")))
1578 return(DB_ERR_PRIV_NOT_REVOKED
);
1579 if (!wxStrcmp(SQLState
, wxT("01S00")))
1580 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1581 if (!wxStrcmp(SQLState
, wxT("01S01")))
1582 return(DB_ERR_ERROR_IN_ROW
);
1583 if (!wxStrcmp(SQLState
, wxT("01S02")))
1584 return(DB_ERR_OPTION_VALUE_CHANGED
);
1585 if (!wxStrcmp(SQLState
, wxT("01S03")))
1586 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1587 if (!wxStrcmp(SQLState
, wxT("01S04")))
1588 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1589 if (!wxStrcmp(SQLState
, wxT("07001")))
1590 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1591 if (!wxStrcmp(SQLState
, wxT("07006")))
1592 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1593 if (!wxStrcmp(SQLState
, wxT("08001")))
1594 return(DB_ERR_UNABLE_TO_CONNECT
);
1595 if (!wxStrcmp(SQLState
, wxT("08002")))
1596 return(DB_ERR_CONNECTION_IN_USE
);
1597 if (!wxStrcmp(SQLState
, wxT("08003")))
1598 return(DB_ERR_CONNECTION_NOT_OPEN
);
1599 if (!wxStrcmp(SQLState
, wxT("08004")))
1600 return(DB_ERR_REJECTED_CONNECTION
);
1601 if (!wxStrcmp(SQLState
, wxT("08007")))
1602 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1603 if (!wxStrcmp(SQLState
, wxT("08S01")))
1604 return(DB_ERR_COMM_LINK_FAILURE
);
1605 if (!wxStrcmp(SQLState
, wxT("21S01")))
1606 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1607 if (!wxStrcmp(SQLState
, wxT("21S02")))
1608 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1609 if (!wxStrcmp(SQLState
, wxT("22001")))
1610 return(DB_ERR_STRING_RIGHT_TRUNC
);
1611 if (!wxStrcmp(SQLState
, wxT("22003")))
1612 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1613 if (!wxStrcmp(SQLState
, wxT("22005")))
1614 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1615 if (!wxStrcmp(SQLState
, wxT("22008")))
1616 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1617 if (!wxStrcmp(SQLState
, wxT("22012")))
1618 return(DB_ERR_DIVIDE_BY_ZERO
);
1619 if (!wxStrcmp(SQLState
, wxT("22026")))
1620 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1621 if (!wxStrcmp(SQLState
, wxT("23000")))
1622 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1623 if (!wxStrcmp(SQLState
, wxT("24000")))
1624 return(DB_ERR_INVALID_CURSOR_STATE
);
1625 if (!wxStrcmp(SQLState
, wxT("25000")))
1626 return(DB_ERR_INVALID_TRANS_STATE
);
1627 if (!wxStrcmp(SQLState
, wxT("28000")))
1628 return(DB_ERR_INVALID_AUTH_SPEC
);
1629 if (!wxStrcmp(SQLState
, wxT("34000")))
1630 return(DB_ERR_INVALID_CURSOR_NAME
);
1631 if (!wxStrcmp(SQLState
, wxT("37000")))
1632 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1633 if (!wxStrcmp(SQLState
, wxT("3C000")))
1634 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1635 if (!wxStrcmp(SQLState
, wxT("40001")))
1636 return(DB_ERR_SERIALIZATION_FAILURE
);
1637 if (!wxStrcmp(SQLState
, wxT("42000")))
1638 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1639 if (!wxStrcmp(SQLState
, wxT("70100")))
1640 return(DB_ERR_OPERATION_ABORTED
);
1641 if (!wxStrcmp(SQLState
, wxT("IM001")))
1642 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1643 if (!wxStrcmp(SQLState
, wxT("IM002")))
1644 return(DB_ERR_NO_DATA_SOURCE
);
1645 if (!wxStrcmp(SQLState
, wxT("IM003")))
1646 return(DB_ERR_DRIVER_LOAD_ERROR
);
1647 if (!wxStrcmp(SQLState
, wxT("IM004")))
1648 return(DB_ERR_SQLALLOCENV_FAILED
);
1649 if (!wxStrcmp(SQLState
, wxT("IM005")))
1650 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1651 if (!wxStrcmp(SQLState
, wxT("IM006")))
1652 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1653 if (!wxStrcmp(SQLState
, wxT("IM007")))
1654 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1655 if (!wxStrcmp(SQLState
, wxT("IM008")))
1656 return(DB_ERR_DIALOG_FAILED
);
1657 if (!wxStrcmp(SQLState
, wxT("IM009")))
1658 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1659 if (!wxStrcmp(SQLState
, wxT("IM010")))
1660 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1661 if (!wxStrcmp(SQLState
, wxT("IM011")))
1662 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1663 if (!wxStrcmp(SQLState
, wxT("IM012")))
1664 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1665 if (!wxStrcmp(SQLState
, wxT("IM013")))
1666 return(DB_ERR_TRACE_FILE_ERROR
);
1667 if (!wxStrcmp(SQLState
, wxT("S0001")))
1668 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1669 if (!wxStrcmp(SQLState
, wxT("S0002")))
1670 return(DB_ERR_TABLE_NOT_FOUND
);
1671 if (!wxStrcmp(SQLState
, wxT("S0011")))
1672 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1673 if (!wxStrcmp(SQLState
, wxT("S0012")))
1674 return(DB_ERR_INDEX_NOT_FOUND
);
1675 if (!wxStrcmp(SQLState
, wxT("S0021")))
1676 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1677 if (!wxStrcmp(SQLState
, wxT("S0022")))
1678 return(DB_ERR_COLUMN_NOT_FOUND
);
1679 if (!wxStrcmp(SQLState
, wxT("S0023")))
1680 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1681 if (!wxStrcmp(SQLState
, wxT("S1000")))
1682 return(DB_ERR_GENERAL_ERROR
);
1683 if (!wxStrcmp(SQLState
, wxT("S1001")))
1684 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1685 if (!wxStrcmp(SQLState
, wxT("S1002")))
1686 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1687 if (!wxStrcmp(SQLState
, wxT("S1003")))
1688 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1689 if (!wxStrcmp(SQLState
, wxT("S1004")))
1690 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1691 if (!wxStrcmp(SQLState
, wxT("S1008")))
1692 return(DB_ERR_OPERATION_CANCELLED
);
1693 if (!wxStrcmp(SQLState
, wxT("S1009")))
1694 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1695 if (!wxStrcmp(SQLState
, wxT("S1010")))
1696 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1697 if (!wxStrcmp(SQLState
, wxT("S1011")))
1698 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1699 if (!wxStrcmp(SQLState
, wxT("S1012")))
1700 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1701 if (!wxStrcmp(SQLState
, wxT("S1015")))
1702 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1703 if (!wxStrcmp(SQLState
, wxT("S1090")))
1704 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1705 if (!wxStrcmp(SQLState
, wxT("S1091")))
1706 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1707 if (!wxStrcmp(SQLState
, wxT("S1092")))
1708 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1709 if (!wxStrcmp(SQLState
, wxT("S1093")))
1710 return(DB_ERR_INVALID_PARAM_NO
);
1711 if (!wxStrcmp(SQLState
, wxT("S1094")))
1712 return(DB_ERR_INVALID_SCALE_VALUE
);
1713 if (!wxStrcmp(SQLState
, wxT("S1095")))
1714 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1715 if (!wxStrcmp(SQLState
, wxT("S1096")))
1716 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1717 if (!wxStrcmp(SQLState
, wxT("S1097")))
1718 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1719 if (!wxStrcmp(SQLState
, wxT("S1098")))
1720 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1721 if (!wxStrcmp(SQLState
, wxT("S1099")))
1722 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1723 if (!wxStrcmp(SQLState
, wxT("S1100")))
1724 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1725 if (!wxStrcmp(SQLState
, wxT("S1101")))
1726 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1727 if (!wxStrcmp(SQLState
, wxT("S1103")))
1728 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1729 if (!wxStrcmp(SQLState
, wxT("S1104")))
1730 return(DB_ERR_INVALID_PRECISION_VALUE
);
1731 if (!wxStrcmp(SQLState
, wxT("S1105")))
1732 return(DB_ERR_INVALID_PARAM_TYPE
);
1733 if (!wxStrcmp(SQLState
, wxT("S1106")))
1734 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1735 if (!wxStrcmp(SQLState
, wxT("S1107")))
1736 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1737 if (!wxStrcmp(SQLState
, wxT("S1108")))
1738 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1739 if (!wxStrcmp(SQLState
, wxT("S1109")))
1740 return(DB_ERR_INVALID_CURSOR_POSITION
);
1741 if (!wxStrcmp(SQLState
, wxT("S1110")))
1742 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1743 if (!wxStrcmp(SQLState
, wxT("S1111")))
1744 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1745 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1746 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1747 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1748 return(DB_ERR_TIMEOUT_EXPIRED
);
1753 } // wxDb::TranslateSqlState()
1756 /********** wxDb::Grant() **********/
1757 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1761 // Build the grant statement
1762 sqlStmt
= wxT("GRANT ");
1763 if (privileges
== DB_GRANT_ALL
)
1764 sqlStmt
+= wxT("ALL");
1768 if (privileges
& DB_GRANT_SELECT
)
1770 sqlStmt
+= wxT("SELECT");
1773 if (privileges
& DB_GRANT_INSERT
)
1776 sqlStmt
+= wxT(", ");
1777 sqlStmt
+= wxT("INSERT");
1779 if (privileges
& DB_GRANT_UPDATE
)
1782 sqlStmt
+= wxT(", ");
1783 sqlStmt
+= wxT("UPDATE");
1785 if (privileges
& DB_GRANT_DELETE
)
1788 sqlStmt
+= wxT(", ");
1789 sqlStmt
+= wxT("DELETE");
1793 sqlStmt
+= wxT(" ON ");
1794 sqlStmt
+= SQLTableName(tableName
);
1795 sqlStmt
+= wxT(" TO ");
1796 sqlStmt
+= userList
;
1798 #ifdef DBDEBUG_CONSOLE
1799 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1802 WriteSqlLog(sqlStmt
);
1804 return(ExecSql(sqlStmt
));
1809 /********** wxDb::CreateView() **********/
1810 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1811 const wxString
&pSqlStmt
, bool attemptDrop
)
1815 // Drop the view first
1816 if (attemptDrop
&& !DropView(viewName
))
1819 // Build the create view statement
1820 sqlStmt
= wxT("CREATE VIEW ");
1821 sqlStmt
+= viewName
;
1823 if (colList
.Length())
1825 sqlStmt
+= wxT(" (");
1827 sqlStmt
+= wxT(")");
1830 sqlStmt
+= wxT(" AS ");
1831 sqlStmt
+= pSqlStmt
;
1833 WriteSqlLog(sqlStmt
);
1835 #ifdef DBDEBUG_CONSOLE
1836 cout
<< sqlStmt
.c_str() << endl
;
1839 return(ExecSql(sqlStmt
));
1841 } // wxDb::CreateView()
1844 /********** wxDb::DropView() **********/
1845 bool wxDb::DropView(const wxString
&viewName
)
1848 * NOTE: This function returns TRUE if the View does not exist, but
1849 * only for identified databases. Code will need to be added
1850 * below for any other databases when those databases are defined
1851 * to handle this situation consistently
1855 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1857 WriteSqlLog(sqlStmt
);
1859 #ifdef DBDEBUG_CONSOLE
1860 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1863 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1865 // Check for "Base table not found" error and ignore
1866 GetNextError(henv
, hdbc
, hstmt
);
1867 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1869 // Check for product specific error codes
1870 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1873 DispAllErrors(henv
, hdbc
, hstmt
);
1880 // Commit the transaction
1886 } // wxDb::DropView()
1889 /********** wxDb::ExecSql() **********/
1890 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1894 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1896 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
1897 if (retcode
== SQL_SUCCESS
||
1898 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
1904 DispAllErrors(henv
, hdbc
, hstmt
);
1908 } // wxDb::ExecSql()
1911 /********** wxDb::GetNext() **********/
1912 bool wxDb::GetNext(void)
1914 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1918 DispAllErrors(henv
, hdbc
, hstmt
);
1922 } // wxDb::GetNext()
1925 /********** wxDb::GetData() **********/
1926 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1929 wxASSERT(cbReturned
);
1931 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1935 DispAllErrors(henv
, hdbc
, hstmt
);
1939 } // wxDb::GetData()
1942 /********** wxDb::GetKeyFields() **********/
1943 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1945 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1946 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1948 // SQLSMALLINT iKeySeq;
1949 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1950 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1956 * -----------------------------------------------------------------------
1957 * -- 19991224 : mj10777 : Create ------
1958 * -- : Three things are done and stored here : ------
1959 * -- : 1) which Column(s) is/are Primary Key(s) ------
1960 * -- : 2) which tables use this Key as a Foreign Key ------
1961 * -- : 3) which columns are Foreign Key and the name ------
1962 * -- : of the Table where the Key is the Primary Key -----
1963 * -- : Called from GetColumns(const wxString &tableName, ------
1964 * -- int *numCols,const wxChar *userID ) ------
1965 * -----------------------------------------------------------------------
1968 /*---------------------------------------------------------------------*/
1969 /* Get the names of the columns in the primary key. */
1970 /*---------------------------------------------------------------------*/
1971 retcode
= SQLPrimaryKeys(hstmt
,
1972 NULL
, 0, /* Catalog name */
1973 NULL
, 0, /* Schema name */
1974 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1976 /*---------------------------------------------------------------------*/
1977 /* Fetch and display the result set. This will be a list of the */
1978 /* columns in the primary key of the tableName table. */
1979 /*---------------------------------------------------------------------*/
1980 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1982 retcode
= SQLFetch(hstmt
);
1983 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1985 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1986 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1988 for (i
=0;i
<noCols
;i
++) // Find the Column name
1989 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1990 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1993 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1995 /*---------------------------------------------------------------------*/
1996 /* Get all the foreign keys that refer to tableName primary key. */
1997 /*---------------------------------------------------------------------*/
1998 retcode
= SQLForeignKeys(hstmt
,
1999 NULL
, 0, /* Primary catalog */
2000 NULL
, 0, /* Primary schema */
2001 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2002 NULL
, 0, /* Foreign catalog */
2003 NULL
, 0, /* Foreign schema */
2004 NULL
, 0); /* Foreign table */
2006 /*---------------------------------------------------------------------*/
2007 /* Fetch and display the result set. This will be all of the foreign */
2008 /* keys in other tables that refer to the tableName primary key. */
2009 /*---------------------------------------------------------------------*/
2012 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2014 retcode
= SQLFetch(hstmt
);
2015 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2017 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2018 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2019 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2020 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2021 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2022 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2026 tempStr
.Trim(); // Get rid of any unneeded blanks
2027 if (!tempStr
.IsEmpty())
2029 for (i
=0; i
<noCols
; i
++)
2030 { // Find the Column name
2031 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2032 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2036 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2038 /*---------------------------------------------------------------------*/
2039 /* Get all the foreign keys in the tablename table. */
2040 /*---------------------------------------------------------------------*/
2041 retcode
= SQLForeignKeys(hstmt
,
2042 NULL
, 0, /* Primary catalog */
2043 NULL
, 0, /* Primary schema */
2044 NULL
, 0, /* Primary table */
2045 NULL
, 0, /* Foreign catalog */
2046 NULL
, 0, /* Foreign schema */
2047 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2049 /*---------------------------------------------------------------------*/
2050 /* Fetch and display the result set. This will be all of the */
2051 /* primary keys in other tables that are referred to by foreign */
2052 /* keys in the tableName table. */
2053 /*---------------------------------------------------------------------*/
2055 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2057 retcode
= SQLFetch(hstmt
);
2058 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2060 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2061 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2062 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2064 for (i
=0; i
<noCols
; i
++) // Find the Column name
2066 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2068 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2069 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2074 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2078 } // wxDb::GetKeyFields()
2082 /********** wxDb::GetColumns() **********/
2083 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2085 * 1) The last array element of the tableName[] argument must be zero (null).
2086 * This is how the end of the array is detected.
2087 * 2) This function returns an array of wxDbColInf structures. If no columns
2088 * were found, or an error occured, this pointer will be zero (null). THE
2089 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2090 * IS FINISHED WITH IT. i.e.
2092 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2095 * // Use the column inf
2097 * // Destroy the memory
2101 * userID is evaluated in the following manner:
2102 * userID == NULL ... UserID is ignored
2103 * userID == "" ... UserID set equal to 'this->uid'
2104 * userID != "" ... UserID set equal to 'userID'
2106 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2107 * by this function. This function should use its own wxDb instance
2108 * to avoid undesired unbinding of columns.
2113 wxDbColInf
*colInf
= 0;
2121 convertUserID(userID
,UserID
);
2123 // Pass 1 - Determine how many columns there are.
2124 // Pass 2 - Allocate the wxDbColInf array and fill in
2125 // the array with the column information.
2127 for (pass
= 1; pass
<= 2; pass
++)
2131 if (noCols
== 0) // Probably a bogus table name(s)
2133 // Allocate n wxDbColInf objects to hold the column information
2134 colInf
= new wxDbColInf
[noCols
+1];
2137 // Mark the end of the array
2138 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2139 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2140 colInf
[noCols
].sqlDataType
= 0;
2142 // Loop through each table name
2144 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2146 TableName
= tableName
[tbl
];
2147 // Oracle and Interbase table names are uppercase only, so force
2148 // the name to uppercase just in case programmer forgot to do this
2149 if ((Dbms() == dbmsORACLE
) ||
2150 (Dbms() == dbmsINTERBASE
))
2151 TableName
= TableName
.Upper();
2153 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2155 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2156 // use the call below that leaves out the user name
2157 if (!UserID
.IsEmpty() &&
2158 Dbms() != dbmsMY_SQL
&&
2159 Dbms() != dbmsACCESS
&&
2160 Dbms() != dbmsMS_SQL_SERVER
)
2162 retcode
= SQLColumns(hstmt
,
2163 NULL
, 0, // All qualifiers
2164 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2165 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2166 NULL
, 0); // All columns
2170 retcode
= SQLColumns(hstmt
,
2171 NULL
, 0, // All qualifiers
2173 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2174 NULL
, 0); // All columns
2176 if (retcode
!= SQL_SUCCESS
)
2177 { // Error occured, abort
2178 DispAllErrors(henv
, hdbc
, hstmt
);
2181 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2185 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2187 if (pass
== 1) // First pass, just add up the number of columns
2189 else // Pass 2; Fill in the array of structures
2191 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2193 // NOTE: Only the ODBC 1.x fields are retrieved
2194 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2195 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2196 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2197 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2198 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2199 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2200 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2201 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2202 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2203 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2204 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2205 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2207 // Determine the wxDb data type that is used to represent the native data type of this data source
2208 colInf
[colNo
].dbDataType
= 0;
2209 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2212 // IODBC does not return a correct columnSize, so we set
2213 // columnSize = bufferLength if no column size was returned
2214 // IODBC returns the columnSize in bufferLength.. (bug)
2215 if (colInf
[colNo
].columnSize
< 1)
2217 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2220 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2222 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2223 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2224 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2225 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2226 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2227 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2228 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2229 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2234 if (retcode
!= SQL_NO_DATA_FOUND
)
2235 { // Error occured, abort
2236 DispAllErrors(henv
, hdbc
, hstmt
);
2239 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2245 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2248 } // wxDb::GetColumns()
2251 /********** wxDb::GetColumns() **********/
2253 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2255 // Same as the above GetColumns() function except this one gets columns
2256 // only for a single table, and if 'numCols' is not NULL, the number of
2257 // columns stored in the returned wxDbColInf is set in '*numCols'
2259 // userID is evaluated in the following manner:
2260 // userID == NULL ... UserID is ignored
2261 // userID == "" ... UserID set equal to 'this->uid'
2262 // userID != "" ... UserID set equal to 'userID'
2264 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2265 // by this function. This function should use its own wxDb instance
2266 // to avoid undesired unbinding of columns.
2271 wxDbColInf
*colInf
= 0;
2279 convertUserID(userID
,UserID
);
2281 // Pass 1 - Determine how many columns there are.
2282 // Pass 2 - Allocate the wxDbColInf array and fill in
2283 // the array with the column information.
2285 for (pass
= 1; pass
<= 2; pass
++)
2289 if (noCols
== 0) // Probably a bogus table name(s)
2291 // Allocate n wxDbColInf objects to hold the column information
2292 colInf
= new wxDbColInf
[noCols
+1];
2295 // Mark the end of the array
2296 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2297 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2298 colInf
[noCols
].sqlDataType
= 0;
2301 TableName
= tableName
;
2302 // Oracle and Interbase table names are uppercase only, so force
2303 // the name to uppercase just in case programmer forgot to do this
2304 if ((Dbms() == dbmsORACLE
) ||
2305 (Dbms() == dbmsINTERBASE
))
2306 TableName
= TableName
.Upper();
2308 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2310 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2311 // use the call below that leaves out the user name
2312 if (!UserID
.IsEmpty() &&
2313 Dbms() != dbmsMY_SQL
&&
2314 Dbms() != dbmsACCESS
&&
2315 Dbms() != dbmsMS_SQL_SERVER
)
2317 retcode
= SQLColumns(hstmt
,
2318 NULL
, 0, // All qualifiers
2319 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2320 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2321 NULL
, 0); // All columns
2325 retcode
= SQLColumns(hstmt
,
2326 NULL
, 0, // All qualifiers
2328 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2329 NULL
, 0); // All columns
2331 if (retcode
!= SQL_SUCCESS
)
2332 { // Error occured, abort
2333 DispAllErrors(henv
, hdbc
, hstmt
);
2336 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2342 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2344 if (pass
== 1) // First pass, just add up the number of columns
2346 else // Pass 2; Fill in the array of structures
2348 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2350 // NOTE: Only the ODBC 1.x fields are retrieved
2351 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2352 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2353 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2354 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2355 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2356 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2357 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2358 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2359 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2360 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2361 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2362 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2363 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2364 // Start Values for Primary/Foriegn Key (=No)
2365 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2366 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2367 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2368 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2370 // BJO 20000428 : Virtuoso returns type names with upper cases!
2371 if (Dbms() == dbmsVIRTUOSO
)
2373 wxString s
= colInf
[colNo
].typeName
;
2375 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2378 // Determine the wxDb data type that is used to represent the native data type of this data source
2379 colInf
[colNo
].dbDataType
= 0;
2380 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2383 // IODBC does not return a correct columnSize, so we set
2384 // columnSize = bufferLength if no column size was returned
2385 // IODBC returns the columnSize in bufferLength.. (bug)
2386 if (colInf
[colNo
].columnSize
< 1)
2388 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2392 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2394 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2395 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2396 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2397 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2398 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2399 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2400 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2401 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2407 if (retcode
!= SQL_NO_DATA_FOUND
)
2408 { // Error occured, abort
2409 DispAllErrors(henv
, hdbc
, hstmt
);
2412 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2419 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2421 // Store Primary and Foriegn Keys
2422 GetKeyFields(tableName
,colInf
,noCols
);
2428 } // wxDb::GetColumns()
2431 #else // New GetColumns
2436 These are tentative new GetColumns members which should be more database
2437 independant and which always returns the columns in the order they were
2440 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2441 wxChar* userID)) calls the second implementation for each separate table
2442 before merging the results. This makes the code easier to maintain as
2443 only one member (the second) makes the real work
2444 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2445 wxChar *userID) is a little bit improved
2446 - It doesn't anymore rely on the type-name to find out which database-type
2448 - It ends by sorting the columns, so that they are returned in the same
2449 order they were created
2459 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2462 // The last array element of the tableName[] argument must be zero (null).
2463 // This is how the end of the array is detected.
2467 // How many tables ?
2469 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2471 // Create a table to maintain the columns for each separate table
2472 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2475 for (i
= 0 ; i
< tbl
; i
++)
2478 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2479 if (TableColumns
[i
].colInf
== NULL
)
2481 noCols
+= TableColumns
[i
].noCols
;
2484 // Now merge all the separate table infos
2485 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2487 // Mark the end of the array
2488 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2489 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2490 colInf
[noCols
].sqlDataType
= 0;
2495 for (i
= 0 ; i
< tbl
; i
++)
2497 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2499 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2503 delete [] TableColumns
;
2506 } // wxDb::GetColumns() -- NEW
2509 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2511 // Same as the above GetColumns() function except this one gets columns
2512 // only for a single table, and if 'numCols' is not NULL, the number of
2513 // columns stored in the returned wxDbColInf is set in '*numCols'
2515 // userID is evaluated in the following manner:
2516 // userID == NULL ... UserID is ignored
2517 // userID == "" ... UserID set equal to 'this->uid'
2518 // userID != "" ... UserID set equal to 'userID'
2520 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2521 // by this function. This function should use its own wxDb instance
2522 // to avoid undesired unbinding of columns.
2526 wxDbColInf
*colInf
= 0;
2534 convertUserID(userID
,UserID
);
2536 // Pass 1 - Determine how many columns there are.
2537 // Pass 2 - Allocate the wxDbColInf array and fill in
2538 // the array with the column information.
2540 for (pass
= 1; pass
<= 2; pass
++)
2544 if (noCols
== 0) // Probably a bogus table name(s)
2546 // Allocate n wxDbColInf objects to hold the column information
2547 colInf
= new wxDbColInf
[noCols
+1];
2550 // Mark the end of the array
2551 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2552 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2553 colInf
[noCols
].sqlDataType
= 0;
2556 TableName
= tableName
;
2557 // Oracle and Interbase table names are uppercase only, so force
2558 // the name to uppercase just in case programmer forgot to do this
2559 if ((Dbms() == dbmsORACLE
) ||
2560 (Dbms() == dbmsINTERBASE
))
2561 TableName
= TableName
.Upper();
2563 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2565 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2566 // use the call below that leaves out the user name
2567 if (!UserID
.IsEmpty() &&
2568 Dbms() != dbmsMY_SQL
&&
2569 Dbms() != dbmsACCESS
&&
2570 Dbms() != dbmsMS_SQL_SERVER
)
2572 retcode
= SQLColumns(hstmt
,
2573 NULL
, 0, // All qualifiers
2574 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2575 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2576 NULL
, 0); // All columns
2580 retcode
= SQLColumns(hstmt
,
2581 NULL
, 0, // All qualifiers
2583 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2584 NULL
, 0); // All columns
2586 if (retcode
!= SQL_SUCCESS
)
2587 { // Error occured, abort
2588 DispAllErrors(henv
, hdbc
, hstmt
);
2591 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2597 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2599 if (pass
== 1) // First pass, just add up the number of columns
2601 else // Pass 2; Fill in the array of structures
2603 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2605 // NOTE: Only the ODBC 1.x fields are retrieved
2606 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2607 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2608 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2609 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2610 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2611 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2612 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2613 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2614 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2615 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2616 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2617 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2618 // Start Values for Primary/Foriegn Key (=No)
2619 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2620 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2621 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2622 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2625 // IODBC does not return a correct columnSize, so we set
2626 // columnSize = bufferLength if no column size was returned
2627 // IODBC returns the columnSize in bufferLength.. (bug)
2628 if (colInf
[colNo
].columnSize
< 1)
2630 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2634 // Determine the wxDb data type that is used to represent the native data type of this data source
2635 colInf
[colNo
].dbDataType
= 0;
2636 // Get the intern datatype
2637 switch (colInf
[colNo
].sqlDataType
)
2641 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2647 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2654 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2657 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2660 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2665 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2666 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2673 if (retcode
!= SQL_NO_DATA_FOUND
)
2674 { // Error occured, abort
2675 DispAllErrors(henv
, hdbc
, hstmt
);
2678 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2685 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2687 // Store Primary and Foreign Keys
2688 GetKeyFields(tableName
,colInf
,noCols
);
2690 ///////////////////////////////////////////////////////////////////////////
2691 // Now sort the the columns in order to make them appear in the right order
2692 ///////////////////////////////////////////////////////////////////////////
2694 // Build a generic SELECT statement which returns 0 rows
2697 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2700 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2702 DispAllErrors(henv
, hdbc
, hstmt
);
2706 // Get the number of result columns
2707 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2709 DispAllErrors(henv
, hdbc
, hstmt
);
2713 if (noCols
== 0) // Probably a bogus table name
2722 for (colNum
= 0; colNum
< noCols
; colNum
++)
2724 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2726 &Sword
, &Sdword
) != SQL_SUCCESS
)
2728 DispAllErrors(henv
, hdbc
, hstmt
);
2732 wxString Name1
= name
;
2733 Name1
= Name1
.Upper();
2735 // Where is this name in the array ?
2736 for (i
= colNum
; i
< noCols
; i
++)
2738 wxString Name2
= colInf
[i
].colName
;
2739 Name2
= Name2
.Upper();
2742 if (colNum
!= i
) // swap to sort
2744 wxDbColInf tmpColInf
= colInf
[colNum
];
2745 colInf
[colNum
] = colInf
[i
];
2746 colInf
[i
] = tmpColInf
;
2752 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2754 ///////////////////////////////////////////////////////////////////////////
2756 ///////////////////////////////////////////////////////////////////////////
2762 } // wxDb::GetColumns()
2765 #endif // #else OLD_GETCOLUMNS
2768 /********** wxDb::GetColumnCount() **********/
2769 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2771 * Returns a count of how many columns are in a table.
2772 * If an error occurs in computing the number of columns
2773 * this function will return a -1 for the count
2775 * userID is evaluated in the following manner:
2776 * userID == NULL ... UserID is ignored
2777 * userID == "" ... UserID set equal to 'this->uid'
2778 * userID != "" ... UserID set equal to 'userID'
2780 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2781 * by this function. This function should use its own wxDb instance
2782 * to avoid undesired unbinding of columns.
2792 convertUserID(userID
,UserID
);
2794 TableName
= tableName
;
2795 // Oracle and Interbase table names are uppercase only, so force
2796 // the name to uppercase just in case programmer forgot to do this
2797 if ((Dbms() == dbmsORACLE
) ||
2798 (Dbms() == dbmsINTERBASE
))
2799 TableName
= TableName
.Upper();
2801 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2803 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2804 // use the call below that leaves out the user name
2805 if (!UserID
.IsEmpty() &&
2806 Dbms() != dbmsMY_SQL
&&
2807 Dbms() != dbmsACCESS
&&
2808 Dbms() != dbmsMS_SQL_SERVER
)
2810 retcode
= SQLColumns(hstmt
,
2811 NULL
, 0, // All qualifiers
2812 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2813 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2814 NULL
, 0); // All columns
2818 retcode
= SQLColumns(hstmt
,
2819 NULL
, 0, // All qualifiers
2821 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2822 NULL
, 0); // All columns
2824 if (retcode
!= SQL_SUCCESS
)
2825 { // Error occured, abort
2826 DispAllErrors(henv
, hdbc
, hstmt
);
2827 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2831 // Count the columns
2832 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2835 if (retcode
!= SQL_NO_DATA_FOUND
)
2836 { // Error occured, abort
2837 DispAllErrors(henv
, hdbc
, hstmt
);
2838 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2842 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2845 } // wxDb::GetColumnCount()
2848 /********** wxDb::GetCatalog() *******/
2849 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2851 * ---------------------------------------------------------------------
2852 * -- 19991203 : mj10777 : Create ------
2853 * -- : Creates a wxDbInf with Tables / Cols Array ------
2854 * -- : uses SQLTables and fills pTableInf; ------
2855 * -- : pColInf is set to NULL and numCols to 0; ------
2856 * -- : returns pDbInf (wxDbInf) ------
2857 * -- - if unsuccesfull (pDbInf == NULL) ------
2858 * -- : pColInf can be filled with GetColumns(..); ------
2859 * -- : numCols can be filled with GetColumnCount(..); ------
2860 * ---------------------------------------------------------------------
2862 * userID is evaluated in the following manner:
2863 * userID == NULL ... UserID is ignored
2864 * userID == "" ... UserID set equal to 'this->uid'
2865 * userID != "" ... UserID set equal to 'userID'
2867 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2868 * by this function. This function should use its own wxDb instance
2869 * to avoid undesired unbinding of columns.
2872 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2873 int noTab
= 0; // Counter while filling table entries
2877 wxString tblNameSave
;
2880 convertUserID(userID
,UserID
);
2882 //-------------------------------------------------------------
2883 pDbInf
= new wxDbInf
; // Create the Database Array
2884 //-------------------------------------------------------------
2885 // Table Information
2886 // Pass 1 - Determine how many Tables there are.
2887 // Pass 2 - Create the Table array and fill it
2888 // - Create the Cols array = NULL
2889 //-------------------------------------------------------------
2891 for (pass
= 1; pass
<= 2; pass
++)
2893 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2894 tblNameSave
.Empty();
2896 if (!UserID
.IsEmpty() &&
2897 Dbms() != dbmsMY_SQL
&&
2898 Dbms() != dbmsACCESS
&&
2899 Dbms() != dbmsMS_SQL_SERVER
)
2901 retcode
= SQLTables(hstmt
,
2902 NULL
, 0, // All qualifiers
2903 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2904 NULL
, 0, // All tables
2905 NULL
, 0); // All columns
2909 retcode
= SQLTables(hstmt
,
2910 NULL
, 0, // All qualifiers
2911 NULL
, 0, // User specified
2912 NULL
, 0, // All tables
2913 NULL
, 0); // All columns
2916 if (retcode
!= SQL_SUCCESS
)
2918 DispAllErrors(henv
, hdbc
, hstmt
);
2920 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2924 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2926 if (pass
== 1) // First pass, just count the Tables
2928 if (pDbInf
->numTables
== 0)
2930 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2931 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2933 pDbInf
->numTables
++; // Counter for Tables
2935 if (pass
== 2) // Create and fill the Table entries
2937 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2938 { // no, then create the Array
2939 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2941 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2943 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2944 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2945 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2951 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2953 // Query how many columns are in each table
2954 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2956 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2961 } // wxDb::GetCatalog()
2964 /********** wxDb::Catalog() **********/
2965 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2967 * Creates the text file specified in 'filename' which will contain
2968 * a minimal data dictionary of all tables accessible by the user specified
2971 * userID is evaluated in the following manner:
2972 * userID == NULL ... UserID is ignored
2973 * userID == "" ... UserID set equal to 'this->uid'
2974 * userID != "" ... UserID set equal to 'userID'
2976 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2977 * by this function. This function should use its own wxDb instance
2978 * to avoid undesired unbinding of columns.
2981 wxASSERT(fileName
.Length());
2985 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2986 wxString tblNameSave
;
2987 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2989 wxChar typeName
[30+1];
2990 SDWORD precision
, length
;
2992 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
2996 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2999 convertUserID(userID
,UserID
);
3001 if (!UserID
.IsEmpty() &&
3002 Dbms() != dbmsMY_SQL
&&
3003 Dbms() != dbmsACCESS
&&
3004 Dbms() != dbmsINTERBASE
&&
3005 Dbms() != dbmsMS_SQL_SERVER
)
3007 retcode
= SQLColumns(hstmt
,
3008 NULL
, 0, // All qualifiers
3009 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3010 NULL
, 0, // All tables
3011 NULL
, 0); // All columns
3015 retcode
= SQLColumns(hstmt
,
3016 NULL
, 0, // All qualifiers
3017 NULL
, 0, // User specified
3018 NULL
, 0, // All tables
3019 NULL
, 0); // All columns
3021 if (retcode
!= SQL_SUCCESS
)
3023 DispAllErrors(henv
, hdbc
, hstmt
);
3029 tblNameSave
.Empty();
3034 retcode
= SQLFetch(hstmt
);
3035 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3038 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3041 fputs(wxT("\n"), fp
);
3042 fputs(wxT("================================ "), fp
);
3043 fputs(wxT("================================ "), fp
);
3044 fputs(wxT("===================== "), fp
);
3045 fputs(wxT("========= "), fp
);
3046 fputs(wxT("=========\n"), fp
);
3047 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3048 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3049 fputs(outStr
.c_str(), fp
);
3050 fputs(wxT("================================ "), fp
);
3051 fputs(wxT("================================ "), fp
);
3052 fputs(wxT("===================== "), fp
);
3053 fputs(wxT("========= "), fp
);
3054 fputs(wxT("=========\n"), fp
);
3055 tblNameSave
= tblName
;
3058 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3059 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3060 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3061 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3062 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3063 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3065 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
3066 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3067 if (fputs(outStr
.c_str(), fp
) == EOF
)
3069 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3076 if (retcode
!= SQL_NO_DATA_FOUND
)
3077 DispAllErrors(henv
, hdbc
, hstmt
);
3079 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3082 return(retcode
== SQL_NO_DATA_FOUND
);
3084 } // wxDb::Catalog()
3087 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3089 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3090 * if the object exists in the database. This function does not indicate
3091 * whether or not the user has privleges to query or perform other functions
3094 * userID is evaluated in the following manner:
3095 * userID == NULL ... UserID is ignored
3096 * userID == "" ... UserID set equal to 'this->uid'
3097 * userID != "" ... UserID set equal to 'userID'
3100 wxASSERT(tableName
.Length());
3104 if (Dbms() == dbmsDBASE
)
3107 if (tablePath
.Length())
3108 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3110 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3113 exists
= wxFileExists(dbName
);
3118 convertUserID(userID
,UserID
);
3120 TableName
= tableName
;
3121 // Oracle and Interbase table names are uppercase only, so force
3122 // the name to uppercase just in case programmer forgot to do this
3123 if ((Dbms() == dbmsORACLE
) ||
3124 (Dbms() == dbmsINTERBASE
))
3125 TableName
= TableName
.Upper();
3127 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3130 // Some databases cannot accept a user name when looking up table names,
3131 // so we use the call below that leaves out the user name
3132 if (!UserID
.IsEmpty() &&
3133 Dbms() != dbmsMY_SQL
&&
3134 Dbms() != dbmsACCESS
&&
3135 Dbms() != dbmsMS_SQL_SERVER
&&
3136 Dbms() != dbmsDB2
&&
3137 Dbms() != dbmsINTERBASE
&&
3138 Dbms() != dbmsPERVASIVE_SQL
)
3140 retcode
= SQLTables(hstmt
,
3141 NULL
, 0, // All qualifiers
3142 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3143 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3144 NULL
, 0); // All table types
3148 retcode
= SQLTables(hstmt
,
3149 NULL
, 0, // All qualifiers
3150 NULL
, 0, // All owners
3151 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3152 NULL
, 0); // All table types
3154 if (retcode
!= SQL_SUCCESS
)
3155 return(DispAllErrors(henv
, hdbc
, hstmt
));
3157 retcode
= SQLFetch(hstmt
);
3158 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3160 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3161 return(DispAllErrors(henv
, hdbc
, hstmt
));
3164 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3168 } // wxDb::TableExists()
3171 /********** wxDb::TablePrivileges() **********/
3172 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3173 const wxChar
*schema
, const wxString
&tablePath
)
3175 wxASSERT(tableName
.Length());
3177 wxDbTablePrivilegeInfo result
;
3181 // We probably need to be able to dynamically set this based on
3182 // the driver type, and state.
3183 wxChar curRole
[]=wxT("public");
3187 wxString UserID
,Schema
;
3188 convertUserID(userID
,UserID
);
3189 convertUserID(schema
,Schema
);
3191 TableName
= tableName
;
3192 // Oracle and Interbase table names are uppercase only, so force
3193 // the name to uppercase just in case programmer forgot to do this
3194 if ((Dbms() == dbmsORACLE
) ||
3195 (Dbms() == dbmsINTERBASE
))
3196 TableName
= TableName
.Upper();
3198 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3200 // Some databases cannot accept a user name when looking up table names,
3201 // so we use the call below that leaves out the user name
3202 if (!Schema
.IsEmpty() &&
3203 Dbms() != dbmsMY_SQL
&&
3204 Dbms() != dbmsACCESS
&&
3205 Dbms() != dbmsMS_SQL_SERVER
)
3207 retcode
= SQLTablePrivileges(hstmt
,
3209 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3210 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3214 retcode
= SQLTablePrivileges(hstmt
,
3217 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3220 #ifdef DBDEBUG_CONSOLE
3221 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3224 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3225 return(DispAllErrors(henv
, hdbc
, hstmt
));
3227 bool failed
= FALSE
;
3228 retcode
= SQLFetch(hstmt
);
3229 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3231 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3234 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3237 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3240 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3243 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3246 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3249 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3254 return(DispAllErrors(henv
, hdbc
, hstmt
));
3256 #ifdef DBDEBUG_CONSOLE
3257 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3258 result
.privilege
,result
.tableOwner
,result
.tableName
,
3259 result
.grantor
, result
.grantee
);
3262 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3264 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3268 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3269 !wxStrcmp(result
.privilege
,priv
))
3271 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3275 if (!wxStrcmp(result
.grantee
,curRole
) &&
3276 !wxStrcmp(result
.privilege
,priv
))
3278 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3282 retcode
= SQLFetch(hstmt
);
3285 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3288 } // wxDb::TablePrivileges
3291 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3295 if (Dbms() == dbmsACCESS
)
3297 TableName
+= tableName
;
3298 if (Dbms() == dbmsACCESS
)
3302 } // wxDb::SQLTableName()
3305 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3309 if (Dbms() == dbmsACCESS
)
3312 if (Dbms() == dbmsACCESS
)
3316 } // wxDb::SQLColumnName()
3319 /********** wxDb::SetSqlLogging() **********/
3320 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3322 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3323 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3325 if (state
== sqlLogON
)
3329 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3330 if (fpSqlLog
== NULL
)
3338 if (fclose(fpSqlLog
))
3344 sqlLogState
= state
;
3347 } // wxDb::SetSqlLogging()
3350 /********** wxDb::WriteSqlLog() **********/
3351 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3353 wxASSERT(logMsg
.Length());
3355 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3358 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3360 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3362 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3367 } // wxDb::WriteSqlLog()
3370 /********** wxDb::Dbms() **********/
3371 wxDBMS
wxDb::Dbms(void)
3373 * Be aware that not all database engines use the exact same syntax, and not
3374 * every ODBC compliant database is compliant to the same level of compliancy.
3375 * Some manufacturers support the minimum Level 1 compliancy, and others up
3376 * through Level 3. Others support subsets of features for levels above 1.
3378 * If you find an inconsistency between the wxDb class and a specific database
3379 * engine, and an identifier to this section, and special handle the database in
3380 * the area where behavior is non-conforming with the other databases.
3383 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3384 * ---------------------------------------------------
3387 * - Currently the only database supported by the class to support VIEWS
3390 * - Does not support the SQL_TIMESTAMP structure
3391 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3392 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3393 * is TRUE. The user must create ALL indexes from their program.
3394 * - Table names can only be 8 characters long
3395 * - Column names can only be 10 characters long
3398 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3399 * after every table name involved in the query/join if that tables matching record(s)
3401 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3403 * SYBASE (Enterprise)
3404 * - If a column is part of the Primary Key, the column cannot be NULL
3405 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3408 * - If a column is part of the Primary Key, the column cannot be NULL
3409 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3410 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3411 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3412 * column definition if it is not defined correctly, but it is experimental
3413 * - Does not support sub-queries in SQL statements
3416 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3417 * - Does not support sub-queries in SQL statements
3420 * - Primary keys must be declared as NOT NULL
3421 * - Table and index names must not be longer than 13 characters in length (technically
3422 * table names can be up to 18 characters, but the primary index is created using the
3423 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3428 * - Columns that are part of primary keys must be defined as being NOT NULL
3429 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3430 * column definition if it is not defined correctly, but it is experimental
3433 // Should only need to do this once for each new database connection
3434 // so return the value we already determined it to be to save time
3435 // and lots of string comparisons
3436 if (dbmsType
!= dbmsUNIDENTIFIED
)
3439 wxChar baseName
[25+1];
3440 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3443 // RGG 20001025 : add support for Interbase
3444 // GT : Integrated to base classes on 20001121
3445 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3446 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3448 // BJO 20000428 : add support for Virtuoso
3449 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3450 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3452 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3453 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3455 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3456 // connected through an OpenLink driver.
3457 // Is it also returned by Sybase Adapatitve server?
3458 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3459 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3461 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3462 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3463 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3465 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3468 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3469 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3470 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3471 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3472 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3473 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3476 if (!wxStricmp(dbInf
.dbmsName
,wxT("Pervasive")))
3477 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3480 if (!wxStricmp(baseName
,wxT("Informix")))
3481 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3484 if (!wxStricmp(baseName
,wxT("Oracle")))
3485 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3486 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3487 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3488 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3489 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3490 if (!wxStricmp(baseName
,wxT("Sybase")))
3491 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3494 if (!wxStricmp(baseName
,wxT("DBASE")))
3495 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3497 if (!wxStricmp(baseName
,wxT("MySQL")))
3498 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3501 if (!wxStricmp(baseName
,wxT("DB2")))
3502 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3504 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3509 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3510 int dataType
, ULONG columnLength
,
3511 const wxString
&optionalParam
)
3513 wxASSERT(tableName
.Length());
3514 wxASSERT(columnName
.Length());
3515 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3516 dataType
!= DB_DATA_TYPE_VARCHAR
);
3518 // Must specify a columnLength if modifying a VARCHAR type column
3519 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3522 wxString dataTypeName
;
3524 wxString alterSlashModify
;
3528 case DB_DATA_TYPE_VARCHAR
:
3529 dataTypeName
= typeInfVarchar
.TypeName
;
3531 case DB_DATA_TYPE_INTEGER
:
3532 dataTypeName
= typeInfInteger
.TypeName
;
3534 case DB_DATA_TYPE_FLOAT
:
3535 dataTypeName
= typeInfFloat
.TypeName
;
3537 case DB_DATA_TYPE_DATE
:
3538 dataTypeName
= typeInfDate
.TypeName
;
3540 case DB_DATA_TYPE_BLOB
:
3541 dataTypeName
= typeInfBlob
.TypeName
;
3547 // Set the modify or alter syntax depending on the type of database connected to
3551 alterSlashModify
= "MODIFY";
3553 case dbmsMS_SQL_SERVER
:
3554 alterSlashModify
= "ALTER COLUMN";
3556 case dbmsUNIDENTIFIED
:
3558 case dbmsSYBASE_ASA
:
3559 case dbmsSYBASE_ASE
:
3565 alterSlashModify
= "MODIFY";
3569 // create the SQL statement
3570 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3571 columnName
.c_str(), dataTypeName
.c_str());
3573 // For varchars only, append the size of the column
3574 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3575 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= "text"))
3578 s
.Printf(wxT("(%d)"), columnLength
);
3582 // for passing things like "NOT NULL"
3583 if (optionalParam
.Length())
3585 sqlStmt
+= wxT(" ");
3586 sqlStmt
+= optionalParam
;
3589 return ExecSql(sqlStmt
);
3591 } // wxDb::ModifyColumn()
3594 /********** wxDbGetConnection() **********/
3595 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3599 // Used to keep a pointer to a DB connection that matches the requested
3600 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3601 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3602 // rather than having to re-query the datasource to get all the values
3603 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3604 wxDb
*matchingDbConnection
= NULL
;
3606 // Scan the linked list searching for an available database connection
3607 // that's already been opened but is currently not in use.
3608 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3610 // The database connection must be for the same datasource
3611 // name and must currently not be in use.
3613 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3614 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3616 pList
->Free
= FALSE
;
3617 return(pList
->PtrDb
);
3620 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3621 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3622 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3623 matchingDbConnection
= pList
->PtrDb
;
3626 // No available connections. A new connection must be made and
3627 // appended to the end of the linked list.
3630 // Find the end of the list
3631 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3632 // Append a new list item
3633 pList
->PtrNext
= new wxDbList
;
3634 pList
->PtrNext
->PtrPrev
= pList
;
3635 pList
= pList
->PtrNext
;
3639 // Create the first node on the list
3640 pList
= PtrBegDbList
= new wxDbList
;
3644 // Initialize new node in the linked list
3646 pList
->Free
= FALSE
;
3647 pList
->Dsn
= pDbConfig
->GetDsn();
3648 pList
->Uid
= pDbConfig
->GetUserID();
3649 pList
->AuthStr
= pDbConfig
->GetPassword();
3651 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3653 bool opened
= FALSE
;
3655 if (!matchingDbConnection
)
3656 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3658 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3660 // Connect to the datasource
3663 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3664 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3665 return(pList
->PtrDb
);
3667 else // Unable to connect, destroy list item
3670 pList
->PtrPrev
->PtrNext
= 0;
3672 PtrBegDbList
= 0; // Empty list again
3673 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3674 pList
->PtrDb
->Close(); // Close the wxDb object
3675 delete pList
->PtrDb
; // Deletes the wxDb object
3676 delete pList
; // Deletes the linked list object
3680 } // wxDbGetConnection()
3683 /********** wxDbFreeConnection() **********/
3684 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3688 // Scan the linked list searching for the database connection
3689 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3691 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3692 return (pList
->Free
= TRUE
);
3695 // Never found the database object, return failure
3698 } // wxDbFreeConnection()
3701 /********** wxDbCloseConnections() **********/
3702 void WXDLLEXPORT
wxDbCloseConnections(void)
3704 wxDbList
*pList
, *pNext
;
3706 // Traverse the linked list closing database connections and freeing memory as I go.
3707 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3709 pNext
= pList
->PtrNext
; // Save the pointer to next
3710 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3711 pList
->PtrDb
->Close(); // Close the wxDb object
3712 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3713 delete pList
->PtrDb
; // Deletes the wxDb object
3714 delete pList
; // Deletes the linked list object
3717 // Mark the list as empty
3720 } // wxDbCloseConnections()
3723 /********** wxDbConnectionsInUse() **********/
3724 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3729 // Scan the linked list counting db connections that are currently in use
3730 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3732 if (pList
->Free
== FALSE
)
3738 } // wxDbConnectionsInUse()
3742 /********** wxDbLogExtendedErrorMsg() **********/
3743 // DEBUG ONLY function
3744 const wxChar WXDLLEXPORT
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3746 const wxChar
*ErrFile
,
3749 static wxString msg
;
3754 if (ErrFile
|| ErrLine
)
3756 msg
+= wxT("File: ");
3758 msg
+= wxT(" Line: ");
3759 tStr
.Printf(wxT("%d"),ErrLine
);
3760 msg
+= tStr
.c_str();
3764 msg
.Append (wxT("\nODBC errors:\n"));
3767 // Display errors for this connection
3769 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3771 if (pDb
->errorList
[i
])
3773 msg
.Append(pDb
->errorList
[i
]);
3774 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3775 msg
.Append(wxT("\n"));
3776 // Clear the errmsg buffer so the next error will not
3777 // end up showing the previous error that have occurred
3778 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3783 wxLogDebug(msg
.c_str());
3786 } // wxDbLogExtendedErrorMsg()
3789 /********** wxDbSqlLog() **********/
3790 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3792 bool append
= FALSE
;
3795 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3797 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3802 SQLLOGstate
= state
;
3803 SQLLOGfn
= filename
;
3811 /********** wxDbCreateDataSource() **********/
3812 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3813 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3815 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3816 * Very rudimentary creation of an ODBC data source.
3818 * ODBC driver must be ODBC 3.0 compliant to use this function
3823 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3829 dsnLocation
= ODBC_ADD_SYS_DSN
;
3831 dsnLocation
= ODBC_ADD_DSN
;
3833 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3834 // so that is why I used it, as wxString does not deal well with
3835 // embedded nulls in strings
3836 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3838 // Replace the separator from above with the '\0' seperator needed
3839 // by the SQLConfigDataSource() function
3843 k
= setupStr
.Find((wxChar
)2,TRUE
);
3844 if (k
!= wxNOT_FOUND
)
3845 setupStr
[(UINT
)k
] = wxT('\0');
3847 while (k
!= wxNOT_FOUND
);
3849 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3850 driverName
, setupStr
.c_str());
3852 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3854 // check for errors caused by ConfigDSN based functions
3857 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3858 errMsg
[0] = wxT('\0');
3860 // This function is only supported in ODBC drivers v3.0 compliant and above
3861 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3864 #ifdef DBDEBUG_CONSOLE
3865 // When run in console mode, use standard out to display errors.
3866 cout
<< errMsg
<< endl
;
3867 cout
<< wxT("Press any key to continue...") << endl
;
3869 #endif // DBDEBUG_CONSOLE
3872 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3873 #endif // __WXDEBUG__
3879 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3880 // necessary to use this function, so this function is not supported
3882 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3885 #endif // __VISUALC__
3889 } // wxDbCreateDataSource()
3893 /********** wxDbGetDataSource() **********/
3894 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3895 SWORD DsDescMax
, UWORD direction
)
3897 * Dsn and DsDesc will contain the data source name and data source
3898 * description upon return
3903 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3904 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3909 } // wxDbGetDataSource()
3912 // Change this to 0 to remove use of all deprecated functions
3913 #if wxODBC_BACKWARD_COMPATABILITY
3914 /********************************************************************
3915 ********************************************************************
3917 * The following functions are all DEPRECATED and are included for
3918 * backward compatability reasons only
3920 ********************************************************************
3921 ********************************************************************/
3922 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3924 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3926 /***** DEPRECATED: use wxGetDataSource() *****/
3927 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3930 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3932 /***** DEPRECATED: use wxDbGetConnection() *****/
3933 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3935 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3937 /***** DEPRECATED: use wxDbFreeConnection() *****/
3938 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3940 return wxDbFreeConnection(pDb
);
3942 /***** DEPRECATED: use wxDbCloseConnections() *****/
3943 void WXDLLEXPORT
CloseDbConnections(void)
3945 wxDbCloseConnections();
3947 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3948 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3950 return wxDbConnectionsInUse();