1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/db.cpp
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
24 #include "wx/wxprec.h"
33 #include "wx/object.h"
35 #include "wx/string.h"
41 #ifdef DBDEBUG_CONSOLE
42 #include "wx/ioswrap.h"
45 #include "wx/filefn.h"
46 #include "wx/wxchar.h"
56 // DLL options compatibility check:
57 WX_CHECK_BUILD_OPTIONS("wxODBC")
59 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
61 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
62 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
65 extern wxList TablesInUse
;
68 // SQL Log defaults to be used by GetDbConnection
69 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
71 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
73 // The wxDb::errorList is copied to this variable when the wxDb object
74 // is closed. This way, the error list is still available after the
75 // database object is closed. This is necessary if the database
76 // connection fails so the calling application can show the operator
77 // why the connection failed. Note: as each wxDb object is closed, it
78 // will overwrite the errors of the previously destroyed wxDb object in
79 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
81 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
84 // This type defines the return row-struct form
85 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
88 wxChar tableQual
[128+1];
89 wxChar tableOwner
[128+1];
90 wxChar tableName
[128+1];
91 wxChar grantor
[128+1];
92 wxChar grantee
[128+1];
93 wxChar privilege
[128+1];
94 wxChar grantable
[3+1];
95 } wxDbTablePrivilegeInfo
;
98 /********** wxDbConnectInf Constructor - form 1 **********/
99 wxDbConnectInf::wxDbConnectInf()
102 freeHenvOnDestroy
= false;
108 /********** wxDbConnectInf Constructor - form 2 **********/
109 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
110 const wxString
&password
, const wxString
&defaultDir
,
111 const wxString
&fileType
, const wxString
&description
)
114 freeHenvOnDestroy
= false;
125 SetPassword(password
);
126 SetDescription(description
);
127 SetFileType(fileType
);
128 SetDefaultDir(defaultDir
);
129 } // wxDbConnectInf Constructor
132 wxDbConnectInf::~wxDbConnectInf()
134 if (freeHenvOnDestroy
)
138 } // wxDbConnectInf Destructor
142 /********** wxDbConnectInf::Initialize() **********/
143 bool wxDbConnectInf::Initialize()
145 freeHenvOnDestroy
= false;
147 if (freeHenvOnDestroy
&& Henv
)
154 ConnectionStr
[0] = 0;
159 useConnectionStr
= false;
162 } // wxDbConnectInf::Initialize()
165 /********** wxDbConnectInf::AllocHenv() **********/
166 bool wxDbConnectInf::AllocHenv()
168 // This is here to help trap if you are getting a new henv
169 // without releasing an existing henv
172 // Initialize the ODBC Environment for Database Operations
173 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
175 wxLogDebug(wxT("A problem occurred while trying to get a connection to the data source"));
179 freeHenvOnDestroy
= true;
182 } // wxDbConnectInf::AllocHenv()
185 void wxDbConnectInf::FreeHenv()
193 freeHenvOnDestroy
= false;
195 } // wxDbConnectInf::FreeHenv()
198 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
200 wxASSERT(dsn
.length() < WXSIZEOF(Dsn
));
202 wxStrncpy(Dsn
, dsn
, WXSIZEOF(Dsn
)-1);
203 Dsn
[WXSIZEOF(Dsn
)-1] = 0; // Prevent buffer overrun
204 } // wxDbConnectInf::SetDsn()
207 void wxDbConnectInf::SetUserID(const wxString
&uid
)
209 wxASSERT(uid
.length() < WXSIZEOF(Uid
));
210 wxStrncpy(Uid
, uid
, WXSIZEOF(Uid
)-1);
211 Uid
[WXSIZEOF(Uid
)-1] = 0; // Prevent buffer overrun
212 } // wxDbConnectInf::SetUserID()
215 void wxDbConnectInf::SetPassword(const wxString
&password
)
217 wxASSERT(password
.length() < WXSIZEOF(AuthStr
));
219 wxStrncpy(AuthStr
, password
, WXSIZEOF(AuthStr
)-1);
220 AuthStr
[WXSIZEOF(AuthStr
)-1] = 0; // Prevent buffer overrun
221 } // wxDbConnectInf::SetPassword()
223 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
225 wxASSERT(connectStr
.length() < WXSIZEOF(ConnectionStr
));
227 useConnectionStr
= wxStrlen(connectStr
) > 0;
229 wxStrncpy(ConnectionStr
, connectStr
, WXSIZEOF(ConnectionStr
)-1);
230 ConnectionStr
[WXSIZEOF(ConnectionStr
)-1] = 0; // Prevent buffer overrun
231 } // wxDbConnectInf::SetConnectionStr()
234 /********** wxDbColFor Constructor **********/
235 wxDbColFor::wxDbColFor()
238 } // wxDbColFor::wxDbColFor()
241 /********** wxDbColFor::Initialize() **********/
242 void wxDbColFor::Initialize()
252 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
255 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
256 } // wxDbColFor::Initialize()
259 /********** wxDbColFor::Format() **********/
260 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
261 short columnLength
, short decimalDigits
)
263 // ----------------------------------------------------------------------------------------
264 // -- 19991224 : mj10777 : Create
265 // There is still a lot of work to do here, but it is a start
266 // It handles all the basic data-types that I have run into up to now
267 // The main work will have be with Dates and float Formatting
268 // (US 1,000.00 ; EU 1.000,00)
269 // There are wxWindow plans for locale support and the new wxDateTime. If
270 // they define some constants (wxEUROPEAN) that can be gloably used,
271 // they should be used here.
272 // ----------------------------------------------------------------------------------------
273 // There should also be a function to scan in a string to fill the variable
274 // ----------------------------------------------------------------------------------------
276 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
277 i_dbDataType
= dbDataType
;
278 i_sqlDataType
= sqlDataType
;
279 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
281 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
283 if ((i_sqlDataType
== SQL_VARCHAR
)
285 #if defined(SQL_WCHAR)
286 || (i_sqlDataType
== SQL_WCHAR
)
288 #if defined(SQL_WVARCHAR)
289 || (i_sqlDataType
== SQL_WVARCHAR
)
292 || (i_sqlDataType
== SQL_LONGVARCHAR
))
293 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
294 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
295 i_dbDataType
= DB_DATA_TYPE_DATE
;
296 if (i_sqlDataType
== SQL_C_BIT
)
297 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
298 if (i_sqlDataType
== SQL_NUMERIC
)
299 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
300 if (i_sqlDataType
== SQL_REAL
)
301 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
302 if (i_sqlDataType
== SQL_C_BINARY
)
303 i_dbDataType
= DB_DATA_TYPE_BLOB
;
306 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
308 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
311 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
313 case DB_DATA_TYPE_VARCHAR
:
316 case DB_DATA_TYPE_INTEGER
:
319 case DB_DATA_TYPE_FLOAT
:
320 if (decimalDigits
== 0)
322 tempStr
.Printf(wxT("%%%d.%d"), columnLength
, decimalDigits
);
323 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
325 case DB_DATA_TYPE_DATE
:
326 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
328 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
330 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
332 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
334 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
336 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
338 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
340 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
342 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
344 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
347 case DB_DATA_TYPE_BLOB
:
348 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
351 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
355 } // wxDbColFor::Format()
358 /********** wxDbColInf Constructor **********/
359 wxDbColInf::wxDbColInf()
362 } // wxDbColInf::wxDbColInf()
365 /********** wxDbColInf Destructor ********/
366 wxDbColInf::~wxDbColInf()
371 } // wxDbColInf::~wxDbColInf()
374 bool wxDbColInf::Initialize()
396 } // wxDbColInf::Initialize()
399 /********** wxDbTableInf Constructor ********/
400 wxDbTableInf::wxDbTableInf()
403 } // wxDbTableInf::wxDbTableInf()
406 /********** wxDbTableInf Constructor ********/
407 wxDbTableInf::~wxDbTableInf()
412 } // wxDbTableInf::~wxDbTableInf()
415 bool wxDbTableInf::Initialize()
424 } // wxDbTableInf::Initialize()
427 /********** wxDbInf Constructor *************/
431 } // wxDbInf::wxDbInf()
434 /********** wxDbInf Destructor *************/
440 } // wxDbInf::~wxDbInf()
443 /********** wxDbInf::Initialize() *************/
444 bool wxDbInf::Initialize()
452 } // wxDbInf::Initialize()
455 /********** wxDb Constructor **********/
456 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
458 // Copy the HENV into the db class
460 fwdOnlyCursors
= FwdOnlyCursors
;
466 /********** wxDb Destructor **********/
469 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
479 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
480 /********** wxDb::initialize() **********/
481 void wxDb::initialize()
483 * Private member function that sets all wxDb member variables to
484 * known values at creation of the wxDb
489 fpSqlLog
= 0; // Sql Log file pointer
490 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
492 dbmsType
= dbmsUNIDENTIFIED
;
494 wxStrcpy(sqlState
,wxEmptyString
);
495 wxStrcpy(errorMsg
,wxEmptyString
);
496 nativeError
= cbErrorMsg
= 0;
497 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
498 wxStrcpy(errorList
[i
], wxEmptyString
);
500 // Init typeInf structures
501 typeInfVarchar
.TypeName
.Empty();
502 typeInfVarchar
.FsqlType
= 0;
503 typeInfVarchar
.Precision
= 0;
504 typeInfVarchar
.CaseSensitive
= 0;
505 typeInfVarchar
.MaximumScale
= 0;
507 typeInfInteger
.TypeName
.Empty();
508 typeInfInteger
.FsqlType
= 0;
509 typeInfInteger
.Precision
= 0;
510 typeInfInteger
.CaseSensitive
= 0;
511 typeInfInteger
.MaximumScale
= 0;
513 typeInfFloat
.TypeName
.Empty();
514 typeInfFloat
.FsqlType
= 0;
515 typeInfFloat
.Precision
= 0;
516 typeInfFloat
.CaseSensitive
= 0;
517 typeInfFloat
.MaximumScale
= 0;
519 typeInfDate
.TypeName
.Empty();
520 typeInfDate
.FsqlType
= 0;
521 typeInfDate
.Precision
= 0;
522 typeInfDate
.CaseSensitive
= 0;
523 typeInfDate
.MaximumScale
= 0;
525 typeInfBlob
.TypeName
.Empty();
526 typeInfBlob
.FsqlType
= 0;
527 typeInfBlob
.Precision
= 0;
528 typeInfBlob
.CaseSensitive
= 0;
529 typeInfBlob
.MaximumScale
= 0;
531 typeInfMemo
.TypeName
.Empty();
532 typeInfMemo
.FsqlType
= 0;
533 typeInfMemo
.Precision
= 0;
534 typeInfMemo
.CaseSensitive
= 0;
535 typeInfMemo
.MaximumScale
= 0;
537 // Error reporting is turned OFF by default
540 // Allocate a data source connection handle
541 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
544 // Initialize the db status flag
547 // Mark database as not open as of yet
550 dbOpenedWithConnectionString
= false;
551 } // wxDb::initialize()
554 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
556 // NOTE: Return value from this function MUST be copied
557 // immediately, as the value is not good after
558 // this function has left scope.
560 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
564 if (!wxStrlen(userID
))
572 // dBase does not use user names, and some drivers fail if you try to pass one
573 if ( Dbms() == dbmsDBASE
574 || Dbms() == dbmsXBASE_SEQUITER
)
577 // Some databases require user names to be specified in uppercase,
578 // so force the name to uppercase
579 if ((Dbms() == dbmsORACLE
) ||
580 (Dbms() == dbmsMAXDB
))
581 UserID
= UserID
.Upper();
583 return UserID
.c_str();
584 } // wxDb::convertUserID()
587 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
591 // These are the possible SQL types we check for use against the datasource we are connected
592 // to for the purpose of determining which data type to use for the basic character strings
595 // NOTE: The first type in this enumeration that is determined to be supported by the
596 // datasource/driver is the one that will be used.
597 SWORD PossibleSqlCharTypes
[] = {
598 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
602 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
608 // These are the possible SQL types we check for use against the datasource we are connected
609 // to for the purpose of determining which data type to use for the basic non-floating point
612 // NOTE: The first type in this enumeration that is determined to be supported by the
613 // datasource/driver is the one that will be used.
614 SWORD PossibleSqlIntegerTypes
[] = {
618 // These are the possible SQL types we check for use against the datasource we are connected
619 // to for the purpose of determining which data type to use for the basic floating point number
622 // NOTE: The first type in this enumeration that is determined to be supported by the
623 // datasource/driver is the one that will be used.
624 SWORD PossibleSqlFloatTypes
[] = {
632 // These are the possible SQL types we check for use agains the datasource we are connected
633 // to for the purpose of determining which data type to use for the date/time column types
635 // NOTE: The first type in this enumeration that is determined to be supported by the
636 // datasource/driver is the one that will be used.
637 SWORD PossibleSqlDateTypes
[] = {
645 // These are the possible SQL types we check for use agains the datasource we are connected
646 // to for the purpose of determining which data type to use for the BLOB column types.
648 // NOTE: The first type in this enumeration that is determined to be supported by the
649 // datasource/driver is the one that will be used.
650 SWORD PossibleSqlBlobTypes
[] = {
655 // These are the possible SQL types we check for use agains the datasource we are connected
656 // to for the purpose of determining which data type to use for the MEMO column types
657 // (a type which allow to store large strings; like VARCHAR just with a bigger precision)
659 // NOTE: The first type in this enumeration that is determined to be supported by the
660 // datasource/driver is the one that will be used.
661 SWORD PossibleSqlMemoTypes
[] = {
666 // Query the data source regarding data type information
669 // The way it was determined which SQL data types to use was by calling SQLGetInfo
670 // for all of the possible SQL data types to see which ones were supported. If
671 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
672 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
673 // types I've selected below will not always be what we want. These are just
674 // what happened to work against an Oracle 7/Intersolv combination. The following is
675 // a complete list of the results I got back against the Oracle 7 database:
677 // SQL_BIGINT SQL_NO_DATA_FOUND
678 // SQL_BINARY SQL_NO_DATA_FOUND
679 // SQL_BIT SQL_NO_DATA_FOUND
680 // SQL_CHAR type name = 'CHAR', Precision = 255
681 // SQL_DATE SQL_NO_DATA_FOUND
682 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
683 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
684 // SQL_FLOAT SQL_NO_DATA_FOUND
685 // SQL_INTEGER SQL_NO_DATA_FOUND
686 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
687 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
688 // SQL_NUMERIC SQL_NO_DATA_FOUND
689 // SQL_REAL SQL_NO_DATA_FOUND
690 // SQL_SMALLINT SQL_NO_DATA_FOUND
691 // SQL_TIME SQL_NO_DATA_FOUND
692 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
693 // SQL_VARBINARY type name = 'RAW', Precision = 255
694 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
695 // =====================================================================
696 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
698 // SQL_VARCHAR type name = 'TEXT', Precision = 255
699 // SQL_TIMESTAMP type name = 'DATETIME'
700 // SQL_DECIMAL SQL_NO_DATA_FOUND
701 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
702 // SQL_FLOAT SQL_NO_DATA_FOUND
703 // SQL_REAL type name = 'SINGLE', Precision = 7
704 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
705 // SQL_INTEGER type name = 'LONG', Precision = 10
707 // Query the data source for info about itself
708 if (!getDbInfo(failOnDataTypeUnsupported
))
711 // --------------- Varchar - (Variable length character string) ---------------
712 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
713 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
716 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
717 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
718 else if (failOnDataTypeUnsupported
)
721 // --------------- Float ---------------
722 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
723 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
726 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
727 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
728 else if (failOnDataTypeUnsupported
)
731 // --------------- Integer -------------
732 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
733 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
736 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
737 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
738 else if (failOnDataTypeUnsupported
)
740 // If no non-floating point data types are supported, we'll
741 // use the type assigned for floats to store integers as well
742 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
744 if (failOnDataTypeUnsupported
)
748 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
751 // --------------- Date/Time ---------------
752 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
753 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
756 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
757 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
758 else if (failOnDataTypeUnsupported
)
761 // --------------- BLOB ---------------
762 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
763 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
766 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
767 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
768 else if (failOnDataTypeUnsupported
)
771 // --------------- MEMO ---------------
772 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlMemoTypes
) &&
773 !getDataTypeInfo(PossibleSqlMemoTypes
[iIndex
], typeInfMemo
); ++iIndex
)
776 if (iIndex
< WXSIZEOF(PossibleSqlMemoTypes
))
777 typeInfMemo
.FsqlType
= PossibleSqlMemoTypes
[iIndex
];
778 else if (failOnDataTypeUnsupported
)
782 } // wxDb::determineDataTypes
785 bool wxDb::open(bool failOnDataTypeUnsupported
)
788 If using Intersolv branded ODBC drivers, this is the place where you would substitute
789 your branded driver license information
791 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
792 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
795 // Mark database as open
798 // Allocate a statement handle for the database connection
799 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
800 return(DispAllErrors(henv
, hdbc
));
802 // Set Connection Options
803 if (!setConnectionOptions())
806 if (!determineDataTypes(failOnDataTypeUnsupported
))
809 #ifdef DBDEBUG_CONSOLE
810 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
811 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
812 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
813 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
814 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
815 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
819 // Completed Successfully
823 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
825 wxASSERT(inConnectStr
.length());
826 return Open(inConnectStr
, NULL
, failOnDataTypeUnsupported
);
829 bool wxDb::Open(const wxString
& inConnectStr
, SQLHWND parentWnd
, bool failOnDataTypeUnsupported
)
833 authStr
= wxEmptyString
;
837 if (!FwdOnlyCursors())
839 // Specify that the ODBC cursor library be used, if needed. This must be
840 // specified before the connection is made.
841 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
843 #ifdef DBDEBUG_CONSOLE
844 if (retcode
== SQL_SUCCESS
)
845 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
847 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
849 wxUnusedVar(retcode
);
853 // Connect to the data source
854 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
855 short outConnectBufferLen
;
857 inConnectionStr
= inConnectStr
;
859 retcode
= SQLDriverConnect(hdbc
, parentWnd
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
860 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
861 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
863 if ((retcode
!= SQL_SUCCESS
) &&
864 (retcode
!= SQL_SUCCESS_WITH_INFO
))
865 return(DispAllErrors(henv
, hdbc
));
867 outConnectBuffer
[outConnectBufferLen
] = 0;
868 outConnectionStr
= outConnectBuffer
;
869 dbOpenedWithConnectionString
= true;
871 return open(failOnDataTypeUnsupported
);
874 /********** wxDb::Open() **********/
875 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
877 wxASSERT(!Dsn
.empty());
882 inConnectionStr
= wxEmptyString
;
883 outConnectionStr
= wxEmptyString
;
887 if (!FwdOnlyCursors())
889 // Specify that the ODBC cursor library be used, if needed. This must be
890 // specified before the connection is made.
891 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
893 #ifdef DBDEBUG_CONSOLE
894 if (retcode
== SQL_SUCCESS
)
895 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
897 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
899 wxUnusedVar( retcode
);
903 // Connect to the data source
904 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
905 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
906 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
908 if ((retcode
!= SQL_SUCCESS
) &&
909 (retcode
!= SQL_SUCCESS_WITH_INFO
))
910 return(DispAllErrors(henv
, hdbc
));
912 return open(failOnDataTypeUnsupported
);
917 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
919 wxASSERT(dbConnectInf
);
921 // Use the connection string if one is present
922 if (dbConnectInf
->UseConnectionStr())
923 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
925 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
926 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
930 bool wxDb::Open(wxDb
*copyDb
)
932 dsn
= copyDb
->GetDatasourceName();
933 uid
= copyDb
->GetUsername();
934 authStr
= copyDb
->GetPassword();
935 inConnectionStr
= copyDb
->GetConnectionInStr();
936 outConnectionStr
= copyDb
->GetConnectionOutStr();
940 if (!FwdOnlyCursors())
942 // Specify that the ODBC cursor library be used, if needed. This must be
943 // specified before the connection is made.
944 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
946 #ifdef DBDEBUG_CONSOLE
947 if (retcode
== SQL_SUCCESS
)
948 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
950 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
952 wxUnusedVar( retcode
);
956 if (copyDb
->OpenedWithConnectionString())
958 // Connect to the data source
959 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
960 short outConnectBufferLen
;
962 inConnectionStr
= copyDb
->GetConnectionInStr();
964 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
965 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
966 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
968 if ((retcode
!= SQL_SUCCESS
) &&
969 (retcode
!= SQL_SUCCESS_WITH_INFO
))
970 return(DispAllErrors(henv
, hdbc
));
972 outConnectBuffer
[outConnectBufferLen
] = 0;
973 outConnectionStr
= outConnectBuffer
;
974 dbOpenedWithConnectionString
= true;
978 // Connect to the data source
979 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
980 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
981 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
984 if ((retcode
!= SQL_SUCCESS
) &&
985 (retcode
!= SQL_SUCCESS_WITH_INFO
))
986 return(DispAllErrors(henv
, hdbc
));
989 If using Intersolv branded ODBC drivers, this is the place where you would substitute
990 your branded driver license information
992 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
993 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
996 // Mark database as open
999 // Allocate a statement handle for the database connection
1000 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
1001 return(DispAllErrors(henv
, hdbc
));
1003 // Set Connection Options
1004 if (!setConnectionOptions())
1007 // Instead of Querying the data source for info about itself, it can just be copied
1008 // from the wxDb instance that was passed in (copyDb).
1009 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
1010 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
1011 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
1012 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
1013 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
1014 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
1015 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
1016 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
1017 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
1018 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
1019 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
1020 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
1021 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
1022 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
1023 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
1024 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1025 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1026 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1027 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1028 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1029 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1030 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1031 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1032 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1033 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1034 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1035 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1036 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1037 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1038 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1039 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1041 // VARCHAR = Variable length character string
1042 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1043 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1044 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1045 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1046 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1049 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1050 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1051 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1052 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1053 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1056 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1057 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1058 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1059 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1060 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1063 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1064 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1065 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1066 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1067 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1070 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1071 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1072 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1073 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1074 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1077 typeInfMemo
.FsqlType
= copyDb
->typeInfMemo
.FsqlType
;
1078 typeInfMemo
.TypeName
= copyDb
->typeInfMemo
.TypeName
;
1079 typeInfMemo
.Precision
= copyDb
->typeInfMemo
.Precision
;
1080 typeInfMemo
.CaseSensitive
= copyDb
->typeInfMemo
.CaseSensitive
;
1081 typeInfMemo
.MaximumScale
= copyDb
->typeInfMemo
.MaximumScale
;
1083 #ifdef DBDEBUG_CONSOLE
1084 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1085 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1086 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1087 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1088 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1089 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
1093 // Completed Successfully
1098 /********** wxDb::setConnectionOptions() **********/
1099 bool wxDb::setConnectionOptions(void)
1101 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1106 // I need to get the DBMS name here, because some of the connection options
1107 // are database specific and need to call the Dbms() function.
1110 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1111 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1112 return(DispAllErrors(henv
, hdbc
));
1114 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1115 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1116 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1118 // By default, MS Sql Server closes cursors on commit and rollback. The following
1119 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1120 // after a transaction. This is a driver specific option and is not part of the
1121 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1122 // The database settings don't have any effect one way or the other.
1123 if (Dbms() == dbmsMS_SQL_SERVER
)
1125 const long SQL_PRESERVE_CURSORS
= 1204L;
1126 const long SQL_PC_ON
= 1L;
1127 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1130 // Display the connection options to verify them
1131 #ifdef DBDEBUG_CONSOLE
1133 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1135 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1136 if (retcode
!= SQL_SUCCESS
)
1137 return(DispAllErrors(henv
, hdbc
));
1138 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1140 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1141 if (retcode
!= SQL_SUCCESS
)
1142 return(DispAllErrors(henv
, hdbc
));
1143 cout
<< wxT("ODBC CURSORS: ");
1146 case(SQL_CUR_USE_IF_NEEDED
):
1147 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1149 case(SQL_CUR_USE_ODBC
):
1150 cout
<< wxT("SQL_CUR_USE_ODBC");
1152 case(SQL_CUR_USE_DRIVER
):
1153 cout
<< wxT("SQL_CUR_USE_DRIVER");
1158 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1159 if (retcode
!= SQL_SUCCESS
)
1160 return(DispAllErrors(henv
, hdbc
));
1161 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1166 // Completed Successfully
1169 } // wxDb::setConnectionOptions()
1172 /********** wxDb::getDbInfo() **********/
1173 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1178 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1179 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1181 DispAllErrors(henv
, hdbc
);
1182 if (failOnDataTypeUnsupported
)
1186 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1187 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1189 DispAllErrors(henv
, hdbc
);
1190 if (failOnDataTypeUnsupported
)
1194 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1195 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1197 DispAllErrors(henv
, hdbc
);
1198 if (failOnDataTypeUnsupported
)
1203 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1204 // causing database connectivity to fail in some cases.
1205 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1206 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1208 DispAllErrors(henv
, hdbc
);
1209 if (failOnDataTypeUnsupported
)
1213 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1214 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1216 DispAllErrors(henv
, hdbc
);
1217 if (failOnDataTypeUnsupported
)
1221 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1222 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1224 DispAllErrors(henv
, hdbc
);
1225 if (failOnDataTypeUnsupported
)
1229 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1230 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1232 DispAllErrors(henv
, hdbc
);
1233 if (failOnDataTypeUnsupported
)
1237 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1238 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1240 DispAllErrors(henv
, hdbc
);
1241 if (failOnDataTypeUnsupported
)
1245 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1246 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1248 DispAllErrors(henv
, hdbc
);
1249 if (failOnDataTypeUnsupported
)
1253 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1254 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1256 DispAllErrors(henv
, hdbc
);
1257 if (failOnDataTypeUnsupported
)
1261 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1262 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1264 DispAllErrors(henv
, hdbc
);
1265 if (failOnDataTypeUnsupported
)
1269 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1270 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1272 // Not all drivers support this call - Nick Gorham(unixODBC)
1273 dbInf
.cliConfLvl
= 0;
1274 DispAllErrors(henv
, hdbc
);
1275 if (failOnDataTypeUnsupported
)
1279 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1280 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1282 DispAllErrors(henv
, hdbc
);
1283 if (failOnDataTypeUnsupported
)
1287 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1288 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1290 DispAllErrors(henv
, hdbc
);
1291 if (failOnDataTypeUnsupported
)
1295 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1296 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1298 DispAllErrors(henv
, hdbc
);
1299 if (failOnDataTypeUnsupported
)
1303 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1304 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1306 DispAllErrors(henv
, hdbc
);
1307 if (failOnDataTypeUnsupported
)
1311 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1312 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1314 DispAllErrors(henv
, hdbc
);
1315 if (failOnDataTypeUnsupported
)
1319 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1320 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1322 DispAllErrors(henv
, hdbc
);
1323 if (failOnDataTypeUnsupported
)
1327 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1328 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1330 DispAllErrors(henv
, hdbc
);
1331 if (failOnDataTypeUnsupported
)
1335 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1336 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1338 DispAllErrors(henv
, hdbc
);
1339 if (failOnDataTypeUnsupported
)
1343 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1344 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1346 DispAllErrors(henv
, hdbc
);
1347 if (failOnDataTypeUnsupported
)
1351 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1352 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1354 DispAllErrors(henv
, hdbc
);
1355 if (failOnDataTypeUnsupported
)
1359 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1360 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1362 DispAllErrors(henv
, hdbc
);
1363 if (failOnDataTypeUnsupported
)
1367 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1368 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1370 DispAllErrors(henv
, hdbc
);
1371 if (failOnDataTypeUnsupported
)
1375 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1376 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1378 DispAllErrors(henv
, hdbc
);
1379 if (failOnDataTypeUnsupported
)
1383 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1384 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1386 DispAllErrors(henv
, hdbc
);
1387 if (failOnDataTypeUnsupported
)
1391 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1392 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1394 DispAllErrors(henv
, hdbc
);
1395 if (failOnDataTypeUnsupported
)
1399 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1400 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1402 DispAllErrors(henv
, hdbc
);
1403 if (failOnDataTypeUnsupported
)
1407 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1408 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1410 DispAllErrors(henv
, hdbc
);
1411 if (failOnDataTypeUnsupported
)
1415 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1416 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1418 DispAllErrors(henv
, hdbc
);
1419 if (failOnDataTypeUnsupported
)
1423 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1424 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1426 DispAllErrors(henv
, hdbc
);
1427 if (failOnDataTypeUnsupported
)
1431 #ifdef DBDEBUG_CONSOLE
1432 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1433 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1434 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1435 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1437 cout
<< wxT("API Conf. Level: ");
1438 switch(dbInf
.apiConfLvl
)
1440 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1441 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1442 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1446 cout
<< wxT("SAG CLI Conf. Level: ");
1447 switch(dbInf
.cliConfLvl
)
1449 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1450 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1454 cout
<< wxT("SQL Conf. Level: ");
1455 switch(dbInf
.sqlConfLvl
)
1457 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1458 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1459 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1463 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1464 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1465 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1466 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1467 cout
<< wxT("Cursor COMMIT Behavior: ");
1468 switch(dbInf
.cursorCommitBehavior
)
1470 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1471 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1472 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1476 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1477 switch(dbInf
.cursorRollbackBehavior
)
1479 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1480 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1481 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1485 cout
<< wxT("Support NOT NULL clause: ");
1486 switch(dbInf
.supportNotNullClause
)
1488 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1489 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1493 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1494 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1496 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1499 cout
<< wxT("Default Transaction Isolation: ";
1500 switch(dbInf
.txnIsolation
)
1502 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1503 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1504 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1505 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1507 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1512 cout
<< wxT("Transaction Isolation Options: ");
1513 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1514 cout
<< wxT("Read Uncommitted, ");
1515 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1516 cout
<< wxT("Read Committed, ");
1517 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1518 cout
<< wxT("Repeatable Read, ");
1519 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1520 cout
<< wxT("Serializable, ");
1522 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1523 cout
<< wxT("Versioning");
1527 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1528 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1529 cout
<< wxT("Next, ");
1530 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1531 cout
<< wxT("Prev, ");
1532 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1533 cout
<< wxT("First, ");
1534 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1535 cout
<< wxT("Last, ");
1536 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1537 cout
<< wxT("Absolute, ");
1538 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1539 cout
<< wxT("Relative, ");
1541 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1542 cout
<< wxT("Resume, ");
1544 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1545 cout
<< wxT("Bookmark");
1548 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1549 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1550 cout
<< wxT("No Change, ");
1551 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1552 cout
<< wxT("Exclusive, ");
1553 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1554 cout
<< wxT("UnLock");
1557 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1558 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1559 cout
<< wxT("Position, ");
1560 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1561 cout
<< wxT("Refresh, ");
1562 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1563 cout
<< wxT("Upd, "));
1564 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1565 cout
<< wxT("Del, ");
1566 if (dbInf
.posOperations
& SQL_POS_ADD
)
1570 cout
<< wxT("Positioned Statements Supported: ");
1571 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1572 cout
<< wxT("Pos delete, ");
1573 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1574 cout
<< wxT("Pos update, ");
1575 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1576 cout
<< wxT("Select for update");
1579 cout
<< wxT("Scroll Concurrency: ");
1580 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1581 cout
<< wxT("Read Only, ");
1582 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1583 cout
<< wxT("Lock, ");
1584 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1585 cout
<< wxT("Opt. Rowver, ");
1586 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1587 cout
<< wxT("Opt. Values");
1590 cout
<< wxT("Scroll Options: ");
1591 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1592 cout
<< wxT("Fwd Only, ");
1593 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1594 cout
<< wxT("Static, ");
1595 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1596 cout
<< wxT("Keyset Driven, ");
1597 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1598 cout
<< wxT("Dynamic, ");
1599 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1600 cout
<< wxT("Mixed");
1603 cout
<< wxT("Static Sensitivity: ");
1604 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1605 cout
<< wxT("Additions, ");
1606 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1607 cout
<< wxT("Deletions, ");
1608 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1609 cout
<< wxT("Updates");
1612 cout
<< wxT("Transaction Capable?: ");
1613 switch(dbInf
.txnCapable
)
1615 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1616 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1617 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1618 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1619 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1626 // Completed Successfully
1629 } // wxDb::getDbInfo()
1632 /********** wxDb::getDataTypeInfo() **********/
1633 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1636 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1637 * the data type inf. is gathered for.
1639 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1644 // Get information about the data type specified
1645 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1646 return(DispAllErrors(henv
, hdbc
, hstmt
));
1649 retcode
= SQLFetch(hstmt
);
1650 if (retcode
!= SQL_SUCCESS
)
1652 #ifdef DBDEBUG_CONSOLE
1653 if (retcode
== SQL_NO_DATA_FOUND
)
1654 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1656 DispAllErrors(henv
, hdbc
, hstmt
);
1657 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1661 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1663 // Obtain columns from the record
1664 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1665 return(DispAllErrors(henv
, hdbc
, hstmt
));
1667 structSQLTypeInfo
.TypeName
= typeName
;
1669 // BJO 20000503: no more needed with new GetColumns...
1672 if (Dbms() == dbmsMY_SQL
)
1674 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1675 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1676 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1677 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1678 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1679 structSQLTypeInfo
.TypeName
= wxT("int");
1680 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1681 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1682 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1683 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1684 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1685 structSQLTypeInfo
.TypeName
= wxT("char");
1688 // BJO 20000427 : OpenLink driver
1689 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1690 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1692 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1693 structSQLTypeInfo
.TypeName
= wxT("real");
1697 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1698 return(DispAllErrors(henv
, hdbc
, hstmt
));
1699 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1700 return(DispAllErrors(henv
, hdbc
, hstmt
));
1701 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1702 // return(DispAllErrors(henv, hdbc, hstmt));
1704 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1705 return(DispAllErrors(henv
, hdbc
, hstmt
));
1707 if (structSQLTypeInfo
.MaximumScale
< 0)
1708 structSQLTypeInfo
.MaximumScale
= 0;
1710 // Close the statement handle which closes open cursors
1711 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1712 return(DispAllErrors(henv
, hdbc
, hstmt
));
1714 // Completed Successfully
1717 } // wxDb::getDataTypeInfo()
1720 /********** wxDb::Close() **********/
1721 void wxDb::Close(void)
1723 // Close the Sql Log file
1730 // Free statement handle
1733 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1734 DispAllErrors(henv
, hdbc
);
1737 // Disconnect from the datasource
1738 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1739 DispAllErrors(henv
, hdbc
);
1741 // Free the connection to the datasource
1742 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1743 DispAllErrors(henv
, hdbc
);
1745 // There should be zero Ctable objects still connected to this db object
1746 wxASSERT(nTables
== 0);
1750 wxList::compatibility_iterator pNode
;
1751 pNode
= TablesInUse
.GetFirst();
1755 tiu
= (wxTablesInUse
*)pNode
->GetData();
1756 if (tiu
->pDb
== this)
1758 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"),
1759 tiu
->tableName
, tiu
->tableID
, wx_static_cast(void*, tiu
->pDb
));
1760 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"), wx_static_cast(void*, this));
1761 wxLogDebug(s
.c_str(),s2
.c_str());
1763 pNode
= pNode
->GetNext();
1767 // Copy the error messages to a global variable
1769 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1770 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1772 dbmsType
= dbmsUNIDENTIFIED
;
1778 /********** wxDb::CommitTrans() **********/
1779 bool wxDb::CommitTrans(void)
1783 // Commit the transaction
1784 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1785 return(DispAllErrors(henv
, hdbc
));
1788 // Completed successfully
1791 } // wxDb::CommitTrans()
1794 /********** wxDb::RollbackTrans() **********/
1795 bool wxDb::RollbackTrans(void)
1797 // Rollback the transaction
1798 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1799 return(DispAllErrors(henv
, hdbc
));
1801 // Completed successfully
1804 } // wxDb::RollbackTrans()
1807 /********** wxDb::DispAllErrors() **********/
1808 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1810 * This function is called internally whenever an error condition prevents the user's
1811 * request from being executed. This function will query the datasource as to the
1812 * actual error(s) that just occurred on the previous request of the datasource.
1814 * The function will retrieve each error condition from the datasource and
1815 * Printf the codes/text values into a string which it then logs via logError().
1816 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1817 * window and program execution will be paused until the user presses a key.
1819 * This function always returns false, so that functions which call this function
1820 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1821 * of the user's request, so that the calling code can then process the error message log.
1824 wxString odbcErrMsg
;
1826 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1828 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1829 logError(odbcErrMsg
, sqlState
);
1832 #ifdef DBDEBUG_CONSOLE
1833 // When run in console mode, use standard out to display errors.
1834 cout
<< odbcErrMsg
.c_str() << endl
;
1835 cout
<< wxT("Press any key to continue...") << endl
;
1840 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1845 return false; // This function always returns false.
1847 } // wxDb::DispAllErrors()
1850 /********** wxDb::GetNextError() **********/
1851 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1853 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1858 } // wxDb::GetNextError()
1861 /********** wxDb::DispNextError() **********/
1862 void wxDb::DispNextError(void)
1864 wxString odbcErrMsg
;
1866 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1867 logError(odbcErrMsg
, sqlState
);
1872 #ifdef DBDEBUG_CONSOLE
1873 // When run in console mode, use standard out to display errors.
1874 cout
<< odbcErrMsg
.c_str() << endl
;
1875 cout
<< wxT("Press any key to continue...") << endl
;
1880 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1881 #endif // __WXDEBUG__
1883 } // wxDb::DispNextError()
1886 /********** wxDb::logError() **********/
1887 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1889 wxASSERT(errMsg
.length());
1891 static int pLast
= -1;
1894 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1897 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1898 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1902 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1903 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1905 if (SQLState
.length())
1906 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1907 DB_STATUS
= dbStatus
;
1909 // Add the errmsg to the sql log
1910 WriteSqlLog(errMsg
);
1912 } // wxDb::logError()
1915 /**********wxDb::TranslateSqlState() **********/
1916 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1918 if (!wxStrcmp(SQLState
, wxT("01000")))
1919 return(DB_ERR_GENERAL_WARNING
);
1920 if (!wxStrcmp(SQLState
, wxT("01002")))
1921 return(DB_ERR_DISCONNECT_ERROR
);
1922 if (!wxStrcmp(SQLState
, wxT("01004")))
1923 return(DB_ERR_DATA_TRUNCATED
);
1924 if (!wxStrcmp(SQLState
, wxT("01006")))
1925 return(DB_ERR_PRIV_NOT_REVOKED
);
1926 if (!wxStrcmp(SQLState
, wxT("01S00")))
1927 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1928 if (!wxStrcmp(SQLState
, wxT("01S01")))
1929 return(DB_ERR_ERROR_IN_ROW
);
1930 if (!wxStrcmp(SQLState
, wxT("01S02")))
1931 return(DB_ERR_OPTION_VALUE_CHANGED
);
1932 if (!wxStrcmp(SQLState
, wxT("01S03")))
1933 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1934 if (!wxStrcmp(SQLState
, wxT("01S04")))
1935 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1936 if (!wxStrcmp(SQLState
, wxT("07001")))
1937 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1938 if (!wxStrcmp(SQLState
, wxT("07006")))
1939 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1940 if (!wxStrcmp(SQLState
, wxT("08001")))
1941 return(DB_ERR_UNABLE_TO_CONNECT
);
1942 if (!wxStrcmp(SQLState
, wxT("08002")))
1943 return(DB_ERR_CONNECTION_IN_USE
);
1944 if (!wxStrcmp(SQLState
, wxT("08003")))
1945 return(DB_ERR_CONNECTION_NOT_OPEN
);
1946 if (!wxStrcmp(SQLState
, wxT("08004")))
1947 return(DB_ERR_REJECTED_CONNECTION
);
1948 if (!wxStrcmp(SQLState
, wxT("08007")))
1949 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1950 if (!wxStrcmp(SQLState
, wxT("08S01")))
1951 return(DB_ERR_COMM_LINK_FAILURE
);
1952 if (!wxStrcmp(SQLState
, wxT("21S01")))
1953 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1954 if (!wxStrcmp(SQLState
, wxT("21S02")))
1955 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1956 if (!wxStrcmp(SQLState
, wxT("22001")))
1957 return(DB_ERR_STRING_RIGHT_TRUNC
);
1958 if (!wxStrcmp(SQLState
, wxT("22003")))
1959 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1960 if (!wxStrcmp(SQLState
, wxT("22005")))
1961 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1962 if (!wxStrcmp(SQLState
, wxT("22008")))
1963 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1964 if (!wxStrcmp(SQLState
, wxT("22012")))
1965 return(DB_ERR_DIVIDE_BY_ZERO
);
1966 if (!wxStrcmp(SQLState
, wxT("22026")))
1967 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1968 if (!wxStrcmp(SQLState
, wxT("23000")))
1969 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1970 if (!wxStrcmp(SQLState
, wxT("24000")))
1971 return(DB_ERR_INVALID_CURSOR_STATE
);
1972 if (!wxStrcmp(SQLState
, wxT("25000")))
1973 return(DB_ERR_INVALID_TRANS_STATE
);
1974 if (!wxStrcmp(SQLState
, wxT("28000")))
1975 return(DB_ERR_INVALID_AUTH_SPEC
);
1976 if (!wxStrcmp(SQLState
, wxT("34000")))
1977 return(DB_ERR_INVALID_CURSOR_NAME
);
1978 if (!wxStrcmp(SQLState
, wxT("37000")))
1979 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1980 if (!wxStrcmp(SQLState
, wxT("3C000")))
1981 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1982 if (!wxStrcmp(SQLState
, wxT("40001")))
1983 return(DB_ERR_SERIALIZATION_FAILURE
);
1984 if (!wxStrcmp(SQLState
, wxT("42000")))
1985 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1986 if (!wxStrcmp(SQLState
, wxT("70100")))
1987 return(DB_ERR_OPERATION_ABORTED
);
1988 if (!wxStrcmp(SQLState
, wxT("IM001")))
1989 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1990 if (!wxStrcmp(SQLState
, wxT("IM002")))
1991 return(DB_ERR_NO_DATA_SOURCE
);
1992 if (!wxStrcmp(SQLState
, wxT("IM003")))
1993 return(DB_ERR_DRIVER_LOAD_ERROR
);
1994 if (!wxStrcmp(SQLState
, wxT("IM004")))
1995 return(DB_ERR_SQLALLOCENV_FAILED
);
1996 if (!wxStrcmp(SQLState
, wxT("IM005")))
1997 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1998 if (!wxStrcmp(SQLState
, wxT("IM006")))
1999 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
2000 if (!wxStrcmp(SQLState
, wxT("IM007")))
2001 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
2002 if (!wxStrcmp(SQLState
, wxT("IM008")))
2003 return(DB_ERR_DIALOG_FAILED
);
2004 if (!wxStrcmp(SQLState
, wxT("IM009")))
2005 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
2006 if (!wxStrcmp(SQLState
, wxT("IM010")))
2007 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
2008 if (!wxStrcmp(SQLState
, wxT("IM011")))
2009 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
2010 if (!wxStrcmp(SQLState
, wxT("IM012")))
2011 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
2012 if (!wxStrcmp(SQLState
, wxT("IM013")))
2013 return(DB_ERR_TRACE_FILE_ERROR
);
2014 if (!wxStrcmp(SQLState
, wxT("S0001")))
2015 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
2016 if (!wxStrcmp(SQLState
, wxT("S0002")))
2017 return(DB_ERR_TABLE_NOT_FOUND
);
2018 if (!wxStrcmp(SQLState
, wxT("S0011")))
2019 return(DB_ERR_INDEX_ALREADY_EXISTS
);
2020 if (!wxStrcmp(SQLState
, wxT("S0012")))
2021 return(DB_ERR_INDEX_NOT_FOUND
);
2022 if (!wxStrcmp(SQLState
, wxT("S0021")))
2023 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
2024 if (!wxStrcmp(SQLState
, wxT("S0022")))
2025 return(DB_ERR_COLUMN_NOT_FOUND
);
2026 if (!wxStrcmp(SQLState
, wxT("S0023")))
2027 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2028 if (!wxStrcmp(SQLState
, wxT("S1000")))
2029 return(DB_ERR_GENERAL_ERROR
);
2030 if (!wxStrcmp(SQLState
, wxT("S1001")))
2031 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2032 if (!wxStrcmp(SQLState
, wxT("S1002")))
2033 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2034 if (!wxStrcmp(SQLState
, wxT("S1003")))
2035 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2036 if (!wxStrcmp(SQLState
, wxT("S1004")))
2037 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2038 if (!wxStrcmp(SQLState
, wxT("S1008")))
2039 return(DB_ERR_OPERATION_CANCELLED
);
2040 if (!wxStrcmp(SQLState
, wxT("S1009")))
2041 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2042 if (!wxStrcmp(SQLState
, wxT("S1010")))
2043 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2044 if (!wxStrcmp(SQLState
, wxT("S1011")))
2045 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2046 if (!wxStrcmp(SQLState
, wxT("S1012")))
2047 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2048 if (!wxStrcmp(SQLState
, wxT("S1015")))
2049 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2050 if (!wxStrcmp(SQLState
, wxT("S1090")))
2051 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2052 if (!wxStrcmp(SQLState
, wxT("S1091")))
2053 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2054 if (!wxStrcmp(SQLState
, wxT("S1092")))
2055 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2056 if (!wxStrcmp(SQLState
, wxT("S1093")))
2057 return(DB_ERR_INVALID_PARAM_NO
);
2058 if (!wxStrcmp(SQLState
, wxT("S1094")))
2059 return(DB_ERR_INVALID_SCALE_VALUE
);
2060 if (!wxStrcmp(SQLState
, wxT("S1095")))
2061 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2062 if (!wxStrcmp(SQLState
, wxT("S1096")))
2063 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2064 if (!wxStrcmp(SQLState
, wxT("S1097")))
2065 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2066 if (!wxStrcmp(SQLState
, wxT("S1098")))
2067 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2068 if (!wxStrcmp(SQLState
, wxT("S1099")))
2069 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2070 if (!wxStrcmp(SQLState
, wxT("S1100")))
2071 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2072 if (!wxStrcmp(SQLState
, wxT("S1101")))
2073 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2074 if (!wxStrcmp(SQLState
, wxT("S1103")))
2075 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2076 if (!wxStrcmp(SQLState
, wxT("S1104")))
2077 return(DB_ERR_INVALID_PRECISION_VALUE
);
2078 if (!wxStrcmp(SQLState
, wxT("S1105")))
2079 return(DB_ERR_INVALID_PARAM_TYPE
);
2080 if (!wxStrcmp(SQLState
, wxT("S1106")))
2081 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2082 if (!wxStrcmp(SQLState
, wxT("S1107")))
2083 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2084 if (!wxStrcmp(SQLState
, wxT("S1108")))
2085 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2086 if (!wxStrcmp(SQLState
, wxT("S1109")))
2087 return(DB_ERR_INVALID_CURSOR_POSITION
);
2088 if (!wxStrcmp(SQLState
, wxT("S1110")))
2089 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2090 if (!wxStrcmp(SQLState
, wxT("S1111")))
2091 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2092 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2093 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2094 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2095 return(DB_ERR_TIMEOUT_EXPIRED
);
2100 } // wxDb::TranslateSqlState()
2103 /********** wxDb::Grant() **********/
2104 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2108 // Build the grant statement
2109 sqlStmt
= wxT("GRANT ");
2110 if (privileges
== DB_GRANT_ALL
)
2111 sqlStmt
+= wxT("ALL");
2115 if (privileges
& DB_GRANT_SELECT
)
2117 sqlStmt
+= wxT("SELECT");
2120 if (privileges
& DB_GRANT_INSERT
)
2123 sqlStmt
+= wxT(", ");
2124 sqlStmt
+= wxT("INSERT");
2126 if (privileges
& DB_GRANT_UPDATE
)
2129 sqlStmt
+= wxT(", ");
2130 sqlStmt
+= wxT("UPDATE");
2132 if (privileges
& DB_GRANT_DELETE
)
2135 sqlStmt
+= wxT(", ");
2136 sqlStmt
+= wxT("DELETE");
2140 sqlStmt
+= wxT(" ON ");
2141 sqlStmt
+= SQLTableName(tableName
);
2142 sqlStmt
+= wxT(" TO ");
2143 sqlStmt
+= userList
;
2145 #ifdef DBDEBUG_CONSOLE
2146 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2149 WriteSqlLog(sqlStmt
);
2151 return(ExecSql(sqlStmt
));
2156 /********** wxDb::CreateView() **********/
2157 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2158 const wxString
&pSqlStmt
, bool attemptDrop
)
2162 // Drop the view first
2163 if (attemptDrop
&& !DropView(viewName
))
2166 // Build the create view statement
2167 sqlStmt
= wxT("CREATE VIEW ");
2168 sqlStmt
+= viewName
;
2170 if (colList
.length())
2172 sqlStmt
+= wxT(" (");
2174 sqlStmt
+= wxT(")");
2177 sqlStmt
+= wxT(" AS ");
2178 sqlStmt
+= pSqlStmt
;
2180 WriteSqlLog(sqlStmt
);
2182 #ifdef DBDEBUG_CONSOLE
2183 cout
<< sqlStmt
.c_str() << endl
;
2186 return(ExecSql(sqlStmt
));
2188 } // wxDb::CreateView()
2191 /********** wxDb::DropView() **********/
2192 bool wxDb::DropView(const wxString
&viewName
)
2195 * NOTE: This function returns true if the View does not exist, but
2196 * only for identified databases. Code will need to be added
2197 * below for any other databases when those databases are defined
2198 * to handle this situation consistently
2202 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2204 WriteSqlLog(sqlStmt
);
2206 #ifdef DBDEBUG_CONSOLE
2207 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2210 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2212 // Check for "Base table not found" error and ignore
2213 GetNextError(henv
, hdbc
, hstmt
);
2214 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2216 // Check for product specific error codes
2217 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2220 DispAllErrors(henv
, hdbc
, hstmt
);
2227 // Commit the transaction
2233 } // wxDb::DropView()
2236 /********** wxDb::ExecSql() **********/
2237 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2241 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2243 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2244 if (retcode
== SQL_SUCCESS
||
2245 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2251 DispAllErrors(henv
, hdbc
, hstmt
);
2255 } // wxDb::ExecSql()
2258 /********** wxDb::ExecSql() with column info **********/
2259 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2261 //execute the statement first
2262 if (!ExecSql(pSqlStmt
))
2266 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2268 DispAllErrors(henv
, hdbc
, hstmt
);
2277 // Get column information
2279 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2282 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2284 // Fill in column information (name, datatype)
2285 for (colNum
= 0; colNum
< noCols
; colNum
++)
2287 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2289 &Sword
, &Sqllen
) != SQL_SUCCESS
)
2291 DispAllErrors(henv
, hdbc
, hstmt
);
2296 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2297 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2299 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2300 NULL
, 0, &Sword
, &Sqllen
) != SQL_SUCCESS
)
2302 DispAllErrors(henv
, hdbc
, hstmt
);
2310 #if defined(SQL_WCHAR)
2313 #if defined(SQL_WVARCHAR)
2319 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2321 case SQL_LONGVARCHAR
:
2322 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_MEMO
;
2328 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2335 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2339 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2342 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2347 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen
);
2348 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2355 } // wxDb::ExecSql()
2357 /********** wxDb::GetNext() **********/
2358 bool wxDb::GetNext(void)
2360 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2364 DispAllErrors(henv
, hdbc
, hstmt
);
2368 } // wxDb::GetNext()
2371 /********** wxDb::GetData() **********/
2372 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SQLLEN FAR
*cbReturned
)
2375 wxASSERT(cbReturned
);
2377 long bufferSize
= maxLen
;
2379 if (cType
== SQL_C_WXCHAR
)
2380 bufferSize
= maxLen
* sizeof(wxChar
);
2382 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2386 DispAllErrors(henv
, hdbc
, hstmt
);
2390 } // wxDb::GetData()
2393 /********** wxDb::GetKeyFields() **********/
2394 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2396 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2397 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2399 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2400 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2406 * -----------------------------------------------------------------------
2407 * -- 19991224 : mj10777 : Create ------
2408 * -- : Three things are done and stored here : ------
2409 * -- : 1) which Column(s) is/are Primary Key(s) ------
2410 * -- : 2) which tables use this Key as a Foreign Key ------
2411 * -- : 3) which columns are Foreign Key and the name ------
2412 * -- : of the Table where the Key is the Primary Key -----
2413 * -- : Called from GetColumns(const wxString &tableName, ------
2414 * -- int *numCols,const wxChar *userID ) ------
2415 * -----------------------------------------------------------------------
2418 /*---------------------------------------------------------------------*/
2419 /* Get the names of the columns in the primary key. */
2420 /*---------------------------------------------------------------------*/
2421 retcode
= SQLPrimaryKeys(hstmt
,
2422 NULL
, 0, /* Catalog name */
2423 NULL
, 0, /* Schema name */
2424 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2426 /*---------------------------------------------------------------------*/
2427 /* Fetch and display the result set. This will be a list of the */
2428 /* columns in the primary key of the tableName table. */
2429 /*---------------------------------------------------------------------*/
2430 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2432 retcode
= SQLFetch(hstmt
);
2433 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2435 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2436 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2438 for (i
=0;i
<noCols
;i
++) // Find the Column name
2439 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2440 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2443 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2445 /*---------------------------------------------------------------------*/
2446 /* Get all the foreign keys that refer to tableName primary key. */
2447 /*---------------------------------------------------------------------*/
2448 retcode
= SQLForeignKeys(hstmt
,
2449 NULL
, 0, /* Primary catalog */
2450 NULL
, 0, /* Primary schema */
2451 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2452 NULL
, 0, /* Foreign catalog */
2453 NULL
, 0, /* Foreign schema */
2454 NULL
, 0); /* Foreign table */
2456 /*---------------------------------------------------------------------*/
2457 /* Fetch and display the result set. This will be all of the foreign */
2458 /* keys in other tables that refer to the tableName primary key. */
2459 /*---------------------------------------------------------------------*/
2462 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2464 retcode
= SQLFetch(hstmt
);
2465 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2467 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2468 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2469 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2470 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2471 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2472 tempStr
<< _T('[') << szFkTable
<< _T(']'); // [ ] in case there is a blank in the Table name
2476 tempStr
.Trim(); // Get rid of any unneeded blanks
2477 if (!tempStr
.empty())
2479 for (i
=0; i
<noCols
; i
++)
2480 { // Find the Column name
2481 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2483 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
2484 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2489 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2491 /*---------------------------------------------------------------------*/
2492 /* Get all the foreign keys in the tablename table. */
2493 /*---------------------------------------------------------------------*/
2494 retcode
= SQLForeignKeys(hstmt
,
2495 NULL
, 0, /* Primary catalog */
2496 NULL
, 0, /* Primary schema */
2497 NULL
, 0, /* Primary table */
2498 NULL
, 0, /* Foreign catalog */
2499 NULL
, 0, /* Foreign schema */
2500 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2502 /*---------------------------------------------------------------------*/
2503 /* Fetch and display the result set. This will be all of the */
2504 /* primary keys in other tables that are referred to by foreign */
2505 /* keys in the tableName table. */
2506 /*---------------------------------------------------------------------*/
2507 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2509 retcode
= SQLFetch(hstmt
);
2510 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2512 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2513 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2514 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2516 for (i
=0; i
<noCols
; i
++) // Find the Column name
2518 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2520 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2521 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2522 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2527 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2531 } // wxDb::GetKeyFields()
2535 /********** wxDb::GetColumns() **********/
2536 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2538 * 1) The last array element of the tableName[] argument must be zero (null).
2539 * This is how the end of the array is detected.
2540 * 2) This function returns an array of wxDbColInf structures. If no columns
2541 * were found, or an error occurred, this pointer will be zero (null). THE
2542 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2543 * IS FINISHED WITH IT. i.e.
2545 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2548 * // Use the column inf
2550 * // Destroy the memory
2554 * userID is evaluated in the following manner:
2555 * userID == NULL ... UserID is ignored
2556 * userID == "" ... UserID set equal to 'this->uid'
2557 * userID != "" ... UserID set equal to 'userID'
2559 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2560 * by this function. This function should use its own wxDb instance
2561 * to avoid undesired unbinding of columns.
2566 wxDbColInf
*colInf
= 0;
2574 convertUserID(userID
,UserID
);
2576 // Pass 1 - Determine how many columns there are.
2577 // Pass 2 - Allocate the wxDbColInf array and fill in
2578 // the array with the column information.
2580 for (pass
= 1; pass
<= 2; pass
++)
2584 if (noCols
== 0) // Probably a bogus table name(s)
2586 // Allocate n wxDbColInf objects to hold the column information
2587 colInf
= new wxDbColInf
[noCols
+1];
2590 // Mark the end of the array
2591 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2592 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2593 colInf
[noCols
].sqlDataType
= 0;
2595 // Loop through each table name
2597 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2599 TableName
= tableName
[tbl
];
2600 // Oracle and Interbase table names are uppercase only, so force
2601 // the name to uppercase just in case programmer forgot to do this
2602 if ((Dbms() == dbmsORACLE
) ||
2603 (Dbms() == dbmsFIREBIRD
) ||
2604 (Dbms() == dbmsINTERBASE
))
2605 TableName
= TableName
.Upper();
2607 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2609 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2610 // use the call below that leaves out the user name
2611 if (!UserID
.empty() &&
2612 Dbms() != dbmsMY_SQL
&&
2613 Dbms() != dbmsACCESS
&&
2614 Dbms() != dbmsMS_SQL_SERVER
)
2616 retcode
= SQLColumns(hstmt
,
2617 NULL
, 0, // All qualifiers
2618 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2619 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2620 NULL
, 0); // All columns
2624 retcode
= SQLColumns(hstmt
,
2625 NULL
, 0, // All qualifiers
2627 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2628 NULL
, 0); // All columns
2630 if (retcode
!= SQL_SUCCESS
)
2631 { // Error occurred, abort
2632 DispAllErrors(henv
, hdbc
, hstmt
);
2635 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2639 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2641 if (pass
== 1) // First pass, just add up the number of columns
2643 else // Pass 2; Fill in the array of structures
2645 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2647 // NOTE: Only the ODBC 1.x fields are retrieved
2648 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2649 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2650 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2651 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2652 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2653 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2654 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2655 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2656 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2657 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2658 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2659 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2661 // Determine the wxDb data type that is used to represent the native data type of this data source
2662 colInf
[colNo
].dbDataType
= 0;
2663 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2666 // IODBC does not return a correct columnLength, so we set
2667 // columnLength = bufferSize if no column length was returned
2668 // IODBC returns the columnLength in bufferSize. (bug)
2669 if (colInf
[colNo
].columnLength
< 1)
2671 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2674 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2676 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2677 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2678 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2679 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2680 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2681 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2682 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2683 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2688 if (retcode
!= SQL_NO_DATA_FOUND
)
2689 { // Error occurred, abort
2690 DispAllErrors(henv
, hdbc
, hstmt
);
2693 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2699 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2702 } // wxDb::GetColumns()
2705 /********** wxDb::GetColumns() **********/
2707 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2709 // Same as the above GetColumns() function except this one gets columns
2710 // only for a single table, and if 'numCols' is not NULL, the number of
2711 // columns stored in the returned wxDbColInf is set in '*numCols'
2713 // userID is evaluated in the following manner:
2714 // userID == NULL ... UserID is ignored
2715 // userID == "" ... UserID set equal to 'this->uid'
2716 // userID != "" ... UserID set equal to 'userID'
2718 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2719 // by this function. This function should use its own wxDb instance
2720 // to avoid undesired unbinding of columns.
2725 wxDbColInf
*colInf
= 0;
2733 convertUserID(userID
,UserID
);
2735 // Pass 1 - Determine how many columns there are.
2736 // Pass 2 - Allocate the wxDbColInf array and fill in
2737 // the array with the column information.
2739 for (pass
= 1; pass
<= 2; pass
++)
2743 if (noCols
== 0) // Probably a bogus table name(s)
2745 // Allocate n wxDbColInf objects to hold the column information
2746 colInf
= new wxDbColInf
[noCols
+1];
2749 // Mark the end of the array
2750 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2751 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2752 colInf
[noCols
].sqlDataType
= 0;
2755 TableName
= tableName
;
2756 // Oracle and Interbase table names are uppercase only, so force
2757 // the name to uppercase just in case programmer forgot to do this
2758 if ((Dbms() == dbmsORACLE
) ||
2759 (Dbms() == dbmsFIREBIRD
) ||
2760 (Dbms() == dbmsINTERBASE
))
2761 TableName
= TableName
.Upper();
2763 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2765 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2766 // use the call below that leaves out the user name
2767 if (!UserID
.empty() &&
2768 Dbms() != dbmsMY_SQL
&&
2769 Dbms() != dbmsACCESS
&&
2770 Dbms() != dbmsMS_SQL_SERVER
)
2772 retcode
= SQLColumns(hstmt
,
2773 NULL
, 0, // All qualifiers
2774 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2775 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2776 NULL
, 0); // All columns
2780 retcode
= SQLColumns(hstmt
,
2781 NULL
, 0, // All qualifiers
2783 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2784 NULL
, 0); // All columns
2786 if (retcode
!= SQL_SUCCESS
)
2787 { // Error occurred, abort
2788 DispAllErrors(henv
, hdbc
, hstmt
);
2791 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2797 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2799 if (pass
== 1) // First pass, just add up the number of columns
2801 else // Pass 2; Fill in the array of structures
2803 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2805 // NOTE: Only the ODBC 1.x fields are retrieved
2806 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2807 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2808 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2809 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2810 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2811 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2812 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2813 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2814 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2815 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2816 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2817 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2818 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2819 // Start Values for Primary/Foriegn Key (=No)
2820 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2821 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2822 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2823 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2825 // BJO 20000428 : Virtuoso returns type names with upper cases!
2826 if (Dbms() == dbmsVIRTUOSO
)
2828 wxString s
= colInf
[colNo
].typeName
;
2830 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2833 // Determine the wxDb data type that is used to represent the native data type of this data source
2834 colInf
[colNo
].dbDataType
= 0;
2835 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2838 // IODBC does not return a correct columnLength, so we set
2839 // columnLength = bufferSize if no column length was returned
2840 // IODBC returns the columnLength in bufferSize. (bug)
2841 if (colInf
[colNo
].columnLength
< 1)
2843 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2847 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2849 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2850 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2851 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2852 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2853 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2854 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2855 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2856 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2862 if (retcode
!= SQL_NO_DATA_FOUND
)
2863 { // Error occurred, abort
2864 DispAllErrors(henv
, hdbc
, hstmt
);
2867 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2874 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2876 // Store Primary and Foriegn Keys
2877 GetKeyFields(tableName
,colInf
,noCols
);
2883 } // wxDb::GetColumns()
2886 #else // New GetColumns
2891 These are tentative new GetColumns members which should be more database
2892 independent and which always returns the columns in the order they were
2895 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2896 wxChar* userID)) calls the second implementation for each separate table
2897 before merging the results. This makes the code easier to maintain as
2898 only one member (the second) makes the real work
2899 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2900 wxChar *userID) is a little bit improved
2901 - It doesn't anymore rely on the type-name to find out which database-type
2903 - It ends by sorting the columns, so that they are returned in the same
2904 order they were created
2914 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2917 // The last array element of the tableName[] argument must be zero (null).
2918 // This is how the end of the array is detected.
2922 // How many tables ?
2924 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2926 // Create a table to maintain the columns for each separate table
2927 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2930 for (i
= 0 ; i
< tbl
; i
++)
2933 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2934 if (TableColumns
[i
].colInf
== NULL
)
2936 noCols
+= TableColumns
[i
].noCols
;
2939 // Now merge all the separate table infos
2940 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2942 // Mark the end of the array
2943 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2944 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2945 colInf
[noCols
].sqlDataType
= 0;
2950 for (i
= 0 ; i
< tbl
; i
++)
2952 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2954 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2958 delete [] TableColumns
;
2961 } // wxDb::GetColumns() -- NEW
2964 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2966 // Same as the above GetColumns() function except this one gets columns
2967 // only for a single table, and if 'numCols' is not NULL, the number of
2968 // columns stored in the returned wxDbColInf is set in '*numCols'
2970 // userID is evaluated in the following manner:
2971 // userID == NULL ... UserID is ignored
2972 // userID == "" ... UserID set equal to 'this->uid'
2973 // userID != "" ... UserID set equal to 'userID'
2975 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2976 // by this function. This function should use its own wxDb instance
2977 // to avoid undesired unbinding of columns.
2981 wxDbColInf
*colInf
= 0;
2989 convertUserID(userID
,UserID
);
2991 // Pass 1 - Determine how many columns there are.
2992 // Pass 2 - Allocate the wxDbColInf array and fill in
2993 // the array with the column information.
2995 for (pass
= 1; pass
<= 2; pass
++)
2999 if (noCols
== 0) // Probably a bogus table name(s)
3001 // Allocate n wxDbColInf objects to hold the column information
3002 colInf
= new wxDbColInf
[noCols
+1];
3005 // Mark the end of the array
3006 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
3007 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
3008 colInf
[noCols
].sqlDataType
= 0;
3011 TableName
= tableName
;
3012 // Oracle and Interbase table names are uppercase only, so force
3013 // the name to uppercase just in case programmer forgot to do this
3014 if ((Dbms() == dbmsORACLE
) ||
3015 (Dbms() == dbmsFIREBIRD
) ||
3016 (Dbms() == dbmsINTERBASE
))
3017 TableName
= TableName
.Upper();
3019 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3021 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3022 // use the call below that leaves out the user name
3023 if (!UserID
.empty() &&
3024 Dbms() != dbmsMY_SQL
&&
3025 Dbms() != dbmsACCESS
&&
3026 Dbms() != dbmsMS_SQL_SERVER
)
3028 retcode
= SQLColumns(hstmt
,
3029 NULL
, 0, // All qualifiers
3030 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3031 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3032 NULL
, 0); // All columns
3036 retcode
= SQLColumns(hstmt
,
3037 NULL
, 0, // All qualifiers
3039 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3040 NULL
, 0); // All columns
3042 if (retcode
!= SQL_SUCCESS
)
3043 { // Error occurred, abort
3044 DispAllErrors(henv
, hdbc
, hstmt
);
3047 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3053 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3055 if (pass
== 1) // First pass, just add up the number of columns
3057 else // Pass 2; Fill in the array of structures
3059 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3061 // NOTE: Only the ODBC 1.x fields are retrieved
3062 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3063 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3064 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3065 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3066 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3067 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3068 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3069 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3070 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3071 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3072 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3073 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3074 // Start Values for Primary/Foriegn Key (=No)
3075 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3076 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3077 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3078 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3081 // IODBC does not return a correct columnLength, so we set
3082 // columnLength = bufferSize if no column length was returned
3083 // IODBC returns the columnLength in bufferSize. (bug)
3084 if (colInf
[colNo
].columnLength
< 1)
3086 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3090 // Determine the wxDb data type that is used to represent the native data type of this data source
3091 colInf
[colNo
].dbDataType
= 0;
3092 // Get the intern datatype
3093 switch (colInf
[colNo
].sqlDataType
)
3096 #if defined(SQL_WCHAR)
3099 #if defined(SQL_WVARCHAR)
3105 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3107 case SQL_LONGVARCHAR
:
3108 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_MEMO
;
3114 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3121 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3125 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3128 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3133 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3134 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3141 if (retcode
!= SQL_NO_DATA_FOUND
)
3142 { // Error occurred, abort
3143 DispAllErrors(henv
, hdbc
, hstmt
);
3146 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3153 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3155 // Store Primary and Foreign Keys
3156 GetKeyFields(tableName
,colInf
,noCols
);
3158 ///////////////////////////////////////////////////////////////////////////
3159 // Now sort the the columns in order to make them appear in the right order
3160 ///////////////////////////////////////////////////////////////////////////
3162 // Build a generic SELECT statement which returns 0 rows
3165 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3168 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3170 DispAllErrors(henv
, hdbc
, hstmt
);
3174 // Get the number of result columns
3175 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3177 DispAllErrors(henv
, hdbc
, hstmt
);
3181 if (noCols
== 0) // Probably a bogus table name
3190 for (colNum
= 0; colNum
< noCols
; colNum
++)
3192 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3194 &Sword
, &Sdword
) != SQL_SUCCESS
)
3196 DispAllErrors(henv
, hdbc
, hstmt
);
3200 wxString Name1
= name
;
3201 Name1
= Name1
.Upper();
3203 // Where is this name in the array ?
3204 for (i
= colNum
; i
< noCols
; i
++)
3206 wxString Name2
= colInf
[i
].colName
;
3207 Name2
= Name2
.Upper();
3210 if (colNum
!= i
) // swap to sort
3212 wxDbColInf tmpColInf
= colInf
[colNum
];
3213 colInf
[colNum
] = colInf
[i
];
3214 colInf
[i
] = tmpColInf
;
3220 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3222 ///////////////////////////////////////////////////////////////////////////
3224 ///////////////////////////////////////////////////////////////////////////
3230 } // wxDb::GetColumns()
3233 #endif // #else OLD_GETCOLUMNS
3236 /********** wxDb::GetColumnCount() **********/
3237 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3239 * Returns a count of how many columns are in a table.
3240 * If an error occurs in computing the number of columns
3241 * this function will return a -1 for the count
3243 * userID is evaluated in the following manner:
3244 * userID == NULL ... UserID is ignored
3245 * userID == "" ... UserID set equal to 'this->uid'
3246 * userID != "" ... UserID set equal to 'userID'
3248 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3249 * by this function. This function should use its own wxDb instance
3250 * to avoid undesired unbinding of columns.
3260 convertUserID(userID
,UserID
);
3262 TableName
= tableName
;
3263 // Oracle and Interbase table names are uppercase only, so force
3264 // the name to uppercase just in case programmer forgot to do this
3265 if ((Dbms() == dbmsORACLE
) ||
3266 (Dbms() == dbmsFIREBIRD
) ||
3267 (Dbms() == dbmsINTERBASE
))
3268 TableName
= TableName
.Upper();
3270 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3272 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3273 // use the call below that leaves out the user name
3274 if (!UserID
.empty() &&
3275 Dbms() != dbmsMY_SQL
&&
3276 Dbms() != dbmsACCESS
&&
3277 Dbms() != dbmsMS_SQL_SERVER
)
3279 retcode
= SQLColumns(hstmt
,
3280 NULL
, 0, // All qualifiers
3281 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3282 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3283 NULL
, 0); // All columns
3287 retcode
= SQLColumns(hstmt
,
3288 NULL
, 0, // All qualifiers
3290 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3291 NULL
, 0); // All columns
3293 if (retcode
!= SQL_SUCCESS
)
3294 { // Error occurred, abort
3295 DispAllErrors(henv
, hdbc
, hstmt
);
3296 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3300 // Count the columns
3301 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3304 if (retcode
!= SQL_NO_DATA_FOUND
)
3305 { // Error occurred, abort
3306 DispAllErrors(henv
, hdbc
, hstmt
);
3307 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3311 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3314 } // wxDb::GetColumnCount()
3317 /********** wxDb::GetCatalog() *******/
3318 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3320 * ---------------------------------------------------------------------
3321 * -- 19991203 : mj10777 : Create ------
3322 * -- : Creates a wxDbInf with Tables / Cols Array ------
3323 * -- : uses SQLTables and fills pTableInf; ------
3324 * -- : pColInf is set to NULL and numCols to 0; ------
3325 * -- : returns pDbInf (wxDbInf) ------
3326 * -- - if unsuccessful (pDbInf == NULL) ------
3327 * -- : pColInf can be filled with GetColumns(..); ------
3328 * -- : numCols can be filled with GetColumnCount(..); ------
3329 * ---------------------------------------------------------------------
3331 * userID is evaluated in the following manner:
3332 * userID == NULL ... UserID is ignored
3333 * userID == "" ... UserID set equal to 'this->uid'
3334 * userID != "" ... UserID set equal to 'userID'
3336 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3337 * by this function. This function should use its own wxDb instance
3338 * to avoid undesired unbinding of columns.
3341 int noTab
= 0; // Counter while filling table entries
3345 wxString tblNameSave
;
3348 convertUserID(userID
,UserID
);
3350 //-------------------------------------------------------------
3351 // Create the Database Array of catalog entries
3353 wxDbInf
*pDbInf
= new wxDbInf
;
3355 //-------------------------------------------------------------
3356 // Table Information
3357 // Pass 1 - Determine how many Tables there are.
3358 // Pass 2 - Create the Table array and fill it
3359 // - Create the Cols array = NULL
3360 //-------------------------------------------------------------
3362 for (pass
= 1; pass
<= 2; pass
++)
3364 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3365 tblNameSave
.Empty();
3367 if (!UserID
.empty() &&
3368 Dbms() != dbmsMY_SQL
&&
3369 Dbms() != dbmsACCESS
&&
3370 Dbms() != dbmsMS_SQL_SERVER
)
3372 retcode
= SQLTables(hstmt
,
3373 NULL
, 0, // All qualifiers
3374 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3375 NULL
, 0, // All tables
3376 NULL
, 0); // All columns
3380 retcode
= SQLTables(hstmt
,
3381 NULL
, 0, // All qualifiers
3382 NULL
, 0, // User specified
3383 NULL
, 0, // All tables
3384 NULL
, 0); // All columns
3387 if (retcode
!= SQL_SUCCESS
)
3389 DispAllErrors(henv
, hdbc
, hstmt
);
3391 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3395 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3397 if (pass
== 1) // First pass, just count the Tables
3399 if (pDbInf
->numTables
== 0)
3401 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3402 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3404 pDbInf
->numTables
++; // Counter for Tables
3406 if (pass
== 2) // Create and fill the Table entries
3408 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3409 { // no, then create the Array
3410 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3412 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3414 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3415 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3416 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3422 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3424 // Query how many columns are in each table
3425 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3427 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3432 } // wxDb::GetCatalog()
3435 /********** wxDb::Catalog() **********/
3436 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3438 * Creates the text file specified in 'filename' which will contain
3439 * a minimal data dictionary of all tables accessible by the user specified
3442 * userID is evaluated in the following manner:
3443 * userID == NULL ... UserID is ignored
3444 * userID == "" ... UserID set equal to 'this->uid'
3445 * userID != "" ... UserID set equal to 'userID'
3447 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3448 * by this function. This function should use its own wxDb instance
3449 * to avoid undesired unbinding of columns.
3452 wxASSERT(fileName
.length());
3456 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3457 wxString tblNameSave
;
3458 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3460 wxChar typeName
[30+1];
3461 SDWORD precision
, length
;
3463 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3467 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3470 convertUserID(userID
,UserID
);
3472 if (!UserID
.empty() &&
3473 Dbms() != dbmsMY_SQL
&&
3474 Dbms() != dbmsACCESS
&&
3475 Dbms() != dbmsFIREBIRD
&&
3476 Dbms() != dbmsINTERBASE
&&
3477 Dbms() != dbmsMS_SQL_SERVER
)
3479 retcode
= SQLColumns(hstmt
,
3480 NULL
, 0, // All qualifiers
3481 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3482 NULL
, 0, // All tables
3483 NULL
, 0); // All columns
3487 retcode
= SQLColumns(hstmt
,
3488 NULL
, 0, // All qualifiers
3489 NULL
, 0, // User specified
3490 NULL
, 0, // All tables
3491 NULL
, 0); // All columns
3493 if (retcode
!= SQL_SUCCESS
)
3495 DispAllErrors(henv
, hdbc
, hstmt
);
3501 tblNameSave
.Empty();
3506 retcode
= SQLFetch(hstmt
);
3507 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3510 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3511 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3512 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3513 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3514 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3515 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3517 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3520 wxFputs(wxT("\n"), fp
);
3521 wxFputs(wxT("================================ "), fp
);
3522 wxFputs(wxT("================================ "), fp
);
3523 wxFputs(wxT("===================== "), fp
);
3524 wxFputs(wxT("========= "), fp
);
3525 wxFputs(wxT("=========\n"), fp
);
3526 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3527 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3528 wxFputs(outStr
.c_str(), fp
);
3529 wxFputs(wxT("================================ "), fp
);
3530 wxFputs(wxT("================================ "), fp
);
3531 wxFputs(wxT("===================== "), fp
);
3532 wxFputs(wxT("========= "), fp
);
3533 wxFputs(wxT("=========\n"), fp
);
3534 tblNameSave
= tblName
;
3537 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3538 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3539 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3541 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3548 if (retcode
!= SQL_NO_DATA_FOUND
)
3549 DispAllErrors(henv
, hdbc
, hstmt
);
3551 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3554 return(retcode
== SQL_NO_DATA_FOUND
);
3556 } // wxDb::Catalog()
3559 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3561 * Table name can refer to a table, view, alias or synonym. Returns true
3562 * if the object exists in the database. This function does not indicate
3563 * whether or not the user has privleges to query or perform other functions
3566 * userID is evaluated in the following manner:
3567 * userID == NULL ... UserID is ignored
3568 * userID == "" ... UserID set equal to 'this->uid'
3569 * userID != "" ... UserID set equal to 'userID'
3572 wxASSERT(tableName
.length());
3576 if (Dbms() == dbmsDBASE
)
3579 if (tablePath
.length())
3580 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3582 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3585 exists
= wxFileExists(dbName
);
3590 convertUserID(userID
,UserID
);
3592 TableName
= tableName
;
3593 // Oracle and Interbase table names are uppercase only, so force
3594 // the name to uppercase just in case programmer forgot to do this
3595 if ((Dbms() == dbmsORACLE
) ||
3596 (Dbms() == dbmsFIREBIRD
) ||
3597 (Dbms() == dbmsINTERBASE
))
3598 TableName
= TableName
.Upper();
3600 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3603 // Some databases cannot accept a user name when looking up table names,
3604 // so we use the call below that leaves out the user name
3605 if (!UserID
.empty() &&
3606 Dbms() != dbmsMY_SQL
&&
3607 Dbms() != dbmsACCESS
&&
3608 Dbms() != dbmsMS_SQL_SERVER
&&
3609 Dbms() != dbmsDB2
&&
3610 Dbms() != dbmsFIREBIRD
&&
3611 Dbms() != dbmsINTERBASE
&&
3612 Dbms() != dbmsPERVASIVE_SQL
)
3614 retcode
= SQLTables(hstmt
,
3615 NULL
, 0, // All qualifiers
3616 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3617 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3618 NULL
, 0); // All table types
3622 retcode
= SQLTables(hstmt
,
3623 NULL
, 0, // All qualifiers
3624 NULL
, 0, // All owners
3625 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3626 NULL
, 0); // All table types
3628 if (retcode
!= SQL_SUCCESS
)
3629 return(DispAllErrors(henv
, hdbc
, hstmt
));
3631 retcode
= SQLFetch(hstmt
);
3632 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3634 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3635 return(DispAllErrors(henv
, hdbc
, hstmt
));
3638 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3642 } // wxDb::TableExists()
3645 /********** wxDb::TablePrivileges() **********/
3646 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3647 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3649 wxASSERT(tableName
.length());
3651 wxDbTablePrivilegeInfo result
;
3655 // We probably need to be able to dynamically set this based on
3656 // the driver type, and state.
3657 wxChar curRole
[]=wxT("public");
3661 wxString UserID
,Schema
;
3662 convertUserID(userID
,UserID
);
3663 convertUserID(schema
,Schema
);
3665 TableName
= tableName
;
3666 // Oracle and Interbase table names are uppercase only, so force
3667 // the name to uppercase just in case programmer forgot to do this
3668 if ((Dbms() == dbmsORACLE
) ||
3669 (Dbms() == dbmsFIREBIRD
) ||
3670 (Dbms() == dbmsINTERBASE
))
3671 TableName
= TableName
.Upper();
3673 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3675 // Some databases cannot accept a user name when looking up table names,
3676 // so we use the call below that leaves out the user name
3677 if (!Schema
.empty() &&
3678 Dbms() != dbmsMY_SQL
&&
3679 Dbms() != dbmsACCESS
&&
3680 Dbms() != dbmsMS_SQL_SERVER
)
3682 retcode
= SQLTablePrivileges(hstmt
,
3684 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3685 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3689 retcode
= SQLTablePrivileges(hstmt
,
3692 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3695 #ifdef DBDEBUG_CONSOLE
3696 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3699 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3700 return (DispAllErrors(henv
, hdbc
, hstmt
));
3702 bool failed
= false;
3703 retcode
= SQLFetch(hstmt
);
3704 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3706 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3709 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3712 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3715 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3718 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3721 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3724 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3729 return(DispAllErrors(henv
, hdbc
, hstmt
));
3731 #ifdef DBDEBUG_CONSOLE
3732 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3733 result
.privilege
,result
.tableOwner
,result
.tableName
,
3734 result
.grantor
, result
.grantee
);
3737 if (UserID
.IsSameAs(result
.tableOwner
,false))
3739 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3743 if (UserID
.IsSameAs(result
.grantee
,false) &&
3744 !wxStrcmp(result
.privilege
,priv
))
3746 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3750 if (!wxStrcmp(result
.grantee
,curRole
) &&
3751 !wxStrcmp(result
.privilege
,priv
))
3753 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3757 retcode
= SQLFetch(hstmt
);
3760 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3763 } // wxDb::TablePrivileges
3766 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3770 if (Dbms() == dbmsACCESS
)
3771 TableName
= _T("\"");
3772 TableName
+= tableName
;
3773 if (Dbms() == dbmsACCESS
)
3774 TableName
+= _T("\"");
3777 } // wxDb::SQLTableName()
3780 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3784 if (Dbms() == dbmsACCESS
)
3787 if (Dbms() == dbmsACCESS
)
3788 ColName
+= _T("\"");
3791 } // wxDb::SQLColumnName()
3794 /********** wxDb::SetSqlLogging() **********/
3795 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3797 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3798 wxASSERT(state
== sqlLogOFF
|| filename
.length());
3800 if (state
== sqlLogON
)
3804 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3805 if (fpSqlLog
== NULL
)
3813 if (fclose(fpSqlLog
))
3819 sqlLogState
= state
;
3822 } // wxDb::SetSqlLogging()
3825 /********** wxDb::WriteSqlLog() **********/
3826 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3828 wxASSERT(logMsg
.length());
3830 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3833 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3835 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3837 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3842 } // wxDb::WriteSqlLog()
3845 /********** wxDb::Dbms() **********/
3846 wxDBMS
wxDb::Dbms(void)
3848 * Be aware that not all database engines use the exact same syntax, and not
3849 * every ODBC compliant database is compliant to the same level of compliancy.
3850 * Some manufacturers support the minimum Level 1 compliancy, and others up
3851 * through Level 3. Others support subsets of features for levels above 1.
3853 * If you find an inconsistency between the wxDb class and a specific database
3854 * engine, and an identifier to this section, and special handle the database in
3855 * the area where behavior is non-conforming with the other databases.
3858 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3859 * ---------------------------------------------------
3862 * - Currently the only database supported by the class to support VIEWS
3865 * - Does not support the SQL_TIMESTAMP structure
3866 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3867 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3868 * is true. The user must create ALL indexes from their program.
3869 * - Table names can only be 8 characters long
3870 * - Column names can only be 10 characters long
3873 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3874 * after every table name involved in the query/join if that tables matching record(s)
3876 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3878 * SYBASE (Enterprise)
3879 * - If a column is part of the Primary Key, the column cannot be NULL
3880 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3883 * - If a column is part of the Primary Key, the column cannot be NULL
3884 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3885 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3886 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3887 * column definition if it is not defined correctly, but it is experimental
3888 * - Does not support sub-queries in SQL statements
3891 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3892 * - Does not support sub-queries in SQL statements
3895 * - Primary keys must be declared as NOT NULL
3896 * - Table and index names must not be longer than 13 characters in length (technically
3897 * table names can be up to 18 characters, but the primary index is created using the
3898 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3903 * - Columns that are part of primary keys must be defined as being NOT NULL
3904 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3905 * column definition if it is not defined correctly, but it is experimental
3908 // Should only need to do this once for each new database connection
3909 // so return the value we already determined it to be to save time
3910 // and lots of string comparisons
3911 if (dbmsType
!= dbmsUNIDENTIFIED
)
3914 #ifdef DBDEBUG_CONSOLE
3915 // When run in console mode, use standard out to display errors.
3916 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3917 #endif // DBDEBUG_CONSOLE
3919 wxLogDebug(wxT("Database connecting to: "));
3920 wxLogDebug(dbInf
.dbmsName
);
3922 wxChar baseName
[25+1];
3923 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3926 // RGG 20001025 : add support for Interbase
3927 // GT : Integrated to base classes on 20001121
3928 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3929 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3931 // BJO 20000428 : add support for Virtuoso
3932 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3933 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3935 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3936 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3938 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3939 // connected through an OpenLink driver.
3940 // Is it also returned by Sybase Adapatitve server?
3941 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3942 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3944 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3945 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3946 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3948 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3951 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3952 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3955 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3956 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3959 if (!wxStricmp(baseName
,wxT("Pervasive")))
3960 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3963 if (!wxStricmp(baseName
,wxT("Informix")))
3964 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3966 if (!wxStricmp(baseName
,wxT("Firebird")))
3967 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3970 if (!wxStricmp(baseName
,wxT("Oracle")))
3971 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3972 if (!wxStricmp(baseName
,wxT("ACCESS")))
3973 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3974 if (!wxStricmp(baseName
,wxT("Sybase")))
3975 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3978 if (!wxStricmp(baseName
,wxT("DBASE")))
3979 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3980 if (!wxStricmp(baseName
,wxT("xBase")))
3981 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3982 if (!wxStricmp(baseName
,wxT("MySQL")))
3983 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3984 if (!wxStricmp(baseName
,wxT("MaxDB")))
3985 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
3988 if (!wxStricmp(baseName
,wxT("DB2")))
3989 return((wxDBMS
)(dbmsType
= dbmsDB2
));
3991 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3996 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3997 int dataType
, ULONG columnLength
,
3998 const wxString
&optionalParam
)
4000 wxASSERT(tableName
.length());
4001 wxASSERT(columnName
.length());
4002 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
4003 dataType
!= DB_DATA_TYPE_VARCHAR
);
4005 // Must specify a columnLength if modifying a VARCHAR type column
4006 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
4009 wxString dataTypeName
;
4011 wxString alterSlashModify
;
4015 case DB_DATA_TYPE_VARCHAR
:
4016 dataTypeName
= typeInfVarchar
.TypeName
;
4018 case DB_DATA_TYPE_INTEGER
:
4019 dataTypeName
= typeInfInteger
.TypeName
;
4021 case DB_DATA_TYPE_FLOAT
:
4022 dataTypeName
= typeInfFloat
.TypeName
;
4024 case DB_DATA_TYPE_DATE
:
4025 dataTypeName
= typeInfDate
.TypeName
;
4027 case DB_DATA_TYPE_BLOB
:
4028 dataTypeName
= typeInfBlob
.TypeName
;
4034 // Set the modify or alter syntax depending on the type of database connected to
4038 alterSlashModify
= _T("MODIFY");
4040 case dbmsMS_SQL_SERVER
:
4041 alterSlashModify
= _T("ALTER COLUMN");
4043 case dbmsUNIDENTIFIED
:
4045 case dbmsSYBASE_ASA
:
4046 case dbmsSYBASE_ASE
:
4051 case dbmsXBASE_SEQUITER
:
4053 alterSlashModify
= _T("MODIFY");
4057 // create the SQL statement
4058 if ( Dbms() == dbmsMY_SQL
)
4060 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4061 columnName
.c_str(), dataTypeName
.c_str());
4065 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4066 columnName
.c_str(), dataTypeName
.c_str());
4069 // For varchars only, append the size of the column
4070 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4071 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4074 s
.Printf(wxT("(%lu)"), columnLength
);
4078 // for passing things like "NOT NULL"
4079 if (optionalParam
.length())
4081 sqlStmt
+= wxT(" ");
4082 sqlStmt
+= optionalParam
;
4085 return ExecSql(sqlStmt
);
4087 } // wxDb::ModifyColumn()
4090 /********** wxDbGetConnection() **********/
4091 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4095 // Used to keep a pointer to a DB connection that matches the requested
4096 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4097 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4098 // rather than having to re-query the datasource to get all the values
4099 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4100 wxDb
*matchingDbConnection
= NULL
;
4102 // Scan the linked list searching for an available database connection
4103 // that's already been opened but is currently not in use.
4104 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4106 // The database connection must be for the same datasource
4107 // name and must currently not be in use.
4109 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4111 if (pDbConfig
->UseConnectionStr())
4113 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4114 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4116 // Found a free connection
4117 pList
->Free
= false;
4118 return(pList
->PtrDb
);
4123 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4124 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4126 // Found a free connection
4127 pList
->Free
= false;
4128 return(pList
->PtrDb
);
4133 if (pDbConfig
->UseConnectionStr())
4135 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4136 matchingDbConnection
= pList
->PtrDb
;
4140 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4141 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4142 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4143 matchingDbConnection
= pList
->PtrDb
;
4147 // No available connections. A new connection must be made and
4148 // appended to the end of the linked list.
4151 // Find the end of the list
4152 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4153 // Append a new list item
4154 pList
->PtrNext
= new wxDbList
;
4155 pList
->PtrNext
->PtrPrev
= pList
;
4156 pList
= pList
->PtrNext
;
4160 // Create the first node on the list
4161 pList
= PtrBegDbList
= new wxDbList
;
4165 // Initialize new node in the linked list
4167 pList
->Free
= false;
4168 pList
->Dsn
= pDbConfig
->GetDsn();
4169 pList
->Uid
= pDbConfig
->GetUserID();
4170 pList
->AuthStr
= pDbConfig
->GetPassword();
4171 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4173 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4177 if (!matchingDbConnection
)
4179 if (pDbConfig
->UseConnectionStr())
4181 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4185 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4189 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4191 // Connect to the datasource
4194 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4195 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4196 return(pList
->PtrDb
);
4198 else // Unable to connect, destroy list item
4201 pList
->PtrPrev
->PtrNext
= 0;
4203 PtrBegDbList
= 0; // Empty list again
4205 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4206 pList
->PtrDb
->Close(); // Close the wxDb object
4207 delete pList
->PtrDb
; // Deletes the wxDb object
4208 delete pList
; // Deletes the linked list object
4212 } // wxDbGetConnection()
4215 /********** wxDbFreeConnection() **********/
4216 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4220 // Scan the linked list searching for the database connection
4221 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4223 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4224 return (pList
->Free
= true);
4227 // Never found the database object, return failure
4230 } // wxDbFreeConnection()
4233 /********** wxDbCloseConnections() **********/
4234 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4236 wxDbList
*pList
, *pNext
;
4238 // Traverse the linked list closing database connections and freeing memory as I go.
4239 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4241 pNext
= pList
->PtrNext
; // Save the pointer to next
4242 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4243 pList
->PtrDb
->Close(); // Close the wxDb object
4244 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4245 delete pList
->PtrDb
; // Deletes the wxDb object
4246 delete pList
; // Deletes the linked list object
4249 // Mark the list as empty
4252 } // wxDbCloseConnections()
4255 /********** wxDbConnectionsInUse() **********/
4256 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4261 // Scan the linked list counting db connections that are currently in use
4262 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4264 if (pList
->Free
== false)
4270 } // wxDbConnectionsInUse()
4274 /********** wxDbLogExtendedErrorMsg() **********/
4275 // DEBUG ONLY function
4276 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4278 const wxChar
*ErrFile
,
4281 static wxString msg
;
4286 if (ErrFile
|| ErrLine
)
4288 msg
+= wxT("File: ");
4290 msg
+= wxT(" Line: ");
4291 tStr
.Printf(wxT("%d"),ErrLine
);
4292 msg
+= tStr
.c_str();
4296 msg
.Append (wxT("\nODBC errors:\n"));
4299 // Display errors for this connection
4301 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4303 if (pDb
->errorList
[i
])
4305 msg
.Append(pDb
->errorList
[i
]);
4306 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4307 msg
.Append(wxT("\n"));
4308 // Clear the errmsg buffer so the next error will not
4309 // end up showing the previous error that have occurred
4310 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4315 wxLogDebug(msg
.c_str());
4318 } // wxDbLogExtendedErrorMsg()
4321 /********** wxDbSqlLog() **********/
4322 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4324 bool append
= false;
4327 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4329 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4334 SQLLOGstate
= state
;
4335 SQLLOGfn
= filename
;
4343 /********** wxDbCreateDataSource() **********/
4344 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4345 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4347 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4348 * Very rudimentary creation of an ODBC data source.
4350 * ODBC driver must be ODBC 3.0 compliant to use this function
4355 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4361 dsnLocation
= ODBC_ADD_SYS_DSN
;
4363 dsnLocation
= ODBC_ADD_DSN
;
4365 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4366 // so that is why I used it, as wxString does not deal well with
4367 // embedded nulls in strings
4368 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4370 // Replace the separator from above with the '\0' separator needed
4371 // by the SQLConfigDataSource() function
4375 k
= setupStr
.Find((wxChar
)2,true);
4376 if (k
!= wxNOT_FOUND
)
4377 setupStr
[(UINT
)k
] = wxT('\0');
4379 while (k
!= wxNOT_FOUND
);
4381 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4382 driverName
, setupStr
.c_str());
4384 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4386 // check for errors caused by ConfigDSN based functions
4389 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4390 errMsg
[0] = wxT('\0');
4392 // This function is only supported in ODBC drivers v3.0 compliant and above
4393 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4396 #ifdef DBDEBUG_CONSOLE
4397 // When run in console mode, use standard out to display errors.
4398 cout
<< errMsg
<< endl
;
4399 cout
<< wxT("Press any key to continue...") << endl
;
4401 #endif // DBDEBUG_CONSOLE
4404 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4405 #endif // __WXDEBUG__
4411 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4412 // necessary to use this function, so this function is not supported
4414 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4417 #endif // __VISUALC__
4421 } // wxDbCreateDataSource()
4425 /********** wxDbGetDataSource() **********/
4426 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4427 SWORD DsDescMaxLength
, UWORD direction
)
4429 * Dsn and DsDesc will contain the data source name and data source
4430 * description upon return
4434 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4435 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4437 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4438 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4443 } // wxDbGetDataSource()
4446 // Change this to 0 to remove use of all deprecated functions
4447 #if wxODBC_BACKWARD_COMPATABILITY
4448 /********************************************************************
4449 ********************************************************************
4451 * The following functions are all DEPRECATED and are included for
4452 * backward compatibility reasons only
4454 ********************************************************************
4455 ********************************************************************/
4456 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4458 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4460 /***** DEPRECATED: use wxGetDataSource() *****/
4461 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4464 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4466 /***** DEPRECATED: use wxDbGetConnection() *****/
4467 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4469 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4471 /***** DEPRECATED: use wxDbFreeConnection() *****/
4472 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4474 return wxDbFreeConnection(pDb
);
4476 /***** DEPRECATED: use wxDbCloseConnections() *****/
4477 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4479 wxDbCloseConnections();
4481 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4482 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4484 return wxDbConnectionsInUse();