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
))
726 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
728 typeInfDate
.FsqlType
= SQL_TIME
;
731 #endif // SQL_DATETIME defined
733 if (failOnDataTypeUnsupported
)
738 typeInfDate
.FsqlType
= SQL_DATE
;
741 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
744 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
746 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
748 if (failOnDataTypeUnsupported
)
752 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
755 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
757 //typeInfBlob.TypeName = "BLOB";
759 #ifdef DBDEBUG_CONSOLE
760 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
761 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
762 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
763 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
764 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
768 // Completed Successfully
774 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
776 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
777 dbConnectInf
->GetPassword());
781 bool wxDb::Open(wxDb
*copyDb
)
783 dsn
= copyDb
->GetDatasourceName();
784 uid
= copyDb
->GetUsername();
785 authStr
= copyDb
->GetPassword();
789 if (!FwdOnlyCursors())
791 // Specify that the ODBC cursor library be used, if needed. This must be
792 // specified before the connection is made.
793 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
795 #ifdef DBDEBUG_CONSOLE
796 if (retcode
== SQL_SUCCESS
)
797 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
799 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
803 // Connect to the data source
804 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
805 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
806 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
808 if (retcode
== SQL_ERROR
)
809 return(DispAllErrors(henv
, hdbc
));
812 If using Intersolv branded ODBC drivers, this is the place where you would substitute
813 your branded driver license information
815 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
816 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
819 // Mark database as open
822 // Allocate a statement handle for the database connection
823 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
824 return(DispAllErrors(henv
, hdbc
));
826 // Set Connection Options
827 if (!setConnectionOptions())
830 // Instead of Querying the data source for info about itself, it can just be copied
831 // from the wxDb instance that was passed in (copyDb).
832 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
833 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
834 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
835 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
836 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
837 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
838 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
839 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
840 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
841 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
842 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
843 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
844 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
845 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
846 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
847 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
848 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
849 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
850 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
851 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
852 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
853 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
854 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
855 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
856 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
857 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
858 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
859 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
860 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
861 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
862 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
864 // VARCHAR = Variable length character string
865 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
866 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
867 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
868 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
869 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
872 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
873 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
874 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
875 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
876 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
879 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
880 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
881 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
882 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
883 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
886 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
887 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
888 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
889 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
890 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
893 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
894 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
895 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
896 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
897 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
899 #ifdef DBDEBUG_CONSOLE
900 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
901 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
902 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
903 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
904 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
908 // Completed Successfully
913 /********** wxDb::setConnectionOptions() **********/
914 bool wxDb::setConnectionOptions(void)
916 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
921 // I need to get the DBMS name here, because some of the connection options
922 // are database specific and need to call the Dbms() function.
923 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
924 return(DispAllErrors(henv
, hdbc
));
926 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
927 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
928 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
930 // By default, MS Sql Server closes cursors on commit and rollback. The following
931 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
932 // after a transaction. This is a driver specific option and is not part of the
933 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
934 // The database settings don't have any effect one way or the other.
935 if (Dbms() == dbmsMS_SQL_SERVER
)
937 const long SQL_PRESERVE_CURSORS
= 1204L;
938 const long SQL_PC_ON
= 1L;
939 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
942 // Display the connection options to verify them
943 #ifdef DBDEBUG_CONSOLE
945 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
947 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
948 return(DispAllErrors(henv
, hdbc
));
949 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
951 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
952 return(DispAllErrors(henv
, hdbc
));
953 cout
<< wxT("ODBC CURSORS: ");
956 case(SQL_CUR_USE_IF_NEEDED
):
957 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
959 case(SQL_CUR_USE_ODBC
):
960 cout
<< wxT("SQL_CUR_USE_ODBC");
962 case(SQL_CUR_USE_DRIVER
):
963 cout
<< wxT("SQL_CUR_USE_DRIVER");
968 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
969 return(DispAllErrors(henv
, hdbc
));
970 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
975 // Completed Successfully
978 } // wxDb::setConnectionOptions()
981 /********** wxDb::getDbInfo() **********/
982 bool wxDb::getDbInfo(void)
987 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
988 return(DispAllErrors(henv
, hdbc
));
990 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
991 return(DispAllErrors(henv
, hdbc
));
993 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
994 return(DispAllErrors(henv
, hdbc
));
997 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
998 // causing database connectivity to fail in some cases.
999 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1001 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1002 return(DispAllErrors(henv
, hdbc
));
1004 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1005 return(DispAllErrors(henv
, hdbc
));
1007 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1008 return(DispAllErrors(henv
, hdbc
));
1010 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1011 return(DispAllErrors(henv
, hdbc
));
1013 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1014 return(DispAllErrors(henv
, hdbc
));
1016 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1017 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1018 return(DispAllErrors(henv
, hdbc
));
1020 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1021 return(DispAllErrors(henv
, hdbc
));
1023 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1024 return(DispAllErrors(henv
, hdbc
));
1026 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1027 // return(DispAllErrors(henv, hdbc));
1029 // Not all drivers support this call - Nick Gorham(unixODBC)
1030 dbInf
.cliConfLvl
= 0;
1033 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1034 return(DispAllErrors(henv
, hdbc
));
1036 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1037 return(DispAllErrors(henv
, hdbc
));
1039 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1040 return(DispAllErrors(henv
, hdbc
));
1042 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1043 return(DispAllErrors(henv
, hdbc
));
1045 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1046 return(DispAllErrors(henv
, hdbc
));
1048 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1049 return(DispAllErrors(henv
, hdbc
));
1051 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1052 return(DispAllErrors(henv
, hdbc
));
1054 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1055 return(DispAllErrors(henv
, hdbc
));
1057 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1058 return(DispAllErrors(henv
, hdbc
));
1060 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1061 return(DispAllErrors(henv
, hdbc
));
1063 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1064 return(DispAllErrors(henv
, hdbc
));
1066 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1067 return(DispAllErrors(henv
, hdbc
));
1069 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1070 return(DispAllErrors(henv
, hdbc
));
1072 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1073 return(DispAllErrors(henv
, hdbc
));
1075 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1076 return(DispAllErrors(henv
, hdbc
));
1078 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1079 return(DispAllErrors(henv
, hdbc
));
1081 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1082 return(DispAllErrors(henv
, hdbc
));
1084 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1085 return(DispAllErrors(henv
, hdbc
));
1087 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1088 return(DispAllErrors(henv
, hdbc
));
1090 #ifdef DBDEBUG_CONSOLE
1091 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1092 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1093 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1094 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1096 cout
<< wxT("API Conf. Level: ");
1097 switch(dbInf
.apiConfLvl
)
1099 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1100 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1101 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1105 cout
<< wxT("SAG CLI Conf. Level: ");
1106 switch(dbInf
.cliConfLvl
)
1108 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1109 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1113 cout
<< wxT("SQL Conf. Level: ");
1114 switch(dbInf
.sqlConfLvl
)
1116 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1117 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1118 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1122 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1123 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1124 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1125 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1126 cout
<< wxT("Cursor COMMIT Behavior: ");
1127 switch(dbInf
.cursorCommitBehavior
)
1129 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1130 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1131 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1135 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1136 switch(dbInf
.cursorRollbackBehavior
)
1138 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1139 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1140 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1144 cout
<< wxT("Support NOT NULL clause: ");
1145 switch(dbInf
.supportNotNullClause
)
1147 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1148 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1152 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1153 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1155 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1158 cout
<< wxT("Default Transaction Isolation: ";
1159 switch(dbInf
.txnIsolation
)
1161 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1162 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1163 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1164 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1166 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1171 cout
<< wxT("Transaction Isolation Options: ");
1172 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1173 cout
<< wxT("Read Uncommitted, ");
1174 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1175 cout
<< wxT("Read Committed, ");
1176 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1177 cout
<< wxT("Repeatable Read, ");
1178 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1179 cout
<< wxT("Serializable, ");
1181 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1182 cout
<< wxT("Versioning");
1186 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1187 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1188 cout
<< wxT("Next, ");
1189 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1190 cout
<< wxT("Prev, ");
1191 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1192 cout
<< wxT("First, ");
1193 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1194 cout
<< wxT("Last, ");
1195 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1196 cout
<< wxT("Absolute, ");
1197 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1198 cout
<< wxT("Relative, ");
1200 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1201 cout
<< wxT("Resume, ");
1203 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1204 cout
<< wxT("Bookmark");
1207 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1208 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1209 cout
<< wxT("No Change, ");
1210 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1211 cout
<< wxT("Exclusive, ");
1212 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1213 cout
<< wxT("UnLock");
1216 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1217 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1218 cout
<< wxT("Position, ");
1219 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1220 cout
<< wxT("Refresh, ");
1221 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1222 cout
<< wxT("Upd, "));
1223 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1224 cout
<< wxT("Del, ");
1225 if (dbInf
.posOperations
& SQL_POS_ADD
)
1229 cout
<< wxT("Positioned Statements Supported: ");
1230 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1231 cout
<< wxT("Pos delete, ");
1232 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1233 cout
<< wxT("Pos update, ");
1234 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1235 cout
<< wxT("Select for update");
1238 cout
<< wxT("Scroll Concurrency: ");
1239 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1240 cout
<< wxT("Read Only, ");
1241 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1242 cout
<< wxT("Lock, ");
1243 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1244 cout
<< wxT("Opt. Rowver, ");
1245 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1246 cout
<< wxT("Opt. Values");
1249 cout
<< wxT("Scroll Options: ");
1250 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1251 cout
<< wxT("Fwd Only, ");
1252 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1253 cout
<< wxT("Static, ");
1254 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1255 cout
<< wxT("Keyset Driven, ");
1256 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1257 cout
<< wxT("Dynamic, ");
1258 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1259 cout
<< wxT("Mixed");
1262 cout
<< wxT("Static Sensitivity: ");
1263 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1264 cout
<< wxT("Additions, ");
1265 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1266 cout
<< wxT("Deletions, ");
1267 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1268 cout
<< wxT("Updates");
1271 cout
<< wxT("Transaction Capable?: ");
1272 switch(dbInf
.txnCapable
)
1274 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1275 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1276 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1277 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1278 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1285 // Completed Successfully
1288 } // wxDb::getDbInfo()
1291 /********** wxDb::getDataTypeInfo() **********/
1292 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1295 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1296 * the data type inf. is gathered for.
1298 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1303 // Get information about the data type specified
1304 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1305 return(DispAllErrors(henv
, hdbc
, hstmt
));
1308 retcode
= SQLFetch(hstmt
);
1309 if (retcode
!= SQL_SUCCESS
)
1311 #ifdef DBDEBUG_CONSOLE
1312 if (retcode
== SQL_NO_DATA_FOUND
)
1313 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1315 DispAllErrors(henv
, hdbc
, hstmt
);
1316 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1320 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1322 // Obtain columns from the record
1323 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1324 return(DispAllErrors(henv
, hdbc
, hstmt
));
1326 structSQLTypeInfo
.TypeName
= typeName
;
1328 // BJO 20000503: no more needed with new GetColumns...
1331 if (Dbms() == dbmsMY_SQL
)
1333 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1334 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1335 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1336 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1337 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1338 structSQLTypeInfo
.TypeName
= wxT("int");
1339 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1340 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1341 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1342 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1343 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1344 structSQLTypeInfo
.TypeName
= wxT("char");
1347 // BJO 20000427 : OpenLink driver
1348 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1349 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1351 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1352 structSQLTypeInfo
.TypeName
= wxT("real");
1356 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1357 return(DispAllErrors(henv
, hdbc
, hstmt
));
1358 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1359 return(DispAllErrors(henv
, hdbc
, hstmt
));
1360 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1361 // return(DispAllErrors(henv, hdbc, hstmt));
1363 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1364 return(DispAllErrors(henv
, hdbc
, hstmt
));
1366 if (structSQLTypeInfo
.MaximumScale
< 0)
1367 structSQLTypeInfo
.MaximumScale
= 0;
1369 // Close the statement handle which closes open cursors
1370 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1371 return(DispAllErrors(henv
, hdbc
, hstmt
));
1373 // Completed Successfully
1376 } // wxDb::getDataTypeInfo()
1379 /********** wxDb::Close() **********/
1380 void wxDb::Close(void)
1382 // Close the Sql Log file
1389 // Free statement handle
1392 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1393 DispAllErrors(henv
, hdbc
);
1396 // Disconnect from the datasource
1397 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1398 DispAllErrors(henv
, hdbc
);
1400 // Free the connection to the datasource
1401 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1402 DispAllErrors(henv
, hdbc
);
1404 // There should be zero Ctable objects still connected to this db object
1405 wxASSERT(nTables
== 0);
1410 pNode
= TablesInUse
.First();
1414 tiu
= (wxTablesInUse
*)pNode
->Data();
1415 if (tiu
->pDb
== this)
1417 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1418 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1419 wxLogDebug (s
.c_str(),s2
.c_str());
1421 pNode
= pNode
->Next();
1425 // Copy the error messages to a global variable
1427 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1428 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1430 dbmsType
= dbmsUNIDENTIFIED
;
1436 /********** wxDb::CommitTrans() **********/
1437 bool wxDb::CommitTrans(void)
1441 // Commit the transaction
1442 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1443 return(DispAllErrors(henv
, hdbc
));
1446 // Completed successfully
1449 } // wxDb::CommitTrans()
1452 /********** wxDb::RollbackTrans() **********/
1453 bool wxDb::RollbackTrans(void)
1455 // Rollback the transaction
1456 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1457 return(DispAllErrors(henv
, hdbc
));
1459 // Completed successfully
1462 } // wxDb::RollbackTrans()
1465 /********** wxDb::DispAllErrors() **********/
1466 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1468 * This function is called internally whenever an error condition prevents the user's
1469 * request from being executed. This function will query the datasource as to the
1470 * actual error(s) that just occured on the previous request of the datasource.
1472 * The function will retrieve each error condition from the datasource and
1473 * Printf the codes/text values into a string which it then logs via logError().
1474 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1475 * window and program execution will be paused until the user presses a key.
1477 * This function always returns a FALSE, so that functions which call this function
1478 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1479 * of the users request, so that the calling code can then process the error msg log
1482 wxString odbcErrMsg
;
1484 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1486 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1487 logError(odbcErrMsg
, sqlState
);
1490 #ifdef DBDEBUG_CONSOLE
1491 // When run in console mode, use standard out to display errors.
1492 cout
<< odbcErrMsg
.c_str() << endl
;
1493 cout
<< wxT("Press any key to continue...") << endl
;
1498 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1503 return(FALSE
); // This function always returns FALSE.
1505 } // wxDb::DispAllErrors()
1508 /********** wxDb::GetNextError() **********/
1509 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1511 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1516 } // wxDb::GetNextError()
1519 /********** wxDb::DispNextError() **********/
1520 void wxDb::DispNextError(void)
1522 wxString odbcErrMsg
;
1524 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1525 logError(odbcErrMsg
, sqlState
);
1530 #ifdef DBDEBUG_CONSOLE
1531 // When run in console mode, use standard out to display errors.
1532 cout
<< odbcErrMsg
.c_str() << endl
;
1533 cout
<< wxT("Press any key to continue...") << endl
;
1538 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1539 #endif // __WXDEBUG__
1541 } // wxDb::DispNextError()
1544 /********** wxDb::logError() **********/
1545 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1547 wxASSERT(errMsg
.Length());
1549 static int pLast
= -1;
1552 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1555 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1556 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1560 wxStrcpy(errorList
[pLast
], errMsg
);
1562 if (SQLState
.Length())
1563 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1564 DB_STATUS
= dbStatus
;
1566 // Add the errmsg to the sql log
1567 WriteSqlLog(errMsg
);
1569 } // wxDb::logError()
1572 /**********wxDb::TranslateSqlState() **********/
1573 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1575 if (!wxStrcmp(SQLState
, wxT("01000")))
1576 return(DB_ERR_GENERAL_WARNING
);
1577 if (!wxStrcmp(SQLState
, wxT("01002")))
1578 return(DB_ERR_DISCONNECT_ERROR
);
1579 if (!wxStrcmp(SQLState
, wxT("01004")))
1580 return(DB_ERR_DATA_TRUNCATED
);
1581 if (!wxStrcmp(SQLState
, wxT("01006")))
1582 return(DB_ERR_PRIV_NOT_REVOKED
);
1583 if (!wxStrcmp(SQLState
, wxT("01S00")))
1584 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1585 if (!wxStrcmp(SQLState
, wxT("01S01")))
1586 return(DB_ERR_ERROR_IN_ROW
);
1587 if (!wxStrcmp(SQLState
, wxT("01S02")))
1588 return(DB_ERR_OPTION_VALUE_CHANGED
);
1589 if (!wxStrcmp(SQLState
, wxT("01S03")))
1590 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1591 if (!wxStrcmp(SQLState
, wxT("01S04")))
1592 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1593 if (!wxStrcmp(SQLState
, wxT("07001")))
1594 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1595 if (!wxStrcmp(SQLState
, wxT("07006")))
1596 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1597 if (!wxStrcmp(SQLState
, wxT("08001")))
1598 return(DB_ERR_UNABLE_TO_CONNECT
);
1599 if (!wxStrcmp(SQLState
, wxT("08002")))
1600 return(DB_ERR_CONNECTION_IN_USE
);
1601 if (!wxStrcmp(SQLState
, wxT("08003")))
1602 return(DB_ERR_CONNECTION_NOT_OPEN
);
1603 if (!wxStrcmp(SQLState
, wxT("08004")))
1604 return(DB_ERR_REJECTED_CONNECTION
);
1605 if (!wxStrcmp(SQLState
, wxT("08007")))
1606 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1607 if (!wxStrcmp(SQLState
, wxT("08S01")))
1608 return(DB_ERR_COMM_LINK_FAILURE
);
1609 if (!wxStrcmp(SQLState
, wxT("21S01")))
1610 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1611 if (!wxStrcmp(SQLState
, wxT("21S02")))
1612 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1613 if (!wxStrcmp(SQLState
, wxT("22001")))
1614 return(DB_ERR_STRING_RIGHT_TRUNC
);
1615 if (!wxStrcmp(SQLState
, wxT("22003")))
1616 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1617 if (!wxStrcmp(SQLState
, wxT("22005")))
1618 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1619 if (!wxStrcmp(SQLState
, wxT("22008")))
1620 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1621 if (!wxStrcmp(SQLState
, wxT("22012")))
1622 return(DB_ERR_DIVIDE_BY_ZERO
);
1623 if (!wxStrcmp(SQLState
, wxT("22026")))
1624 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1625 if (!wxStrcmp(SQLState
, wxT("23000")))
1626 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1627 if (!wxStrcmp(SQLState
, wxT("24000")))
1628 return(DB_ERR_INVALID_CURSOR_STATE
);
1629 if (!wxStrcmp(SQLState
, wxT("25000")))
1630 return(DB_ERR_INVALID_TRANS_STATE
);
1631 if (!wxStrcmp(SQLState
, wxT("28000")))
1632 return(DB_ERR_INVALID_AUTH_SPEC
);
1633 if (!wxStrcmp(SQLState
, wxT("34000")))
1634 return(DB_ERR_INVALID_CURSOR_NAME
);
1635 if (!wxStrcmp(SQLState
, wxT("37000")))
1636 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1637 if (!wxStrcmp(SQLState
, wxT("3C000")))
1638 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1639 if (!wxStrcmp(SQLState
, wxT("40001")))
1640 return(DB_ERR_SERIALIZATION_FAILURE
);
1641 if (!wxStrcmp(SQLState
, wxT("42000")))
1642 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1643 if (!wxStrcmp(SQLState
, wxT("70100")))
1644 return(DB_ERR_OPERATION_ABORTED
);
1645 if (!wxStrcmp(SQLState
, wxT("IM001")))
1646 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1647 if (!wxStrcmp(SQLState
, wxT("IM002")))
1648 return(DB_ERR_NO_DATA_SOURCE
);
1649 if (!wxStrcmp(SQLState
, wxT("IM003")))
1650 return(DB_ERR_DRIVER_LOAD_ERROR
);
1651 if (!wxStrcmp(SQLState
, wxT("IM004")))
1652 return(DB_ERR_SQLALLOCENV_FAILED
);
1653 if (!wxStrcmp(SQLState
, wxT("IM005")))
1654 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1655 if (!wxStrcmp(SQLState
, wxT("IM006")))
1656 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1657 if (!wxStrcmp(SQLState
, wxT("IM007")))
1658 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1659 if (!wxStrcmp(SQLState
, wxT("IM008")))
1660 return(DB_ERR_DIALOG_FAILED
);
1661 if (!wxStrcmp(SQLState
, wxT("IM009")))
1662 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1663 if (!wxStrcmp(SQLState
, wxT("IM010")))
1664 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1665 if (!wxStrcmp(SQLState
, wxT("IM011")))
1666 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1667 if (!wxStrcmp(SQLState
, wxT("IM012")))
1668 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1669 if (!wxStrcmp(SQLState
, wxT("IM013")))
1670 return(DB_ERR_TRACE_FILE_ERROR
);
1671 if (!wxStrcmp(SQLState
, wxT("S0001")))
1672 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1673 if (!wxStrcmp(SQLState
, wxT("S0002")))
1674 return(DB_ERR_TABLE_NOT_FOUND
);
1675 if (!wxStrcmp(SQLState
, wxT("S0011")))
1676 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1677 if (!wxStrcmp(SQLState
, wxT("S0012")))
1678 return(DB_ERR_INDEX_NOT_FOUND
);
1679 if (!wxStrcmp(SQLState
, wxT("S0021")))
1680 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1681 if (!wxStrcmp(SQLState
, wxT("S0022")))
1682 return(DB_ERR_COLUMN_NOT_FOUND
);
1683 if (!wxStrcmp(SQLState
, wxT("S0023")))
1684 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1685 if (!wxStrcmp(SQLState
, wxT("S1000")))
1686 return(DB_ERR_GENERAL_ERROR
);
1687 if (!wxStrcmp(SQLState
, wxT("S1001")))
1688 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1689 if (!wxStrcmp(SQLState
, wxT("S1002")))
1690 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1691 if (!wxStrcmp(SQLState
, wxT("S1003")))
1692 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1693 if (!wxStrcmp(SQLState
, wxT("S1004")))
1694 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1695 if (!wxStrcmp(SQLState
, wxT("S1008")))
1696 return(DB_ERR_OPERATION_CANCELLED
);
1697 if (!wxStrcmp(SQLState
, wxT("S1009")))
1698 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1699 if (!wxStrcmp(SQLState
, wxT("S1010")))
1700 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1701 if (!wxStrcmp(SQLState
, wxT("S1011")))
1702 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1703 if (!wxStrcmp(SQLState
, wxT("S1012")))
1704 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1705 if (!wxStrcmp(SQLState
, wxT("S1015")))
1706 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1707 if (!wxStrcmp(SQLState
, wxT("S1090")))
1708 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1709 if (!wxStrcmp(SQLState
, wxT("S1091")))
1710 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1711 if (!wxStrcmp(SQLState
, wxT("S1092")))
1712 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1713 if (!wxStrcmp(SQLState
, wxT("S1093")))
1714 return(DB_ERR_INVALID_PARAM_NO
);
1715 if (!wxStrcmp(SQLState
, wxT("S1094")))
1716 return(DB_ERR_INVALID_SCALE_VALUE
);
1717 if (!wxStrcmp(SQLState
, wxT("S1095")))
1718 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1719 if (!wxStrcmp(SQLState
, wxT("S1096")))
1720 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1721 if (!wxStrcmp(SQLState
, wxT("S1097")))
1722 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1723 if (!wxStrcmp(SQLState
, wxT("S1098")))
1724 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1725 if (!wxStrcmp(SQLState
, wxT("S1099")))
1726 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1727 if (!wxStrcmp(SQLState
, wxT("S1100")))
1728 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1729 if (!wxStrcmp(SQLState
, wxT("S1101")))
1730 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1731 if (!wxStrcmp(SQLState
, wxT("S1103")))
1732 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1733 if (!wxStrcmp(SQLState
, wxT("S1104")))
1734 return(DB_ERR_INVALID_PRECISION_VALUE
);
1735 if (!wxStrcmp(SQLState
, wxT("S1105")))
1736 return(DB_ERR_INVALID_PARAM_TYPE
);
1737 if (!wxStrcmp(SQLState
, wxT("S1106")))
1738 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1739 if (!wxStrcmp(SQLState
, wxT("S1107")))
1740 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1741 if (!wxStrcmp(SQLState
, wxT("S1108")))
1742 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1743 if (!wxStrcmp(SQLState
, wxT("S1109")))
1744 return(DB_ERR_INVALID_CURSOR_POSITION
);
1745 if (!wxStrcmp(SQLState
, wxT("S1110")))
1746 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1747 if (!wxStrcmp(SQLState
, wxT("S1111")))
1748 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1749 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1750 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1751 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1752 return(DB_ERR_TIMEOUT_EXPIRED
);
1757 } // wxDb::TranslateSqlState()
1760 /********** wxDb::Grant() **********/
1761 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1765 // Build the grant statement
1766 sqlStmt
= wxT("GRANT ");
1767 if (privileges
== DB_GRANT_ALL
)
1768 sqlStmt
+= wxT("ALL");
1772 if (privileges
& DB_GRANT_SELECT
)
1774 sqlStmt
+= wxT("SELECT");
1777 if (privileges
& DB_GRANT_INSERT
)
1780 sqlStmt
+= wxT(", ");
1781 sqlStmt
+= wxT("INSERT");
1783 if (privileges
& DB_GRANT_UPDATE
)
1786 sqlStmt
+= wxT(", ");
1787 sqlStmt
+= wxT("UPDATE");
1789 if (privileges
& DB_GRANT_DELETE
)
1792 sqlStmt
+= wxT(", ");
1793 sqlStmt
+= wxT("DELETE");
1797 sqlStmt
+= wxT(" ON ");
1798 sqlStmt
+= SQLTableName(tableName
);
1799 sqlStmt
+= wxT(" TO ");
1800 sqlStmt
+= userList
;
1802 #ifdef DBDEBUG_CONSOLE
1803 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1806 WriteSqlLog(sqlStmt
);
1808 return(ExecSql(sqlStmt
));
1813 /********** wxDb::CreateView() **********/
1814 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1815 const wxString
&pSqlStmt
, bool attemptDrop
)
1819 // Drop the view first
1820 if (attemptDrop
&& !DropView(viewName
))
1823 // Build the create view statement
1824 sqlStmt
= wxT("CREATE VIEW ");
1825 sqlStmt
+= viewName
;
1827 if (colList
.Length())
1829 sqlStmt
+= wxT(" (");
1831 sqlStmt
+= wxT(")");
1834 sqlStmt
+= wxT(" AS ");
1835 sqlStmt
+= pSqlStmt
;
1837 WriteSqlLog(sqlStmt
);
1839 #ifdef DBDEBUG_CONSOLE
1840 cout
<< sqlStmt
.c_str() << endl
;
1843 return(ExecSql(sqlStmt
));
1845 } // wxDb::CreateView()
1848 /********** wxDb::DropView() **********/
1849 bool wxDb::DropView(const wxString
&viewName
)
1852 * NOTE: This function returns TRUE if the View does not exist, but
1853 * only for identified databases. Code will need to be added
1854 * below for any other databases when those databases are defined
1855 * to handle this situation consistently
1859 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1861 WriteSqlLog(sqlStmt
);
1863 #ifdef DBDEBUG_CONSOLE
1864 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1867 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1869 // Check for "Base table not found" error and ignore
1870 GetNextError(henv
, hdbc
, hstmt
);
1871 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1873 // Check for product specific error codes
1874 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1877 DispAllErrors(henv
, hdbc
, hstmt
);
1884 // Commit the transaction
1890 } // wxDb::DropView()
1893 /********** wxDb::ExecSql() **********/
1894 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1898 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1900 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
1901 if (retcode
== SQL_SUCCESS
||
1902 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
1908 DispAllErrors(henv
, hdbc
, hstmt
);
1912 } // wxDb::ExecSql()
1915 /********** wxDb::GetNext() **********/
1916 bool wxDb::GetNext(void)
1918 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1922 DispAllErrors(henv
, hdbc
, hstmt
);
1926 } // wxDb::GetNext()
1929 /********** wxDb::GetData() **********/
1930 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1933 wxASSERT(cbReturned
);
1935 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1939 DispAllErrors(henv
, hdbc
, hstmt
);
1943 } // wxDb::GetData()
1946 /********** wxDb::GetKeyFields() **********/
1947 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1949 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1950 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1952 // SQLSMALLINT iKeySeq;
1953 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1954 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1960 * -----------------------------------------------------------------------
1961 * -- 19991224 : mj10777 : Create ------
1962 * -- : Three things are done and stored here : ------
1963 * -- : 1) which Column(s) is/are Primary Key(s) ------
1964 * -- : 2) which tables use this Key as a Foreign Key ------
1965 * -- : 3) which columns are Foreign Key and the name ------
1966 * -- : of the Table where the Key is the Primary Key -----
1967 * -- : Called from GetColumns(const wxString &tableName, ------
1968 * -- int *numCols,const wxChar *userID ) ------
1969 * -----------------------------------------------------------------------
1972 /*---------------------------------------------------------------------*/
1973 /* Get the names of the columns in the primary key. */
1974 /*---------------------------------------------------------------------*/
1975 retcode
= SQLPrimaryKeys(hstmt
,
1976 NULL
, 0, /* Catalog name */
1977 NULL
, 0, /* Schema name */
1978 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1980 /*---------------------------------------------------------------------*/
1981 /* Fetch and display the result set. This will be a list of the */
1982 /* columns in the primary key of the tableName table. */
1983 /*---------------------------------------------------------------------*/
1984 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1986 retcode
= SQLFetch(hstmt
);
1987 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1989 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1990 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1992 for (i
=0;i
<noCols
;i
++) // Find the Column name
1993 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1994 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1997 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1999 /*---------------------------------------------------------------------*/
2000 /* Get all the foreign keys that refer to tableName primary key. */
2001 /*---------------------------------------------------------------------*/
2002 retcode
= SQLForeignKeys(hstmt
,
2003 NULL
, 0, /* Primary catalog */
2004 NULL
, 0, /* Primary schema */
2005 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2006 NULL
, 0, /* Foreign catalog */
2007 NULL
, 0, /* Foreign schema */
2008 NULL
, 0); /* Foreign table */
2010 /*---------------------------------------------------------------------*/
2011 /* Fetch and display the result set. This will be all of the foreign */
2012 /* keys in other tables that refer to the tableName primary key. */
2013 /*---------------------------------------------------------------------*/
2016 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2018 retcode
= SQLFetch(hstmt
);
2019 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2021 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2022 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2023 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2024 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2025 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2026 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2030 tempStr
.Trim(); // Get rid of any unneeded blanks
2031 if (!tempStr
.IsEmpty())
2033 for (i
=0; i
<noCols
; i
++)
2034 { // Find the Column name
2035 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2036 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2040 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2042 /*---------------------------------------------------------------------*/
2043 /* Get all the foreign keys in the tablename table. */
2044 /*---------------------------------------------------------------------*/
2045 retcode
= SQLForeignKeys(hstmt
,
2046 NULL
, 0, /* Primary catalog */
2047 NULL
, 0, /* Primary schema */
2048 NULL
, 0, /* Primary table */
2049 NULL
, 0, /* Foreign catalog */
2050 NULL
, 0, /* Foreign schema */
2051 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2053 /*---------------------------------------------------------------------*/
2054 /* Fetch and display the result set. This will be all of the */
2055 /* primary keys in other tables that are referred to by foreign */
2056 /* keys in the tableName table. */
2057 /*---------------------------------------------------------------------*/
2059 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2061 retcode
= SQLFetch(hstmt
);
2062 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2064 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2065 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2066 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2068 for (i
=0; i
<noCols
; i
++) // Find the Column name
2070 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2072 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2073 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2078 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2082 } // wxDb::GetKeyFields()
2086 /********** wxDb::GetColumns() **********/
2087 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2089 * 1) The last array element of the tableName[] argument must be zero (null).
2090 * This is how the end of the array is detected.
2091 * 2) This function returns an array of wxDbColInf structures. If no columns
2092 * were found, or an error occured, this pointer will be zero (null). THE
2093 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2094 * IS FINISHED WITH IT. i.e.
2096 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2099 * // Use the column inf
2101 * // Destroy the memory
2105 * userID is evaluated in the following manner:
2106 * userID == NULL ... UserID is ignored
2107 * userID == "" ... UserID set equal to 'this->uid'
2108 * userID != "" ... UserID set equal to 'userID'
2110 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2111 * by this function. This function should use its own wxDb instance
2112 * to avoid undesired unbinding of columns.
2117 wxDbColInf
*colInf
= 0;
2125 convertUserID(userID
,UserID
);
2127 // Pass 1 - Determine how many columns there are.
2128 // Pass 2 - Allocate the wxDbColInf array and fill in
2129 // the array with the column information.
2131 for (pass
= 1; pass
<= 2; pass
++)
2135 if (noCols
== 0) // Probably a bogus table name(s)
2137 // Allocate n wxDbColInf objects to hold the column information
2138 colInf
= new wxDbColInf
[noCols
+1];
2141 // Mark the end of the array
2142 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2143 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2144 colInf
[noCols
].sqlDataType
= 0;
2146 // Loop through each table name
2148 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2150 TableName
= tableName
[tbl
];
2151 // Oracle and Interbase table names are uppercase only, so force
2152 // the name to uppercase just in case programmer forgot to do this
2153 if ((Dbms() == dbmsORACLE
) ||
2154 (Dbms() == dbmsINTERBASE
))
2155 TableName
= TableName
.Upper();
2157 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2159 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2160 // use the call below that leaves out the user name
2161 if (!UserID
.IsEmpty() &&
2162 Dbms() != dbmsMY_SQL
&&
2163 Dbms() != dbmsACCESS
&&
2164 Dbms() != dbmsMS_SQL_SERVER
)
2166 retcode
= SQLColumns(hstmt
,
2167 NULL
, 0, // All qualifiers
2168 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2169 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2170 NULL
, 0); // All columns
2174 retcode
= SQLColumns(hstmt
,
2175 NULL
, 0, // All qualifiers
2177 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2178 NULL
, 0); // All columns
2180 if (retcode
!= SQL_SUCCESS
)
2181 { // Error occured, abort
2182 DispAllErrors(henv
, hdbc
, hstmt
);
2185 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2189 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2191 if (pass
== 1) // First pass, just add up the number of columns
2193 else // Pass 2; Fill in the array of structures
2195 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2197 // NOTE: Only the ODBC 1.x fields are retrieved
2198 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2199 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2200 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2201 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2202 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2203 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2204 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2205 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2206 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2207 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2208 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2209 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2211 // Determine the wxDb data type that is used to represent the native data type of this data source
2212 colInf
[colNo
].dbDataType
= 0;
2213 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2216 // IODBC does not return a correct columnSize, so we set
2217 // columnSize = bufferLength if no column size was returned
2218 // IODBC returns the columnSize in bufferLength.. (bug)
2219 if (colInf
[colNo
].columnSize
< 1)
2221 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2224 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2226 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2227 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2228 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2229 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2230 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2231 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2232 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2233 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2238 if (retcode
!= SQL_NO_DATA_FOUND
)
2239 { // Error occured, abort
2240 DispAllErrors(henv
, hdbc
, hstmt
);
2243 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2249 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2252 } // wxDb::GetColumns()
2255 /********** wxDb::GetColumns() **********/
2257 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2259 // Same as the above GetColumns() function except this one gets columns
2260 // only for a single table, and if 'numCols' is not NULL, the number of
2261 // columns stored in the returned wxDbColInf is set in '*numCols'
2263 // userID is evaluated in the following manner:
2264 // userID == NULL ... UserID is ignored
2265 // userID == "" ... UserID set equal to 'this->uid'
2266 // userID != "" ... UserID set equal to 'userID'
2268 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2269 // by this function. This function should use its own wxDb instance
2270 // to avoid undesired unbinding of columns.
2275 wxDbColInf
*colInf
= 0;
2283 convertUserID(userID
,UserID
);
2285 // Pass 1 - Determine how many columns there are.
2286 // Pass 2 - Allocate the wxDbColInf array and fill in
2287 // the array with the column information.
2289 for (pass
= 1; pass
<= 2; pass
++)
2293 if (noCols
== 0) // Probably a bogus table name(s)
2295 // Allocate n wxDbColInf objects to hold the column information
2296 colInf
= new wxDbColInf
[noCols
+1];
2299 // Mark the end of the array
2300 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2301 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2302 colInf
[noCols
].sqlDataType
= 0;
2305 TableName
= tableName
;
2306 // Oracle and Interbase table names are uppercase only, so force
2307 // the name to uppercase just in case programmer forgot to do this
2308 if ((Dbms() == dbmsORACLE
) ||
2309 (Dbms() == dbmsINTERBASE
))
2310 TableName
= TableName
.Upper();
2312 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2314 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2315 // use the call below that leaves out the user name
2316 if (!UserID
.IsEmpty() &&
2317 Dbms() != dbmsMY_SQL
&&
2318 Dbms() != dbmsACCESS
&&
2319 Dbms() != dbmsMS_SQL_SERVER
)
2321 retcode
= SQLColumns(hstmt
,
2322 NULL
, 0, // All qualifiers
2323 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2324 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2325 NULL
, 0); // All columns
2329 retcode
= SQLColumns(hstmt
,
2330 NULL
, 0, // All qualifiers
2332 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2333 NULL
, 0); // All columns
2335 if (retcode
!= SQL_SUCCESS
)
2336 { // Error occured, abort
2337 DispAllErrors(henv
, hdbc
, hstmt
);
2340 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2346 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2348 if (pass
== 1) // First pass, just add up the number of columns
2350 else // Pass 2; Fill in the array of structures
2352 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2354 // NOTE: Only the ODBC 1.x fields are retrieved
2355 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2356 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2357 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2358 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2359 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2360 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2361 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2362 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2363 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2364 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2365 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2366 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2367 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2368 // Start Values for Primary/Foriegn Key (=No)
2369 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2370 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2371 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2372 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2374 // BJO 20000428 : Virtuoso returns type names with upper cases!
2375 if (Dbms() == dbmsVIRTUOSO
)
2377 wxString s
= colInf
[colNo
].typeName
;
2379 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2382 // Determine the wxDb data type that is used to represent the native data type of this data source
2383 colInf
[colNo
].dbDataType
= 0;
2384 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2387 // IODBC does not return a correct columnSize, so we set
2388 // columnSize = bufferLength if no column size was returned
2389 // IODBC returns the columnSize in bufferLength.. (bug)
2390 if (colInf
[colNo
].columnSize
< 1)
2392 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2396 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2398 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2399 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2400 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2401 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2402 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2403 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2404 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2405 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2411 if (retcode
!= SQL_NO_DATA_FOUND
)
2412 { // Error occured, abort
2413 DispAllErrors(henv
, hdbc
, hstmt
);
2416 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2423 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2425 // Store Primary and Foriegn Keys
2426 GetKeyFields(tableName
,colInf
,noCols
);
2432 } // wxDb::GetColumns()
2435 #else // New GetColumns
2440 These are tentative new GetColumns members which should be more database
2441 independant and which always returns the columns in the order they were
2444 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2445 wxChar* userID)) calls the second implementation for each separate table
2446 before merging the results. This makes the code easier to maintain as
2447 only one member (the second) makes the real work
2448 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2449 wxChar *userID) is a little bit improved
2450 - It doesn't anymore rely on the type-name to find out which database-type
2452 - It ends by sorting the columns, so that they are returned in the same
2453 order they were created
2463 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2466 // The last array element of the tableName[] argument must be zero (null).
2467 // This is how the end of the array is detected.
2471 // How many tables ?
2473 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2475 // Create a table to maintain the columns for each separate table
2476 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2479 for (i
= 0 ; i
< tbl
; i
++)
2482 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2483 if (TableColumns
[i
].colInf
== NULL
)
2485 noCols
+= TableColumns
[i
].noCols
;
2488 // Now merge all the separate table infos
2489 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2491 // Mark the end of the array
2492 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2493 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2494 colInf
[noCols
].sqlDataType
= 0;
2499 for (i
= 0 ; i
< tbl
; i
++)
2501 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2503 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2507 delete [] TableColumns
;
2510 } // wxDb::GetColumns() -- NEW
2513 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2515 // Same as the above GetColumns() function except this one gets columns
2516 // only for a single table, and if 'numCols' is not NULL, the number of
2517 // columns stored in the returned wxDbColInf is set in '*numCols'
2519 // userID is evaluated in the following manner:
2520 // userID == NULL ... UserID is ignored
2521 // userID == "" ... UserID set equal to 'this->uid'
2522 // userID != "" ... UserID set equal to 'userID'
2524 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2525 // by this function. This function should use its own wxDb instance
2526 // to avoid undesired unbinding of columns.
2530 wxDbColInf
*colInf
= 0;
2538 convertUserID(userID
,UserID
);
2540 // Pass 1 - Determine how many columns there are.
2541 // Pass 2 - Allocate the wxDbColInf array and fill in
2542 // the array with the column information.
2544 for (pass
= 1; pass
<= 2; pass
++)
2548 if (noCols
== 0) // Probably a bogus table name(s)
2550 // Allocate n wxDbColInf objects to hold the column information
2551 colInf
= new wxDbColInf
[noCols
+1];
2554 // Mark the end of the array
2555 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2556 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2557 colInf
[noCols
].sqlDataType
= 0;
2560 TableName
= tableName
;
2561 // Oracle and Interbase table names are uppercase only, so force
2562 // the name to uppercase just in case programmer forgot to do this
2563 if ((Dbms() == dbmsORACLE
) ||
2564 (Dbms() == dbmsINTERBASE
))
2565 TableName
= TableName
.Upper();
2567 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2569 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2570 // use the call below that leaves out the user name
2571 if (!UserID
.IsEmpty() &&
2572 Dbms() != dbmsMY_SQL
&&
2573 Dbms() != dbmsACCESS
&&
2574 Dbms() != dbmsMS_SQL_SERVER
)
2576 retcode
= SQLColumns(hstmt
,
2577 NULL
, 0, // All qualifiers
2578 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2579 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2580 NULL
, 0); // All columns
2584 retcode
= SQLColumns(hstmt
,
2585 NULL
, 0, // All qualifiers
2587 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2588 NULL
, 0); // All columns
2590 if (retcode
!= SQL_SUCCESS
)
2591 { // Error occured, abort
2592 DispAllErrors(henv
, hdbc
, hstmt
);
2595 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2601 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2603 if (pass
== 1) // First pass, just add up the number of columns
2605 else // Pass 2; Fill in the array of structures
2607 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2609 // NOTE: Only the ODBC 1.x fields are retrieved
2610 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2611 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2612 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2613 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2614 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2615 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2616 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2617 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2618 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2619 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2620 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2621 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2622 // Start Values for Primary/Foriegn Key (=No)
2623 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2624 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2625 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2626 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2629 // IODBC does not return a correct columnSize, so we set
2630 // columnSize = bufferLength if no column size was returned
2631 // IODBC returns the columnSize in bufferLength.. (bug)
2632 if (colInf
[colNo
].columnSize
< 1)
2634 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2638 // Determine the wxDb data type that is used to represent the native data type of this data source
2639 colInf
[colNo
].dbDataType
= 0;
2640 // Get the intern datatype
2641 switch (colInf
[colNo
].sqlDataType
)
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2651 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2658 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2661 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2664 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2669 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2670 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2677 if (retcode
!= SQL_NO_DATA_FOUND
)
2678 { // Error occured, abort
2679 DispAllErrors(henv
, hdbc
, hstmt
);
2682 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2689 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2691 // Store Primary and Foreign Keys
2692 GetKeyFields(tableName
,colInf
,noCols
);
2694 ///////////////////////////////////////////////////////////////////////////
2695 // Now sort the the columns in order to make them appear in the right order
2696 ///////////////////////////////////////////////////////////////////////////
2698 // Build a generic SELECT statement which returns 0 rows
2701 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2704 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2706 DispAllErrors(henv
, hdbc
, hstmt
);
2710 // Get the number of result columns
2711 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2713 DispAllErrors(henv
, hdbc
, hstmt
);
2717 if (noCols
== 0) // Probably a bogus table name
2726 for (colNum
= 0; colNum
< noCols
; colNum
++)
2728 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2730 &Sword
, &Sdword
) != SQL_SUCCESS
)
2732 DispAllErrors(henv
, hdbc
, hstmt
);
2736 wxString Name1
= name
;
2737 Name1
= Name1
.Upper();
2739 // Where is this name in the array ?
2740 for (i
= colNum
; i
< noCols
; i
++)
2742 wxString Name2
= colInf
[i
].colName
;
2743 Name2
= Name2
.Upper();
2746 if (colNum
!= i
) // swap to sort
2748 wxDbColInf tmpColInf
= colInf
[colNum
];
2749 colInf
[colNum
] = colInf
[i
];
2750 colInf
[i
] = tmpColInf
;
2756 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2758 ///////////////////////////////////////////////////////////////////////////
2760 ///////////////////////////////////////////////////////////////////////////
2766 } // wxDb::GetColumns()
2769 #endif // #else OLD_GETCOLUMNS
2772 /********** wxDb::GetColumnCount() **********/
2773 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2775 * Returns a count of how many columns are in a table.
2776 * If an error occurs in computing the number of columns
2777 * this function will return a -1 for the count
2779 * userID is evaluated in the following manner:
2780 * userID == NULL ... UserID is ignored
2781 * userID == "" ... UserID set equal to 'this->uid'
2782 * userID != "" ... UserID set equal to 'userID'
2784 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2785 * by this function. This function should use its own wxDb instance
2786 * to avoid undesired unbinding of columns.
2796 convertUserID(userID
,UserID
);
2798 TableName
= tableName
;
2799 // Oracle and Interbase table names are uppercase only, so force
2800 // the name to uppercase just in case programmer forgot to do this
2801 if ((Dbms() == dbmsORACLE
) ||
2802 (Dbms() == dbmsINTERBASE
))
2803 TableName
= TableName
.Upper();
2805 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2807 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2808 // use the call below that leaves out the user name
2809 if (!UserID
.IsEmpty() &&
2810 Dbms() != dbmsMY_SQL
&&
2811 Dbms() != dbmsACCESS
&&
2812 Dbms() != dbmsMS_SQL_SERVER
)
2814 retcode
= SQLColumns(hstmt
,
2815 NULL
, 0, // All qualifiers
2816 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2817 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2818 NULL
, 0); // All columns
2822 retcode
= SQLColumns(hstmt
,
2823 NULL
, 0, // All qualifiers
2825 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2826 NULL
, 0); // All columns
2828 if (retcode
!= SQL_SUCCESS
)
2829 { // Error occured, abort
2830 DispAllErrors(henv
, hdbc
, hstmt
);
2831 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2835 // Count the columns
2836 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2839 if (retcode
!= SQL_NO_DATA_FOUND
)
2840 { // Error occured, abort
2841 DispAllErrors(henv
, hdbc
, hstmt
);
2842 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2846 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2849 } // wxDb::GetColumnCount()
2852 /********** wxDb::GetCatalog() *******/
2853 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2855 * ---------------------------------------------------------------------
2856 * -- 19991203 : mj10777 : Create ------
2857 * -- : Creates a wxDbInf with Tables / Cols Array ------
2858 * -- : uses SQLTables and fills pTableInf; ------
2859 * -- : pColInf is set to NULL and numCols to 0; ------
2860 * -- : returns pDbInf (wxDbInf) ------
2861 * -- - if unsuccesfull (pDbInf == NULL) ------
2862 * -- : pColInf can be filled with GetColumns(..); ------
2863 * -- : numCols can be filled with GetColumnCount(..); ------
2864 * ---------------------------------------------------------------------
2866 * userID is evaluated in the following manner:
2867 * userID == NULL ... UserID is ignored
2868 * userID == "" ... UserID set equal to 'this->uid'
2869 * userID != "" ... UserID set equal to 'userID'
2871 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2872 * by this function. This function should use its own wxDb instance
2873 * to avoid undesired unbinding of columns.
2876 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2877 int noTab
= 0; // Counter while filling table entries
2881 wxString tblNameSave
;
2884 convertUserID(userID
,UserID
);
2886 //-------------------------------------------------------------
2887 pDbInf
= new wxDbInf
; // Create the Database Array
2888 //-------------------------------------------------------------
2889 // Table Information
2890 // Pass 1 - Determine how many Tables there are.
2891 // Pass 2 - Create the Table array and fill it
2892 // - Create the Cols array = NULL
2893 //-------------------------------------------------------------
2895 for (pass
= 1; pass
<= 2; pass
++)
2897 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2898 tblNameSave
.Empty();
2900 if (!UserID
.IsEmpty() &&
2901 Dbms() != dbmsMY_SQL
&&
2902 Dbms() != dbmsACCESS
&&
2903 Dbms() != dbmsMS_SQL_SERVER
)
2905 retcode
= SQLTables(hstmt
,
2906 NULL
, 0, // All qualifiers
2907 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2908 NULL
, 0, // All tables
2909 NULL
, 0); // All columns
2913 retcode
= SQLTables(hstmt
,
2914 NULL
, 0, // All qualifiers
2915 NULL
, 0, // User specified
2916 NULL
, 0, // All tables
2917 NULL
, 0); // All columns
2920 if (retcode
!= SQL_SUCCESS
)
2922 DispAllErrors(henv
, hdbc
, hstmt
);
2924 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2928 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2930 if (pass
== 1) // First pass, just count the Tables
2932 if (pDbInf
->numTables
== 0)
2934 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2935 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2937 pDbInf
->numTables
++; // Counter for Tables
2939 if (pass
== 2) // Create and fill the Table entries
2941 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2942 { // no, then create the Array
2943 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2945 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2947 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2948 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2949 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2955 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2957 // Query how many columns are in each table
2958 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2960 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2965 } // wxDb::GetCatalog()
2968 /********** wxDb::Catalog() **********/
2969 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2971 * Creates the text file specified in 'filename' which will contain
2972 * a minimal data dictionary of all tables accessible by the user specified
2975 * userID is evaluated in the following manner:
2976 * userID == NULL ... UserID is ignored
2977 * userID == "" ... UserID set equal to 'this->uid'
2978 * userID != "" ... UserID set equal to 'userID'
2980 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2981 * by this function. This function should use its own wxDb instance
2982 * to avoid undesired unbinding of columns.
2985 wxASSERT(fileName
.Length());
2989 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2990 wxString tblNameSave
;
2991 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2993 wxChar typeName
[30+1];
2994 SDWORD precision
, length
;
2996 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
3000 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3003 convertUserID(userID
,UserID
);
3005 if (!UserID
.IsEmpty() &&
3006 Dbms() != dbmsMY_SQL
&&
3007 Dbms() != dbmsACCESS
&&
3008 Dbms() != dbmsINTERBASE
&&
3009 Dbms() != dbmsMS_SQL_SERVER
)
3011 retcode
= SQLColumns(hstmt
,
3012 NULL
, 0, // All qualifiers
3013 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3014 NULL
, 0, // All tables
3015 NULL
, 0); // All columns
3019 retcode
= SQLColumns(hstmt
,
3020 NULL
, 0, // All qualifiers
3021 NULL
, 0, // User specified
3022 NULL
, 0, // All tables
3023 NULL
, 0); // All columns
3025 if (retcode
!= SQL_SUCCESS
)
3027 DispAllErrors(henv
, hdbc
, hstmt
);
3033 tblNameSave
.Empty();
3038 retcode
= SQLFetch(hstmt
);
3039 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3042 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3045 fputs(wxT("\n"), fp
);
3046 fputs(wxT("================================ "), fp
);
3047 fputs(wxT("================================ "), fp
);
3048 fputs(wxT("===================== "), fp
);
3049 fputs(wxT("========= "), fp
);
3050 fputs(wxT("=========\n"), fp
);
3051 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3052 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3053 fputs(outStr
.c_str(), fp
);
3054 fputs(wxT("================================ "), fp
);
3055 fputs(wxT("================================ "), fp
);
3056 fputs(wxT("===================== "), fp
);
3057 fputs(wxT("========= "), fp
);
3058 fputs(wxT("=========\n"), fp
);
3059 tblNameSave
= tblName
;
3062 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3063 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3064 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3065 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3066 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3067 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3069 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
3070 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3071 if (fputs(outStr
.c_str(), fp
) == EOF
)
3073 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3080 if (retcode
!= SQL_NO_DATA_FOUND
)
3081 DispAllErrors(henv
, hdbc
, hstmt
);
3083 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3086 return(retcode
== SQL_NO_DATA_FOUND
);
3088 } // wxDb::Catalog()
3091 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3093 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3094 * if the object exists in the database. This function does not indicate
3095 * whether or not the user has privleges to query or perform other functions
3098 * userID is evaluated in the following manner:
3099 * userID == NULL ... UserID is ignored
3100 * userID == "" ... UserID set equal to 'this->uid'
3101 * userID != "" ... UserID set equal to 'userID'
3104 wxASSERT(tableName
.Length());
3108 if (Dbms() == dbmsDBASE
)
3111 if (tablePath
.Length())
3112 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3114 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3117 exists
= wxFileExists(dbName
);
3122 convertUserID(userID
,UserID
);
3124 TableName
= tableName
;
3125 // Oracle and Interbase table names are uppercase only, so force
3126 // the name to uppercase just in case programmer forgot to do this
3127 if ((Dbms() == dbmsORACLE
) ||
3128 (Dbms() == dbmsINTERBASE
))
3129 TableName
= TableName
.Upper();
3131 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3134 // Some databases cannot accept a user name when looking up table names,
3135 // so we use the call below that leaves out the user name
3136 if (!UserID
.IsEmpty() &&
3137 Dbms() != dbmsMY_SQL
&&
3138 Dbms() != dbmsACCESS
&&
3139 Dbms() != dbmsMS_SQL_SERVER
&&
3140 Dbms() != dbmsDB2
&&
3141 Dbms() != dbmsINTERBASE
&&
3142 Dbms() != dbmsPERVASIVE_SQL
)
3144 retcode
= SQLTables(hstmt
,
3145 NULL
, 0, // All qualifiers
3146 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3147 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3148 NULL
, 0); // All table types
3152 retcode
= SQLTables(hstmt
,
3153 NULL
, 0, // All qualifiers
3154 NULL
, 0, // All owners
3155 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3156 NULL
, 0); // All table types
3158 if (retcode
!= SQL_SUCCESS
)
3159 return(DispAllErrors(henv
, hdbc
, hstmt
));
3161 retcode
= SQLFetch(hstmt
);
3162 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3164 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3165 return(DispAllErrors(henv
, hdbc
, hstmt
));
3168 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3172 } // wxDb::TableExists()
3175 /********** wxDb::TablePrivileges() **********/
3176 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3177 const wxChar
*schema
, const wxString
&tablePath
)
3179 wxASSERT(tableName
.Length());
3181 wxDbTablePrivilegeInfo result
;
3185 // We probably need to be able to dynamically set this based on
3186 // the driver type, and state.
3187 wxChar curRole
[]=wxT("public");
3191 wxString UserID
,Schema
;
3192 convertUserID(userID
,UserID
);
3193 convertUserID(schema
,Schema
);
3195 TableName
= tableName
;
3196 // Oracle and Interbase table names are uppercase only, so force
3197 // the name to uppercase just in case programmer forgot to do this
3198 if ((Dbms() == dbmsORACLE
) ||
3199 (Dbms() == dbmsINTERBASE
))
3200 TableName
= TableName
.Upper();
3202 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3204 // Some databases cannot accept a user name when looking up table names,
3205 // so we use the call below that leaves out the user name
3206 if (!Schema
.IsEmpty() &&
3207 Dbms() != dbmsMY_SQL
&&
3208 Dbms() != dbmsACCESS
&&
3209 Dbms() != dbmsMS_SQL_SERVER
)
3211 retcode
= SQLTablePrivileges(hstmt
,
3213 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3214 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3218 retcode
= SQLTablePrivileges(hstmt
,
3221 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3224 #ifdef DBDEBUG_CONSOLE
3225 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3228 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3229 return(DispAllErrors(henv
, hdbc
, hstmt
));
3231 bool failed
= FALSE
;
3232 retcode
= SQLFetch(hstmt
);
3233 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3235 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3238 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3241 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3244 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3247 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3250 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3253 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3258 return(DispAllErrors(henv
, hdbc
, hstmt
));
3260 #ifdef DBDEBUG_CONSOLE
3261 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3262 result
.privilege
,result
.tableOwner
,result
.tableName
,
3263 result
.grantor
, result
.grantee
);
3266 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3268 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3272 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3273 !wxStrcmp(result
.privilege
,priv
))
3275 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3279 if (!wxStrcmp(result
.grantee
,curRole
) &&
3280 !wxStrcmp(result
.privilege
,priv
))
3282 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3286 retcode
= SQLFetch(hstmt
);
3289 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3292 } // wxDb::TablePrivileges
3295 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3299 if (Dbms() == dbmsACCESS
)
3301 TableName
+= tableName
;
3302 if (Dbms() == dbmsACCESS
)
3306 } // wxDb::SQLTableName()
3309 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3313 if (Dbms() == dbmsACCESS
)
3316 if (Dbms() == dbmsACCESS
)
3320 } // wxDb::SQLColumnName()
3323 /********** wxDb::SetSqlLogging() **********/
3324 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3326 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3327 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3329 if (state
== sqlLogON
)
3333 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3334 if (fpSqlLog
== NULL
)
3342 if (fclose(fpSqlLog
))
3348 sqlLogState
= state
;
3351 } // wxDb::SetSqlLogging()
3354 /********** wxDb::WriteSqlLog() **********/
3355 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3357 wxASSERT(logMsg
.Length());
3359 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3362 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3364 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3366 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3371 } // wxDb::WriteSqlLog()
3374 /********** wxDb::Dbms() **********/
3375 wxDBMS
wxDb::Dbms(void)
3377 * Be aware that not all database engines use the exact same syntax, and not
3378 * every ODBC compliant database is compliant to the same level of compliancy.
3379 * Some manufacturers support the minimum Level 1 compliancy, and others up
3380 * through Level 3. Others support subsets of features for levels above 1.
3382 * If you find an inconsistency between the wxDb class and a specific database
3383 * engine, and an identifier to this section, and special handle the database in
3384 * the area where behavior is non-conforming with the other databases.
3387 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3388 * ---------------------------------------------------
3391 * - Currently the only database supported by the class to support VIEWS
3394 * - Does not support the SQL_TIMESTAMP structure
3395 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3396 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3397 * is TRUE. The user must create ALL indexes from their program.
3398 * - Table names can only be 8 characters long
3399 * - Column names can only be 10 characters long
3402 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3403 * after every table name involved in the query/join if that tables matching record(s)
3405 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3407 * SYBASE (Enterprise)
3408 * - If a column is part of the Primary Key, the column cannot be NULL
3409 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3412 * - If a column is part of the Primary Key, the column cannot be NULL
3413 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3414 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3415 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3416 * column definition if it is not defined correctly, but it is experimental
3417 * - Does not support sub-queries in SQL statements
3420 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3421 * - Does not support sub-queries in SQL statements
3424 * - Primary keys must be declared as NOT NULL
3425 * - Table and index names must not be longer than 13 characters in length (technically
3426 * table names can be up to 18 characters, but the primary index is created using the
3427 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3432 * - Columns that are part of primary keys must be defined as being NOT NULL
3433 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3434 * column definition if it is not defined correctly, but it is experimental
3437 // Should only need to do this once for each new database connection
3438 // so return the value we already determined it to be to save time
3439 // and lots of string comparisons
3440 if (dbmsType
!= dbmsUNIDENTIFIED
)
3443 wxChar baseName
[25+1];
3444 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3447 // RGG 20001025 : add support for Interbase
3448 // GT : Integrated to base classes on 20001121
3449 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3450 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3452 // BJO 20000428 : add support for Virtuoso
3453 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3454 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3456 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3457 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3459 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3460 // connected through an OpenLink driver.
3461 // Is it also returned by Sybase Adapatitve server?
3462 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3463 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3465 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3466 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3467 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3469 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3472 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3473 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3474 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3475 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3476 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3477 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3480 if (!wxStricmp(dbInf
.dbmsName
,wxT("Pervasive")))
3481 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3484 if (!wxStricmp(baseName
,wxT("Informix")))
3485 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3488 if (!wxStricmp(baseName
,wxT("Oracle")))
3489 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3490 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3491 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3492 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3493 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3494 if (!wxStricmp(baseName
,wxT("Sybase")))
3495 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3498 if (!wxStricmp(baseName
,wxT("DBASE")))
3499 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3501 if (!wxStricmp(baseName
,wxT("MySQL")))
3502 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3505 if (!wxStricmp(baseName
,wxT("DB2")))
3506 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3508 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3513 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3514 int dataType
, ULONG columnLength
,
3515 const wxString
&optionalParam
)
3517 wxASSERT(tableName
.Length());
3518 wxASSERT(columnName
.Length());
3519 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3520 dataType
!= DB_DATA_TYPE_VARCHAR
);
3522 // Must specify a columnLength if modifying a VARCHAR type column
3523 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3526 wxString dataTypeName
;
3528 wxString alterSlashModify
;
3532 case DB_DATA_TYPE_VARCHAR
:
3533 dataTypeName
= typeInfVarchar
.TypeName
;
3535 case DB_DATA_TYPE_INTEGER
:
3536 dataTypeName
= typeInfInteger
.TypeName
;
3538 case DB_DATA_TYPE_FLOAT
:
3539 dataTypeName
= typeInfFloat
.TypeName
;
3541 case DB_DATA_TYPE_DATE
:
3542 dataTypeName
= typeInfDate
.TypeName
;
3544 case DB_DATA_TYPE_BLOB
:
3545 dataTypeName
= typeInfBlob
.TypeName
;
3551 // Set the modify or alter syntax depending on the type of database connected to
3555 alterSlashModify
= "MODIFY";
3557 case dbmsMS_SQL_SERVER
:
3558 alterSlashModify
= "ALTER COLUMN";
3560 case dbmsUNIDENTIFIED
:
3562 case dbmsSYBASE_ASA
:
3563 case dbmsSYBASE_ASE
:
3569 alterSlashModify
= "MODIFY";
3573 // create the SQL statement
3574 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3575 columnName
.c_str(), dataTypeName
.c_str());
3577 // For varchars only, append the size of the column
3578 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3579 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= "text"))
3582 s
.Printf(wxT("(%d)"), columnLength
);
3586 // for passing things like "NOT NULL"
3587 if (optionalParam
.Length())
3589 sqlStmt
+= wxT(" ");
3590 sqlStmt
+= optionalParam
;
3593 return ExecSql(sqlStmt
);
3595 } // wxDb::ModifyColumn()
3598 /********** wxDbGetConnection() **********/
3599 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3603 // Used to keep a pointer to a DB connection that matches the requested
3604 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3605 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3606 // rather than having to re-query the datasource to get all the values
3607 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3608 wxDb
*matchingDbConnection
= NULL
;
3610 // Scan the linked list searching for an available database connection
3611 // that's already been opened but is currently not in use.
3612 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3614 // The database connection must be for the same datasource
3615 // name and must currently not be in use.
3617 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3618 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3620 pList
->Free
= FALSE
;
3621 return(pList
->PtrDb
);
3624 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3625 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3626 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3627 matchingDbConnection
= pList
->PtrDb
;
3630 // No available connections. A new connection must be made and
3631 // appended to the end of the linked list.
3634 // Find the end of the list
3635 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3636 // Append a new list item
3637 pList
->PtrNext
= new wxDbList
;
3638 pList
->PtrNext
->PtrPrev
= pList
;
3639 pList
= pList
->PtrNext
;
3643 // Create the first node on the list
3644 pList
= PtrBegDbList
= new wxDbList
;
3648 // Initialize new node in the linked list
3650 pList
->Free
= FALSE
;
3651 pList
->Dsn
= pDbConfig
->GetDsn();
3652 pList
->Uid
= pDbConfig
->GetUserID();
3653 pList
->AuthStr
= pDbConfig
->GetPassword();
3655 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3657 bool opened
= FALSE
;
3659 if (!matchingDbConnection
)
3660 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3662 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3664 // Connect to the datasource
3667 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3668 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3669 return(pList
->PtrDb
);
3671 else // Unable to connect, destroy list item
3674 pList
->PtrPrev
->PtrNext
= 0;
3676 PtrBegDbList
= 0; // Empty list again
3677 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3678 pList
->PtrDb
->Close(); // Close the wxDb object
3679 delete pList
->PtrDb
; // Deletes the wxDb object
3680 delete pList
; // Deletes the linked list object
3684 } // wxDbGetConnection()
3687 /********** wxDbFreeConnection() **********/
3688 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3692 // Scan the linked list searching for the database connection
3693 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3695 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3696 return (pList
->Free
= TRUE
);
3699 // Never found the database object, return failure
3702 } // wxDbFreeConnection()
3705 /********** wxDbCloseConnections() **********/
3706 void WXDLLEXPORT
wxDbCloseConnections(void)
3708 wxDbList
*pList
, *pNext
;
3710 // Traverse the linked list closing database connections and freeing memory as I go.
3711 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3713 pNext
= pList
->PtrNext
; // Save the pointer to next
3714 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3715 pList
->PtrDb
->Close(); // Close the wxDb object
3716 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3717 delete pList
->PtrDb
; // Deletes the wxDb object
3718 delete pList
; // Deletes the linked list object
3721 // Mark the list as empty
3724 } // wxDbCloseConnections()
3727 /********** wxDbConnectionsInUse() **********/
3728 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3733 // Scan the linked list counting db connections that are currently in use
3734 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3736 if (pList
->Free
== FALSE
)
3742 } // wxDbConnectionsInUse()
3746 /********** wxDbLogExtendedErrorMsg() **********/
3747 // DEBUG ONLY function
3748 const wxChar WXDLLEXPORT
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3750 const wxChar
*ErrFile
,
3753 static wxString msg
;
3758 if (ErrFile
|| ErrLine
)
3760 msg
+= wxT("File: ");
3762 msg
+= wxT(" Line: ");
3763 tStr
.Printf(wxT("%d"),ErrLine
);
3764 msg
+= tStr
.c_str();
3768 msg
.Append (wxT("\nODBC errors:\n"));
3771 // Display errors for this connection
3773 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3775 if (pDb
->errorList
[i
])
3777 msg
.Append(pDb
->errorList
[i
]);
3778 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3779 msg
.Append(wxT("\n"));
3780 // Clear the errmsg buffer so the next error will not
3781 // end up showing the previous error that have occurred
3782 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3787 wxLogDebug(msg
.c_str());
3790 } // wxDbLogExtendedErrorMsg()
3793 /********** wxDbSqlLog() **********/
3794 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3796 bool append
= FALSE
;
3799 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3801 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3806 SQLLOGstate
= state
;
3807 SQLLOGfn
= filename
;
3815 /********** wxDbCreateDataSource() **********/
3816 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3817 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3819 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3820 * Very rudimentary creation of an ODBC data source.
3822 * ODBC driver must be ODBC 3.0 compliant to use this function
3827 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3833 dsnLocation
= ODBC_ADD_SYS_DSN
;
3835 dsnLocation
= ODBC_ADD_DSN
;
3837 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3838 // so that is why I used it, as wxString does not deal well with
3839 // embedded nulls in strings
3840 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3842 // Replace the separator from above with the '\0' seperator needed
3843 // by the SQLConfigDataSource() function
3847 k
= setupStr
.Find((wxChar
)2,TRUE
);
3848 if (k
!= wxNOT_FOUND
)
3849 setupStr
[(UINT
)k
] = wxT('\0');
3851 while (k
!= wxNOT_FOUND
);
3853 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3854 driverName
, setupStr
.c_str());
3856 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3858 // check for errors caused by ConfigDSN based functions
3861 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3862 errMsg
[0] = wxT('\0');
3864 // This function is only supported in ODBC drivers v3.0 compliant and above
3865 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3868 #ifdef DBDEBUG_CONSOLE
3869 // When run in console mode, use standard out to display errors.
3870 cout
<< errMsg
<< endl
;
3871 cout
<< wxT("Press any key to continue...") << endl
;
3873 #endif // DBDEBUG_CONSOLE
3876 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3877 #endif // __WXDEBUG__
3883 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3884 // necessary to use this function, so this function is not supported
3886 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3889 #endif // __VISUALC__
3893 } // wxDbCreateDataSource()
3897 /********** wxDbGetDataSource() **********/
3898 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3899 SWORD DsDescMax
, UWORD direction
)
3901 * Dsn and DsDesc will contain the data source name and data source
3902 * description upon return
3907 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3908 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3913 } // wxDbGetDataSource()
3916 // Change this to 0 to remove use of all deprecated functions
3917 #if wxODBC_BACKWARD_COMPATABILITY
3918 /********************************************************************
3919 ********************************************************************
3921 * The following functions are all DEPRECATED and are included for
3922 * backward compatability reasons only
3924 ********************************************************************
3925 ********************************************************************/
3926 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3928 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3930 /***** DEPRECATED: use wxGetDataSource() *****/
3931 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3934 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3936 /***** DEPRECATED: use wxDbGetConnection() *****/
3937 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3939 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3941 /***** DEPRECATED: use wxDbFreeConnection() *****/
3942 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3944 return wxDbFreeConnection(pDb
);
3946 /***** DEPRECATED: use wxDbCloseConnections() *****/
3947 void WXDLLEXPORT
CloseDbConnections(void)
3949 wxDbCloseConnections();
3951 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3952 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3954 return wxDbConnectionsInUse();