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"
56 // FIXME-UTF8: get rid of this after switching to Unicode-only builds:
58 #define WXSQLCAST(s) ((SQLTCHAR FAR *)(wchar_t*)(s).wchar_str())
60 #define WXSQLCAST(s) ((SQLTCHAR FAR *)(char*)(s).char_str())
63 // DLL options compatibility check:
64 WX_CHECK_BUILD_OPTIONS("wxODBC")
66 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
68 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
69 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
72 #include "wx/thread.h"
74 extern wxList TablesInUse
;
75 extern wxCriticalSection csTablesInUse
;
78 // SQL Log defaults to be used by GetDbConnection
79 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
81 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
83 // The wxDb::errorList is copied to this variable when the wxDb object
84 // is closed. This way, the error list is still available after the
85 // database object is closed. This is necessary if the database
86 // connection fails so the calling application can show the operator
87 // why the connection failed. Note: as each wxDb object is closed, it
88 // will overwrite the errors of the previously destroyed wxDb object in
89 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
91 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
94 // This type defines the return row-struct form
95 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
98 wxChar tableQual
[128+1];
99 wxChar tableOwner
[128+1];
100 wxChar tableName
[128+1];
101 wxChar grantor
[128+1];
102 wxChar grantee
[128+1];
103 wxChar privilege
[128+1];
104 wxChar grantable
[3+1];
105 } wxDbTablePrivilegeInfo
;
108 /********** wxDbConnectInf Constructor - form 1 **********/
109 wxDbConnectInf::wxDbConnectInf()
112 freeHenvOnDestroy
= false;
118 /********** wxDbConnectInf Constructor - form 2 **********/
119 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
120 const wxString
&password
, const wxString
&defaultDir
,
121 const wxString
&fileType
, const wxString
&description
)
124 freeHenvOnDestroy
= false;
135 SetPassword(password
);
136 SetDescription(description
);
137 SetFileType(fileType
);
138 SetDefaultDir(defaultDir
);
139 } // wxDbConnectInf Constructor
142 wxDbConnectInf::~wxDbConnectInf()
144 if (freeHenvOnDestroy
)
148 } // wxDbConnectInf Destructor
152 /********** wxDbConnectInf::Initialize() **********/
153 bool wxDbConnectInf::Initialize()
155 freeHenvOnDestroy
= false;
157 if (freeHenvOnDestroy
&& Henv
)
164 ConnectionStr
[0] = 0;
169 useConnectionStr
= false;
172 } // wxDbConnectInf::Initialize()
175 /********** wxDbConnectInf::AllocHenv() **********/
176 bool wxDbConnectInf::AllocHenv()
178 // This is here to help trap if you are getting a new henv
179 // without releasing an existing henv
182 // Initialize the ODBC Environment for Database Operations
183 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
185 wxLogDebug(wxT("A problem occurred while trying to get a connection to the data source"));
189 freeHenvOnDestroy
= true;
192 } // wxDbConnectInf::AllocHenv()
195 void wxDbConnectInf::FreeHenv()
203 freeHenvOnDestroy
= false;
205 } // wxDbConnectInf::FreeHenv()
208 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
210 wxASSERT(dsn
.length() < WXSIZEOF(Dsn
));
212 wxStrncpy(Dsn
, dsn
, WXSIZEOF(Dsn
)-1);
213 Dsn
[WXSIZEOF(Dsn
)-1] = 0; // Prevent buffer overrun
214 } // wxDbConnectInf::SetDsn()
217 void wxDbConnectInf::SetUserID(const wxString
&uid
)
219 wxASSERT(uid
.length() < WXSIZEOF(Uid
));
220 wxStrncpy(Uid
, uid
, WXSIZEOF(Uid
)-1);
221 Uid
[WXSIZEOF(Uid
)-1] = 0; // Prevent buffer overrun
222 } // wxDbConnectInf::SetUserID()
225 void wxDbConnectInf::SetPassword(const wxString
&password
)
227 wxASSERT(password
.length() < WXSIZEOF(AuthStr
));
229 wxStrncpy(AuthStr
, password
, WXSIZEOF(AuthStr
)-1);
230 AuthStr
[WXSIZEOF(AuthStr
)-1] = 0; // Prevent buffer overrun
231 } // wxDbConnectInf::SetPassword()
233 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
235 wxASSERT(connectStr
.length() < WXSIZEOF(ConnectionStr
));
237 useConnectionStr
= wxStrlen(connectStr
) > 0;
239 wxStrncpy(ConnectionStr
, connectStr
, WXSIZEOF(ConnectionStr
)-1);
240 ConnectionStr
[WXSIZEOF(ConnectionStr
)-1] = 0; // Prevent buffer overrun
241 } // wxDbConnectInf::SetConnectionStr()
244 /********** wxDbColFor Constructor **********/
245 wxDbColFor::wxDbColFor()
248 } // wxDbColFor::wxDbColFor()
251 /********** wxDbColFor::Initialize() **********/
252 void wxDbColFor::Initialize()
262 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
265 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
266 } // wxDbColFor::Initialize()
269 /********** wxDbColFor::Format() **********/
270 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
271 short columnLength
, short decimalDigits
)
273 // ----------------------------------------------------------------------------------------
274 // -- 19991224 : mj10777 : Create
275 // There is still a lot of work to do here, but it is a start
276 // It handles all the basic data-types that I have run into up to now
277 // The main work will have be with Dates and float Formatting
278 // (US 1,000.00 ; EU 1.000,00)
279 // There are wxWindow plans for locale support and the new wxDateTime. If
280 // they define some constants (wxEUROPEAN) that can be gloably used,
281 // they should be used here.
282 // ----------------------------------------------------------------------------------------
283 // There should also be a function to scan in a string to fill the variable
284 // ----------------------------------------------------------------------------------------
286 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
287 i_dbDataType
= dbDataType
;
288 i_sqlDataType
= sqlDataType
;
289 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
291 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
293 if ((i_sqlDataType
== SQL_VARCHAR
)
295 #if defined(SQL_WCHAR)
296 || (i_sqlDataType
== SQL_WCHAR
)
298 #if defined(SQL_WVARCHAR)
299 || (i_sqlDataType
== SQL_WVARCHAR
)
302 || (i_sqlDataType
== SQL_LONGVARCHAR
))
303 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
304 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
305 i_dbDataType
= DB_DATA_TYPE_DATE
;
306 if (i_sqlDataType
== SQL_C_BIT
)
307 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
308 if (i_sqlDataType
== SQL_NUMERIC
)
309 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
310 if (i_sqlDataType
== SQL_REAL
)
311 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
312 if (i_sqlDataType
== SQL_C_BINARY
)
313 i_dbDataType
= DB_DATA_TYPE_BLOB
;
316 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
318 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
321 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
323 case DB_DATA_TYPE_VARCHAR
:
326 case DB_DATA_TYPE_INTEGER
:
329 case DB_DATA_TYPE_FLOAT
:
330 if (decimalDigits
== 0)
332 tempStr
.Printf(wxT("%%%d.%d"), columnLength
, decimalDigits
);
333 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
335 case DB_DATA_TYPE_DATE
:
336 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
338 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
340 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
342 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
344 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
346 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
348 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
350 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
352 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
354 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
357 case DB_DATA_TYPE_BLOB
:
358 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
361 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
365 } // wxDbColFor::Format()
368 /********** wxDbColInf Constructor **********/
369 wxDbColInf::wxDbColInf()
372 } // wxDbColInf::wxDbColInf()
375 /********** wxDbColInf Destructor ********/
376 wxDbColInf::~wxDbColInf()
381 } // wxDbColInf::~wxDbColInf()
384 bool wxDbColInf::Initialize()
406 } // wxDbColInf::Initialize()
409 /********** wxDbTableInf Constructor ********/
410 wxDbTableInf::wxDbTableInf()
413 } // wxDbTableInf::wxDbTableInf()
416 /********** wxDbTableInf Constructor ********/
417 wxDbTableInf::~wxDbTableInf()
422 } // wxDbTableInf::~wxDbTableInf()
425 bool wxDbTableInf::Initialize()
434 } // wxDbTableInf::Initialize()
437 /********** wxDbInf Constructor *************/
441 } // wxDbInf::wxDbInf()
444 /********** wxDbInf Destructor *************/
450 } // wxDbInf::~wxDbInf()
453 /********** wxDbInf::Initialize() *************/
454 bool wxDbInf::Initialize()
462 } // wxDbInf::Initialize()
465 /********** wxDb Constructor **********/
466 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
468 // Copy the HENV into the db class
470 fwdOnlyCursors
= FwdOnlyCursors
;
476 /********** wxDb Destructor **********/
479 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
489 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
490 /********** wxDb::initialize() **********/
491 void wxDb::initialize()
493 * Private member function that sets all wxDb member variables to
494 * known values at creation of the wxDb
499 fpSqlLog
= 0; // Sql Log file pointer
500 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
502 dbmsType
= dbmsUNIDENTIFIED
;
504 wxStrcpy(sqlState
,wxEmptyString
);
505 wxStrcpy(errorMsg
,wxEmptyString
);
506 nativeError
= cbErrorMsg
= 0;
507 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
508 wxStrcpy(errorList
[i
], wxEmptyString
);
510 // Init typeInf structures
511 typeInfVarchar
.TypeName
.Empty();
512 typeInfVarchar
.FsqlType
= 0;
513 typeInfVarchar
.Precision
= 0;
514 typeInfVarchar
.CaseSensitive
= 0;
515 typeInfVarchar
.MaximumScale
= 0;
517 typeInfInteger
.TypeName
.Empty();
518 typeInfInteger
.FsqlType
= 0;
519 typeInfInteger
.Precision
= 0;
520 typeInfInteger
.CaseSensitive
= 0;
521 typeInfInteger
.MaximumScale
= 0;
523 typeInfFloat
.TypeName
.Empty();
524 typeInfFloat
.FsqlType
= 0;
525 typeInfFloat
.Precision
= 0;
526 typeInfFloat
.CaseSensitive
= 0;
527 typeInfFloat
.MaximumScale
= 0;
529 typeInfDate
.TypeName
.Empty();
530 typeInfDate
.FsqlType
= 0;
531 typeInfDate
.Precision
= 0;
532 typeInfDate
.CaseSensitive
= 0;
533 typeInfDate
.MaximumScale
= 0;
535 typeInfBlob
.TypeName
.Empty();
536 typeInfBlob
.FsqlType
= 0;
537 typeInfBlob
.Precision
= 0;
538 typeInfBlob
.CaseSensitive
= 0;
539 typeInfBlob
.MaximumScale
= 0;
541 typeInfMemo
.TypeName
.Empty();
542 typeInfMemo
.FsqlType
= 0;
543 typeInfMemo
.Precision
= 0;
544 typeInfMemo
.CaseSensitive
= 0;
545 typeInfMemo
.MaximumScale
= 0;
547 // Error reporting is turned OFF by default
550 // Allocate a data source connection handle
551 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
554 // Initialize the db status flag
557 // Mark database as not open as of yet
560 dbOpenedWithConnectionString
= false;
561 } // wxDb::initialize()
564 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
566 // NOTE: Return value from this function MUST be copied
567 // immediately, as the value is not good after
568 // this function has left scope.
570 void wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
574 if (!wxStrlen(userID
))
582 // dBase does not use user names, and some drivers fail if you try to pass one
583 if ( Dbms() == dbmsDBASE
584 || Dbms() == dbmsXBASE_SEQUITER
)
587 // Some databases require user names to be specified in uppercase,
588 // so force the name to uppercase
589 if ((Dbms() == dbmsORACLE
) ||
590 (Dbms() == dbmsMAXDB
))
591 UserID
= UserID
.Upper();
592 } // wxDb::convertUserID()
595 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
599 // These are the possible SQL types we check for use against the datasource we are connected
600 // to for the purpose of determining which data type to use for the basic character strings
603 // NOTE: The first type in this enumeration that is determined to be supported by the
604 // datasource/driver is the one that will be used.
605 SWORD PossibleSqlCharTypes
[] = {
606 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
610 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
616 // These are the possible SQL types we check for use against the datasource we are connected
617 // to for the purpose of determining which data type to use for the basic non-floating point
620 // NOTE: The first type in this enumeration that is determined to be supported by the
621 // datasource/driver is the one that will be used.
622 SWORD PossibleSqlIntegerTypes
[] = {
626 // These are the possible SQL types we check for use against the datasource we are connected
627 // to for the purpose of determining which data type to use for the basic floating point number
630 // NOTE: The first type in this enumeration that is determined to be supported by the
631 // datasource/driver is the one that will be used.
632 SWORD PossibleSqlFloatTypes
[] = {
640 // These are the possible SQL types we check for use agains the datasource we are connected
641 // to for the purpose of determining which data type to use for the date/time column types
643 // NOTE: The first type in this enumeration that is determined to be supported by the
644 // datasource/driver is the one that will be used.
645 SWORD PossibleSqlDateTypes
[] = {
653 // These are the possible SQL types we check for use agains the datasource we are connected
654 // to for the purpose of determining which data type to use for the BLOB column types.
656 // NOTE: The first type in this enumeration that is determined to be supported by the
657 // datasource/driver is the one that will be used.
658 SWORD PossibleSqlBlobTypes
[] = {
663 // These are the possible SQL types we check for use agains the datasource we are connected
664 // to for the purpose of determining which data type to use for the MEMO column types
665 // (a type which allow to store large strings; like VARCHAR just with a bigger precision)
667 // NOTE: The first type in this enumeration that is determined to be supported by the
668 // datasource/driver is the one that will be used.
669 SWORD PossibleSqlMemoTypes
[] = {
674 // Query the data source regarding data type information
677 // The way it was determined which SQL data types to use was by calling SQLGetInfo
678 // for all of the possible SQL data types to see which ones were supported. If
679 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
680 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
681 // types I've selected below will not always be what we want. These are just
682 // what happened to work against an Oracle 7/Intersolv combination. The following is
683 // a complete list of the results I got back against the Oracle 7 database:
685 // SQL_BIGINT SQL_NO_DATA_FOUND
686 // SQL_BINARY SQL_NO_DATA_FOUND
687 // SQL_BIT SQL_NO_DATA_FOUND
688 // SQL_CHAR type name = 'CHAR', Precision = 255
689 // SQL_DATE SQL_NO_DATA_FOUND
690 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
691 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
692 // SQL_FLOAT SQL_NO_DATA_FOUND
693 // SQL_INTEGER SQL_NO_DATA_FOUND
694 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
695 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
696 // SQL_NUMERIC SQL_NO_DATA_FOUND
697 // SQL_REAL SQL_NO_DATA_FOUND
698 // SQL_SMALLINT SQL_NO_DATA_FOUND
699 // SQL_TIME SQL_NO_DATA_FOUND
700 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
701 // SQL_VARBINARY type name = 'RAW', Precision = 255
702 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
703 // =====================================================================
704 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
706 // SQL_VARCHAR type name = 'TEXT', Precision = 255
707 // SQL_TIMESTAMP type name = 'DATETIME'
708 // SQL_DECIMAL SQL_NO_DATA_FOUND
709 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
710 // SQL_FLOAT SQL_NO_DATA_FOUND
711 // SQL_REAL type name = 'SINGLE', Precision = 7
712 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
713 // SQL_INTEGER type name = 'LONG', Precision = 10
715 // Query the data source for info about itself
716 if (!getDbInfo(failOnDataTypeUnsupported
))
719 // --------------- Varchar - (Variable length character string) ---------------
720 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
721 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
724 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
725 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
726 else if (failOnDataTypeUnsupported
)
729 // --------------- Float ---------------
730 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
731 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
734 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
735 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
736 else if (failOnDataTypeUnsupported
)
739 // --------------- Integer -------------
740 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
741 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
744 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
745 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
746 else if (failOnDataTypeUnsupported
)
748 // If no non-floating point data types are supported, we'll
749 // use the type assigned for floats to store integers as well
750 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
752 if (failOnDataTypeUnsupported
)
756 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
759 // --------------- Date/Time ---------------
760 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
761 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
764 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
765 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
766 else if (failOnDataTypeUnsupported
)
769 // --------------- BLOB ---------------
770 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
771 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
774 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
775 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
776 else if (failOnDataTypeUnsupported
)
779 // --------------- MEMO ---------------
780 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlMemoTypes
) &&
781 !getDataTypeInfo(PossibleSqlMemoTypes
[iIndex
], typeInfMemo
); ++iIndex
)
784 if (iIndex
< WXSIZEOF(PossibleSqlMemoTypes
))
785 typeInfMemo
.FsqlType
= PossibleSqlMemoTypes
[iIndex
];
786 else if (failOnDataTypeUnsupported
)
790 } // wxDb::determineDataTypes
793 bool wxDb::open(bool failOnDataTypeUnsupported
)
796 If using Intersolv branded ODBC drivers, this is the place where you would substitute
797 your branded driver license information
799 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
800 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
803 // Mark database as open
806 // Allocate a statement handle for the database connection
807 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
808 return(DispAllErrors(henv
, hdbc
));
810 // Set Connection Options
811 if (!setConnectionOptions())
814 if (!determineDataTypes(failOnDataTypeUnsupported
))
817 #ifdef DBDEBUG_CONSOLE
818 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
819 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
820 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
821 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
822 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
823 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
827 // Completed Successfully
831 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
833 wxASSERT(inConnectStr
.length());
834 return Open(inConnectStr
, NULL
, failOnDataTypeUnsupported
);
837 bool wxDb::Open(const wxString
& inConnectStr
, SQLHWND parentWnd
, bool failOnDataTypeUnsupported
)
841 authStr
= wxEmptyString
;
845 if (!FwdOnlyCursors())
847 // Specify that the ODBC cursor library be used, if needed. This must be
848 // specified before the connection is made.
849 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
851 #ifdef DBDEBUG_CONSOLE
852 if (retcode
== SQL_SUCCESS
)
853 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
855 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
857 wxUnusedVar(retcode
);
861 // Connect to the data source
862 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
863 short outConnectBufferLen
;
865 inConnectionStr
= inConnectStr
;
867 retcode
= SQLDriverConnect(hdbc
, parentWnd
, WXSQLCAST(inConnectionStr
),
868 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
869 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
871 if ((retcode
!= SQL_SUCCESS
) &&
872 (retcode
!= SQL_SUCCESS_WITH_INFO
))
873 return(DispAllErrors(henv
, hdbc
));
875 outConnectBuffer
[outConnectBufferLen
] = 0;
876 outConnectionStr
= outConnectBuffer
;
877 dbOpenedWithConnectionString
= true;
879 return open(failOnDataTypeUnsupported
);
882 /********** wxDb::Open() **********/
883 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
885 wxASSERT(!Dsn
.empty());
890 inConnectionStr
= wxEmptyString
;
891 outConnectionStr
= wxEmptyString
;
895 if (!FwdOnlyCursors())
897 // Specify that the ODBC cursor library be used, if needed. This must be
898 // specified before the connection is made.
899 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
901 #ifdef DBDEBUG_CONSOLE
902 if (retcode
== SQL_SUCCESS
)
903 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
905 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
907 wxUnusedVar( retcode
);
911 // Connect to the data source
912 retcode
= SQLConnect(hdbc
,
913 WXSQLCAST(dsn
), SQL_NTS
,
914 WXSQLCAST(uid
), SQL_NTS
,
915 WXSQLCAST(authStr
), SQL_NTS
);
917 if ((retcode
!= SQL_SUCCESS
) &&
918 (retcode
!= SQL_SUCCESS_WITH_INFO
))
919 return(DispAllErrors(henv
, hdbc
));
921 return open(failOnDataTypeUnsupported
);
926 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
928 wxASSERT(dbConnectInf
);
930 // Use the connection string if one is present
931 if (dbConnectInf
->UseConnectionStr())
932 return Open(dbConnectInf
->GetConnectionStr(), failOnDataTypeUnsupported
);
934 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
935 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
939 bool wxDb::Open(wxDb
*copyDb
)
941 dsn
= copyDb
->GetDatasourceName();
942 uid
= copyDb
->GetUsername();
943 authStr
= copyDb
->GetPassword();
944 inConnectionStr
= copyDb
->GetConnectionInStr();
945 outConnectionStr
= copyDb
->GetConnectionOutStr();
949 if (!FwdOnlyCursors())
951 // Specify that the ODBC cursor library be used, if needed. This must be
952 // specified before the connection is made.
953 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
955 #ifdef DBDEBUG_CONSOLE
956 if (retcode
== SQL_SUCCESS
)
957 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
959 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
961 wxUnusedVar( retcode
);
965 if (copyDb
->OpenedWithConnectionString())
967 // Connect to the data source
968 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
969 short outConnectBufferLen
;
971 inConnectionStr
= copyDb
->GetConnectionInStr();
973 retcode
= SQLDriverConnect(hdbc
, NULL
, WXSQLCAST(inConnectionStr
),
974 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
975 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
977 if ((retcode
!= SQL_SUCCESS
) &&
978 (retcode
!= SQL_SUCCESS_WITH_INFO
))
979 return(DispAllErrors(henv
, hdbc
));
981 outConnectBuffer
[outConnectBufferLen
] = 0;
982 outConnectionStr
= outConnectBuffer
;
983 dbOpenedWithConnectionString
= true;
987 // Connect to the data source
988 retcode
= SQLConnect(hdbc
,
989 WXSQLCAST(dsn
), SQL_NTS
,
990 WXSQLCAST(uid
), SQL_NTS
,
991 WXSQLCAST(authStr
), SQL_NTS
);
994 if ((retcode
!= SQL_SUCCESS
) &&
995 (retcode
!= SQL_SUCCESS_WITH_INFO
))
996 return(DispAllErrors(henv
, hdbc
));
999 If using Intersolv branded ODBC drivers, this is the place where you would substitute
1000 your branded driver license information
1002 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
1003 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
1006 // Mark database as open
1009 // Allocate a statement handle for the database connection
1010 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
1011 return(DispAllErrors(henv
, hdbc
));
1013 // Set Connection Options
1014 if (!setConnectionOptions())
1017 // Instead of Querying the data source for info about itself, it can just be copied
1018 // from the wxDb instance that was passed in (copyDb).
1019 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
1020 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
1021 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
1022 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
1023 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
1024 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
1025 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
1026 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
1027 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
1028 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
1029 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
1030 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
1031 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
1032 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
1033 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
1034 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1035 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1036 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1037 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1038 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1039 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1040 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1041 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1042 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1043 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1044 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1045 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1046 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1047 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1048 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1049 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1051 // VARCHAR = Variable length character string
1052 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1053 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1054 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1055 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1056 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1059 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1060 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1061 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1062 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1063 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1066 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1067 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1068 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1069 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1070 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1073 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1074 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1075 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1076 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1077 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1080 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1081 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1082 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1083 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1084 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1087 typeInfMemo
.FsqlType
= copyDb
->typeInfMemo
.FsqlType
;
1088 typeInfMemo
.TypeName
= copyDb
->typeInfMemo
.TypeName
;
1089 typeInfMemo
.Precision
= copyDb
->typeInfMemo
.Precision
;
1090 typeInfMemo
.CaseSensitive
= copyDb
->typeInfMemo
.CaseSensitive
;
1091 typeInfMemo
.MaximumScale
= copyDb
->typeInfMemo
.MaximumScale
;
1093 #ifdef DBDEBUG_CONSOLE
1094 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1095 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1096 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1097 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1098 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1099 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
1103 // Completed Successfully
1108 /********** wxDb::setConnectionOptions() **********/
1109 bool wxDb::setConnectionOptions(void)
1111 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1116 // I need to get the DBMS name here, because some of the connection options
1117 // are database specific and need to call the Dbms() function.
1120 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1121 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1122 return(DispAllErrors(henv
, hdbc
));
1124 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1125 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1126 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1128 // By default, MS Sql Server closes cursors on commit and rollback. The following
1129 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1130 // after a transaction. This is a driver specific option and is not part of the
1131 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1132 // The database settings don't have any effect one way or the other.
1133 if (Dbms() == dbmsMS_SQL_SERVER
)
1135 const long SQL_PRESERVE_CURSORS
= 1204L;
1136 const long SQL_PC_ON
= 1L;
1137 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1140 // Display the connection options to verify them
1141 #ifdef DBDEBUG_CONSOLE
1143 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1145 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1146 if (retcode
!= SQL_SUCCESS
)
1147 return(DispAllErrors(henv
, hdbc
));
1148 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1150 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1151 if (retcode
!= SQL_SUCCESS
)
1152 return(DispAllErrors(henv
, hdbc
));
1153 cout
<< wxT("ODBC CURSORS: ");
1156 case(SQL_CUR_USE_IF_NEEDED
):
1157 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1159 case(SQL_CUR_USE_ODBC
):
1160 cout
<< wxT("SQL_CUR_USE_ODBC");
1162 case(SQL_CUR_USE_DRIVER
):
1163 cout
<< wxT("SQL_CUR_USE_DRIVER");
1168 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1169 if (retcode
!= SQL_SUCCESS
)
1170 return(DispAllErrors(henv
, hdbc
));
1171 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1176 // Completed Successfully
1179 } // wxDb::setConnectionOptions()
1182 /********** wxDb::getDbInfo() **********/
1183 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1188 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1189 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1191 DispAllErrors(henv
, hdbc
);
1192 if (failOnDataTypeUnsupported
)
1196 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1197 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1199 DispAllErrors(henv
, hdbc
);
1200 if (failOnDataTypeUnsupported
)
1204 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1205 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1207 DispAllErrors(henv
, hdbc
);
1208 if (failOnDataTypeUnsupported
)
1213 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1214 // causing database connectivity to fail in some cases.
1215 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1216 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1218 DispAllErrors(henv
, hdbc
);
1219 if (failOnDataTypeUnsupported
)
1223 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1224 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1226 DispAllErrors(henv
, hdbc
);
1227 if (failOnDataTypeUnsupported
)
1231 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1232 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1234 DispAllErrors(henv
, hdbc
);
1235 if (failOnDataTypeUnsupported
)
1239 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1240 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1242 DispAllErrors(henv
, hdbc
);
1243 if (failOnDataTypeUnsupported
)
1247 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1248 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1250 DispAllErrors(henv
, hdbc
);
1251 if (failOnDataTypeUnsupported
)
1255 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1256 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1258 DispAllErrors(henv
, hdbc
);
1259 if (failOnDataTypeUnsupported
)
1263 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1264 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1266 DispAllErrors(henv
, hdbc
);
1267 if (failOnDataTypeUnsupported
)
1271 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1272 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1274 DispAllErrors(henv
, hdbc
);
1275 if (failOnDataTypeUnsupported
)
1279 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1280 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1282 // Not all drivers support this call - Nick Gorham(unixODBC)
1283 dbInf
.cliConfLvl
= 0;
1284 DispAllErrors(henv
, hdbc
);
1285 if (failOnDataTypeUnsupported
)
1289 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1290 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1292 DispAllErrors(henv
, hdbc
);
1293 if (failOnDataTypeUnsupported
)
1297 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1298 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1300 DispAllErrors(henv
, hdbc
);
1301 if (failOnDataTypeUnsupported
)
1305 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1306 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1308 DispAllErrors(henv
, hdbc
);
1309 if (failOnDataTypeUnsupported
)
1313 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1314 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1316 DispAllErrors(henv
, hdbc
);
1317 if (failOnDataTypeUnsupported
)
1321 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1322 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1324 DispAllErrors(henv
, hdbc
);
1325 if (failOnDataTypeUnsupported
)
1329 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1330 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1332 DispAllErrors(henv
, hdbc
);
1333 if (failOnDataTypeUnsupported
)
1337 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1338 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1340 DispAllErrors(henv
, hdbc
);
1341 if (failOnDataTypeUnsupported
)
1345 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1346 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1348 DispAllErrors(henv
, hdbc
);
1349 if (failOnDataTypeUnsupported
)
1353 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1354 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1356 DispAllErrors(henv
, hdbc
);
1357 if (failOnDataTypeUnsupported
)
1361 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1362 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1364 DispAllErrors(henv
, hdbc
);
1365 if (failOnDataTypeUnsupported
)
1369 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1370 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1372 DispAllErrors(henv
, hdbc
);
1373 if (failOnDataTypeUnsupported
)
1377 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1378 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1380 DispAllErrors(henv
, hdbc
);
1381 if (failOnDataTypeUnsupported
)
1385 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1386 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1388 DispAllErrors(henv
, hdbc
);
1389 if (failOnDataTypeUnsupported
)
1393 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1394 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1396 DispAllErrors(henv
, hdbc
);
1397 if (failOnDataTypeUnsupported
)
1401 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1402 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1404 DispAllErrors(henv
, hdbc
);
1405 if (failOnDataTypeUnsupported
)
1409 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1410 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1412 DispAllErrors(henv
, hdbc
);
1413 if (failOnDataTypeUnsupported
)
1417 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1418 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1420 DispAllErrors(henv
, hdbc
);
1421 if (failOnDataTypeUnsupported
)
1425 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1426 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1428 DispAllErrors(henv
, hdbc
);
1429 if (failOnDataTypeUnsupported
)
1433 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1434 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1436 DispAllErrors(henv
, hdbc
);
1437 if (failOnDataTypeUnsupported
)
1441 #ifdef DBDEBUG_CONSOLE
1442 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1443 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1444 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1445 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1447 cout
<< wxT("API Conf. Level: ");
1448 switch(dbInf
.apiConfLvl
)
1450 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1451 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1452 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1456 cout
<< wxT("SAG CLI Conf. Level: ");
1457 switch(dbInf
.cliConfLvl
)
1459 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1460 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1464 cout
<< wxT("SQL Conf. Level: ");
1465 switch(dbInf
.sqlConfLvl
)
1467 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1468 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1469 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1473 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1474 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1475 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1476 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1477 cout
<< wxT("Cursor COMMIT Behavior: ");
1478 switch(dbInf
.cursorCommitBehavior
)
1480 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1481 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1482 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1486 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1487 switch(dbInf
.cursorRollbackBehavior
)
1489 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1490 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1491 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1495 cout
<< wxT("Support NOT NULL clause: ");
1496 switch(dbInf
.supportNotNullClause
)
1498 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1499 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1503 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1504 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1506 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1509 cout
<< wxT("Default Transaction Isolation: ";
1510 switch(dbInf
.txnIsolation
)
1512 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1513 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1514 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1515 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1517 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1522 cout
<< wxT("Transaction Isolation Options: ");
1523 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1524 cout
<< wxT("Read Uncommitted, ");
1525 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1526 cout
<< wxT("Read Committed, ");
1527 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1528 cout
<< wxT("Repeatable Read, ");
1529 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1530 cout
<< wxT("Serializable, ");
1532 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1533 cout
<< wxT("Versioning");
1537 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1538 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1539 cout
<< wxT("Next, ");
1540 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1541 cout
<< wxT("Prev, ");
1542 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1543 cout
<< wxT("First, ");
1544 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1545 cout
<< wxT("Last, ");
1546 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1547 cout
<< wxT("Absolute, ");
1548 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1549 cout
<< wxT("Relative, ");
1551 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1552 cout
<< wxT("Resume, ");
1554 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1555 cout
<< wxT("Bookmark");
1558 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1559 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1560 cout
<< wxT("No Change, ");
1561 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1562 cout
<< wxT("Exclusive, ");
1563 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1564 cout
<< wxT("UnLock");
1567 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1568 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1569 cout
<< wxT("Position, ");
1570 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1571 cout
<< wxT("Refresh, ");
1572 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1573 cout
<< wxT("Upd, "));
1574 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1575 cout
<< wxT("Del, ");
1576 if (dbInf
.posOperations
& SQL_POS_ADD
)
1580 cout
<< wxT("Positioned Statements Supported: ");
1581 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1582 cout
<< wxT("Pos delete, ");
1583 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1584 cout
<< wxT("Pos update, ");
1585 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1586 cout
<< wxT("Select for update");
1589 cout
<< wxT("Scroll Concurrency: ");
1590 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1591 cout
<< wxT("Read Only, ");
1592 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1593 cout
<< wxT("Lock, ");
1594 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1595 cout
<< wxT("Opt. Rowver, ");
1596 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1597 cout
<< wxT("Opt. Values");
1600 cout
<< wxT("Scroll Options: ");
1601 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1602 cout
<< wxT("Fwd Only, ");
1603 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1604 cout
<< wxT("Static, ");
1605 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1606 cout
<< wxT("Keyset Driven, ");
1607 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1608 cout
<< wxT("Dynamic, ");
1609 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1610 cout
<< wxT("Mixed");
1613 cout
<< wxT("Static Sensitivity: ");
1614 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1615 cout
<< wxT("Additions, ");
1616 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1617 cout
<< wxT("Deletions, ");
1618 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1619 cout
<< wxT("Updates");
1622 cout
<< wxT("Transaction Capable?: ");
1623 switch(dbInf
.txnCapable
)
1625 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1626 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1627 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1628 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1629 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1636 // Completed Successfully
1639 } // wxDb::getDbInfo()
1642 /********** wxDb::getDataTypeInfo() **********/
1643 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1646 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1647 * the data type inf. is gathered for.
1649 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1654 // Get information about the data type specified
1655 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1656 return(DispAllErrors(henv
, hdbc
, hstmt
));
1659 retcode
= SQLFetch(hstmt
);
1660 if (retcode
!= SQL_SUCCESS
)
1662 #ifdef DBDEBUG_CONSOLE
1663 if (retcode
== SQL_NO_DATA_FOUND
)
1664 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1666 DispAllErrors(henv
, hdbc
, hstmt
);
1667 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1671 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1673 // Obtain columns from the record
1674 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1675 return(DispAllErrors(henv
, hdbc
, hstmt
));
1677 structSQLTypeInfo
.TypeName
= typeName
;
1679 // BJO 20000503: no more needed with new GetColumns...
1682 if (Dbms() == dbmsMY_SQL
)
1684 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1685 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1686 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1687 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1688 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1689 structSQLTypeInfo
.TypeName
= wxT("int");
1690 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1691 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1692 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1693 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1694 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1695 structSQLTypeInfo
.TypeName
= wxT("char");
1698 // BJO 20000427 : OpenLink driver
1699 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1700 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1702 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1703 structSQLTypeInfo
.TypeName
= wxT("real");
1707 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1708 return(DispAllErrors(henv
, hdbc
, hstmt
));
1709 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1710 return(DispAllErrors(henv
, hdbc
, hstmt
));
1711 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1712 // return(DispAllErrors(henv, hdbc, hstmt));
1714 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1715 return(DispAllErrors(henv
, hdbc
, hstmt
));
1717 if (structSQLTypeInfo
.MaximumScale
< 0)
1718 structSQLTypeInfo
.MaximumScale
= 0;
1720 // Close the statement handle which closes open cursors
1721 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1722 return(DispAllErrors(henv
, hdbc
, hstmt
));
1724 // Completed Successfully
1727 } // wxDb::getDataTypeInfo()
1730 /********** wxDb::Close() **********/
1731 void wxDb::Close(void)
1733 // Close the Sql Log file
1740 // Free statement handle
1743 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1744 DispAllErrors(henv
, hdbc
);
1747 // Disconnect from the datasource
1748 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1749 DispAllErrors(henv
, hdbc
);
1751 // Free the connection to the datasource
1752 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1753 DispAllErrors(henv
, hdbc
);
1755 // There should be zero Ctable objects still connected to this db object
1756 wxASSERT(nTables
== 0);
1760 wxCriticalSectionLocker
lock(csTablesInUse
);
1762 wxList::compatibility_iterator pNode
;
1763 pNode
= TablesInUse
.GetFirst();
1767 tiu
= (wxTablesInUse
*)pNode
->GetData();
1768 if (tiu
->pDb
== this)
1770 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"),
1771 tiu
->tableName
, tiu
->tableID
, wx_static_cast(void*, tiu
->pDb
));
1772 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"), wx_static_cast(void*, this));
1773 wxLogDebug(s
.c_str(),s2
.c_str());
1775 pNode
= pNode
->GetNext();
1780 // Copy the error messages to a global variable
1782 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1783 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1785 dbmsType
= dbmsUNIDENTIFIED
;
1791 /********** wxDb::CommitTrans() **********/
1792 bool wxDb::CommitTrans(void)
1796 // Commit the transaction
1797 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1798 return(DispAllErrors(henv
, hdbc
));
1801 // Completed successfully
1804 } // wxDb::CommitTrans()
1807 /********** wxDb::RollbackTrans() **********/
1808 bool wxDb::RollbackTrans(void)
1810 // Rollback the transaction
1811 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1812 return(DispAllErrors(henv
, hdbc
));
1814 // Completed successfully
1817 } // wxDb::RollbackTrans()
1820 /********** wxDb::DispAllErrors() **********/
1821 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1823 * This function is called internally whenever an error condition prevents the user's
1824 * request from being executed. This function will query the datasource as to the
1825 * actual error(s) that just occurred on the previous request of the datasource.
1827 * The function will retrieve each error condition from the datasource and
1828 * Printf the codes/text values into a string which it then logs via logError().
1829 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1830 * window and program execution will be paused until the user presses a key.
1832 * This function always returns false, so that functions which call this function
1833 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1834 * of the user's request, so that the calling code can then process the error message log.
1837 wxString odbcErrMsg
;
1839 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1841 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1842 sqlState
, (long)nativeError
, errorMsg
);
1843 logError(odbcErrMsg
, sqlState
);
1846 #ifdef DBDEBUG_CONSOLE
1847 // When run in console mode, use standard out to display errors.
1848 cout
<< odbcErrMsg
.c_str() << endl
;
1849 cout
<< wxT("Press any key to continue...") << endl
;
1854 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1859 return false; // This function always returns false.
1861 } // wxDb::DispAllErrors()
1864 /********** wxDb::GetNextError() **********/
1865 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1867 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1872 } // wxDb::GetNextError()
1875 /********** wxDb::DispNextError() **********/
1876 void wxDb::DispNextError(void)
1878 wxString odbcErrMsg
;
1880 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1881 sqlState
, (long)nativeError
, errorMsg
);
1882 logError(odbcErrMsg
, sqlState
);
1887 #ifdef DBDEBUG_CONSOLE
1888 // When run in console mode, use standard out to display errors.
1889 cout
<< odbcErrMsg
.c_str() << endl
;
1890 cout
<< wxT("Press any key to continue...") << endl
;
1895 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1896 #endif // __WXDEBUG__
1898 } // wxDb::DispNextError()
1901 /********** wxDb::logError() **********/
1902 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1904 wxASSERT(errMsg
.length());
1906 static int pLast
= -1;
1909 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1912 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1913 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1917 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1918 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1920 if (SQLState
.length())
1921 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1922 DB_STATUS
= dbStatus
;
1924 // Add the errmsg to the sql log
1925 WriteSqlLog(errMsg
);
1927 } // wxDb::logError()
1930 /**********wxDb::TranslateSqlState() **********/
1931 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1933 if (!wxStrcmp(SQLState
, wxT("01000")))
1934 return(DB_ERR_GENERAL_WARNING
);
1935 if (!wxStrcmp(SQLState
, wxT("01002")))
1936 return(DB_ERR_DISCONNECT_ERROR
);
1937 if (!wxStrcmp(SQLState
, wxT("01004")))
1938 return(DB_ERR_DATA_TRUNCATED
);
1939 if (!wxStrcmp(SQLState
, wxT("01006")))
1940 return(DB_ERR_PRIV_NOT_REVOKED
);
1941 if (!wxStrcmp(SQLState
, wxT("01S00")))
1942 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1943 if (!wxStrcmp(SQLState
, wxT("01S01")))
1944 return(DB_ERR_ERROR_IN_ROW
);
1945 if (!wxStrcmp(SQLState
, wxT("01S02")))
1946 return(DB_ERR_OPTION_VALUE_CHANGED
);
1947 if (!wxStrcmp(SQLState
, wxT("01S03")))
1948 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1949 if (!wxStrcmp(SQLState
, wxT("01S04")))
1950 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1951 if (!wxStrcmp(SQLState
, wxT("07001")))
1952 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1953 if (!wxStrcmp(SQLState
, wxT("07006")))
1954 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1955 if (!wxStrcmp(SQLState
, wxT("08001")))
1956 return(DB_ERR_UNABLE_TO_CONNECT
);
1957 if (!wxStrcmp(SQLState
, wxT("08002")))
1958 return(DB_ERR_CONNECTION_IN_USE
);
1959 if (!wxStrcmp(SQLState
, wxT("08003")))
1960 return(DB_ERR_CONNECTION_NOT_OPEN
);
1961 if (!wxStrcmp(SQLState
, wxT("08004")))
1962 return(DB_ERR_REJECTED_CONNECTION
);
1963 if (!wxStrcmp(SQLState
, wxT("08007")))
1964 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1965 if (!wxStrcmp(SQLState
, wxT("08S01")))
1966 return(DB_ERR_COMM_LINK_FAILURE
);
1967 if (!wxStrcmp(SQLState
, wxT("21S01")))
1968 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1969 if (!wxStrcmp(SQLState
, wxT("21S02")))
1970 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1971 if (!wxStrcmp(SQLState
, wxT("22001")))
1972 return(DB_ERR_STRING_RIGHT_TRUNC
);
1973 if (!wxStrcmp(SQLState
, wxT("22003")))
1974 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1975 if (!wxStrcmp(SQLState
, wxT("22005")))
1976 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1977 if (!wxStrcmp(SQLState
, wxT("22008")))
1978 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1979 if (!wxStrcmp(SQLState
, wxT("22012")))
1980 return(DB_ERR_DIVIDE_BY_ZERO
);
1981 if (!wxStrcmp(SQLState
, wxT("22026")))
1982 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1983 if (!wxStrcmp(SQLState
, wxT("23000")))
1984 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1985 if (!wxStrcmp(SQLState
, wxT("24000")))
1986 return(DB_ERR_INVALID_CURSOR_STATE
);
1987 if (!wxStrcmp(SQLState
, wxT("25000")))
1988 return(DB_ERR_INVALID_TRANS_STATE
);
1989 if (!wxStrcmp(SQLState
, wxT("28000")))
1990 return(DB_ERR_INVALID_AUTH_SPEC
);
1991 if (!wxStrcmp(SQLState
, wxT("34000")))
1992 return(DB_ERR_INVALID_CURSOR_NAME
);
1993 if (!wxStrcmp(SQLState
, wxT("37000")))
1994 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1995 if (!wxStrcmp(SQLState
, wxT("3C000")))
1996 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1997 if (!wxStrcmp(SQLState
, wxT("40001")))
1998 return(DB_ERR_SERIALIZATION_FAILURE
);
1999 if (!wxStrcmp(SQLState
, wxT("42000")))
2000 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
2001 if (!wxStrcmp(SQLState
, wxT("70100")))
2002 return(DB_ERR_OPERATION_ABORTED
);
2003 if (!wxStrcmp(SQLState
, wxT("IM001")))
2004 return(DB_ERR_UNSUPPORTED_FUNCTION
);
2005 if (!wxStrcmp(SQLState
, wxT("IM002")))
2006 return(DB_ERR_NO_DATA_SOURCE
);
2007 if (!wxStrcmp(SQLState
, wxT("IM003")))
2008 return(DB_ERR_DRIVER_LOAD_ERROR
);
2009 if (!wxStrcmp(SQLState
, wxT("IM004")))
2010 return(DB_ERR_SQLALLOCENV_FAILED
);
2011 if (!wxStrcmp(SQLState
, wxT("IM005")))
2012 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
2013 if (!wxStrcmp(SQLState
, wxT("IM006")))
2014 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
2015 if (!wxStrcmp(SQLState
, wxT("IM007")))
2016 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
2017 if (!wxStrcmp(SQLState
, wxT("IM008")))
2018 return(DB_ERR_DIALOG_FAILED
);
2019 if (!wxStrcmp(SQLState
, wxT("IM009")))
2020 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
2021 if (!wxStrcmp(SQLState
, wxT("IM010")))
2022 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
2023 if (!wxStrcmp(SQLState
, wxT("IM011")))
2024 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
2025 if (!wxStrcmp(SQLState
, wxT("IM012")))
2026 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
2027 if (!wxStrcmp(SQLState
, wxT("IM013")))
2028 return(DB_ERR_TRACE_FILE_ERROR
);
2029 if (!wxStrcmp(SQLState
, wxT("S0001")))
2030 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
2031 if (!wxStrcmp(SQLState
, wxT("S0002")))
2032 return(DB_ERR_TABLE_NOT_FOUND
);
2033 if (!wxStrcmp(SQLState
, wxT("S0011")))
2034 return(DB_ERR_INDEX_ALREADY_EXISTS
);
2035 if (!wxStrcmp(SQLState
, wxT("S0012")))
2036 return(DB_ERR_INDEX_NOT_FOUND
);
2037 if (!wxStrcmp(SQLState
, wxT("S0021")))
2038 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
2039 if (!wxStrcmp(SQLState
, wxT("S0022")))
2040 return(DB_ERR_COLUMN_NOT_FOUND
);
2041 if (!wxStrcmp(SQLState
, wxT("S0023")))
2042 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2043 if (!wxStrcmp(SQLState
, wxT("S1000")))
2044 return(DB_ERR_GENERAL_ERROR
);
2045 if (!wxStrcmp(SQLState
, wxT("S1001")))
2046 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2047 if (!wxStrcmp(SQLState
, wxT("S1002")))
2048 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2049 if (!wxStrcmp(SQLState
, wxT("S1003")))
2050 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2051 if (!wxStrcmp(SQLState
, wxT("S1004")))
2052 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2053 if (!wxStrcmp(SQLState
, wxT("S1008")))
2054 return(DB_ERR_OPERATION_CANCELLED
);
2055 if (!wxStrcmp(SQLState
, wxT("S1009")))
2056 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2057 if (!wxStrcmp(SQLState
, wxT("S1010")))
2058 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2059 if (!wxStrcmp(SQLState
, wxT("S1011")))
2060 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2061 if (!wxStrcmp(SQLState
, wxT("S1012")))
2062 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2063 if (!wxStrcmp(SQLState
, wxT("S1015")))
2064 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2065 if (!wxStrcmp(SQLState
, wxT("S1090")))
2066 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2067 if (!wxStrcmp(SQLState
, wxT("S1091")))
2068 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2069 if (!wxStrcmp(SQLState
, wxT("S1092")))
2070 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2071 if (!wxStrcmp(SQLState
, wxT("S1093")))
2072 return(DB_ERR_INVALID_PARAM_NO
);
2073 if (!wxStrcmp(SQLState
, wxT("S1094")))
2074 return(DB_ERR_INVALID_SCALE_VALUE
);
2075 if (!wxStrcmp(SQLState
, wxT("S1095")))
2076 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2077 if (!wxStrcmp(SQLState
, wxT("S1096")))
2078 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2079 if (!wxStrcmp(SQLState
, wxT("S1097")))
2080 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2081 if (!wxStrcmp(SQLState
, wxT("S1098")))
2082 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2083 if (!wxStrcmp(SQLState
, wxT("S1099")))
2084 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2085 if (!wxStrcmp(SQLState
, wxT("S1100")))
2086 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2087 if (!wxStrcmp(SQLState
, wxT("S1101")))
2088 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2089 if (!wxStrcmp(SQLState
, wxT("S1103")))
2090 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2091 if (!wxStrcmp(SQLState
, wxT("S1104")))
2092 return(DB_ERR_INVALID_PRECISION_VALUE
);
2093 if (!wxStrcmp(SQLState
, wxT("S1105")))
2094 return(DB_ERR_INVALID_PARAM_TYPE
);
2095 if (!wxStrcmp(SQLState
, wxT("S1106")))
2096 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2097 if (!wxStrcmp(SQLState
, wxT("S1107")))
2098 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2099 if (!wxStrcmp(SQLState
, wxT("S1108")))
2100 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2101 if (!wxStrcmp(SQLState
, wxT("S1109")))
2102 return(DB_ERR_INVALID_CURSOR_POSITION
);
2103 if (!wxStrcmp(SQLState
, wxT("S1110")))
2104 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2105 if (!wxStrcmp(SQLState
, wxT("S1111")))
2106 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2107 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2108 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2109 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2110 return(DB_ERR_TIMEOUT_EXPIRED
);
2115 } // wxDb::TranslateSqlState()
2118 /********** wxDb::Grant() **********/
2119 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2123 // Build the grant statement
2124 sqlStmt
= wxT("GRANT ");
2125 if (privileges
== DB_GRANT_ALL
)
2126 sqlStmt
+= wxT("ALL");
2130 if (privileges
& DB_GRANT_SELECT
)
2132 sqlStmt
+= wxT("SELECT");
2135 if (privileges
& DB_GRANT_INSERT
)
2138 sqlStmt
+= wxT(", ");
2139 sqlStmt
+= wxT("INSERT");
2141 if (privileges
& DB_GRANT_UPDATE
)
2144 sqlStmt
+= wxT(", ");
2145 sqlStmt
+= wxT("UPDATE");
2147 if (privileges
& DB_GRANT_DELETE
)
2150 sqlStmt
+= wxT(", ");
2151 sqlStmt
+= wxT("DELETE");
2155 sqlStmt
+= wxT(" ON ");
2156 sqlStmt
+= SQLTableName(tableName
);
2157 sqlStmt
+= wxT(" TO ");
2158 sqlStmt
+= userList
;
2160 #ifdef DBDEBUG_CONSOLE
2161 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2164 WriteSqlLog(sqlStmt
);
2166 return(ExecSql(sqlStmt
));
2171 /********** wxDb::CreateView() **********/
2172 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2173 const wxString
&pSqlStmt
, bool attemptDrop
)
2177 // Drop the view first
2178 if (attemptDrop
&& !DropView(viewName
))
2181 // Build the create view statement
2182 sqlStmt
= wxT("CREATE VIEW ");
2183 sqlStmt
+= viewName
;
2185 if (colList
.length())
2187 sqlStmt
+= wxT(" (");
2189 sqlStmt
+= wxT(")");
2192 sqlStmt
+= wxT(" AS ");
2193 sqlStmt
+= pSqlStmt
;
2195 WriteSqlLog(sqlStmt
);
2197 #ifdef DBDEBUG_CONSOLE
2198 cout
<< sqlStmt
.c_str() << endl
;
2201 return(ExecSql(sqlStmt
));
2203 } // wxDb::CreateView()
2206 /********** wxDb::DropView() **********/
2207 bool wxDb::DropView(const wxString
&viewName
)
2210 * NOTE: This function returns true if the View does not exist, but
2211 * only for identified databases. Code will need to be added
2212 * below for any other databases when those databases are defined
2213 * to handle this situation consistently
2217 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2219 WriteSqlLog(sqlStmt
);
2221 #ifdef DBDEBUG_CONSOLE
2222 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2225 if (SQLExecDirect(hstmt
, WXSQLCAST(sqlStmt
), SQL_NTS
) != SQL_SUCCESS
)
2227 // Check for "Base table not found" error and ignore
2228 GetNextError(henv
, hdbc
, hstmt
);
2229 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2231 // Check for product specific error codes
2232 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2235 DispAllErrors(henv
, hdbc
, hstmt
);
2242 // Commit the transaction
2248 } // wxDb::DropView()
2251 /********** wxDb::ExecSql() **********/
2252 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2256 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2258 retcode
= SQLExecDirect(hstmt
, WXSQLCAST(pSqlStmt
), SQL_NTS
);
2259 if (retcode
== SQL_SUCCESS
||
2260 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2266 DispAllErrors(henv
, hdbc
, hstmt
);
2270 } // wxDb::ExecSql()
2273 /********** wxDb::ExecSql() with column info **********/
2274 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2276 //execute the statement first
2277 if (!ExecSql(pSqlStmt
))
2281 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2283 DispAllErrors(henv
, hdbc
, hstmt
);
2292 // Get column information
2294 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2297 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2299 // Fill in column information (name, datatype)
2300 for (colNum
= 0; colNum
< noCols
; colNum
++)
2302 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2304 &Sword
, &Sqllen
) != SQL_SUCCESS
)
2306 DispAllErrors(henv
, hdbc
, hstmt
);
2311 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2312 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2314 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2315 NULL
, 0, &Sword
, &Sqllen
) != SQL_SUCCESS
)
2317 DispAllErrors(henv
, hdbc
, hstmt
);
2325 #if defined(SQL_WCHAR)
2328 #if defined(SQL_WVARCHAR)
2334 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2336 case SQL_LONGVARCHAR
:
2337 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_MEMO
;
2343 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2350 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2354 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2357 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2362 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen
);
2363 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2370 } // wxDb::ExecSql()
2372 /********** wxDb::GetNext() **********/
2373 bool wxDb::GetNext(void)
2375 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2379 DispAllErrors(henv
, hdbc
, hstmt
);
2383 } // wxDb::GetNext()
2386 /********** wxDb::GetData() **********/
2387 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SQLLEN FAR
*cbReturned
)
2390 wxASSERT(cbReturned
);
2392 long bufferSize
= maxLen
;
2394 if (cType
== SQL_C_WXCHAR
)
2395 bufferSize
= maxLen
* sizeof(wxChar
);
2397 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2401 DispAllErrors(henv
, hdbc
, hstmt
);
2405 } // wxDb::GetData()
2408 /********** wxDb::GetKeyFields() **********/
2409 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2411 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2412 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2414 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2415 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2421 * -----------------------------------------------------------------------
2422 * -- 19991224 : mj10777 : Create ------
2423 * -- : Three things are done and stored here : ------
2424 * -- : 1) which Column(s) is/are Primary Key(s) ------
2425 * -- : 2) which tables use this Key as a Foreign Key ------
2426 * -- : 3) which columns are Foreign Key and the name ------
2427 * -- : of the Table where the Key is the Primary Key -----
2428 * -- : Called from GetColumns(const wxString &tableName, ------
2429 * -- int *numCols,const wxChar *userID ) ------
2430 * -----------------------------------------------------------------------
2433 /*---------------------------------------------------------------------*/
2434 /* Get the names of the columns in the primary key. */
2435 /*---------------------------------------------------------------------*/
2436 retcode
= SQLPrimaryKeys(hstmt
,
2437 NULL
, 0, /* Catalog name */
2438 NULL
, 0, /* Schema name */
2439 WXSQLCAST(tableName
), SQL_NTS
); /* Table name */
2441 /*---------------------------------------------------------------------*/
2442 /* Fetch and display the result set. This will be a list of the */
2443 /* columns in the primary key of the tableName table. */
2444 /*---------------------------------------------------------------------*/
2445 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2447 retcode
= SQLFetch(hstmt
);
2448 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2450 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2451 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2453 for (i
=0;i
<noCols
;i
++) // Find the Column name
2454 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2455 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2458 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2460 /*---------------------------------------------------------------------*/
2461 /* Get all the foreign keys that refer to tableName primary key. */
2462 /*---------------------------------------------------------------------*/
2463 retcode
= SQLForeignKeys(hstmt
,
2464 NULL
, 0, /* Primary catalog */
2465 NULL
, 0, /* Primary schema */
2466 WXSQLCAST(tableName
), SQL_NTS
,/* Primary table */
2467 NULL
, 0, /* Foreign catalog */
2468 NULL
, 0, /* Foreign schema */
2469 NULL
, 0); /* Foreign table */
2471 /*---------------------------------------------------------------------*/
2472 /* Fetch and display the result set. This will be all of the foreign */
2473 /* keys in other tables that refer to the tableName primary key. */
2474 /*---------------------------------------------------------------------*/
2477 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2479 retcode
= SQLFetch(hstmt
);
2480 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2482 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2483 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2484 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2485 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2486 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2487 tempStr
<< _T('[') << szFkTable
<< _T(']'); // [ ] in case there is a blank in the Table name
2491 tempStr
.Trim(); // Get rid of any unneeded blanks
2492 if (!tempStr
.empty())
2494 for (i
=0; i
<noCols
; i
++)
2495 { // Find the Column name
2496 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2498 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
2499 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2504 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2506 /*---------------------------------------------------------------------*/
2507 /* Get all the foreign keys in the tablename table. */
2508 /*---------------------------------------------------------------------*/
2509 retcode
= SQLForeignKeys(hstmt
,
2510 NULL
, 0, /* Primary catalog */
2511 NULL
, 0, /* Primary schema */
2512 NULL
, 0, /* Primary table */
2513 NULL
, 0, /* Foreign catalog */
2514 NULL
, 0, /* Foreign schema */
2515 WXSQLCAST(tableName
), SQL_NTS
);/* Foreign table */
2517 /*---------------------------------------------------------------------*/
2518 /* Fetch and display the result set. This will be all of the */
2519 /* primary keys in other tables that are referred to by foreign */
2520 /* keys in the tableName table. */
2521 /*---------------------------------------------------------------------*/
2522 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2524 retcode
= SQLFetch(hstmt
);
2525 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2527 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2528 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2529 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2531 for (i
=0; i
<noCols
; i
++) // Find the Column name
2533 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2535 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2536 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2537 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2542 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2546 } // wxDb::GetKeyFields()
2550 /********** wxDb::GetColumns() **********/
2551 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2553 * 1) The last array element of the tableName[] argument must be zero (null).
2554 * This is how the end of the array is detected.
2555 * 2) This function returns an array of wxDbColInf structures. If no columns
2556 * were found, or an error occurred, this pointer will be zero (null). THE
2557 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2558 * IS FINISHED WITH IT. i.e.
2560 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2563 * // Use the column inf
2565 * // Destroy the memory
2569 * userID is evaluated in the following manner:
2570 * userID == NULL ... UserID is ignored
2571 * userID == "" ... UserID set equal to 'this->uid'
2572 * userID != "" ... UserID set equal to 'userID'
2574 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2575 * by this function. This function should use its own wxDb instance
2576 * to avoid undesired unbinding of columns.
2581 wxDbColInf
*colInf
= 0;
2589 convertUserID(userID
,UserID
);
2591 // Pass 1 - Determine how many columns there are.
2592 // Pass 2 - Allocate the wxDbColInf array and fill in
2593 // the array with the column information.
2595 for (pass
= 1; pass
<= 2; pass
++)
2599 if (noCols
== 0) // Probably a bogus table name(s)
2601 // Allocate n wxDbColInf objects to hold the column information
2602 colInf
= new wxDbColInf
[noCols
+1];
2605 // Mark the end of the array
2606 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2607 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2608 colInf
[noCols
].sqlDataType
= 0;
2610 // Loop through each table name
2612 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2614 TableName
= tableName
[tbl
];
2615 // Oracle and Interbase table names are uppercase only, so force
2616 // the name to uppercase just in case programmer forgot to do this
2617 if ((Dbms() == dbmsORACLE
) ||
2618 (Dbms() == dbmsFIREBIRD
) ||
2619 (Dbms() == dbmsINTERBASE
))
2620 TableName
= TableName
.Upper();
2622 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2624 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2625 // use the call below that leaves out the user name
2626 if (!UserID
.empty() &&
2627 Dbms() != dbmsMY_SQL
&&
2628 Dbms() != dbmsACCESS
&&
2629 Dbms() != dbmsMS_SQL_SERVER
)
2631 retcode
= SQLColumns(hstmt
,
2632 NULL
, 0, // All qualifiers
2633 WXSQLCAST(UserID
), SQL_NTS
, // Owner
2634 WXSQLCAST(TableName
), SQL_NTS
,
2635 NULL
, 0); // All columns
2639 retcode
= SQLColumns(hstmt
,
2640 NULL
, 0, // All qualifiers
2642 WXSQLCAST(TableName
), SQL_NTS
,
2643 NULL
, 0); // All columns
2645 if (retcode
!= SQL_SUCCESS
)
2646 { // Error occurred, abort
2647 DispAllErrors(henv
, hdbc
, hstmt
);
2650 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2654 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2656 if (pass
== 1) // First pass, just add up the number of columns
2658 else // Pass 2; Fill in the array of structures
2660 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2662 // NOTE: Only the ODBC 1.x fields are retrieved
2663 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2664 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2665 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2666 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2667 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2668 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2669 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2670 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2671 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2672 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2673 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2674 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2676 // Determine the wxDb data type that is used to represent the native data type of this data source
2677 colInf
[colNo
].dbDataType
= 0;
2678 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2681 // IODBC does not return a correct columnLength, so we set
2682 // columnLength = bufferSize if no column length was returned
2683 // IODBC returns the columnLength in bufferSize. (bug)
2684 if (colInf
[colNo
].columnLength
< 1)
2686 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2689 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2691 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2692 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2693 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2694 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2695 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2696 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2697 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2698 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2703 if (retcode
!= SQL_NO_DATA_FOUND
)
2704 { // Error occurred, abort
2705 DispAllErrors(henv
, hdbc
, hstmt
);
2708 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2714 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2717 } // wxDb::GetColumns()
2720 /********** wxDb::GetColumns() **********/
2722 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2724 // Same as the above GetColumns() function except this one gets columns
2725 // only for a single table, and if 'numCols' is not NULL, the number of
2726 // columns stored in the returned wxDbColInf is set in '*numCols'
2728 // userID is evaluated in the following manner:
2729 // userID == NULL ... UserID is ignored
2730 // userID == "" ... UserID set equal to 'this->uid'
2731 // userID != "" ... UserID set equal to 'userID'
2733 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2734 // by this function. This function should use its own wxDb instance
2735 // to avoid undesired unbinding of columns.
2740 wxDbColInf
*colInf
= 0;
2748 convertUserID(userID
,UserID
);
2750 // Pass 1 - Determine how many columns there are.
2751 // Pass 2 - Allocate the wxDbColInf array and fill in
2752 // the array with the column information.
2754 for (pass
= 1; pass
<= 2; pass
++)
2758 if (noCols
== 0) // Probably a bogus table name(s)
2760 // Allocate n wxDbColInf objects to hold the column information
2761 colInf
= new wxDbColInf
[noCols
+1];
2764 // Mark the end of the array
2765 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2766 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2767 colInf
[noCols
].sqlDataType
= 0;
2770 TableName
= tableName
;
2771 // Oracle and Interbase table names are uppercase only, so force
2772 // the name to uppercase just in case programmer forgot to do this
2773 if ((Dbms() == dbmsORACLE
) ||
2774 (Dbms() == dbmsFIREBIRD
) ||
2775 (Dbms() == dbmsINTERBASE
))
2776 TableName
= TableName
.Upper();
2778 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2780 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2781 // use the call below that leaves out the user name
2782 if (!UserID
.empty() &&
2783 Dbms() != dbmsMY_SQL
&&
2784 Dbms() != dbmsACCESS
&&
2785 Dbms() != dbmsMS_SQL_SERVER
)
2787 retcode
= SQLColumns(hstmt
,
2788 NULL
, 0, // All qualifiers
2789 WXSQLCAST(UserID
), SQL_NTS
, // Owner
2790 WXSQLCAST(TableName
), SQL_NTS
,
2791 NULL
, 0); // All columns
2795 retcode
= SQLColumns(hstmt
,
2796 NULL
, 0, // All qualifiers
2798 WXSQLCAST(TableName
), SQL_NTS
,
2799 NULL
, 0); // All columns
2801 if (retcode
!= SQL_SUCCESS
)
2802 { // Error occurred, abort
2803 DispAllErrors(henv
, hdbc
, hstmt
);
2806 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2812 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2814 if (pass
== 1) // First pass, just add up the number of columns
2816 else // Pass 2; Fill in the array of structures
2818 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2820 // NOTE: Only the ODBC 1.x fields are retrieved
2821 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2822 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2823 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2824 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2825 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2826 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2827 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2828 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2829 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2830 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2831 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2832 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2833 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2834 // Start Values for Primary/Foriegn Key (=No)
2835 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2836 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2837 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2838 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2840 // BJO 20000428 : Virtuoso returns type names with upper cases!
2841 if (Dbms() == dbmsVIRTUOSO
)
2843 wxString s
= colInf
[colNo
].typeName
;
2845 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2848 // Determine the wxDb data type that is used to represent the native data type of this data source
2849 colInf
[colNo
].dbDataType
= 0;
2850 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2853 // IODBC does not return a correct columnLength, so we set
2854 // columnLength = bufferSize if no column length was returned
2855 // IODBC returns the columnLength in bufferSize. (bug)
2856 if (colInf
[colNo
].columnLength
< 1)
2858 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2862 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2864 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2865 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2866 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2867 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2868 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2869 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2870 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2871 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2877 if (retcode
!= SQL_NO_DATA_FOUND
)
2878 { // Error occurred, abort
2879 DispAllErrors(henv
, hdbc
, hstmt
);
2882 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2889 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2891 // Store Primary and Foriegn Keys
2892 GetKeyFields(tableName
,colInf
,noCols
);
2898 } // wxDb::GetColumns()
2901 #else // New GetColumns
2906 These are tentative new GetColumns members which should be more database
2907 independent and which always returns the columns in the order they were
2910 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2911 wxChar* userID)) calls the second implementation for each separate table
2912 before merging the results. This makes the code easier to maintain as
2913 only one member (the second) makes the real work
2914 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2915 wxChar *userID) is a little bit improved
2916 - It doesn't anymore rely on the type-name to find out which database-type
2918 - It ends by sorting the columns, so that they are returned in the same
2919 order they were created
2929 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2932 // The last array element of the tableName[] argument must be zero (null).
2933 // This is how the end of the array is detected.
2937 // How many tables ?
2939 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2941 // Create a table to maintain the columns for each separate table
2942 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2945 for (i
= 0 ; i
< tbl
; i
++)
2948 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2949 if (TableColumns
[i
].colInf
== NULL
)
2951 noCols
+= TableColumns
[i
].noCols
;
2954 // Now merge all the separate table infos
2955 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2957 // Mark the end of the array
2958 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2959 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2960 colInf
[noCols
].sqlDataType
= 0;
2965 for (i
= 0 ; i
< tbl
; i
++)
2967 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2969 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2973 delete [] TableColumns
;
2976 } // wxDb::GetColumns() -- NEW
2979 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2981 // Same as the above GetColumns() function except this one gets columns
2982 // only for a single table, and if 'numCols' is not NULL, the number of
2983 // columns stored in the returned wxDbColInf is set in '*numCols'
2985 // userID is evaluated in the following manner:
2986 // userID == NULL ... UserID is ignored
2987 // userID == "" ... UserID set equal to 'this->uid'
2988 // userID != "" ... UserID set equal to 'userID'
2990 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2991 // by this function. This function should use its own wxDb instance
2992 // to avoid undesired unbinding of columns.
2996 wxDbColInf
*colInf
= 0;
3004 convertUserID(userID
,UserID
);
3006 // Pass 1 - Determine how many columns there are.
3007 // Pass 2 - Allocate the wxDbColInf array and fill in
3008 // the array with the column information.
3010 for (pass
= 1; pass
<= 2; pass
++)
3014 if (noCols
== 0) // Probably a bogus table name(s)
3016 // Allocate n wxDbColInf objects to hold the column information
3017 colInf
= new wxDbColInf
[noCols
+1];
3020 // Mark the end of the array
3021 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
3022 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
3023 colInf
[noCols
].sqlDataType
= 0;
3026 TableName
= tableName
;
3027 // Oracle and Interbase table names are uppercase only, so force
3028 // the name to uppercase just in case programmer forgot to do this
3029 if ((Dbms() == dbmsORACLE
) ||
3030 (Dbms() == dbmsFIREBIRD
) ||
3031 (Dbms() == dbmsINTERBASE
))
3032 TableName
= TableName
.Upper();
3034 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3036 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3037 // use the call below that leaves out the user name
3038 if (!UserID
.empty() &&
3039 Dbms() != dbmsMY_SQL
&&
3040 Dbms() != dbmsACCESS
&&
3041 Dbms() != dbmsMS_SQL_SERVER
)
3043 retcode
= SQLColumns(hstmt
,
3044 NULL
, 0, // All qualifiers
3045 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3046 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3047 NULL
, 0); // All columns
3051 retcode
= SQLColumns(hstmt
,
3052 NULL
, 0, // All qualifiers
3054 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3055 NULL
, 0); // All columns
3057 if (retcode
!= SQL_SUCCESS
)
3058 { // Error occurred, abort
3059 DispAllErrors(henv
, hdbc
, hstmt
);
3062 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3068 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3070 if (pass
== 1) // First pass, just add up the number of columns
3072 else // Pass 2; Fill in the array of structures
3074 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3076 // NOTE: Only the ODBC 1.x fields are retrieved
3077 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3078 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3079 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3080 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3081 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3082 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3083 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3084 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3085 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3086 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3087 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3088 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3089 // Start Values for Primary/Foriegn Key (=No)
3090 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3091 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3092 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3093 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3096 // IODBC does not return a correct columnLength, so we set
3097 // columnLength = bufferSize if no column length was returned
3098 // IODBC returns the columnLength in bufferSize. (bug)
3099 if (colInf
[colNo
].columnLength
< 1)
3101 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3105 // Determine the wxDb data type that is used to represent the native data type of this data source
3106 colInf
[colNo
].dbDataType
= 0;
3107 // Get the intern datatype
3108 switch (colInf
[colNo
].sqlDataType
)
3111 #if defined(SQL_WCHAR)
3114 #if defined(SQL_WVARCHAR)
3120 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3122 case SQL_LONGVARCHAR
:
3123 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_MEMO
;
3129 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3136 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3140 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3143 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3148 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3149 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3156 if (retcode
!= SQL_NO_DATA_FOUND
)
3157 { // Error occurred, abort
3158 DispAllErrors(henv
, hdbc
, hstmt
);
3161 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3168 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3170 // Store Primary and Foreign Keys
3171 GetKeyFields(tableName
,colInf
,noCols
);
3173 ///////////////////////////////////////////////////////////////////////////
3174 // Now sort the the columns in order to make them appear in the right order
3175 ///////////////////////////////////////////////////////////////////////////
3177 // Build a generic SELECT statement which returns 0 rows
3180 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3183 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3185 DispAllErrors(henv
, hdbc
, hstmt
);
3189 // Get the number of result columns
3190 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3192 DispAllErrors(henv
, hdbc
, hstmt
);
3196 if (noCols
== 0) // Probably a bogus table name
3205 for (colNum
= 0; colNum
< noCols
; colNum
++)
3207 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3209 &Sword
, &Sdword
) != SQL_SUCCESS
)
3211 DispAllErrors(henv
, hdbc
, hstmt
);
3215 wxString Name1
= name
;
3216 Name1
= Name1
.Upper();
3218 // Where is this name in the array ?
3219 for (i
= colNum
; i
< noCols
; i
++)
3221 wxString Name2
= colInf
[i
].colName
;
3222 Name2
= Name2
.Upper();
3225 if (colNum
!= i
) // swap to sort
3227 wxDbColInf tmpColInf
= colInf
[colNum
];
3228 colInf
[colNum
] = colInf
[i
];
3229 colInf
[i
] = tmpColInf
;
3235 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3237 ///////////////////////////////////////////////////////////////////////////
3239 ///////////////////////////////////////////////////////////////////////////
3245 } // wxDb::GetColumns()
3248 #endif // #else OLD_GETCOLUMNS
3251 /********** wxDb::GetColumnCount() **********/
3252 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3254 * Returns a count of how many columns are in a table.
3255 * If an error occurs in computing the number of columns
3256 * this function will return a -1 for the count
3258 * userID is evaluated in the following manner:
3259 * userID == NULL ... UserID is ignored
3260 * userID == "" ... UserID set equal to 'this->uid'
3261 * userID != "" ... UserID set equal to 'userID'
3263 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3264 * by this function. This function should use its own wxDb instance
3265 * to avoid undesired unbinding of columns.
3275 convertUserID(userID
,UserID
);
3277 TableName
= tableName
;
3278 // Oracle and Interbase table names are uppercase only, so force
3279 // the name to uppercase just in case programmer forgot to do this
3280 if ((Dbms() == dbmsORACLE
) ||
3281 (Dbms() == dbmsFIREBIRD
) ||
3282 (Dbms() == dbmsINTERBASE
))
3283 TableName
= TableName
.Upper();
3285 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3287 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3288 // use the call below that leaves out the user name
3289 if (!UserID
.empty() &&
3290 Dbms() != dbmsMY_SQL
&&
3291 Dbms() != dbmsACCESS
&&
3292 Dbms() != dbmsMS_SQL_SERVER
)
3294 retcode
= SQLColumns(hstmt
,
3295 NULL
, 0, // All qualifiers
3296 WXSQLCAST(UserID
), SQL_NTS
, // Owner
3297 WXSQLCAST(TableName
), SQL_NTS
,
3298 NULL
, 0); // All columns
3302 retcode
= SQLColumns(hstmt
,
3303 NULL
, 0, // All qualifiers
3305 WXSQLCAST(TableName
), SQL_NTS
,
3306 NULL
, 0); // All columns
3308 if (retcode
!= SQL_SUCCESS
)
3309 { // Error occurred, abort
3310 DispAllErrors(henv
, hdbc
, hstmt
);
3311 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3315 // Count the columns
3316 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3319 if (retcode
!= SQL_NO_DATA_FOUND
)
3320 { // Error occurred, abort
3321 DispAllErrors(henv
, hdbc
, hstmt
);
3322 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3326 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3329 } // wxDb::GetColumnCount()
3332 /********** wxDb::GetCatalog() *******/
3333 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3335 * ---------------------------------------------------------------------
3336 * -- 19991203 : mj10777 : Create ------
3337 * -- : Creates a wxDbInf with Tables / Cols Array ------
3338 * -- : uses SQLTables and fills pTableInf; ------
3339 * -- : pColInf is set to NULL and numCols to 0; ------
3340 * -- : returns pDbInf (wxDbInf) ------
3341 * -- - if unsuccessful (pDbInf == NULL) ------
3342 * -- : pColInf can be filled with GetColumns(..); ------
3343 * -- : numCols can be filled with GetColumnCount(..); ------
3344 * ---------------------------------------------------------------------
3346 * userID is evaluated in the following manner:
3347 * userID == NULL ... UserID is ignored
3348 * userID == "" ... UserID set equal to 'this->uid'
3349 * userID != "" ... UserID set equal to 'userID'
3351 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3352 * by this function. This function should use its own wxDb instance
3353 * to avoid undesired unbinding of columns.
3356 int noTab
= 0; // Counter while filling table entries
3360 wxString tblNameSave
;
3363 convertUserID(userID
,UserID
);
3365 //-------------------------------------------------------------
3366 // Create the Database Array of catalog entries
3368 wxDbInf
*pDbInf
= new wxDbInf
;
3370 //-------------------------------------------------------------
3371 // Table Information
3372 // Pass 1 - Determine how many Tables there are.
3373 // Pass 2 - Create the Table array and fill it
3374 // - Create the Cols array = NULL
3375 //-------------------------------------------------------------
3377 for (pass
= 1; pass
<= 2; pass
++)
3379 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3380 tblNameSave
.Empty();
3382 if (!UserID
.empty() &&
3383 Dbms() != dbmsMY_SQL
&&
3384 Dbms() != dbmsACCESS
&&
3385 Dbms() != dbmsMS_SQL_SERVER
)
3387 retcode
= SQLTables(hstmt
,
3388 NULL
, 0, // All qualifiers
3389 WXSQLCAST(UserID
), SQL_NTS
, // User specified
3390 NULL
, 0, // All tables
3391 NULL
, 0); // All columns
3395 retcode
= SQLTables(hstmt
,
3396 NULL
, 0, // All qualifiers
3397 NULL
, 0, // User specified
3398 NULL
, 0, // All tables
3399 NULL
, 0); // All columns
3402 if (retcode
!= SQL_SUCCESS
)
3404 DispAllErrors(henv
, hdbc
, hstmt
);
3406 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3410 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3412 if (pass
== 1) // First pass, just count the Tables
3414 if (pDbInf
->numTables
== 0)
3416 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3417 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3419 pDbInf
->numTables
++; // Counter for Tables
3421 if (pass
== 2) // Create and fill the Table entries
3423 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3424 { // no, then create the Array
3425 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3427 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3429 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3430 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3431 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3437 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3439 // Query how many columns are in each table
3440 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3442 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3447 } // wxDb::GetCatalog()
3450 /********** wxDb::Catalog() **********/
3451 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3453 * Creates the text file specified in 'filename' which will contain
3454 * a minimal data dictionary of all tables accessible by the user specified
3457 * userID is evaluated in the following manner:
3458 * userID == NULL ... UserID is ignored
3459 * userID == "" ... UserID set equal to 'this->uid'
3460 * userID != "" ... UserID set equal to 'userID'
3462 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3463 * by this function. This function should use its own wxDb instance
3464 * to avoid undesired unbinding of columns.
3467 wxASSERT(fileName
.length());
3471 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3472 wxString tblNameSave
;
3473 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3475 wxChar typeName
[30+1];
3476 SDWORD precision
, length
;
3478 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3482 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3485 convertUserID(userID
,UserID
);
3487 if (!UserID
.empty() &&
3488 Dbms() != dbmsMY_SQL
&&
3489 Dbms() != dbmsACCESS
&&
3490 Dbms() != dbmsFIREBIRD
&&
3491 Dbms() != dbmsINTERBASE
&&
3492 Dbms() != dbmsMS_SQL_SERVER
)
3494 retcode
= SQLColumns(hstmt
,
3495 NULL
, 0, // All qualifiers
3496 WXSQLCAST(UserID
), SQL_NTS
, // User specified
3497 NULL
, 0, // All tables
3498 NULL
, 0); // All columns
3502 retcode
= SQLColumns(hstmt
,
3503 NULL
, 0, // All qualifiers
3504 NULL
, 0, // User specified
3505 NULL
, 0, // All tables
3506 NULL
, 0); // All columns
3508 if (retcode
!= SQL_SUCCESS
)
3510 DispAllErrors(henv
, hdbc
, hstmt
);
3516 tblNameSave
.Empty();
3521 retcode
= SQLFetch(hstmt
);
3522 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3525 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3526 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3527 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3528 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3529 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3530 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3532 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3535 wxFputs(wxT("\n"), fp
);
3536 wxFputs(wxT("================================ "), fp
);
3537 wxFputs(wxT("================================ "), fp
);
3538 wxFputs(wxT("===================== "), fp
);
3539 wxFputs(wxT("========= "), fp
);
3540 wxFputs(wxT("=========\n"), fp
);
3541 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3542 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3543 wxFputs(outStr
.c_str(), fp
);
3544 wxFputs(wxT("================================ "), fp
);
3545 wxFputs(wxT("================================ "), fp
);
3546 wxFputs(wxT("===================== "), fp
);
3547 wxFputs(wxT("========= "), fp
);
3548 wxFputs(wxT("=========\n"), fp
);
3549 tblNameSave
= tblName
;
3552 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3553 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3554 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3556 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3563 if (retcode
!= SQL_NO_DATA_FOUND
)
3564 DispAllErrors(henv
, hdbc
, hstmt
);
3566 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3569 return(retcode
== SQL_NO_DATA_FOUND
);
3571 } // wxDb::Catalog()
3574 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3576 * Table name can refer to a table, view, alias or synonym. Returns true
3577 * if the object exists in the database. This function does not indicate
3578 * whether or not the user has privleges to query or perform other functions
3581 * userID is evaluated in the following manner:
3582 * userID == NULL ... UserID is ignored
3583 * userID == "" ... UserID set equal to 'this->uid'
3584 * userID != "" ... UserID set equal to 'userID'
3587 wxASSERT(tableName
.length());
3591 if (Dbms() == dbmsDBASE
)
3594 if (tablePath
.length())
3595 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3597 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3600 exists
= wxFileExists(dbName
);
3605 convertUserID(userID
,UserID
);
3607 TableName
= tableName
;
3608 // Oracle and Interbase table names are uppercase only, so force
3609 // the name to uppercase just in case programmer forgot to do this
3610 if ((Dbms() == dbmsORACLE
) ||
3611 (Dbms() == dbmsFIREBIRD
) ||
3612 (Dbms() == dbmsINTERBASE
))
3613 TableName
= TableName
.Upper();
3615 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3618 // Some databases cannot accept a user name when looking up table names,
3619 // so we use the call below that leaves out the user name
3620 if (!UserID
.empty() &&
3621 Dbms() != dbmsMY_SQL
&&
3622 Dbms() != dbmsACCESS
&&
3623 Dbms() != dbmsMS_SQL_SERVER
&&
3624 Dbms() != dbmsDB2
&&
3625 Dbms() != dbmsFIREBIRD
&&
3626 Dbms() != dbmsINTERBASE
&&
3627 Dbms() != dbmsPERVASIVE_SQL
)
3629 retcode
= SQLTables(hstmt
,
3630 NULL
, 0, // All qualifiers
3631 WXSQLCAST(UserID
), SQL_NTS
, // Only tables owned by this user
3632 WXSQLCAST(TableName
), SQL_NTS
,
3633 NULL
, 0); // All table types
3637 retcode
= SQLTables(hstmt
,
3638 NULL
, 0, // All qualifiers
3639 NULL
, 0, // All owners
3640 WXSQLCAST(TableName
), SQL_NTS
,
3641 NULL
, 0); // All table types
3643 if (retcode
!= SQL_SUCCESS
)
3644 return(DispAllErrors(henv
, hdbc
, hstmt
));
3646 retcode
= SQLFetch(hstmt
);
3647 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3649 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3650 return(DispAllErrors(henv
, hdbc
, hstmt
));
3653 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3657 } // wxDb::TableExists()
3660 /********** wxDb::TablePrivileges() **********/
3661 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3662 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3664 wxASSERT(tableName
.length());
3666 wxDbTablePrivilegeInfo result
;
3670 // We probably need to be able to dynamically set this based on
3671 // the driver type, and state.
3672 wxChar curRole
[]=wxT("public");
3676 wxString UserID
,Schema
;
3677 convertUserID(userID
,UserID
);
3678 convertUserID(schema
,Schema
);
3680 TableName
= tableName
;
3681 // Oracle and Interbase table names are uppercase only, so force
3682 // the name to uppercase just in case programmer forgot to do this
3683 if ((Dbms() == dbmsORACLE
) ||
3684 (Dbms() == dbmsFIREBIRD
) ||
3685 (Dbms() == dbmsINTERBASE
))
3686 TableName
= TableName
.Upper();
3688 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3690 // Some databases cannot accept a user name when looking up table names,
3691 // so we use the call below that leaves out the user name
3692 if (!Schema
.empty() &&
3693 Dbms() != dbmsMY_SQL
&&
3694 Dbms() != dbmsACCESS
&&
3695 Dbms() != dbmsMS_SQL_SERVER
)
3697 retcode
= SQLTablePrivileges(hstmt
,
3699 WXSQLCAST(Schema
), SQL_NTS
, // Schema
3700 WXSQLCAST(TableName
), SQL_NTS
);
3704 retcode
= SQLTablePrivileges(hstmt
,
3707 WXSQLCAST(TableName
), SQL_NTS
);
3710 #ifdef DBDEBUG_CONSOLE
3711 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3714 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3715 return (DispAllErrors(henv
, hdbc
, hstmt
));
3717 bool failed
= false;
3718 retcode
= SQLFetch(hstmt
);
3719 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3721 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3724 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3727 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3730 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3733 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3736 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3739 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3744 return(DispAllErrors(henv
, hdbc
, hstmt
));
3746 #ifdef DBDEBUG_CONSOLE
3747 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3748 result
.privilege
,result
.tableOwner
,result
.tableName
,
3749 result
.grantor
, result
.grantee
);
3752 if (UserID
.IsSameAs(result
.tableOwner
,false))
3754 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3758 if (UserID
.IsSameAs(result
.grantee
,false) &&
3759 !wxStrcmp(result
.privilege
,priv
))
3761 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3765 if (!wxStrcmp(result
.grantee
,curRole
) &&
3766 !wxStrcmp(result
.privilege
,priv
))
3768 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3772 retcode
= SQLFetch(hstmt
);
3775 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3778 } // wxDb::TablePrivileges
3781 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3785 if (Dbms() == dbmsACCESS
)
3786 TableName
= _T("\"");
3787 TableName
+= tableName
;
3788 if (Dbms() == dbmsACCESS
)
3789 TableName
+= _T("\"");
3792 } // wxDb::SQLTableName()
3795 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3799 if (Dbms() == dbmsACCESS
)
3802 if (Dbms() == dbmsACCESS
)
3803 ColName
+= _T("\"");
3806 } // wxDb::SQLColumnName()
3809 /********** wxDb::SetSqlLogging() **********/
3810 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3812 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3813 wxASSERT(state
== sqlLogOFF
|| filename
.length());
3815 if (state
== sqlLogON
)
3819 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3820 if (fpSqlLog
== NULL
)
3828 if (fclose(fpSqlLog
))
3834 sqlLogState
= state
;
3837 } // wxDb::SetSqlLogging()
3840 /********** wxDb::WriteSqlLog() **********/
3841 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3843 wxASSERT(logMsg
.length());
3845 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3848 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3850 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3852 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3857 } // wxDb::WriteSqlLog()
3860 /********** wxDb::Dbms() **********/
3861 wxDBMS
wxDb::Dbms(void)
3863 * Be aware that not all database engines use the exact same syntax, and not
3864 * every ODBC compliant database is compliant to the same level of compliancy.
3865 * Some manufacturers support the minimum Level 1 compliancy, and others up
3866 * through Level 3. Others support subsets of features for levels above 1.
3868 * If you find an inconsistency between the wxDb class and a specific database
3869 * engine, and an identifier to this section, and special handle the database in
3870 * the area where behavior is non-conforming with the other databases.
3873 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3874 * ---------------------------------------------------
3877 * - Currently the only database supported by the class to support VIEWS
3880 * - Does not support the SQL_TIMESTAMP structure
3881 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3882 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3883 * is true. The user must create ALL indexes from their program.
3884 * - Table names can only be 8 characters long
3885 * - Column names can only be 10 characters long
3888 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3889 * after every table name involved in the query/join if that tables matching record(s)
3891 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3893 * SYBASE (Enterprise)
3894 * - If a column is part of the Primary Key, the column cannot be NULL
3895 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3898 * - If a column is part of the Primary Key, the column cannot be NULL
3899 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3900 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3901 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3902 * column definition if it is not defined correctly, but it is experimental
3903 * - Does not support sub-queries in SQL statements
3906 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3907 * - Does not support sub-queries in SQL statements
3910 * - Primary keys must be declared as NOT NULL
3911 * - Table and index names must not be longer than 13 characters in length (technically
3912 * table names can be up to 18 characters, but the primary index is created using the
3913 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3918 * - Columns that are part of primary keys must be defined as being NOT NULL
3919 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3920 * column definition if it is not defined correctly, but it is experimental
3923 // Should only need to do this once for each new database connection
3924 // so return the value we already determined it to be to save time
3925 // and lots of string comparisons
3926 if (dbmsType
!= dbmsUNIDENTIFIED
)
3929 #ifdef DBDEBUG_CONSOLE
3930 // When run in console mode, use standard out to display errors.
3931 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3932 #endif // DBDEBUG_CONSOLE
3934 wxLogDebug(wxT("Database connecting to: "));
3935 wxLogDebug(dbInf
.dbmsName
);
3937 wxChar baseName
[25+1];
3938 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3941 // RGG 20001025 : add support for Interbase
3942 // GT : Integrated to base classes on 20001121
3943 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3944 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3946 // BJO 20000428 : add support for Virtuoso
3947 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3948 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3950 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3951 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3953 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3954 // connected through an OpenLink driver.
3955 // Is it also returned by Sybase Adapatitve server?
3956 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3957 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3959 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3960 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3961 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3963 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3966 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3967 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3970 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3971 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3974 if (!wxStricmp(baseName
,wxT("Pervasive")))
3975 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3978 if (!wxStricmp(baseName
,wxT("Informix")))
3979 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3981 if (!wxStricmp(baseName
,wxT("Firebird")))
3982 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3985 if (!wxStricmp(baseName
,wxT("Oracle")))
3986 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3987 if (!wxStricmp(baseName
,wxT("ACCESS")))
3988 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3989 if (!wxStricmp(baseName
,wxT("Sybase")))
3990 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3993 if (!wxStricmp(baseName
,wxT("DBASE")))
3994 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3995 if (!wxStricmp(baseName
,wxT("xBase")))
3996 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3997 if (!wxStricmp(baseName
,wxT("MySQL")))
3998 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3999 if (!wxStricmp(baseName
,wxT("MaxDB")))
4000 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
4003 if (!wxStricmp(baseName
,wxT("DB2")))
4004 return((wxDBMS
)(dbmsType
= dbmsDB2
));
4006 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
4011 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
4012 int dataType
, ULONG columnLength
,
4013 const wxString
&optionalParam
)
4015 wxASSERT(tableName
.length());
4016 wxASSERT(columnName
.length());
4017 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
4018 dataType
!= DB_DATA_TYPE_VARCHAR
);
4020 // Must specify a columnLength if modifying a VARCHAR type column
4021 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
4024 wxString dataTypeName
;
4026 wxString alterSlashModify
;
4030 case DB_DATA_TYPE_VARCHAR
:
4031 dataTypeName
= typeInfVarchar
.TypeName
;
4033 case DB_DATA_TYPE_INTEGER
:
4034 dataTypeName
= typeInfInteger
.TypeName
;
4036 case DB_DATA_TYPE_FLOAT
:
4037 dataTypeName
= typeInfFloat
.TypeName
;
4039 case DB_DATA_TYPE_DATE
:
4040 dataTypeName
= typeInfDate
.TypeName
;
4042 case DB_DATA_TYPE_BLOB
:
4043 dataTypeName
= typeInfBlob
.TypeName
;
4049 // Set the modify or alter syntax depending on the type of database connected to
4053 alterSlashModify
= _T("MODIFY");
4055 case dbmsMS_SQL_SERVER
:
4056 alterSlashModify
= _T("ALTER COLUMN");
4058 case dbmsUNIDENTIFIED
:
4060 case dbmsSYBASE_ASA
:
4061 case dbmsSYBASE_ASE
:
4066 case dbmsXBASE_SEQUITER
:
4068 alterSlashModify
= _T("MODIFY");
4072 // create the SQL statement
4073 if ( Dbms() == dbmsMY_SQL
)
4075 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4076 columnName
.c_str(), dataTypeName
.c_str());
4080 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4081 columnName
.c_str(), dataTypeName
.c_str());
4084 // For varchars only, append the size of the column
4085 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4086 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4089 s
.Printf(wxT("(%lu)"), columnLength
);
4093 // for passing things like "NOT NULL"
4094 if (optionalParam
.length())
4096 sqlStmt
+= wxT(" ");
4097 sqlStmt
+= optionalParam
;
4100 return ExecSql(sqlStmt
);
4102 } // wxDb::ModifyColumn()
4104 /********** wxDb::EscapeSqlChars() **********/
4105 wxString
wxDb::EscapeSqlChars(const wxString
& valueOrig
)
4107 wxString
value(valueOrig
);
4111 // Access doesn't seem to care about backslashes, so only escape single quotes.
4112 value
.Replace(wxT("'"), wxT("''"));
4116 // All the others are supposed to be the same for now, add special
4117 // handling for them if necessary
4118 value
.Replace(wxT("\\"), wxT("\\\\"));
4119 value
.Replace(wxT("'"), wxT("\\'"));
4124 } // wxDb::EscapeSqlChars()
4127 /********** wxDbGetConnection() **********/
4128 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4132 // Used to keep a pointer to a DB connection that matches the requested
4133 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4134 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4135 // rather than having to re-query the datasource to get all the values
4136 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4137 wxDb
*matchingDbConnection
= NULL
;
4139 // Scan the linked list searching for an available database connection
4140 // that's already been opened but is currently not in use.
4141 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4143 // The database connection must be for the same datasource
4144 // name and must currently not be in use.
4146 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4148 if (pDbConfig
->UseConnectionStr())
4150 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4151 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4153 // Found a free connection
4154 pList
->Free
= false;
4155 return(pList
->PtrDb
);
4160 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4161 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4163 // Found a free connection
4164 pList
->Free
= false;
4165 return(pList
->PtrDb
);
4170 if (pDbConfig
->UseConnectionStr())
4172 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4173 matchingDbConnection
= pList
->PtrDb
;
4177 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4178 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4179 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4180 matchingDbConnection
= pList
->PtrDb
;
4184 // No available connections. A new connection must be made and
4185 // appended to the end of the linked list.
4188 // Find the end of the list
4189 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4190 // Append a new list item
4191 pList
->PtrNext
= new wxDbList
;
4192 pList
->PtrNext
->PtrPrev
= pList
;
4193 pList
= pList
->PtrNext
;
4197 // Create the first node on the list
4198 pList
= PtrBegDbList
= new wxDbList
;
4202 // Initialize new node in the linked list
4204 pList
->Free
= false;
4205 pList
->Dsn
= pDbConfig
->GetDsn();
4206 pList
->Uid
= pDbConfig
->GetUserID();
4207 pList
->AuthStr
= pDbConfig
->GetPassword();
4208 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4210 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4214 if (!matchingDbConnection
)
4216 if (pDbConfig
->UseConnectionStr())
4218 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4222 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4226 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4228 // Connect to the datasource
4231 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4232 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4233 return(pList
->PtrDb
);
4235 else // Unable to connect, destroy list item
4238 pList
->PtrPrev
->PtrNext
= 0;
4240 PtrBegDbList
= 0; // Empty list again
4242 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4243 pList
->PtrDb
->Close(); // Close the wxDb object
4244 delete pList
->PtrDb
; // Deletes the wxDb object
4245 delete pList
; // Deletes the linked list object
4249 } // wxDbGetConnection()
4252 /********** wxDbFreeConnection() **********/
4253 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4257 // Scan the linked list searching for the database connection
4258 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4260 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4261 return (pList
->Free
= true);
4264 // Never found the database object, return failure
4267 } // wxDbFreeConnection()
4270 /********** wxDbCloseConnections() **********/
4271 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4273 wxDbList
*pList
, *pNext
;
4275 // Traverse the linked list closing database connections and freeing memory as I go.
4276 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4278 pNext
= pList
->PtrNext
; // Save the pointer to next
4279 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4280 pList
->PtrDb
->Close(); // Close the wxDb object
4281 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4282 delete pList
->PtrDb
; // Deletes the wxDb object
4283 delete pList
; // Deletes the linked list object
4286 // Mark the list as empty
4289 } // wxDbCloseConnections()
4292 /********** wxDbConnectionsInUse() **********/
4293 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4298 // Scan the linked list counting db connections that are currently in use
4299 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4301 if (pList
->Free
== false)
4307 } // wxDbConnectionsInUse()
4311 /********** wxDbLogExtendedErrorMsg() **********/
4312 // DEBUG ONLY function
4313 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4315 const wxChar
*ErrFile
,
4318 static wxString msg
;
4323 if (ErrFile
|| ErrLine
)
4325 msg
+= wxT("File: ");
4327 msg
+= wxT(" Line: ");
4328 tStr
.Printf(wxT("%d"),ErrLine
);
4329 msg
+= tStr
.c_str();
4333 msg
.Append (wxT("\nODBC errors:\n"));
4336 // Display errors for this connection
4338 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4340 if (pDb
->errorList
[i
])
4342 msg
.Append(pDb
->errorList
[i
]);
4343 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4344 msg
.Append(wxT("\n"));
4345 // Clear the errmsg buffer so the next error will not
4346 // end up showing the previous error that have occurred
4347 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4352 wxLogDebug(msg
.c_str());
4355 } // wxDbLogExtendedErrorMsg()
4358 /********** wxDbSqlLog() **********/
4359 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4361 bool append
= false;
4364 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4366 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4371 SQLLOGstate
= state
;
4372 SQLLOGfn
= filename
;
4380 /********** wxDbCreateDataSource() **********/
4381 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4382 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4384 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4385 * Very rudimentary creation of an ODBC data source.
4387 * ODBC driver must be ODBC 3.0 compliant to use this function
4392 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4398 dsnLocation
= ODBC_ADD_SYS_DSN
;
4400 dsnLocation
= ODBC_ADD_DSN
;
4402 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4403 // so that is why I used it, as wxString does not deal well with
4404 // embedded nulls in strings
4405 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4407 // Replace the separator from above with the '\0' separator needed
4408 // by the SQLConfigDataSource() function
4412 k
= setupStr
.Find((wxChar
)2,true);
4413 if (k
!= wxNOT_FOUND
)
4414 setupStr
[(UINT
)k
] = wxT('\0');
4416 while (k
!= wxNOT_FOUND
);
4418 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4419 driverName
, setupStr
.c_str());
4421 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4423 // check for errors caused by ConfigDSN based functions
4426 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4427 errMsg
[0] = wxT('\0');
4429 // This function is only supported in ODBC drivers v3.0 compliant and above
4430 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4433 #ifdef DBDEBUG_CONSOLE
4434 // When run in console mode, use standard out to display errors.
4435 cout
<< errMsg
<< endl
;
4436 cout
<< wxT("Press any key to continue...") << endl
;
4438 #endif // DBDEBUG_CONSOLE
4441 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4442 #endif // __WXDEBUG__
4448 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4449 // necessary to use this function, so this function is not supported
4451 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4454 #endif // __VISUALC__
4458 } // wxDbCreateDataSource()
4462 /********** wxDbGetDataSource() **********/
4463 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4464 SWORD DsDescMaxLength
, UWORD direction
)
4466 * Dsn and DsDesc will contain the data source name and data source
4467 * description upon return
4471 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4472 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4474 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4475 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4480 } // wxDbGetDataSource()
4483 // Change this to 0 to remove use of all deprecated functions
4484 #if wxODBC_BACKWARD_COMPATABILITY
4485 /********************************************************************
4486 ********************************************************************
4488 * The following functions are all DEPRECATED and are included for
4489 * backward compatibility reasons only
4491 ********************************************************************
4492 ********************************************************************/
4493 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4495 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4497 /***** DEPRECATED: use wxGetDataSource() *****/
4498 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4501 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4503 /***** DEPRECATED: use wxDbGetConnection() *****/
4504 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4506 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4508 /***** DEPRECATED: use wxDbFreeConnection() *****/
4509 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4511 return wxDbFreeConnection(pDb
);
4513 /***** DEPRECATED: use wxDbCloseConnections() *****/
4514 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4516 wxDbCloseConnections();
4518 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4519 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4521 return wxDbConnectionsInUse();