1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/db.cpp
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
22 ///////////////////////////////////////////////////////////////////////////////
24 #include "wx/wxprec.h"
33 #include "wx/object.h"
35 #include "wx/string.h"
41 #ifdef DBDEBUG_CONSOLE
42 #include "wx/ioswrap.h"
45 #include "wx/filefn.h"
46 #include "wx/wxchar.h"
56 // DLL options compatibility check:
57 WX_CHECK_BUILD_OPTIONS("wxODBC")
59 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
61 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
62 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
65 #include "wx/thread.h"
67 extern wxList TablesInUse
;
68 extern wxCriticalSection csTablesInUse
;
71 // SQL Log defaults to be used by GetDbConnection
72 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
74 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
76 // The wxDb::errorList is copied to this variable when the wxDb object
77 // is closed. This way, the error list is still available after the
78 // database object is closed. This is necessary if the database
79 // connection fails so the calling application can show the operator
80 // why the connection failed. Note: as each wxDb object is closed, it
81 // will overwrite the errors of the previously destroyed wxDb object in
82 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
84 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
87 // This type defines the return row-struct form
88 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
91 wxChar tableQual
[128+1];
92 wxChar tableOwner
[128+1];
93 wxChar tableName
[128+1];
94 wxChar grantor
[128+1];
95 wxChar grantee
[128+1];
96 wxChar privilege
[128+1];
97 wxChar grantable
[3+1];
98 } wxDbTablePrivilegeInfo
;
101 /********** wxDbConnectInf Constructor - form 1 **********/
102 wxDbConnectInf::wxDbConnectInf()
105 freeHenvOnDestroy
= false;
111 /********** wxDbConnectInf Constructor - form 2 **********/
112 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
113 const wxString
&password
, const wxString
&defaultDir
,
114 const wxString
&fileType
, const wxString
&description
)
117 freeHenvOnDestroy
= false;
128 SetPassword(password
);
129 SetDescription(description
);
130 SetFileType(fileType
);
131 SetDefaultDir(defaultDir
);
132 } // wxDbConnectInf Constructor
135 wxDbConnectInf::~wxDbConnectInf()
137 if (freeHenvOnDestroy
)
141 } // wxDbConnectInf Destructor
145 /********** wxDbConnectInf::Initialize() **********/
146 bool wxDbConnectInf::Initialize()
148 freeHenvOnDestroy
= false;
150 if (freeHenvOnDestroy
&& Henv
)
157 ConnectionStr
[0] = 0;
162 useConnectionStr
= false;
165 } // wxDbConnectInf::Initialize()
168 /********** wxDbConnectInf::AllocHenv() **********/
169 bool wxDbConnectInf::AllocHenv()
171 // This is here to help trap if you are getting a new henv
172 // without releasing an existing henv
175 // Initialize the ODBC Environment for Database Operations
176 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
178 wxLogDebug(wxT("A problem occurred while trying to get a connection to the data source"));
182 freeHenvOnDestroy
= true;
185 } // wxDbConnectInf::AllocHenv()
188 void wxDbConnectInf::FreeHenv()
196 freeHenvOnDestroy
= false;
198 } // wxDbConnectInf::FreeHenv()
201 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
203 wxASSERT(dsn
.length() < WXSIZEOF(Dsn
));
205 wxStrncpy(Dsn
, dsn
, WXSIZEOF(Dsn
)-1);
206 Dsn
[WXSIZEOF(Dsn
)-1] = 0; // Prevent buffer overrun
207 } // wxDbConnectInf::SetDsn()
210 void wxDbConnectInf::SetUserID(const wxString
&uid
)
212 wxASSERT(uid
.length() < WXSIZEOF(Uid
));
213 wxStrncpy(Uid
, uid
, WXSIZEOF(Uid
)-1);
214 Uid
[WXSIZEOF(Uid
)-1] = 0; // Prevent buffer overrun
215 } // wxDbConnectInf::SetUserID()
218 void wxDbConnectInf::SetPassword(const wxString
&password
)
220 wxASSERT(password
.length() < WXSIZEOF(AuthStr
));
222 wxStrncpy(AuthStr
, password
, WXSIZEOF(AuthStr
)-1);
223 AuthStr
[WXSIZEOF(AuthStr
)-1] = 0; // Prevent buffer overrun
224 } // wxDbConnectInf::SetPassword()
226 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
228 wxASSERT(connectStr
.length() < WXSIZEOF(ConnectionStr
));
230 useConnectionStr
= wxStrlen(connectStr
) > 0;
232 wxStrncpy(ConnectionStr
, connectStr
, WXSIZEOF(ConnectionStr
)-1);
233 ConnectionStr
[WXSIZEOF(ConnectionStr
)-1] = 0; // Prevent buffer overrun
234 } // wxDbConnectInf::SetConnectionStr()
237 /********** wxDbColFor Constructor **********/
238 wxDbColFor::wxDbColFor()
241 } // wxDbColFor::wxDbColFor()
244 /********** wxDbColFor::Initialize() **********/
245 void wxDbColFor::Initialize()
255 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
258 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
259 } // wxDbColFor::Initialize()
262 /********** wxDbColFor::Format() **********/
263 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
264 short columnLength
, short decimalDigits
)
266 // ----------------------------------------------------------------------------------------
267 // -- 19991224 : mj10777 : Create
268 // There is still a lot of work to do here, but it is a start
269 // It handles all the basic data-types that I have run into up to now
270 // The main work will have be with Dates and float Formatting
271 // (US 1,000.00 ; EU 1.000,00)
272 // There are wxWindow plans for locale support and the new wxDateTime. If
273 // they define some constants (wxEUROPEAN) that can be gloably used,
274 // they should be used here.
275 // ----------------------------------------------------------------------------------------
276 // There should also be a function to scan in a string to fill the variable
277 // ----------------------------------------------------------------------------------------
279 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
280 i_dbDataType
= dbDataType
;
281 i_sqlDataType
= sqlDataType
;
282 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
284 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
286 if ((i_sqlDataType
== SQL_VARCHAR
)
288 #if defined(SQL_WCHAR)
289 || (i_sqlDataType
== SQL_WCHAR
)
291 #if defined(SQL_WVARCHAR)
292 || (i_sqlDataType
== SQL_WVARCHAR
)
295 || (i_sqlDataType
== SQL_LONGVARCHAR
))
296 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
297 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
298 i_dbDataType
= DB_DATA_TYPE_DATE
;
299 if (i_sqlDataType
== SQL_C_BIT
)
300 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
301 if (i_sqlDataType
== SQL_NUMERIC
)
302 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
303 if (i_sqlDataType
== SQL_REAL
)
304 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
305 if (i_sqlDataType
== SQL_C_BINARY
)
306 i_dbDataType
= DB_DATA_TYPE_BLOB
;
309 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
311 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
314 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
316 case DB_DATA_TYPE_VARCHAR
:
319 case DB_DATA_TYPE_INTEGER
:
322 case DB_DATA_TYPE_FLOAT
:
323 if (decimalDigits
== 0)
325 tempStr
.Printf(wxT("%%%d.%d"), columnLength
, decimalDigits
);
326 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
328 case DB_DATA_TYPE_DATE
:
329 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
331 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
333 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
335 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
337 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
339 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
341 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
343 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
345 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
347 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
350 case DB_DATA_TYPE_BLOB
:
351 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
354 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
358 } // wxDbColFor::Format()
361 /********** wxDbColInf Constructor **********/
362 wxDbColInf::wxDbColInf()
365 } // wxDbColInf::wxDbColInf()
368 /********** wxDbColInf Destructor ********/
369 wxDbColInf::~wxDbColInf()
374 } // wxDbColInf::~wxDbColInf()
377 bool wxDbColInf::Initialize()
399 } // wxDbColInf::Initialize()
402 /********** wxDbTableInf Constructor ********/
403 wxDbTableInf::wxDbTableInf()
406 } // wxDbTableInf::wxDbTableInf()
409 /********** wxDbTableInf Constructor ********/
410 wxDbTableInf::~wxDbTableInf()
415 } // wxDbTableInf::~wxDbTableInf()
418 bool wxDbTableInf::Initialize()
427 } // wxDbTableInf::Initialize()
430 /********** wxDbInf Constructor *************/
434 } // wxDbInf::wxDbInf()
437 /********** wxDbInf Destructor *************/
443 } // wxDbInf::~wxDbInf()
446 /********** wxDbInf::Initialize() *************/
447 bool wxDbInf::Initialize()
455 } // wxDbInf::Initialize()
458 /********** wxDb Constructor **********/
459 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
461 // Copy the HENV into the db class
463 fwdOnlyCursors
= FwdOnlyCursors
;
469 /********** wxDb Destructor **********/
472 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
482 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
483 /********** wxDb::initialize() **********/
484 void wxDb::initialize()
486 * Private member function that sets all wxDb member variables to
487 * known values at creation of the wxDb
492 fpSqlLog
= 0; // Sql Log file pointer
493 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
495 dbmsType
= dbmsUNIDENTIFIED
;
497 wxStrcpy(sqlState
,wxEmptyString
);
498 wxStrcpy(errorMsg
,wxEmptyString
);
499 nativeError
= cbErrorMsg
= 0;
500 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
501 wxStrcpy(errorList
[i
], wxEmptyString
);
503 // Init typeInf structures
504 typeInfVarchar
.TypeName
.Empty();
505 typeInfVarchar
.FsqlType
= 0;
506 typeInfVarchar
.Precision
= 0;
507 typeInfVarchar
.CaseSensitive
= 0;
508 typeInfVarchar
.MaximumScale
= 0;
510 typeInfInteger
.TypeName
.Empty();
511 typeInfInteger
.FsqlType
= 0;
512 typeInfInteger
.Precision
= 0;
513 typeInfInteger
.CaseSensitive
= 0;
514 typeInfInteger
.MaximumScale
= 0;
516 typeInfFloat
.TypeName
.Empty();
517 typeInfFloat
.FsqlType
= 0;
518 typeInfFloat
.Precision
= 0;
519 typeInfFloat
.CaseSensitive
= 0;
520 typeInfFloat
.MaximumScale
= 0;
522 typeInfDate
.TypeName
.Empty();
523 typeInfDate
.FsqlType
= 0;
524 typeInfDate
.Precision
= 0;
525 typeInfDate
.CaseSensitive
= 0;
526 typeInfDate
.MaximumScale
= 0;
528 typeInfBlob
.TypeName
.Empty();
529 typeInfBlob
.FsqlType
= 0;
530 typeInfBlob
.Precision
= 0;
531 typeInfBlob
.CaseSensitive
= 0;
532 typeInfBlob
.MaximumScale
= 0;
534 typeInfMemo
.TypeName
.Empty();
535 typeInfMemo
.FsqlType
= 0;
536 typeInfMemo
.Precision
= 0;
537 typeInfMemo
.CaseSensitive
= 0;
538 typeInfMemo
.MaximumScale
= 0;
540 // Error reporting is turned OFF by default
543 // Allocate a data source connection handle
544 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
547 // Initialize the db status flag
550 // Mark database as not open as of yet
553 dbOpenedWithConnectionString
= false;
554 } // wxDb::initialize()
557 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
559 // NOTE: Return value from this function MUST be copied
560 // immediately, as the value is not good after
561 // this function has left scope.
563 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
567 if (!wxStrlen(userID
))
575 // dBase does not use user names, and some drivers fail if you try to pass one
576 if ( Dbms() == dbmsDBASE
577 || Dbms() == dbmsXBASE_SEQUITER
)
580 // Some databases require user names to be specified in uppercase,
581 // so force the name to uppercase
582 if ((Dbms() == dbmsORACLE
) ||
583 (Dbms() == dbmsMAXDB
))
584 UserID
= UserID
.Upper();
586 return UserID
.c_str();
587 } // wxDb::convertUserID()
590 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
594 // These are the possible SQL types we check for use against the datasource we are connected
595 // to for the purpose of determining which data type to use for the basic character strings
598 // NOTE: The first type in this enumeration that is determined to be supported by the
599 // datasource/driver is the one that will be used.
600 SWORD PossibleSqlCharTypes
[] = {
601 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
605 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
611 // These are the possible SQL types we check for use against the datasource we are connected
612 // to for the purpose of determining which data type to use for the basic non-floating point
615 // NOTE: The first type in this enumeration that is determined to be supported by the
616 // datasource/driver is the one that will be used.
617 SWORD PossibleSqlIntegerTypes
[] = {
621 // These are the possible SQL types we check for use against the datasource we are connected
622 // to for the purpose of determining which data type to use for the basic floating point number
625 // NOTE: The first type in this enumeration that is determined to be supported by the
626 // datasource/driver is the one that will be used.
627 SWORD PossibleSqlFloatTypes
[] = {
635 // These are the possible SQL types we check for use agains the datasource we are connected
636 // to for the purpose of determining which data type to use for the date/time column types
638 // NOTE: The first type in this enumeration that is determined to be supported by the
639 // datasource/driver is the one that will be used.
640 SWORD PossibleSqlDateTypes
[] = {
648 // These are the possible SQL types we check for use agains the datasource we are connected
649 // to for the purpose of determining which data type to use for the BLOB column types.
651 // NOTE: The first type in this enumeration that is determined to be supported by the
652 // datasource/driver is the one that will be used.
653 SWORD PossibleSqlBlobTypes
[] = {
658 // These are the possible SQL types we check for use agains the datasource we are connected
659 // to for the purpose of determining which data type to use for the MEMO column types
660 // (a type which allow to store large strings; like VARCHAR just with a bigger precision)
662 // NOTE: The first type in this enumeration that is determined to be supported by the
663 // datasource/driver is the one that will be used.
664 SWORD PossibleSqlMemoTypes
[] = {
669 // Query the data source regarding data type information
672 // The way it was determined which SQL data types to use was by calling SQLGetInfo
673 // for all of the possible SQL data types to see which ones were supported. If
674 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
675 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
676 // types I've selected below will not always be what we want. These are just
677 // what happened to work against an Oracle 7/Intersolv combination. The following is
678 // a complete list of the results I got back against the Oracle 7 database:
680 // SQL_BIGINT SQL_NO_DATA_FOUND
681 // SQL_BINARY SQL_NO_DATA_FOUND
682 // SQL_BIT SQL_NO_DATA_FOUND
683 // SQL_CHAR type name = 'CHAR', Precision = 255
684 // SQL_DATE SQL_NO_DATA_FOUND
685 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
686 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
687 // SQL_FLOAT SQL_NO_DATA_FOUND
688 // SQL_INTEGER SQL_NO_DATA_FOUND
689 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
690 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
691 // SQL_NUMERIC SQL_NO_DATA_FOUND
692 // SQL_REAL SQL_NO_DATA_FOUND
693 // SQL_SMALLINT SQL_NO_DATA_FOUND
694 // SQL_TIME SQL_NO_DATA_FOUND
695 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
696 // SQL_VARBINARY type name = 'RAW', Precision = 255
697 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
698 // =====================================================================
699 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
701 // SQL_VARCHAR type name = 'TEXT', Precision = 255
702 // SQL_TIMESTAMP type name = 'DATETIME'
703 // SQL_DECIMAL SQL_NO_DATA_FOUND
704 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
705 // SQL_FLOAT SQL_NO_DATA_FOUND
706 // SQL_REAL type name = 'SINGLE', Precision = 7
707 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
708 // SQL_INTEGER type name = 'LONG', Precision = 10
710 // Query the data source for info about itself
711 if (!getDbInfo(failOnDataTypeUnsupported
))
714 // --------------- Varchar - (Variable length character string) ---------------
715 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
716 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
719 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
720 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
721 else if (failOnDataTypeUnsupported
)
724 // --------------- Float ---------------
725 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
726 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
729 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
730 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
731 else if (failOnDataTypeUnsupported
)
734 // --------------- Integer -------------
735 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
736 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
739 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
740 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
741 else if (failOnDataTypeUnsupported
)
743 // If no non-floating point data types are supported, we'll
744 // use the type assigned for floats to store integers as well
745 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
747 if (failOnDataTypeUnsupported
)
751 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
754 // --------------- Date/Time ---------------
755 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
756 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
759 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
760 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
761 else if (failOnDataTypeUnsupported
)
764 // --------------- BLOB ---------------
765 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
766 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
769 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
770 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
771 else if (failOnDataTypeUnsupported
)
774 // --------------- MEMO ---------------
775 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlMemoTypes
) &&
776 !getDataTypeInfo(PossibleSqlMemoTypes
[iIndex
], typeInfMemo
); ++iIndex
)
779 if (iIndex
< WXSIZEOF(PossibleSqlMemoTypes
))
780 typeInfMemo
.FsqlType
= PossibleSqlMemoTypes
[iIndex
];
781 else if (failOnDataTypeUnsupported
)
785 } // wxDb::determineDataTypes
788 bool wxDb::open(bool failOnDataTypeUnsupported
)
791 If using Intersolv branded ODBC drivers, this is the place where you would substitute
792 your branded driver license information
794 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
795 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
798 // Mark database as open
801 // Allocate a statement handle for the database connection
802 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
803 return(DispAllErrors(henv
, hdbc
));
805 // Set Connection Options
806 if (!setConnectionOptions())
809 if (!determineDataTypes(failOnDataTypeUnsupported
))
812 #ifdef DBDEBUG_CONSOLE
813 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
814 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
815 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
816 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
817 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
818 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
822 // Completed Successfully
826 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
828 wxASSERT(inConnectStr
.length());
829 return Open(inConnectStr
, NULL
, failOnDataTypeUnsupported
);
832 bool wxDb::Open(const wxString
& inConnectStr
, SQLHWND parentWnd
, bool failOnDataTypeUnsupported
)
836 authStr
= wxEmptyString
;
840 if (!FwdOnlyCursors())
842 // Specify that the ODBC cursor library be used, if needed. This must be
843 // specified before the connection is made.
844 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
846 #ifdef DBDEBUG_CONSOLE
847 if (retcode
== SQL_SUCCESS
)
848 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
850 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
852 wxUnusedVar(retcode
);
856 // Connect to the data source
857 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
858 short outConnectBufferLen
;
860 inConnectionStr
= inConnectStr
;
862 retcode
= SQLDriverConnect(hdbc
, parentWnd
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
863 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
864 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
866 if ((retcode
!= SQL_SUCCESS
) &&
867 (retcode
!= SQL_SUCCESS_WITH_INFO
))
868 return(DispAllErrors(henv
, hdbc
));
870 outConnectBuffer
[outConnectBufferLen
] = 0;
871 outConnectionStr
= outConnectBuffer
;
872 dbOpenedWithConnectionString
= true;
874 return open(failOnDataTypeUnsupported
);
877 /********** wxDb::Open() **********/
878 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
880 wxASSERT(!Dsn
.empty());
885 inConnectionStr
= wxEmptyString
;
886 outConnectionStr
= wxEmptyString
;
890 if (!FwdOnlyCursors())
892 // Specify that the ODBC cursor library be used, if needed. This must be
893 // specified before the connection is made.
894 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
896 #ifdef DBDEBUG_CONSOLE
897 if (retcode
== SQL_SUCCESS
)
898 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
900 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
902 wxUnusedVar( retcode
);
906 // Connect to the data source
907 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
908 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
909 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
911 if ((retcode
!= SQL_SUCCESS
) &&
912 (retcode
!= SQL_SUCCESS_WITH_INFO
))
913 return(DispAllErrors(henv
, hdbc
));
915 return open(failOnDataTypeUnsupported
);
920 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
922 wxASSERT(dbConnectInf
);
924 // Use the connection string if one is present
925 if (dbConnectInf
->UseConnectionStr())
926 return Open(dbConnectInf
->GetConnectionStr(), failOnDataTypeUnsupported
);
928 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
929 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
933 bool wxDb::Open(wxDb
*copyDb
)
935 dsn
= copyDb
->GetDatasourceName();
936 uid
= copyDb
->GetUsername();
937 authStr
= copyDb
->GetPassword();
938 inConnectionStr
= copyDb
->GetConnectionInStr();
939 outConnectionStr
= copyDb
->GetConnectionOutStr();
943 if (!FwdOnlyCursors())
945 // Specify that the ODBC cursor library be used, if needed. This must be
946 // specified before the connection is made.
947 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
949 #ifdef DBDEBUG_CONSOLE
950 if (retcode
== SQL_SUCCESS
)
951 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
953 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
955 wxUnusedVar( retcode
);
959 if (copyDb
->OpenedWithConnectionString())
961 // Connect to the data source
962 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
963 short outConnectBufferLen
;
965 inConnectionStr
= copyDb
->GetConnectionInStr();
967 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
968 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
969 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
971 if ((retcode
!= SQL_SUCCESS
) &&
972 (retcode
!= SQL_SUCCESS_WITH_INFO
))
973 return(DispAllErrors(henv
, hdbc
));
975 outConnectBuffer
[outConnectBufferLen
] = 0;
976 outConnectionStr
= outConnectBuffer
;
977 dbOpenedWithConnectionString
= true;
981 // Connect to the data source
982 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
983 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
984 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
987 if ((retcode
!= SQL_SUCCESS
) &&
988 (retcode
!= SQL_SUCCESS_WITH_INFO
))
989 return(DispAllErrors(henv
, hdbc
));
992 If using Intersolv branded ODBC drivers, this is the place where you would substitute
993 your branded driver license information
995 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
996 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
999 // Mark database as open
1002 // Allocate a statement handle for the database connection
1003 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 // Set Connection Options
1007 if (!setConnectionOptions())
1010 // Instead of Querying the data source for info about itself, it can just be copied
1011 // from the wxDb instance that was passed in (copyDb).
1012 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
1013 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
1014 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
1015 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
1016 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
1017 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
1018 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
1019 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
1020 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
1021 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
1022 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
1023 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
1024 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
1025 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
1026 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
1027 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1028 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1029 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1030 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1031 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1032 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1033 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1034 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1035 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1036 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1037 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1038 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1039 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1040 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1041 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1042 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1044 // VARCHAR = Variable length character string
1045 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1046 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1047 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1048 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1049 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1052 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1053 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1054 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1055 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1056 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1059 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1060 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1061 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1062 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1063 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1066 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1067 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1068 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1069 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1070 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1073 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1074 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1075 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1076 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1077 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1080 typeInfMemo
.FsqlType
= copyDb
->typeInfMemo
.FsqlType
;
1081 typeInfMemo
.TypeName
= copyDb
->typeInfMemo
.TypeName
;
1082 typeInfMemo
.Precision
= copyDb
->typeInfMemo
.Precision
;
1083 typeInfMemo
.CaseSensitive
= copyDb
->typeInfMemo
.CaseSensitive
;
1084 typeInfMemo
.MaximumScale
= copyDb
->typeInfMemo
.MaximumScale
;
1086 #ifdef DBDEBUG_CONSOLE
1087 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1088 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1089 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1090 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1091 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1092 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
1096 // Completed Successfully
1101 /********** wxDb::setConnectionOptions() **********/
1102 bool wxDb::setConnectionOptions(void)
1104 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1109 // I need to get the DBMS name here, because some of the connection options
1110 // are database specific and need to call the Dbms() function.
1113 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1114 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1115 return(DispAllErrors(henv
, hdbc
));
1117 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1118 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1119 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1121 // By default, MS Sql Server closes cursors on commit and rollback. The following
1122 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1123 // after a transaction. This is a driver specific option and is not part of the
1124 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1125 // The database settings don't have any effect one way or the other.
1126 if (Dbms() == dbmsMS_SQL_SERVER
)
1128 const long SQL_PRESERVE_CURSORS
= 1204L;
1129 const long SQL_PC_ON
= 1L;
1130 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1133 // Display the connection options to verify them
1134 #ifdef DBDEBUG_CONSOLE
1136 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1138 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1139 if (retcode
!= SQL_SUCCESS
)
1140 return(DispAllErrors(henv
, hdbc
));
1141 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1143 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1144 if (retcode
!= SQL_SUCCESS
)
1145 return(DispAllErrors(henv
, hdbc
));
1146 cout
<< wxT("ODBC CURSORS: ");
1149 case(SQL_CUR_USE_IF_NEEDED
):
1150 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1152 case(SQL_CUR_USE_ODBC
):
1153 cout
<< wxT("SQL_CUR_USE_ODBC");
1155 case(SQL_CUR_USE_DRIVER
):
1156 cout
<< wxT("SQL_CUR_USE_DRIVER");
1161 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1162 if (retcode
!= SQL_SUCCESS
)
1163 return(DispAllErrors(henv
, hdbc
));
1164 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1169 // Completed Successfully
1172 } // wxDb::setConnectionOptions()
1175 /********** wxDb::getDbInfo() **********/
1176 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1181 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1182 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1184 DispAllErrors(henv
, hdbc
);
1185 if (failOnDataTypeUnsupported
)
1189 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1190 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1192 DispAllErrors(henv
, hdbc
);
1193 if (failOnDataTypeUnsupported
)
1197 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1198 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1200 DispAllErrors(henv
, hdbc
);
1201 if (failOnDataTypeUnsupported
)
1206 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1207 // causing database connectivity to fail in some cases.
1208 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1209 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1211 DispAllErrors(henv
, hdbc
);
1212 if (failOnDataTypeUnsupported
)
1216 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1217 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1219 DispAllErrors(henv
, hdbc
);
1220 if (failOnDataTypeUnsupported
)
1224 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1225 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1227 DispAllErrors(henv
, hdbc
);
1228 if (failOnDataTypeUnsupported
)
1232 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1233 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1235 DispAllErrors(henv
, hdbc
);
1236 if (failOnDataTypeUnsupported
)
1240 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1241 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1243 DispAllErrors(henv
, hdbc
);
1244 if (failOnDataTypeUnsupported
)
1248 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1249 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1251 DispAllErrors(henv
, hdbc
);
1252 if (failOnDataTypeUnsupported
)
1256 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1257 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1259 DispAllErrors(henv
, hdbc
);
1260 if (failOnDataTypeUnsupported
)
1264 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1265 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1267 DispAllErrors(henv
, hdbc
);
1268 if (failOnDataTypeUnsupported
)
1272 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1273 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1275 // Not all drivers support this call - Nick Gorham(unixODBC)
1276 dbInf
.cliConfLvl
= 0;
1277 DispAllErrors(henv
, hdbc
);
1278 if (failOnDataTypeUnsupported
)
1282 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1283 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1285 DispAllErrors(henv
, hdbc
);
1286 if (failOnDataTypeUnsupported
)
1290 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1291 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1293 DispAllErrors(henv
, hdbc
);
1294 if (failOnDataTypeUnsupported
)
1298 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1299 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1301 DispAllErrors(henv
, hdbc
);
1302 if (failOnDataTypeUnsupported
)
1306 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1307 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1309 DispAllErrors(henv
, hdbc
);
1310 if (failOnDataTypeUnsupported
)
1314 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1315 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1317 DispAllErrors(henv
, hdbc
);
1318 if (failOnDataTypeUnsupported
)
1322 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1323 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1325 DispAllErrors(henv
, hdbc
);
1326 if (failOnDataTypeUnsupported
)
1330 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1331 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1333 DispAllErrors(henv
, hdbc
);
1334 if (failOnDataTypeUnsupported
)
1338 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1339 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1341 DispAllErrors(henv
, hdbc
);
1342 if (failOnDataTypeUnsupported
)
1346 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1347 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1349 DispAllErrors(henv
, hdbc
);
1350 if (failOnDataTypeUnsupported
)
1354 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1355 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1357 DispAllErrors(henv
, hdbc
);
1358 if (failOnDataTypeUnsupported
)
1362 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1363 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1365 DispAllErrors(henv
, hdbc
);
1366 if (failOnDataTypeUnsupported
)
1370 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1371 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1373 DispAllErrors(henv
, hdbc
);
1374 if (failOnDataTypeUnsupported
)
1378 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1379 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1381 DispAllErrors(henv
, hdbc
);
1382 if (failOnDataTypeUnsupported
)
1386 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1387 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1389 DispAllErrors(henv
, hdbc
);
1390 if (failOnDataTypeUnsupported
)
1394 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1395 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1397 DispAllErrors(henv
, hdbc
);
1398 if (failOnDataTypeUnsupported
)
1402 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1403 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1405 DispAllErrors(henv
, hdbc
);
1406 if (failOnDataTypeUnsupported
)
1410 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1411 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1413 DispAllErrors(henv
, hdbc
);
1414 if (failOnDataTypeUnsupported
)
1418 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1419 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1421 DispAllErrors(henv
, hdbc
);
1422 if (failOnDataTypeUnsupported
)
1426 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1427 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1429 DispAllErrors(henv
, hdbc
);
1430 if (failOnDataTypeUnsupported
)
1434 #ifdef DBDEBUG_CONSOLE
1435 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1436 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1437 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1438 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1440 cout
<< wxT("API Conf. Level: ");
1441 switch(dbInf
.apiConfLvl
)
1443 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1444 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1445 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1449 cout
<< wxT("SAG CLI Conf. Level: ");
1450 switch(dbInf
.cliConfLvl
)
1452 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1453 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1457 cout
<< wxT("SQL Conf. Level: ");
1458 switch(dbInf
.sqlConfLvl
)
1460 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1461 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1462 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1466 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1467 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1468 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1469 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1470 cout
<< wxT("Cursor COMMIT Behavior: ");
1471 switch(dbInf
.cursorCommitBehavior
)
1473 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1474 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1475 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1479 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1480 switch(dbInf
.cursorRollbackBehavior
)
1482 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1483 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1484 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1488 cout
<< wxT("Support NOT NULL clause: ");
1489 switch(dbInf
.supportNotNullClause
)
1491 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1492 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1496 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1497 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1499 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1502 cout
<< wxT("Default Transaction Isolation: ";
1503 switch(dbInf
.txnIsolation
)
1505 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1506 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1507 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1508 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1510 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1515 cout
<< wxT("Transaction Isolation Options: ");
1516 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1517 cout
<< wxT("Read Uncommitted, ");
1518 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1519 cout
<< wxT("Read Committed, ");
1520 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1521 cout
<< wxT("Repeatable Read, ");
1522 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1523 cout
<< wxT("Serializable, ");
1525 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1526 cout
<< wxT("Versioning");
1530 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1531 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1532 cout
<< wxT("Next, ");
1533 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1534 cout
<< wxT("Prev, ");
1535 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1536 cout
<< wxT("First, ");
1537 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1538 cout
<< wxT("Last, ");
1539 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1540 cout
<< wxT("Absolute, ");
1541 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1542 cout
<< wxT("Relative, ");
1544 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1545 cout
<< wxT("Resume, ");
1547 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1548 cout
<< wxT("Bookmark");
1551 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1552 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1553 cout
<< wxT("No Change, ");
1554 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1555 cout
<< wxT("Exclusive, ");
1556 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1557 cout
<< wxT("UnLock");
1560 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1561 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1562 cout
<< wxT("Position, ");
1563 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1564 cout
<< wxT("Refresh, ");
1565 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1566 cout
<< wxT("Upd, "));
1567 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1568 cout
<< wxT("Del, ");
1569 if (dbInf
.posOperations
& SQL_POS_ADD
)
1573 cout
<< wxT("Positioned Statements Supported: ");
1574 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1575 cout
<< wxT("Pos delete, ");
1576 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1577 cout
<< wxT("Pos update, ");
1578 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1579 cout
<< wxT("Select for update");
1582 cout
<< wxT("Scroll Concurrency: ");
1583 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1584 cout
<< wxT("Read Only, ");
1585 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1586 cout
<< wxT("Lock, ");
1587 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1588 cout
<< wxT("Opt. Rowver, ");
1589 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1590 cout
<< wxT("Opt. Values");
1593 cout
<< wxT("Scroll Options: ");
1594 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1595 cout
<< wxT("Fwd Only, ");
1596 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1597 cout
<< wxT("Static, ");
1598 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1599 cout
<< wxT("Keyset Driven, ");
1600 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1601 cout
<< wxT("Dynamic, ");
1602 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1603 cout
<< wxT("Mixed");
1606 cout
<< wxT("Static Sensitivity: ");
1607 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1608 cout
<< wxT("Additions, ");
1609 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1610 cout
<< wxT("Deletions, ");
1611 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1612 cout
<< wxT("Updates");
1615 cout
<< wxT("Transaction Capable?: ");
1616 switch(dbInf
.txnCapable
)
1618 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1619 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1620 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1621 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1622 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1629 // Completed Successfully
1632 } // wxDb::getDbInfo()
1635 /********** wxDb::getDataTypeInfo() **********/
1636 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1639 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1640 * the data type inf. is gathered for.
1642 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1647 // Get information about the data type specified
1648 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1649 return(DispAllErrors(henv
, hdbc
, hstmt
));
1652 retcode
= SQLFetch(hstmt
);
1653 if (retcode
!= SQL_SUCCESS
)
1655 #ifdef DBDEBUG_CONSOLE
1656 if (retcode
== SQL_NO_DATA_FOUND
)
1657 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1659 DispAllErrors(henv
, hdbc
, hstmt
);
1660 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1664 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1666 // Obtain columns from the record
1667 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1668 return(DispAllErrors(henv
, hdbc
, hstmt
));
1670 structSQLTypeInfo
.TypeName
= typeName
;
1672 // BJO 20000503: no more needed with new GetColumns...
1675 if (Dbms() == dbmsMY_SQL
)
1677 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1678 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1679 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1680 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1681 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1682 structSQLTypeInfo
.TypeName
= wxT("int");
1683 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1684 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1685 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1686 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1687 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1688 structSQLTypeInfo
.TypeName
= wxT("char");
1691 // BJO 20000427 : OpenLink driver
1692 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1693 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1695 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1696 structSQLTypeInfo
.TypeName
= wxT("real");
1700 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1701 return(DispAllErrors(henv
, hdbc
, hstmt
));
1702 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1703 return(DispAllErrors(henv
, hdbc
, hstmt
));
1704 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1705 // return(DispAllErrors(henv, hdbc, hstmt));
1707 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1708 return(DispAllErrors(henv
, hdbc
, hstmt
));
1710 if (structSQLTypeInfo
.MaximumScale
< 0)
1711 structSQLTypeInfo
.MaximumScale
= 0;
1713 // Close the statement handle which closes open cursors
1714 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1715 return(DispAllErrors(henv
, hdbc
, hstmt
));
1717 // Completed Successfully
1720 } // wxDb::getDataTypeInfo()
1723 /********** wxDb::Close() **********/
1724 void wxDb::Close(void)
1726 // Close the Sql Log file
1733 // Free statement handle
1736 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1737 DispAllErrors(henv
, hdbc
);
1740 // Disconnect from the datasource
1741 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1742 DispAllErrors(henv
, hdbc
);
1744 // Free the connection to the datasource
1745 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1746 DispAllErrors(henv
, hdbc
);
1748 // There should be zero Ctable objects still connected to this db object
1749 wxASSERT(nTables
== 0);
1753 wxCriticalSectionLocker
lock(csTablesInUse
);
1755 wxList::compatibility_iterator pNode
;
1756 pNode
= TablesInUse
.GetFirst();
1760 tiu
= (wxTablesInUse
*)pNode
->GetData();
1761 if (tiu
->pDb
== this)
1763 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"),
1764 tiu
->tableName
, tiu
->tableID
, wx_static_cast(void*, tiu
->pDb
));
1765 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"), wx_static_cast(void*, this));
1766 wxLogDebug(s
.c_str(),s2
.c_str());
1768 pNode
= pNode
->GetNext();
1773 // Copy the error messages to a global variable
1775 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1776 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1778 dbmsType
= dbmsUNIDENTIFIED
;
1784 /********** wxDb::CommitTrans() **********/
1785 bool wxDb::CommitTrans(void)
1789 // Commit the transaction
1790 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1791 return(DispAllErrors(henv
, hdbc
));
1794 // Completed successfully
1797 } // wxDb::CommitTrans()
1800 /********** wxDb::RollbackTrans() **********/
1801 bool wxDb::RollbackTrans(void)
1803 // Rollback the transaction
1804 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1805 return(DispAllErrors(henv
, hdbc
));
1807 // Completed successfully
1810 } // wxDb::RollbackTrans()
1813 /********** wxDb::DispAllErrors() **********/
1814 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1816 * This function is called internally whenever an error condition prevents the user's
1817 * request from being executed. This function will query the datasource as to the
1818 * actual error(s) that just occurred on the previous request of the datasource.
1820 * The function will retrieve each error condition from the datasource and
1821 * Printf the codes/text values into a string which it then logs via logError().
1822 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1823 * window and program execution will be paused until the user presses a key.
1825 * This function always returns false, so that functions which call this function
1826 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1827 * of the user's request, so that the calling code can then process the error message log.
1830 wxString odbcErrMsg
;
1832 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1834 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1835 sqlState
, (long)nativeError
, errorMsg
);
1836 logError(odbcErrMsg
, sqlState
);
1839 #ifdef DBDEBUG_CONSOLE
1840 // When run in console mode, use standard out to display errors.
1841 cout
<< odbcErrMsg
.c_str() << endl
;
1842 cout
<< wxT("Press any key to continue...") << endl
;
1847 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1852 return false; // This function always returns false.
1854 } // wxDb::DispAllErrors()
1857 /********** wxDb::GetNextError() **********/
1858 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1860 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1865 } // wxDb::GetNextError()
1868 /********** wxDb::DispNextError() **********/
1869 void wxDb::DispNextError(void)
1871 wxString odbcErrMsg
;
1873 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1874 sqlState
, (long)nativeError
, errorMsg
);
1875 logError(odbcErrMsg
, sqlState
);
1880 #ifdef DBDEBUG_CONSOLE
1881 // When run in console mode, use standard out to display errors.
1882 cout
<< odbcErrMsg
.c_str() << endl
;
1883 cout
<< wxT("Press any key to continue...") << endl
;
1888 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1889 #endif // __WXDEBUG__
1891 } // wxDb::DispNextError()
1894 /********** wxDb::logError() **********/
1895 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1897 wxASSERT(errMsg
.length());
1899 static int pLast
= -1;
1902 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1905 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1906 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1910 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1911 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1913 if (SQLState
.length())
1914 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1915 DB_STATUS
= dbStatus
;
1917 // Add the errmsg to the sql log
1918 WriteSqlLog(errMsg
);
1920 } // wxDb::logError()
1923 /**********wxDb::TranslateSqlState() **********/
1924 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1926 if (!wxStrcmp(SQLState
, wxT("01000")))
1927 return(DB_ERR_GENERAL_WARNING
);
1928 if (!wxStrcmp(SQLState
, wxT("01002")))
1929 return(DB_ERR_DISCONNECT_ERROR
);
1930 if (!wxStrcmp(SQLState
, wxT("01004")))
1931 return(DB_ERR_DATA_TRUNCATED
);
1932 if (!wxStrcmp(SQLState
, wxT("01006")))
1933 return(DB_ERR_PRIV_NOT_REVOKED
);
1934 if (!wxStrcmp(SQLState
, wxT("01S00")))
1935 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1936 if (!wxStrcmp(SQLState
, wxT("01S01")))
1937 return(DB_ERR_ERROR_IN_ROW
);
1938 if (!wxStrcmp(SQLState
, wxT("01S02")))
1939 return(DB_ERR_OPTION_VALUE_CHANGED
);
1940 if (!wxStrcmp(SQLState
, wxT("01S03")))
1941 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1942 if (!wxStrcmp(SQLState
, wxT("01S04")))
1943 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1944 if (!wxStrcmp(SQLState
, wxT("07001")))
1945 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1946 if (!wxStrcmp(SQLState
, wxT("07006")))
1947 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1948 if (!wxStrcmp(SQLState
, wxT("08001")))
1949 return(DB_ERR_UNABLE_TO_CONNECT
);
1950 if (!wxStrcmp(SQLState
, wxT("08002")))
1951 return(DB_ERR_CONNECTION_IN_USE
);
1952 if (!wxStrcmp(SQLState
, wxT("08003")))
1953 return(DB_ERR_CONNECTION_NOT_OPEN
);
1954 if (!wxStrcmp(SQLState
, wxT("08004")))
1955 return(DB_ERR_REJECTED_CONNECTION
);
1956 if (!wxStrcmp(SQLState
, wxT("08007")))
1957 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1958 if (!wxStrcmp(SQLState
, wxT("08S01")))
1959 return(DB_ERR_COMM_LINK_FAILURE
);
1960 if (!wxStrcmp(SQLState
, wxT("21S01")))
1961 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1962 if (!wxStrcmp(SQLState
, wxT("21S02")))
1963 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1964 if (!wxStrcmp(SQLState
, wxT("22001")))
1965 return(DB_ERR_STRING_RIGHT_TRUNC
);
1966 if (!wxStrcmp(SQLState
, wxT("22003")))
1967 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1968 if (!wxStrcmp(SQLState
, wxT("22005")))
1969 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1970 if (!wxStrcmp(SQLState
, wxT("22008")))
1971 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1972 if (!wxStrcmp(SQLState
, wxT("22012")))
1973 return(DB_ERR_DIVIDE_BY_ZERO
);
1974 if (!wxStrcmp(SQLState
, wxT("22026")))
1975 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1976 if (!wxStrcmp(SQLState
, wxT("23000")))
1977 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1978 if (!wxStrcmp(SQLState
, wxT("24000")))
1979 return(DB_ERR_INVALID_CURSOR_STATE
);
1980 if (!wxStrcmp(SQLState
, wxT("25000")))
1981 return(DB_ERR_INVALID_TRANS_STATE
);
1982 if (!wxStrcmp(SQLState
, wxT("28000")))
1983 return(DB_ERR_INVALID_AUTH_SPEC
);
1984 if (!wxStrcmp(SQLState
, wxT("34000")))
1985 return(DB_ERR_INVALID_CURSOR_NAME
);
1986 if (!wxStrcmp(SQLState
, wxT("37000")))
1987 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1988 if (!wxStrcmp(SQLState
, wxT("3C000")))
1989 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1990 if (!wxStrcmp(SQLState
, wxT("40001")))
1991 return(DB_ERR_SERIALIZATION_FAILURE
);
1992 if (!wxStrcmp(SQLState
, wxT("42000")))
1993 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1994 if (!wxStrcmp(SQLState
, wxT("70100")))
1995 return(DB_ERR_OPERATION_ABORTED
);
1996 if (!wxStrcmp(SQLState
, wxT("IM001")))
1997 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1998 if (!wxStrcmp(SQLState
, wxT("IM002")))
1999 return(DB_ERR_NO_DATA_SOURCE
);
2000 if (!wxStrcmp(SQLState
, wxT("IM003")))
2001 return(DB_ERR_DRIVER_LOAD_ERROR
);
2002 if (!wxStrcmp(SQLState
, wxT("IM004")))
2003 return(DB_ERR_SQLALLOCENV_FAILED
);
2004 if (!wxStrcmp(SQLState
, wxT("IM005")))
2005 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
2006 if (!wxStrcmp(SQLState
, wxT("IM006")))
2007 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
2008 if (!wxStrcmp(SQLState
, wxT("IM007")))
2009 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
2010 if (!wxStrcmp(SQLState
, wxT("IM008")))
2011 return(DB_ERR_DIALOG_FAILED
);
2012 if (!wxStrcmp(SQLState
, wxT("IM009")))
2013 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
2014 if (!wxStrcmp(SQLState
, wxT("IM010")))
2015 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
2016 if (!wxStrcmp(SQLState
, wxT("IM011")))
2017 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
2018 if (!wxStrcmp(SQLState
, wxT("IM012")))
2019 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
2020 if (!wxStrcmp(SQLState
, wxT("IM013")))
2021 return(DB_ERR_TRACE_FILE_ERROR
);
2022 if (!wxStrcmp(SQLState
, wxT("S0001")))
2023 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
2024 if (!wxStrcmp(SQLState
, wxT("S0002")))
2025 return(DB_ERR_TABLE_NOT_FOUND
);
2026 if (!wxStrcmp(SQLState
, wxT("S0011")))
2027 return(DB_ERR_INDEX_ALREADY_EXISTS
);
2028 if (!wxStrcmp(SQLState
, wxT("S0012")))
2029 return(DB_ERR_INDEX_NOT_FOUND
);
2030 if (!wxStrcmp(SQLState
, wxT("S0021")))
2031 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
2032 if (!wxStrcmp(SQLState
, wxT("S0022")))
2033 return(DB_ERR_COLUMN_NOT_FOUND
);
2034 if (!wxStrcmp(SQLState
, wxT("S0023")))
2035 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2036 if (!wxStrcmp(SQLState
, wxT("S1000")))
2037 return(DB_ERR_GENERAL_ERROR
);
2038 if (!wxStrcmp(SQLState
, wxT("S1001")))
2039 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2040 if (!wxStrcmp(SQLState
, wxT("S1002")))
2041 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2042 if (!wxStrcmp(SQLState
, wxT("S1003")))
2043 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2044 if (!wxStrcmp(SQLState
, wxT("S1004")))
2045 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2046 if (!wxStrcmp(SQLState
, wxT("S1008")))
2047 return(DB_ERR_OPERATION_CANCELLED
);
2048 if (!wxStrcmp(SQLState
, wxT("S1009")))
2049 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2050 if (!wxStrcmp(SQLState
, wxT("S1010")))
2051 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2052 if (!wxStrcmp(SQLState
, wxT("S1011")))
2053 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2054 if (!wxStrcmp(SQLState
, wxT("S1012")))
2055 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2056 if (!wxStrcmp(SQLState
, wxT("S1015")))
2057 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2058 if (!wxStrcmp(SQLState
, wxT("S1090")))
2059 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2060 if (!wxStrcmp(SQLState
, wxT("S1091")))
2061 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2062 if (!wxStrcmp(SQLState
, wxT("S1092")))
2063 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2064 if (!wxStrcmp(SQLState
, wxT("S1093")))
2065 return(DB_ERR_INVALID_PARAM_NO
);
2066 if (!wxStrcmp(SQLState
, wxT("S1094")))
2067 return(DB_ERR_INVALID_SCALE_VALUE
);
2068 if (!wxStrcmp(SQLState
, wxT("S1095")))
2069 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2070 if (!wxStrcmp(SQLState
, wxT("S1096")))
2071 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2072 if (!wxStrcmp(SQLState
, wxT("S1097")))
2073 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2074 if (!wxStrcmp(SQLState
, wxT("S1098")))
2075 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2076 if (!wxStrcmp(SQLState
, wxT("S1099")))
2077 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2078 if (!wxStrcmp(SQLState
, wxT("S1100")))
2079 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2080 if (!wxStrcmp(SQLState
, wxT("S1101")))
2081 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2082 if (!wxStrcmp(SQLState
, wxT("S1103")))
2083 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2084 if (!wxStrcmp(SQLState
, wxT("S1104")))
2085 return(DB_ERR_INVALID_PRECISION_VALUE
);
2086 if (!wxStrcmp(SQLState
, wxT("S1105")))
2087 return(DB_ERR_INVALID_PARAM_TYPE
);
2088 if (!wxStrcmp(SQLState
, wxT("S1106")))
2089 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2090 if (!wxStrcmp(SQLState
, wxT("S1107")))
2091 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2092 if (!wxStrcmp(SQLState
, wxT("S1108")))
2093 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2094 if (!wxStrcmp(SQLState
, wxT("S1109")))
2095 return(DB_ERR_INVALID_CURSOR_POSITION
);
2096 if (!wxStrcmp(SQLState
, wxT("S1110")))
2097 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2098 if (!wxStrcmp(SQLState
, wxT("S1111")))
2099 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2100 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2101 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2102 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2103 return(DB_ERR_TIMEOUT_EXPIRED
);
2108 } // wxDb::TranslateSqlState()
2111 /********** wxDb::Grant() **********/
2112 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2116 // Build the grant statement
2117 sqlStmt
= wxT("GRANT ");
2118 if (privileges
== DB_GRANT_ALL
)
2119 sqlStmt
+= wxT("ALL");
2123 if (privileges
& DB_GRANT_SELECT
)
2125 sqlStmt
+= wxT("SELECT");
2128 if (privileges
& DB_GRANT_INSERT
)
2131 sqlStmt
+= wxT(", ");
2132 sqlStmt
+= wxT("INSERT");
2134 if (privileges
& DB_GRANT_UPDATE
)
2137 sqlStmt
+= wxT(", ");
2138 sqlStmt
+= wxT("UPDATE");
2140 if (privileges
& DB_GRANT_DELETE
)
2143 sqlStmt
+= wxT(", ");
2144 sqlStmt
+= wxT("DELETE");
2148 sqlStmt
+= wxT(" ON ");
2149 sqlStmt
+= SQLTableName(tableName
);
2150 sqlStmt
+= wxT(" TO ");
2151 sqlStmt
+= userList
;
2153 #ifdef DBDEBUG_CONSOLE
2154 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2157 WriteSqlLog(sqlStmt
);
2159 return(ExecSql(sqlStmt
));
2164 /********** wxDb::CreateView() **********/
2165 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2166 const wxString
&pSqlStmt
, bool attemptDrop
)
2170 // Drop the view first
2171 if (attemptDrop
&& !DropView(viewName
))
2174 // Build the create view statement
2175 sqlStmt
= wxT("CREATE VIEW ");
2176 sqlStmt
+= viewName
;
2178 if (colList
.length())
2180 sqlStmt
+= wxT(" (");
2182 sqlStmt
+= wxT(")");
2185 sqlStmt
+= wxT(" AS ");
2186 sqlStmt
+= pSqlStmt
;
2188 WriteSqlLog(sqlStmt
);
2190 #ifdef DBDEBUG_CONSOLE
2191 cout
<< sqlStmt
.c_str() << endl
;
2194 return(ExecSql(sqlStmt
));
2196 } // wxDb::CreateView()
2199 /********** wxDb::DropView() **********/
2200 bool wxDb::DropView(const wxString
&viewName
)
2203 * NOTE: This function returns true if the View does not exist, but
2204 * only for identified databases. Code will need to be added
2205 * below for any other databases when those databases are defined
2206 * to handle this situation consistently
2210 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2212 WriteSqlLog(sqlStmt
);
2214 #ifdef DBDEBUG_CONSOLE
2215 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2218 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2220 // Check for "Base table not found" error and ignore
2221 GetNextError(henv
, hdbc
, hstmt
);
2222 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2224 // Check for product specific error codes
2225 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2228 DispAllErrors(henv
, hdbc
, hstmt
);
2235 // Commit the transaction
2241 } // wxDb::DropView()
2244 /********** wxDb::ExecSql() **********/
2245 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2249 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2251 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2252 if (retcode
== SQL_SUCCESS
||
2253 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2259 DispAllErrors(henv
, hdbc
, hstmt
);
2263 } // wxDb::ExecSql()
2266 /********** wxDb::ExecSql() with column info **********/
2267 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2269 //execute the statement first
2270 if (!ExecSql(pSqlStmt
))
2274 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2276 DispAllErrors(henv
, hdbc
, hstmt
);
2285 // Get column information
2287 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2290 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2292 // Fill in column information (name, datatype)
2293 for (colNum
= 0; colNum
< noCols
; colNum
++)
2295 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2297 &Sword
, &Sqllen
) != SQL_SUCCESS
)
2299 DispAllErrors(henv
, hdbc
, hstmt
);
2304 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2305 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2307 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2308 NULL
, 0, &Sword
, &Sqllen
) != SQL_SUCCESS
)
2310 DispAllErrors(henv
, hdbc
, hstmt
);
2318 #if defined(SQL_WCHAR)
2321 #if defined(SQL_WVARCHAR)
2327 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2329 case SQL_LONGVARCHAR
:
2330 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_MEMO
;
2336 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2343 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2347 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2350 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2355 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen
);
2356 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2363 } // wxDb::ExecSql()
2365 /********** wxDb::GetNext() **********/
2366 bool wxDb::GetNext(void)
2368 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2372 DispAllErrors(henv
, hdbc
, hstmt
);
2376 } // wxDb::GetNext()
2379 /********** wxDb::GetData() **********/
2380 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SQLLEN FAR
*cbReturned
)
2383 wxASSERT(cbReturned
);
2385 long bufferSize
= maxLen
;
2387 if (cType
== SQL_C_WXCHAR
)
2388 bufferSize
= maxLen
* sizeof(wxChar
);
2390 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2394 DispAllErrors(henv
, hdbc
, hstmt
);
2398 } // wxDb::GetData()
2401 /********** wxDb::GetKeyFields() **********/
2402 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2404 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2405 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2407 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2408 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2414 * -----------------------------------------------------------------------
2415 * -- 19991224 : mj10777 : Create ------
2416 * -- : Three things are done and stored here : ------
2417 * -- : 1) which Column(s) is/are Primary Key(s) ------
2418 * -- : 2) which tables use this Key as a Foreign Key ------
2419 * -- : 3) which columns are Foreign Key and the name ------
2420 * -- : of the Table where the Key is the Primary Key -----
2421 * -- : Called from GetColumns(const wxString &tableName, ------
2422 * -- int *numCols,const wxChar *userID ) ------
2423 * -----------------------------------------------------------------------
2426 /*---------------------------------------------------------------------*/
2427 /* Get the names of the columns in the primary key. */
2428 /*---------------------------------------------------------------------*/
2429 retcode
= SQLPrimaryKeys(hstmt
,
2430 NULL
, 0, /* Catalog name */
2431 NULL
, 0, /* Schema name */
2432 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2434 /*---------------------------------------------------------------------*/
2435 /* Fetch and display the result set. This will be a list of the */
2436 /* columns in the primary key of the tableName table. */
2437 /*---------------------------------------------------------------------*/
2438 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2440 retcode
= SQLFetch(hstmt
);
2441 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2443 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2444 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2446 for (i
=0;i
<noCols
;i
++) // Find the Column name
2447 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2448 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2451 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2453 /*---------------------------------------------------------------------*/
2454 /* Get all the foreign keys that refer to tableName primary key. */
2455 /*---------------------------------------------------------------------*/
2456 retcode
= SQLForeignKeys(hstmt
,
2457 NULL
, 0, /* Primary catalog */
2458 NULL
, 0, /* Primary schema */
2459 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2460 NULL
, 0, /* Foreign catalog */
2461 NULL
, 0, /* Foreign schema */
2462 NULL
, 0); /* Foreign table */
2464 /*---------------------------------------------------------------------*/
2465 /* Fetch and display the result set. This will be all of the foreign */
2466 /* keys in other tables that refer to the tableName primary key. */
2467 /*---------------------------------------------------------------------*/
2470 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2472 retcode
= SQLFetch(hstmt
);
2473 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2475 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2476 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2477 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2478 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2479 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2480 tempStr
<< _T('[') << szFkTable
<< _T(']'); // [ ] in case there is a blank in the Table name
2484 tempStr
.Trim(); // Get rid of any unneeded blanks
2485 if (!tempStr
.empty())
2487 for (i
=0; i
<noCols
; i
++)
2488 { // Find the Column name
2489 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2491 wxStrncpy(colInf
[i
].PkTableName
, tempStr
.c_str(), DB_MAX_TABLE_NAME_LEN
); // Name of the Tables where this Primary Key is used as a Foreign Key
2492 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2497 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2499 /*---------------------------------------------------------------------*/
2500 /* Get all the foreign keys in the tablename table. */
2501 /*---------------------------------------------------------------------*/
2502 retcode
= SQLForeignKeys(hstmt
,
2503 NULL
, 0, /* Primary catalog */
2504 NULL
, 0, /* Primary schema */
2505 NULL
, 0, /* Primary table */
2506 NULL
, 0, /* Foreign catalog */
2507 NULL
, 0, /* Foreign schema */
2508 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2510 /*---------------------------------------------------------------------*/
2511 /* Fetch and display the result set. This will be all of the */
2512 /* primary keys in other tables that are referred to by foreign */
2513 /* keys in the tableName table. */
2514 /*---------------------------------------------------------------------*/
2515 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2517 retcode
= SQLFetch(hstmt
);
2518 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2520 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2521 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2522 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2524 for (i
=0; i
<noCols
; i
++) // Find the Column name
2526 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2528 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2529 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2530 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2535 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2539 } // wxDb::GetKeyFields()
2543 /********** wxDb::GetColumns() **********/
2544 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2546 * 1) The last array element of the tableName[] argument must be zero (null).
2547 * This is how the end of the array is detected.
2548 * 2) This function returns an array of wxDbColInf structures. If no columns
2549 * were found, or an error occurred, this pointer will be zero (null). THE
2550 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2551 * IS FINISHED WITH IT. i.e.
2553 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2556 * // Use the column inf
2558 * // Destroy the memory
2562 * userID is evaluated in the following manner:
2563 * userID == NULL ... UserID is ignored
2564 * userID == "" ... UserID set equal to 'this->uid'
2565 * userID != "" ... UserID set equal to 'userID'
2567 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2568 * by this function. This function should use its own wxDb instance
2569 * to avoid undesired unbinding of columns.
2574 wxDbColInf
*colInf
= 0;
2582 convertUserID(userID
,UserID
);
2584 // Pass 1 - Determine how many columns there are.
2585 // Pass 2 - Allocate the wxDbColInf array and fill in
2586 // the array with the column information.
2588 for (pass
= 1; pass
<= 2; pass
++)
2592 if (noCols
== 0) // Probably a bogus table name(s)
2594 // Allocate n wxDbColInf objects to hold the column information
2595 colInf
= new wxDbColInf
[noCols
+1];
2598 // Mark the end of the array
2599 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2600 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2601 colInf
[noCols
].sqlDataType
= 0;
2603 // Loop through each table name
2605 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2607 TableName
= tableName
[tbl
];
2608 // Oracle and Interbase table names are uppercase only, so force
2609 // the name to uppercase just in case programmer forgot to do this
2610 if ((Dbms() == dbmsORACLE
) ||
2611 (Dbms() == dbmsFIREBIRD
) ||
2612 (Dbms() == dbmsINTERBASE
))
2613 TableName
= TableName
.Upper();
2615 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2617 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2618 // use the call below that leaves out the user name
2619 if (!UserID
.empty() &&
2620 Dbms() != dbmsMY_SQL
&&
2621 Dbms() != dbmsACCESS
&&
2622 Dbms() != dbmsMS_SQL_SERVER
)
2624 retcode
= SQLColumns(hstmt
,
2625 NULL
, 0, // All qualifiers
2626 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2627 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2628 NULL
, 0); // All columns
2632 retcode
= SQLColumns(hstmt
,
2633 NULL
, 0, // All qualifiers
2635 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2636 NULL
, 0); // All columns
2638 if (retcode
!= SQL_SUCCESS
)
2639 { // Error occurred, abort
2640 DispAllErrors(henv
, hdbc
, hstmt
);
2643 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2647 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2649 if (pass
== 1) // First pass, just add up the number of columns
2651 else // Pass 2; Fill in the array of structures
2653 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2655 // NOTE: Only the ODBC 1.x fields are retrieved
2656 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2657 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2658 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2659 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2660 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2661 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2662 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2663 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2664 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2665 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2666 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2667 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2669 // Determine the wxDb data type that is used to represent the native data type of this data source
2670 colInf
[colNo
].dbDataType
= 0;
2671 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2674 // IODBC does not return a correct columnLength, so we set
2675 // columnLength = bufferSize if no column length was returned
2676 // IODBC returns the columnLength in bufferSize. (bug)
2677 if (colInf
[colNo
].columnLength
< 1)
2679 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2682 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2684 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2685 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2686 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2687 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2688 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2689 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2690 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2691 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2696 if (retcode
!= SQL_NO_DATA_FOUND
)
2697 { // Error occurred, abort
2698 DispAllErrors(henv
, hdbc
, hstmt
);
2701 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2707 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2710 } // wxDb::GetColumns()
2713 /********** wxDb::GetColumns() **********/
2715 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2717 // Same as the above GetColumns() function except this one gets columns
2718 // only for a single table, and if 'numCols' is not NULL, the number of
2719 // columns stored in the returned wxDbColInf is set in '*numCols'
2721 // userID is evaluated in the following manner:
2722 // userID == NULL ... UserID is ignored
2723 // userID == "" ... UserID set equal to 'this->uid'
2724 // userID != "" ... UserID set equal to 'userID'
2726 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2727 // by this function. This function should use its own wxDb instance
2728 // to avoid undesired unbinding of columns.
2733 wxDbColInf
*colInf
= 0;
2741 convertUserID(userID
,UserID
);
2743 // Pass 1 - Determine how many columns there are.
2744 // Pass 2 - Allocate the wxDbColInf array and fill in
2745 // the array with the column information.
2747 for (pass
= 1; pass
<= 2; pass
++)
2751 if (noCols
== 0) // Probably a bogus table name(s)
2753 // Allocate n wxDbColInf objects to hold the column information
2754 colInf
= new wxDbColInf
[noCols
+1];
2757 // Mark the end of the array
2758 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2759 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2760 colInf
[noCols
].sqlDataType
= 0;
2763 TableName
= tableName
;
2764 // Oracle and Interbase table names are uppercase only, so force
2765 // the name to uppercase just in case programmer forgot to do this
2766 if ((Dbms() == dbmsORACLE
) ||
2767 (Dbms() == dbmsFIREBIRD
) ||
2768 (Dbms() == dbmsINTERBASE
))
2769 TableName
= TableName
.Upper();
2771 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2773 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2774 // use the call below that leaves out the user name
2775 if (!UserID
.empty() &&
2776 Dbms() != dbmsMY_SQL
&&
2777 Dbms() != dbmsACCESS
&&
2778 Dbms() != dbmsMS_SQL_SERVER
)
2780 retcode
= SQLColumns(hstmt
,
2781 NULL
, 0, // All qualifiers
2782 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2783 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2784 NULL
, 0); // All columns
2788 retcode
= SQLColumns(hstmt
,
2789 NULL
, 0, // All qualifiers
2791 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2792 NULL
, 0); // All columns
2794 if (retcode
!= SQL_SUCCESS
)
2795 { // Error occurred, abort
2796 DispAllErrors(henv
, hdbc
, hstmt
);
2799 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2805 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2807 if (pass
== 1) // First pass, just add up the number of columns
2809 else // Pass 2; Fill in the array of structures
2811 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2813 // NOTE: Only the ODBC 1.x fields are retrieved
2814 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2815 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2816 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2817 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2818 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2819 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2820 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2821 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2822 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2823 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2824 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2825 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2826 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2827 // Start Values for Primary/Foriegn Key (=No)
2828 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2829 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2830 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2831 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2833 // BJO 20000428 : Virtuoso returns type names with upper cases!
2834 if (Dbms() == dbmsVIRTUOSO
)
2836 wxString s
= colInf
[colNo
].typeName
;
2838 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2841 // Determine the wxDb data type that is used to represent the native data type of this data source
2842 colInf
[colNo
].dbDataType
= 0;
2843 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2846 // IODBC does not return a correct columnLength, so we set
2847 // columnLength = bufferSize if no column length was returned
2848 // IODBC returns the columnLength in bufferSize. (bug)
2849 if (colInf
[colNo
].columnLength
< 1)
2851 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2855 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2857 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2858 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2859 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2860 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2861 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2862 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2863 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2864 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2870 if (retcode
!= SQL_NO_DATA_FOUND
)
2871 { // Error occurred, abort
2872 DispAllErrors(henv
, hdbc
, hstmt
);
2875 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2882 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2884 // Store Primary and Foriegn Keys
2885 GetKeyFields(tableName
,colInf
,noCols
);
2891 } // wxDb::GetColumns()
2894 #else // New GetColumns
2899 These are tentative new GetColumns members which should be more database
2900 independent and which always returns the columns in the order they were
2903 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2904 wxChar* userID)) calls the second implementation for each separate table
2905 before merging the results. This makes the code easier to maintain as
2906 only one member (the second) makes the real work
2907 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2908 wxChar *userID) is a little bit improved
2909 - It doesn't anymore rely on the type-name to find out which database-type
2911 - It ends by sorting the columns, so that they are returned in the same
2912 order they were created
2922 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2925 // The last array element of the tableName[] argument must be zero (null).
2926 // This is how the end of the array is detected.
2930 // How many tables ?
2932 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2934 // Create a table to maintain the columns for each separate table
2935 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2938 for (i
= 0 ; i
< tbl
; i
++)
2941 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2942 if (TableColumns
[i
].colInf
== NULL
)
2944 noCols
+= TableColumns
[i
].noCols
;
2947 // Now merge all the separate table infos
2948 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2950 // Mark the end of the array
2951 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2952 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2953 colInf
[noCols
].sqlDataType
= 0;
2958 for (i
= 0 ; i
< tbl
; i
++)
2960 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2962 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2966 delete [] TableColumns
;
2969 } // wxDb::GetColumns() -- NEW
2972 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2974 // Same as the above GetColumns() function except this one gets columns
2975 // only for a single table, and if 'numCols' is not NULL, the number of
2976 // columns stored in the returned wxDbColInf is set in '*numCols'
2978 // userID is evaluated in the following manner:
2979 // userID == NULL ... UserID is ignored
2980 // userID == "" ... UserID set equal to 'this->uid'
2981 // userID != "" ... UserID set equal to 'userID'
2983 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2984 // by this function. This function should use its own wxDb instance
2985 // to avoid undesired unbinding of columns.
2989 wxDbColInf
*colInf
= 0;
2997 convertUserID(userID
,UserID
);
2999 // Pass 1 - Determine how many columns there are.
3000 // Pass 2 - Allocate the wxDbColInf array and fill in
3001 // the array with the column information.
3003 for (pass
= 1; pass
<= 2; pass
++)
3007 if (noCols
== 0) // Probably a bogus table name(s)
3009 // Allocate n wxDbColInf objects to hold the column information
3010 colInf
= new wxDbColInf
[noCols
+1];
3013 // Mark the end of the array
3014 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
3015 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
3016 colInf
[noCols
].sqlDataType
= 0;
3019 TableName
= tableName
;
3020 // Oracle and Interbase table names are uppercase only, so force
3021 // the name to uppercase just in case programmer forgot to do this
3022 if ((Dbms() == dbmsORACLE
) ||
3023 (Dbms() == dbmsFIREBIRD
) ||
3024 (Dbms() == dbmsINTERBASE
))
3025 TableName
= TableName
.Upper();
3027 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3029 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3030 // use the call below that leaves out the user name
3031 if (!UserID
.empty() &&
3032 Dbms() != dbmsMY_SQL
&&
3033 Dbms() != dbmsACCESS
&&
3034 Dbms() != dbmsMS_SQL_SERVER
)
3036 retcode
= SQLColumns(hstmt
,
3037 NULL
, 0, // All qualifiers
3038 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3039 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3040 NULL
, 0); // All columns
3044 retcode
= SQLColumns(hstmt
,
3045 NULL
, 0, // All qualifiers
3047 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3048 NULL
, 0); // All columns
3050 if (retcode
!= SQL_SUCCESS
)
3051 { // Error occurred, abort
3052 DispAllErrors(henv
, hdbc
, hstmt
);
3055 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3061 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3063 if (pass
== 1) // First pass, just add up the number of columns
3065 else // Pass 2; Fill in the array of structures
3067 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3069 // NOTE: Only the ODBC 1.x fields are retrieved
3070 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3071 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3072 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3073 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3074 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3075 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3076 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3077 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3078 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3079 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3080 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3081 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3082 // Start Values for Primary/Foriegn Key (=No)
3083 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3084 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3085 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3086 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3089 // IODBC does not return a correct columnLength, so we set
3090 // columnLength = bufferSize if no column length was returned
3091 // IODBC returns the columnLength in bufferSize. (bug)
3092 if (colInf
[colNo
].columnLength
< 1)
3094 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3098 // Determine the wxDb data type that is used to represent the native data type of this data source
3099 colInf
[colNo
].dbDataType
= 0;
3100 // Get the intern datatype
3101 switch (colInf
[colNo
].sqlDataType
)
3104 #if defined(SQL_WCHAR)
3107 #if defined(SQL_WVARCHAR)
3113 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3115 case SQL_LONGVARCHAR
:
3116 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_MEMO
;
3122 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3129 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3133 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3136 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3141 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3142 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3149 if (retcode
!= SQL_NO_DATA_FOUND
)
3150 { // Error occurred, abort
3151 DispAllErrors(henv
, hdbc
, hstmt
);
3154 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3161 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3163 // Store Primary and Foreign Keys
3164 GetKeyFields(tableName
,colInf
,noCols
);
3166 ///////////////////////////////////////////////////////////////////////////
3167 // Now sort the the columns in order to make them appear in the right order
3168 ///////////////////////////////////////////////////////////////////////////
3170 // Build a generic SELECT statement which returns 0 rows
3173 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3176 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3178 DispAllErrors(henv
, hdbc
, hstmt
);
3182 // Get the number of result columns
3183 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3185 DispAllErrors(henv
, hdbc
, hstmt
);
3189 if (noCols
== 0) // Probably a bogus table name
3198 for (colNum
= 0; colNum
< noCols
; colNum
++)
3200 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3202 &Sword
, &Sdword
) != SQL_SUCCESS
)
3204 DispAllErrors(henv
, hdbc
, hstmt
);
3208 wxString Name1
= name
;
3209 Name1
= Name1
.Upper();
3211 // Where is this name in the array ?
3212 for (i
= colNum
; i
< noCols
; i
++)
3214 wxString Name2
= colInf
[i
].colName
;
3215 Name2
= Name2
.Upper();
3218 if (colNum
!= i
) // swap to sort
3220 wxDbColInf tmpColInf
= colInf
[colNum
];
3221 colInf
[colNum
] = colInf
[i
];
3222 colInf
[i
] = tmpColInf
;
3228 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3230 ///////////////////////////////////////////////////////////////////////////
3232 ///////////////////////////////////////////////////////////////////////////
3238 } // wxDb::GetColumns()
3241 #endif // #else OLD_GETCOLUMNS
3244 /********** wxDb::GetColumnCount() **********/
3245 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3247 * Returns a count of how many columns are in a table.
3248 * If an error occurs in computing the number of columns
3249 * this function will return a -1 for the count
3251 * userID is evaluated in the following manner:
3252 * userID == NULL ... UserID is ignored
3253 * userID == "" ... UserID set equal to 'this->uid'
3254 * userID != "" ... UserID set equal to 'userID'
3256 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3257 * by this function. This function should use its own wxDb instance
3258 * to avoid undesired unbinding of columns.
3268 convertUserID(userID
,UserID
);
3270 TableName
= tableName
;
3271 // Oracle and Interbase table names are uppercase only, so force
3272 // the name to uppercase just in case programmer forgot to do this
3273 if ((Dbms() == dbmsORACLE
) ||
3274 (Dbms() == dbmsFIREBIRD
) ||
3275 (Dbms() == dbmsINTERBASE
))
3276 TableName
= TableName
.Upper();
3278 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3280 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3281 // use the call below that leaves out the user name
3282 if (!UserID
.empty() &&
3283 Dbms() != dbmsMY_SQL
&&
3284 Dbms() != dbmsACCESS
&&
3285 Dbms() != dbmsMS_SQL_SERVER
)
3287 retcode
= SQLColumns(hstmt
,
3288 NULL
, 0, // All qualifiers
3289 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3290 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3291 NULL
, 0); // All columns
3295 retcode
= SQLColumns(hstmt
,
3296 NULL
, 0, // All qualifiers
3298 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3299 NULL
, 0); // All columns
3301 if (retcode
!= SQL_SUCCESS
)
3302 { // Error occurred, abort
3303 DispAllErrors(henv
, hdbc
, hstmt
);
3304 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3308 // Count the columns
3309 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3312 if (retcode
!= SQL_NO_DATA_FOUND
)
3313 { // Error occurred, abort
3314 DispAllErrors(henv
, hdbc
, hstmt
);
3315 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3319 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3322 } // wxDb::GetColumnCount()
3325 /********** wxDb::GetCatalog() *******/
3326 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3328 * ---------------------------------------------------------------------
3329 * -- 19991203 : mj10777 : Create ------
3330 * -- : Creates a wxDbInf with Tables / Cols Array ------
3331 * -- : uses SQLTables and fills pTableInf; ------
3332 * -- : pColInf is set to NULL and numCols to 0; ------
3333 * -- : returns pDbInf (wxDbInf) ------
3334 * -- - if unsuccessful (pDbInf == NULL) ------
3335 * -- : pColInf can be filled with GetColumns(..); ------
3336 * -- : numCols can be filled with GetColumnCount(..); ------
3337 * ---------------------------------------------------------------------
3339 * userID is evaluated in the following manner:
3340 * userID == NULL ... UserID is ignored
3341 * userID == "" ... UserID set equal to 'this->uid'
3342 * userID != "" ... UserID set equal to 'userID'
3344 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3345 * by this function. This function should use its own wxDb instance
3346 * to avoid undesired unbinding of columns.
3349 int noTab
= 0; // Counter while filling table entries
3353 wxString tblNameSave
;
3356 convertUserID(userID
,UserID
);
3358 //-------------------------------------------------------------
3359 // Create the Database Array of catalog entries
3361 wxDbInf
*pDbInf
= new wxDbInf
;
3363 //-------------------------------------------------------------
3364 // Table Information
3365 // Pass 1 - Determine how many Tables there are.
3366 // Pass 2 - Create the Table array and fill it
3367 // - Create the Cols array = NULL
3368 //-------------------------------------------------------------
3370 for (pass
= 1; pass
<= 2; pass
++)
3372 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3373 tblNameSave
.Empty();
3375 if (!UserID
.empty() &&
3376 Dbms() != dbmsMY_SQL
&&
3377 Dbms() != dbmsACCESS
&&
3378 Dbms() != dbmsMS_SQL_SERVER
)
3380 retcode
= SQLTables(hstmt
,
3381 NULL
, 0, // All qualifiers
3382 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3383 NULL
, 0, // All tables
3384 NULL
, 0); // All columns
3388 retcode
= SQLTables(hstmt
,
3389 NULL
, 0, // All qualifiers
3390 NULL
, 0, // User specified
3391 NULL
, 0, // All tables
3392 NULL
, 0); // All columns
3395 if (retcode
!= SQL_SUCCESS
)
3397 DispAllErrors(henv
, hdbc
, hstmt
);
3399 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3403 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3405 if (pass
== 1) // First pass, just count the Tables
3407 if (pDbInf
->numTables
== 0)
3409 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3410 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3412 pDbInf
->numTables
++; // Counter for Tables
3414 if (pass
== 2) // Create and fill the Table entries
3416 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3417 { // no, then create the Array
3418 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3420 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3422 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3423 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3424 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3430 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3432 // Query how many columns are in each table
3433 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3435 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3440 } // wxDb::GetCatalog()
3443 /********** wxDb::Catalog() **********/
3444 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3446 * Creates the text file specified in 'filename' which will contain
3447 * a minimal data dictionary of all tables accessible by the user specified
3450 * userID is evaluated in the following manner:
3451 * userID == NULL ... UserID is ignored
3452 * userID == "" ... UserID set equal to 'this->uid'
3453 * userID != "" ... UserID set equal to 'userID'
3455 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3456 * by this function. This function should use its own wxDb instance
3457 * to avoid undesired unbinding of columns.
3460 wxASSERT(fileName
.length());
3464 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3465 wxString tblNameSave
;
3466 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3468 wxChar typeName
[30+1];
3469 SDWORD precision
, length
;
3471 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3475 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3478 convertUserID(userID
,UserID
);
3480 if (!UserID
.empty() &&
3481 Dbms() != dbmsMY_SQL
&&
3482 Dbms() != dbmsACCESS
&&
3483 Dbms() != dbmsFIREBIRD
&&
3484 Dbms() != dbmsINTERBASE
&&
3485 Dbms() != dbmsMS_SQL_SERVER
)
3487 retcode
= SQLColumns(hstmt
,
3488 NULL
, 0, // All qualifiers
3489 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3490 NULL
, 0, // All tables
3491 NULL
, 0); // All columns
3495 retcode
= SQLColumns(hstmt
,
3496 NULL
, 0, // All qualifiers
3497 NULL
, 0, // User specified
3498 NULL
, 0, // All tables
3499 NULL
, 0); // All columns
3501 if (retcode
!= SQL_SUCCESS
)
3503 DispAllErrors(henv
, hdbc
, hstmt
);
3509 tblNameSave
.Empty();
3514 retcode
= SQLFetch(hstmt
);
3515 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3518 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3519 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3520 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3521 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3522 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3523 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3525 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3528 wxFputs(wxT("\n"), fp
);
3529 wxFputs(wxT("================================ "), fp
);
3530 wxFputs(wxT("================================ "), fp
);
3531 wxFputs(wxT("===================== "), fp
);
3532 wxFputs(wxT("========= "), fp
);
3533 wxFputs(wxT("=========\n"), fp
);
3534 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3535 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3536 wxFputs(outStr
.c_str(), fp
);
3537 wxFputs(wxT("================================ "), fp
);
3538 wxFputs(wxT("================================ "), fp
);
3539 wxFputs(wxT("===================== "), fp
);
3540 wxFputs(wxT("========= "), fp
);
3541 wxFputs(wxT("=========\n"), fp
);
3542 tblNameSave
= tblName
;
3545 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3546 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3547 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3549 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3556 if (retcode
!= SQL_NO_DATA_FOUND
)
3557 DispAllErrors(henv
, hdbc
, hstmt
);
3559 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3562 return(retcode
== SQL_NO_DATA_FOUND
);
3564 } // wxDb::Catalog()
3567 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3569 * Table name can refer to a table, view, alias or synonym. Returns true
3570 * if the object exists in the database. This function does not indicate
3571 * whether or not the user has privleges to query or perform other functions
3574 * userID is evaluated in the following manner:
3575 * userID == NULL ... UserID is ignored
3576 * userID == "" ... UserID set equal to 'this->uid'
3577 * userID != "" ... UserID set equal to 'userID'
3580 wxASSERT(tableName
.length());
3584 if (Dbms() == dbmsDBASE
)
3587 if (tablePath
.length())
3588 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3590 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3593 exists
= wxFileExists(dbName
);
3598 convertUserID(userID
,UserID
);
3600 TableName
= tableName
;
3601 // Oracle and Interbase table names are uppercase only, so force
3602 // the name to uppercase just in case programmer forgot to do this
3603 if ((Dbms() == dbmsORACLE
) ||
3604 (Dbms() == dbmsFIREBIRD
) ||
3605 (Dbms() == dbmsINTERBASE
))
3606 TableName
= TableName
.Upper();
3608 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3611 // Some databases cannot accept a user name when looking up table names,
3612 // so we use the call below that leaves out the user name
3613 if (!UserID
.empty() &&
3614 Dbms() != dbmsMY_SQL
&&
3615 Dbms() != dbmsACCESS
&&
3616 Dbms() != dbmsMS_SQL_SERVER
&&
3617 Dbms() != dbmsDB2
&&
3618 Dbms() != dbmsFIREBIRD
&&
3619 Dbms() != dbmsINTERBASE
&&
3620 Dbms() != dbmsPERVASIVE_SQL
)
3622 retcode
= SQLTables(hstmt
,
3623 NULL
, 0, // All qualifiers
3624 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3625 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3626 NULL
, 0); // All table types
3630 retcode
= SQLTables(hstmt
,
3631 NULL
, 0, // All qualifiers
3632 NULL
, 0, // All owners
3633 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3634 NULL
, 0); // All table types
3636 if (retcode
!= SQL_SUCCESS
)
3637 return(DispAllErrors(henv
, hdbc
, hstmt
));
3639 retcode
= SQLFetch(hstmt
);
3640 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3642 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3643 return(DispAllErrors(henv
, hdbc
, hstmt
));
3646 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3650 } // wxDb::TableExists()
3653 /********** wxDb::TablePrivileges() **********/
3654 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3655 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3657 wxASSERT(tableName
.length());
3659 wxDbTablePrivilegeInfo result
;
3663 // We probably need to be able to dynamically set this based on
3664 // the driver type, and state.
3665 wxChar curRole
[]=wxT("public");
3669 wxString UserID
,Schema
;
3670 convertUserID(userID
,UserID
);
3671 convertUserID(schema
,Schema
);
3673 TableName
= tableName
;
3674 // Oracle and Interbase table names are uppercase only, so force
3675 // the name to uppercase just in case programmer forgot to do this
3676 if ((Dbms() == dbmsORACLE
) ||
3677 (Dbms() == dbmsFIREBIRD
) ||
3678 (Dbms() == dbmsINTERBASE
))
3679 TableName
= TableName
.Upper();
3681 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3683 // Some databases cannot accept a user name when looking up table names,
3684 // so we use the call below that leaves out the user name
3685 if (!Schema
.empty() &&
3686 Dbms() != dbmsMY_SQL
&&
3687 Dbms() != dbmsACCESS
&&
3688 Dbms() != dbmsMS_SQL_SERVER
)
3690 retcode
= SQLTablePrivileges(hstmt
,
3692 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3693 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3697 retcode
= SQLTablePrivileges(hstmt
,
3700 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3703 #ifdef DBDEBUG_CONSOLE
3704 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3707 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3708 return (DispAllErrors(henv
, hdbc
, hstmt
));
3710 bool failed
= false;
3711 retcode
= SQLFetch(hstmt
);
3712 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3714 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3717 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3720 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3723 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3726 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3729 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3732 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3737 return(DispAllErrors(henv
, hdbc
, hstmt
));
3739 #ifdef DBDEBUG_CONSOLE
3740 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3741 result
.privilege
,result
.tableOwner
,result
.tableName
,
3742 result
.grantor
, result
.grantee
);
3745 if (UserID
.IsSameAs(result
.tableOwner
,false))
3747 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3751 if (UserID
.IsSameAs(result
.grantee
,false) &&
3752 !wxStrcmp(result
.privilege
,priv
))
3754 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3758 if (!wxStrcmp(result
.grantee
,curRole
) &&
3759 !wxStrcmp(result
.privilege
,priv
))
3761 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3765 retcode
= SQLFetch(hstmt
);
3768 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3771 } // wxDb::TablePrivileges
3774 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3778 if (Dbms() == dbmsACCESS
)
3779 TableName
= _T("\"");
3780 TableName
+= tableName
;
3781 if (Dbms() == dbmsACCESS
)
3782 TableName
+= _T("\"");
3785 } // wxDb::SQLTableName()
3788 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3792 if (Dbms() == dbmsACCESS
)
3795 if (Dbms() == dbmsACCESS
)
3796 ColName
+= _T("\"");
3799 } // wxDb::SQLColumnName()
3802 /********** wxDb::SetSqlLogging() **********/
3803 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3805 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3806 wxASSERT(state
== sqlLogOFF
|| filename
.length());
3808 if (state
== sqlLogON
)
3812 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3813 if (fpSqlLog
== NULL
)
3821 if (fclose(fpSqlLog
))
3827 sqlLogState
= state
;
3830 } // wxDb::SetSqlLogging()
3833 /********** wxDb::WriteSqlLog() **********/
3834 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3836 wxASSERT(logMsg
.length());
3838 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3841 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3843 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3845 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3850 } // wxDb::WriteSqlLog()
3853 /********** wxDb::Dbms() **********/
3854 wxDBMS
wxDb::Dbms(void)
3856 * Be aware that not all database engines use the exact same syntax, and not
3857 * every ODBC compliant database is compliant to the same level of compliancy.
3858 * Some manufacturers support the minimum Level 1 compliancy, and others up
3859 * through Level 3. Others support subsets of features for levels above 1.
3861 * If you find an inconsistency between the wxDb class and a specific database
3862 * engine, and an identifier to this section, and special handle the database in
3863 * the area where behavior is non-conforming with the other databases.
3866 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3867 * ---------------------------------------------------
3870 * - Currently the only database supported by the class to support VIEWS
3873 * - Does not support the SQL_TIMESTAMP structure
3874 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3875 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3876 * is true. The user must create ALL indexes from their program.
3877 * - Table names can only be 8 characters long
3878 * - Column names can only be 10 characters long
3881 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3882 * after every table name involved in the query/join if that tables matching record(s)
3884 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3886 * SYBASE (Enterprise)
3887 * - If a column is part of the Primary Key, the column cannot be NULL
3888 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3891 * - If a column is part of the Primary Key, the column cannot be NULL
3892 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3893 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3894 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3895 * column definition if it is not defined correctly, but it is experimental
3896 * - Does not support sub-queries in SQL statements
3899 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3900 * - Does not support sub-queries in SQL statements
3903 * - Primary keys must be declared as NOT NULL
3904 * - Table and index names must not be longer than 13 characters in length (technically
3905 * table names can be up to 18 characters, but the primary index is created using the
3906 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3911 * - Columns that are part of primary keys must be defined as being NOT NULL
3912 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3913 * column definition if it is not defined correctly, but it is experimental
3916 // Should only need to do this once for each new database connection
3917 // so return the value we already determined it to be to save time
3918 // and lots of string comparisons
3919 if (dbmsType
!= dbmsUNIDENTIFIED
)
3922 #ifdef DBDEBUG_CONSOLE
3923 // When run in console mode, use standard out to display errors.
3924 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3925 #endif // DBDEBUG_CONSOLE
3927 wxLogDebug(wxT("Database connecting to: "));
3928 wxLogDebug(dbInf
.dbmsName
);
3930 wxChar baseName
[25+1];
3931 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3934 // RGG 20001025 : add support for Interbase
3935 // GT : Integrated to base classes on 20001121
3936 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3937 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3939 // BJO 20000428 : add support for Virtuoso
3940 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3941 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3943 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3944 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3946 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3947 // connected through an OpenLink driver.
3948 // Is it also returned by Sybase Adapatitve server?
3949 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3950 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3952 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3953 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3954 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3956 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3959 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3960 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3963 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3964 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3967 if (!wxStricmp(baseName
,wxT("Pervasive")))
3968 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3971 if (!wxStricmp(baseName
,wxT("Informix")))
3972 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3974 if (!wxStricmp(baseName
,wxT("Firebird")))
3975 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3978 if (!wxStricmp(baseName
,wxT("Oracle")))
3979 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3980 if (!wxStricmp(baseName
,wxT("ACCESS")))
3981 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3982 if (!wxStricmp(baseName
,wxT("Sybase")))
3983 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3986 if (!wxStricmp(baseName
,wxT("DBASE")))
3987 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3988 if (!wxStricmp(baseName
,wxT("xBase")))
3989 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3990 if (!wxStricmp(baseName
,wxT("MySQL")))
3991 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3992 if (!wxStricmp(baseName
,wxT("MaxDB")))
3993 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
3996 if (!wxStricmp(baseName
,wxT("DB2")))
3997 return((wxDBMS
)(dbmsType
= dbmsDB2
));
3999 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
4004 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
4005 int dataType
, ULONG columnLength
,
4006 const wxString
&optionalParam
)
4008 wxASSERT(tableName
.length());
4009 wxASSERT(columnName
.length());
4010 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
4011 dataType
!= DB_DATA_TYPE_VARCHAR
);
4013 // Must specify a columnLength if modifying a VARCHAR type column
4014 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
4017 wxString dataTypeName
;
4019 wxString alterSlashModify
;
4023 case DB_DATA_TYPE_VARCHAR
:
4024 dataTypeName
= typeInfVarchar
.TypeName
;
4026 case DB_DATA_TYPE_INTEGER
:
4027 dataTypeName
= typeInfInteger
.TypeName
;
4029 case DB_DATA_TYPE_FLOAT
:
4030 dataTypeName
= typeInfFloat
.TypeName
;
4032 case DB_DATA_TYPE_DATE
:
4033 dataTypeName
= typeInfDate
.TypeName
;
4035 case DB_DATA_TYPE_BLOB
:
4036 dataTypeName
= typeInfBlob
.TypeName
;
4042 // Set the modify or alter syntax depending on the type of database connected to
4046 alterSlashModify
= _T("MODIFY");
4048 case dbmsMS_SQL_SERVER
:
4049 alterSlashModify
= _T("ALTER COLUMN");
4051 case dbmsUNIDENTIFIED
:
4053 case dbmsSYBASE_ASA
:
4054 case dbmsSYBASE_ASE
:
4059 case dbmsXBASE_SEQUITER
:
4061 alterSlashModify
= _T("MODIFY");
4065 // create the SQL statement
4066 if ( Dbms() == dbmsMY_SQL
)
4068 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4069 columnName
.c_str(), dataTypeName
.c_str());
4073 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4074 columnName
.c_str(), dataTypeName
.c_str());
4077 // For varchars only, append the size of the column
4078 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4079 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4082 s
.Printf(wxT("(%lu)"), columnLength
);
4086 // for passing things like "NOT NULL"
4087 if (optionalParam
.length())
4089 sqlStmt
+= wxT(" ");
4090 sqlStmt
+= optionalParam
;
4093 return ExecSql(sqlStmt
);
4095 } // wxDb::ModifyColumn()
4097 /********** wxDb::EscapeSqlChars() **********/
4098 wxString
wxDb::EscapeSqlChars(const wxString
& valueOrig
)
4100 wxString
value(valueOrig
);
4104 // Access doesn't seem to care about backslashes, so only escape single quotes.
4105 value
.Replace(wxT("'"), wxT("''"));
4109 // All the others are supposed to be the same for now, add special
4110 // handling for them if necessary
4111 value
.Replace(wxT("\\"), wxT("\\\\"));
4112 value
.Replace(wxT("'"), wxT("\\'"));
4117 } // wxDb::EscapeSqlChars()
4120 /********** wxDbGetConnection() **********/
4121 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4125 // Used to keep a pointer to a DB connection that matches the requested
4126 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4127 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4128 // rather than having to re-query the datasource to get all the values
4129 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4130 wxDb
*matchingDbConnection
= NULL
;
4132 // Scan the linked list searching for an available database connection
4133 // that's already been opened but is currently not in use.
4134 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4136 // The database connection must be for the same datasource
4137 // name and must currently not be in use.
4139 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4141 if (pDbConfig
->UseConnectionStr())
4143 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4144 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4146 // Found a free connection
4147 pList
->Free
= false;
4148 return(pList
->PtrDb
);
4153 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4154 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4156 // Found a free connection
4157 pList
->Free
= false;
4158 return(pList
->PtrDb
);
4163 if (pDbConfig
->UseConnectionStr())
4165 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4166 matchingDbConnection
= pList
->PtrDb
;
4170 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4171 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4172 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4173 matchingDbConnection
= pList
->PtrDb
;
4177 // No available connections. A new connection must be made and
4178 // appended to the end of the linked list.
4181 // Find the end of the list
4182 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4183 // Append a new list item
4184 pList
->PtrNext
= new wxDbList
;
4185 pList
->PtrNext
->PtrPrev
= pList
;
4186 pList
= pList
->PtrNext
;
4190 // Create the first node on the list
4191 pList
= PtrBegDbList
= new wxDbList
;
4195 // Initialize new node in the linked list
4197 pList
->Free
= false;
4198 pList
->Dsn
= pDbConfig
->GetDsn();
4199 pList
->Uid
= pDbConfig
->GetUserID();
4200 pList
->AuthStr
= pDbConfig
->GetPassword();
4201 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4203 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4207 if (!matchingDbConnection
)
4209 if (pDbConfig
->UseConnectionStr())
4211 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4215 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4219 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4221 // Connect to the datasource
4224 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4225 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4226 return(pList
->PtrDb
);
4228 else // Unable to connect, destroy list item
4231 pList
->PtrPrev
->PtrNext
= 0;
4233 PtrBegDbList
= 0; // Empty list again
4235 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4236 pList
->PtrDb
->Close(); // Close the wxDb object
4237 delete pList
->PtrDb
; // Deletes the wxDb object
4238 delete pList
; // Deletes the linked list object
4242 } // wxDbGetConnection()
4245 /********** wxDbFreeConnection() **********/
4246 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4250 // Scan the linked list searching for the database connection
4251 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4253 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4254 return (pList
->Free
= true);
4257 // Never found the database object, return failure
4260 } // wxDbFreeConnection()
4263 /********** wxDbCloseConnections() **********/
4264 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4266 wxDbList
*pList
, *pNext
;
4268 // Traverse the linked list closing database connections and freeing memory as I go.
4269 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4271 pNext
= pList
->PtrNext
; // Save the pointer to next
4272 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4273 pList
->PtrDb
->Close(); // Close the wxDb object
4274 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4275 delete pList
->PtrDb
; // Deletes the wxDb object
4276 delete pList
; // Deletes the linked list object
4279 // Mark the list as empty
4282 } // wxDbCloseConnections()
4285 /********** wxDbConnectionsInUse() **********/
4286 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4291 // Scan the linked list counting db connections that are currently in use
4292 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4294 if (pList
->Free
== false)
4300 } // wxDbConnectionsInUse()
4304 /********** wxDbLogExtendedErrorMsg() **********/
4305 // DEBUG ONLY function
4306 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4308 const wxChar
*ErrFile
,
4311 static wxString msg
;
4316 if (ErrFile
|| ErrLine
)
4318 msg
+= wxT("File: ");
4320 msg
+= wxT(" Line: ");
4321 tStr
.Printf(wxT("%d"),ErrLine
);
4322 msg
+= tStr
.c_str();
4326 msg
.Append (wxT("\nODBC errors:\n"));
4329 // Display errors for this connection
4331 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4333 if (pDb
->errorList
[i
])
4335 msg
.Append(pDb
->errorList
[i
]);
4336 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4337 msg
.Append(wxT("\n"));
4338 // Clear the errmsg buffer so the next error will not
4339 // end up showing the previous error that have occurred
4340 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4345 wxLogDebug(msg
.c_str());
4348 } // wxDbLogExtendedErrorMsg()
4351 /********** wxDbSqlLog() **********/
4352 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4354 bool append
= false;
4357 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4359 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4364 SQLLOGstate
= state
;
4365 SQLLOGfn
= filename
;
4373 /********** wxDbCreateDataSource() **********/
4374 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4375 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4377 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4378 * Very rudimentary creation of an ODBC data source.
4380 * ODBC driver must be ODBC 3.0 compliant to use this function
4385 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4391 dsnLocation
= ODBC_ADD_SYS_DSN
;
4393 dsnLocation
= ODBC_ADD_DSN
;
4395 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4396 // so that is why I used it, as wxString does not deal well with
4397 // embedded nulls in strings
4398 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4400 // Replace the separator from above with the '\0' separator needed
4401 // by the SQLConfigDataSource() function
4405 k
= setupStr
.Find((wxChar
)2,true);
4406 if (k
!= wxNOT_FOUND
)
4407 setupStr
[(UINT
)k
] = wxT('\0');
4409 while (k
!= wxNOT_FOUND
);
4411 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4412 driverName
, setupStr
.c_str());
4414 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4416 // check for errors caused by ConfigDSN based functions
4419 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4420 errMsg
[0] = wxT('\0');
4422 // This function is only supported in ODBC drivers v3.0 compliant and above
4423 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4426 #ifdef DBDEBUG_CONSOLE
4427 // When run in console mode, use standard out to display errors.
4428 cout
<< errMsg
<< endl
;
4429 cout
<< wxT("Press any key to continue...") << endl
;
4431 #endif // DBDEBUG_CONSOLE
4434 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4435 #endif // __WXDEBUG__
4441 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4442 // necessary to use this function, so this function is not supported
4444 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4447 #endif // __VISUALC__
4451 } // wxDbCreateDataSource()
4455 /********** wxDbGetDataSource() **********/
4456 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4457 SWORD DsDescMaxLength
, UWORD direction
)
4459 * Dsn and DsDesc will contain the data source name and data source
4460 * description upon return
4464 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4465 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4467 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4468 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4473 } // wxDbGetDataSource()
4476 // Change this to 0 to remove use of all deprecated functions
4477 #if wxODBC_BACKWARD_COMPATABILITY
4478 /********************************************************************
4479 ********************************************************************
4481 * The following functions are all DEPRECATED and are included for
4482 * backward compatibility reasons only
4484 ********************************************************************
4485 ********************************************************************/
4486 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4488 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4490 /***** DEPRECATED: use wxGetDataSource() *****/
4491 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4494 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4496 /***** DEPRECATED: use wxDbGetConnection() *****/
4497 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4499 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4501 /***** DEPRECATED: use wxDbFreeConnection() *****/
4502 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4504 return wxDbFreeConnection(pDb
);
4506 /***** DEPRECATED: use wxDbCloseConnections() *****/
4507 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4509 wxDbCloseConnections();
4511 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4512 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4514 return wxDbConnectionsInUse();