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]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1759 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1760 wxLogDebug(s
.c_str(),s2
.c_str());
1762 pNode
= pNode
->GetNext();
1766 // Copy the error messages to a global variable
1768 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1769 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1771 dbmsType
= dbmsUNIDENTIFIED
;
1777 /********** wxDb::CommitTrans() **********/
1778 bool wxDb::CommitTrans(void)
1782 // Commit the transaction
1783 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1784 return(DispAllErrors(henv
, hdbc
));
1787 // Completed successfully
1790 } // wxDb::CommitTrans()
1793 /********** wxDb::RollbackTrans() **********/
1794 bool wxDb::RollbackTrans(void)
1796 // Rollback the transaction
1797 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1798 return(DispAllErrors(henv
, hdbc
));
1800 // Completed successfully
1803 } // wxDb::RollbackTrans()
1806 /********** wxDb::DispAllErrors() **********/
1807 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1809 * This function is called internally whenever an error condition prevents the user's
1810 * request from being executed. This function will query the datasource as to the
1811 * actual error(s) that just occurred on the previous request of the datasource.
1813 * The function will retrieve each error condition from the datasource and
1814 * Printf the codes/text values into a string which it then logs via logError().
1815 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1816 * window and program execution will be paused until the user presses a key.
1818 * This function always returns false, so that functions which call this function
1819 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1820 * of the user's request, so that the calling code can then process the error message log.
1823 wxString odbcErrMsg
;
1825 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1827 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1828 logError(odbcErrMsg
, sqlState
);
1831 #ifdef DBDEBUG_CONSOLE
1832 // When run in console mode, use standard out to display errors.
1833 cout
<< odbcErrMsg
.c_str() << endl
;
1834 cout
<< wxT("Press any key to continue...") << endl
;
1839 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1844 return false; // This function always returns false.
1846 } // wxDb::DispAllErrors()
1849 /********** wxDb::GetNextError() **********/
1850 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1852 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1857 } // wxDb::GetNextError()
1860 /********** wxDb::DispNextError() **********/
1861 void wxDb::DispNextError(void)
1863 wxString odbcErrMsg
;
1865 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1866 logError(odbcErrMsg
, sqlState
);
1871 #ifdef DBDEBUG_CONSOLE
1872 // When run in console mode, use standard out to display errors.
1873 cout
<< odbcErrMsg
.c_str() << endl
;
1874 cout
<< wxT("Press any key to continue...") << endl
;
1879 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1880 #endif // __WXDEBUG__
1882 } // wxDb::DispNextError()
1885 /********** wxDb::logError() **********/
1886 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1888 wxASSERT(errMsg
.length());
1890 static int pLast
= -1;
1893 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1896 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1897 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1901 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1902 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1904 if (SQLState
.length())
1905 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1906 DB_STATUS
= dbStatus
;
1908 // Add the errmsg to the sql log
1909 WriteSqlLog(errMsg
);
1911 } // wxDb::logError()
1914 /**********wxDb::TranslateSqlState() **********/
1915 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1917 if (!wxStrcmp(SQLState
, wxT("01000")))
1918 return(DB_ERR_GENERAL_WARNING
);
1919 if (!wxStrcmp(SQLState
, wxT("01002")))
1920 return(DB_ERR_DISCONNECT_ERROR
);
1921 if (!wxStrcmp(SQLState
, wxT("01004")))
1922 return(DB_ERR_DATA_TRUNCATED
);
1923 if (!wxStrcmp(SQLState
, wxT("01006")))
1924 return(DB_ERR_PRIV_NOT_REVOKED
);
1925 if (!wxStrcmp(SQLState
, wxT("01S00")))
1926 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1927 if (!wxStrcmp(SQLState
, wxT("01S01")))
1928 return(DB_ERR_ERROR_IN_ROW
);
1929 if (!wxStrcmp(SQLState
, wxT("01S02")))
1930 return(DB_ERR_OPTION_VALUE_CHANGED
);
1931 if (!wxStrcmp(SQLState
, wxT("01S03")))
1932 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1933 if (!wxStrcmp(SQLState
, wxT("01S04")))
1934 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1935 if (!wxStrcmp(SQLState
, wxT("07001")))
1936 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1937 if (!wxStrcmp(SQLState
, wxT("07006")))
1938 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1939 if (!wxStrcmp(SQLState
, wxT("08001")))
1940 return(DB_ERR_UNABLE_TO_CONNECT
);
1941 if (!wxStrcmp(SQLState
, wxT("08002")))
1942 return(DB_ERR_CONNECTION_IN_USE
);
1943 if (!wxStrcmp(SQLState
, wxT("08003")))
1944 return(DB_ERR_CONNECTION_NOT_OPEN
);
1945 if (!wxStrcmp(SQLState
, wxT("08004")))
1946 return(DB_ERR_REJECTED_CONNECTION
);
1947 if (!wxStrcmp(SQLState
, wxT("08007")))
1948 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1949 if (!wxStrcmp(SQLState
, wxT("08S01")))
1950 return(DB_ERR_COMM_LINK_FAILURE
);
1951 if (!wxStrcmp(SQLState
, wxT("21S01")))
1952 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1953 if (!wxStrcmp(SQLState
, wxT("21S02")))
1954 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1955 if (!wxStrcmp(SQLState
, wxT("22001")))
1956 return(DB_ERR_STRING_RIGHT_TRUNC
);
1957 if (!wxStrcmp(SQLState
, wxT("22003")))
1958 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1959 if (!wxStrcmp(SQLState
, wxT("22005")))
1960 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1961 if (!wxStrcmp(SQLState
, wxT("22008")))
1962 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1963 if (!wxStrcmp(SQLState
, wxT("22012")))
1964 return(DB_ERR_DIVIDE_BY_ZERO
);
1965 if (!wxStrcmp(SQLState
, wxT("22026")))
1966 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1967 if (!wxStrcmp(SQLState
, wxT("23000")))
1968 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1969 if (!wxStrcmp(SQLState
, wxT("24000")))
1970 return(DB_ERR_INVALID_CURSOR_STATE
);
1971 if (!wxStrcmp(SQLState
, wxT("25000")))
1972 return(DB_ERR_INVALID_TRANS_STATE
);
1973 if (!wxStrcmp(SQLState
, wxT("28000")))
1974 return(DB_ERR_INVALID_AUTH_SPEC
);
1975 if (!wxStrcmp(SQLState
, wxT("34000")))
1976 return(DB_ERR_INVALID_CURSOR_NAME
);
1977 if (!wxStrcmp(SQLState
, wxT("37000")))
1978 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1979 if (!wxStrcmp(SQLState
, wxT("3C000")))
1980 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1981 if (!wxStrcmp(SQLState
, wxT("40001")))
1982 return(DB_ERR_SERIALIZATION_FAILURE
);
1983 if (!wxStrcmp(SQLState
, wxT("42000")))
1984 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1985 if (!wxStrcmp(SQLState
, wxT("70100")))
1986 return(DB_ERR_OPERATION_ABORTED
);
1987 if (!wxStrcmp(SQLState
, wxT("IM001")))
1988 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1989 if (!wxStrcmp(SQLState
, wxT("IM002")))
1990 return(DB_ERR_NO_DATA_SOURCE
);
1991 if (!wxStrcmp(SQLState
, wxT("IM003")))
1992 return(DB_ERR_DRIVER_LOAD_ERROR
);
1993 if (!wxStrcmp(SQLState
, wxT("IM004")))
1994 return(DB_ERR_SQLALLOCENV_FAILED
);
1995 if (!wxStrcmp(SQLState
, wxT("IM005")))
1996 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1997 if (!wxStrcmp(SQLState
, wxT("IM006")))
1998 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1999 if (!wxStrcmp(SQLState
, wxT("IM007")))
2000 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
2001 if (!wxStrcmp(SQLState
, wxT("IM008")))
2002 return(DB_ERR_DIALOG_FAILED
);
2003 if (!wxStrcmp(SQLState
, wxT("IM009")))
2004 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
2005 if (!wxStrcmp(SQLState
, wxT("IM010")))
2006 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
2007 if (!wxStrcmp(SQLState
, wxT("IM011")))
2008 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
2009 if (!wxStrcmp(SQLState
, wxT("IM012")))
2010 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
2011 if (!wxStrcmp(SQLState
, wxT("IM013")))
2012 return(DB_ERR_TRACE_FILE_ERROR
);
2013 if (!wxStrcmp(SQLState
, wxT("S0001")))
2014 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
2015 if (!wxStrcmp(SQLState
, wxT("S0002")))
2016 return(DB_ERR_TABLE_NOT_FOUND
);
2017 if (!wxStrcmp(SQLState
, wxT("S0011")))
2018 return(DB_ERR_INDEX_ALREADY_EXISTS
);
2019 if (!wxStrcmp(SQLState
, wxT("S0012")))
2020 return(DB_ERR_INDEX_NOT_FOUND
);
2021 if (!wxStrcmp(SQLState
, wxT("S0021")))
2022 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
2023 if (!wxStrcmp(SQLState
, wxT("S0022")))
2024 return(DB_ERR_COLUMN_NOT_FOUND
);
2025 if (!wxStrcmp(SQLState
, wxT("S0023")))
2026 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2027 if (!wxStrcmp(SQLState
, wxT("S1000")))
2028 return(DB_ERR_GENERAL_ERROR
);
2029 if (!wxStrcmp(SQLState
, wxT("S1001")))
2030 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2031 if (!wxStrcmp(SQLState
, wxT("S1002")))
2032 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2033 if (!wxStrcmp(SQLState
, wxT("S1003")))
2034 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2035 if (!wxStrcmp(SQLState
, wxT("S1004")))
2036 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2037 if (!wxStrcmp(SQLState
, wxT("S1008")))
2038 return(DB_ERR_OPERATION_CANCELLED
);
2039 if (!wxStrcmp(SQLState
, wxT("S1009")))
2040 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2041 if (!wxStrcmp(SQLState
, wxT("S1010")))
2042 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2043 if (!wxStrcmp(SQLState
, wxT("S1011")))
2044 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2045 if (!wxStrcmp(SQLState
, wxT("S1012")))
2046 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2047 if (!wxStrcmp(SQLState
, wxT("S1015")))
2048 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2049 if (!wxStrcmp(SQLState
, wxT("S1090")))
2050 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2051 if (!wxStrcmp(SQLState
, wxT("S1091")))
2052 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2053 if (!wxStrcmp(SQLState
, wxT("S1092")))
2054 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2055 if (!wxStrcmp(SQLState
, wxT("S1093")))
2056 return(DB_ERR_INVALID_PARAM_NO
);
2057 if (!wxStrcmp(SQLState
, wxT("S1094")))
2058 return(DB_ERR_INVALID_SCALE_VALUE
);
2059 if (!wxStrcmp(SQLState
, wxT("S1095")))
2060 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2061 if (!wxStrcmp(SQLState
, wxT("S1096")))
2062 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2063 if (!wxStrcmp(SQLState
, wxT("S1097")))
2064 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2065 if (!wxStrcmp(SQLState
, wxT("S1098")))
2066 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2067 if (!wxStrcmp(SQLState
, wxT("S1099")))
2068 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2069 if (!wxStrcmp(SQLState
, wxT("S1100")))
2070 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2071 if (!wxStrcmp(SQLState
, wxT("S1101")))
2072 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2073 if (!wxStrcmp(SQLState
, wxT("S1103")))
2074 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2075 if (!wxStrcmp(SQLState
, wxT("S1104")))
2076 return(DB_ERR_INVALID_PRECISION_VALUE
);
2077 if (!wxStrcmp(SQLState
, wxT("S1105")))
2078 return(DB_ERR_INVALID_PARAM_TYPE
);
2079 if (!wxStrcmp(SQLState
, wxT("S1106")))
2080 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2081 if (!wxStrcmp(SQLState
, wxT("S1107")))
2082 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2083 if (!wxStrcmp(SQLState
, wxT("S1108")))
2084 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2085 if (!wxStrcmp(SQLState
, wxT("S1109")))
2086 return(DB_ERR_INVALID_CURSOR_POSITION
);
2087 if (!wxStrcmp(SQLState
, wxT("S1110")))
2088 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2089 if (!wxStrcmp(SQLState
, wxT("S1111")))
2090 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2091 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2092 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2093 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2094 return(DB_ERR_TIMEOUT_EXPIRED
);
2099 } // wxDb::TranslateSqlState()
2102 /********** wxDb::Grant() **********/
2103 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2107 // Build the grant statement
2108 sqlStmt
= wxT("GRANT ");
2109 if (privileges
== DB_GRANT_ALL
)
2110 sqlStmt
+= wxT("ALL");
2114 if (privileges
& DB_GRANT_SELECT
)
2116 sqlStmt
+= wxT("SELECT");
2119 if (privileges
& DB_GRANT_INSERT
)
2122 sqlStmt
+= wxT(", ");
2123 sqlStmt
+= wxT("INSERT");
2125 if (privileges
& DB_GRANT_UPDATE
)
2128 sqlStmt
+= wxT(", ");
2129 sqlStmt
+= wxT("UPDATE");
2131 if (privileges
& DB_GRANT_DELETE
)
2134 sqlStmt
+= wxT(", ");
2135 sqlStmt
+= wxT("DELETE");
2139 sqlStmt
+= wxT(" ON ");
2140 sqlStmt
+= SQLTableName(tableName
);
2141 sqlStmt
+= wxT(" TO ");
2142 sqlStmt
+= userList
;
2144 #ifdef DBDEBUG_CONSOLE
2145 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2148 WriteSqlLog(sqlStmt
);
2150 return(ExecSql(sqlStmt
));
2155 /********** wxDb::CreateView() **********/
2156 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2157 const wxString
&pSqlStmt
, bool attemptDrop
)
2161 // Drop the view first
2162 if (attemptDrop
&& !DropView(viewName
))
2165 // Build the create view statement
2166 sqlStmt
= wxT("CREATE VIEW ");
2167 sqlStmt
+= viewName
;
2169 if (colList
.length())
2171 sqlStmt
+= wxT(" (");
2173 sqlStmt
+= wxT(")");
2176 sqlStmt
+= wxT(" AS ");
2177 sqlStmt
+= pSqlStmt
;
2179 WriteSqlLog(sqlStmt
);
2181 #ifdef DBDEBUG_CONSOLE
2182 cout
<< sqlStmt
.c_str() << endl
;
2185 return(ExecSql(sqlStmt
));
2187 } // wxDb::CreateView()
2190 /********** wxDb::DropView() **********/
2191 bool wxDb::DropView(const wxString
&viewName
)
2194 * NOTE: This function returns true if the View does not exist, but
2195 * only for identified databases. Code will need to be added
2196 * below for any other databases when those databases are defined
2197 * to handle this situation consistently
2201 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2203 WriteSqlLog(sqlStmt
);
2205 #ifdef DBDEBUG_CONSOLE
2206 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2209 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2211 // Check for "Base table not found" error and ignore
2212 GetNextError(henv
, hdbc
, hstmt
);
2213 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2215 // Check for product specific error codes
2216 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2219 DispAllErrors(henv
, hdbc
, hstmt
);
2226 // Commit the transaction
2232 } // wxDb::DropView()
2235 /********** wxDb::ExecSql() **********/
2236 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2240 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2242 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2243 if (retcode
== SQL_SUCCESS
||
2244 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2250 DispAllErrors(henv
, hdbc
, hstmt
);
2254 } // wxDb::ExecSql()
2257 /********** wxDb::ExecSql() with column info **********/
2258 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2260 //execute the statement first
2261 if (!ExecSql(pSqlStmt
))
2265 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2267 DispAllErrors(henv
, hdbc
, hstmt
);
2276 // Get column information
2278 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2281 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2283 // Fill in column information (name, datatype)
2284 for (colNum
= 0; colNum
< noCols
; colNum
++)
2286 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2288 &Sword
, &Sqllen
) != SQL_SUCCESS
)
2290 DispAllErrors(henv
, hdbc
, hstmt
);
2295 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2296 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2298 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2299 NULL
, 0, &Sword
, &Sqllen
) != SQL_SUCCESS
)
2301 DispAllErrors(henv
, hdbc
, hstmt
);
2309 #if defined(SQL_WCHAR)
2312 #if defined(SQL_WVARCHAR)
2318 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2320 case SQL_LONGVARCHAR
:
2321 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_MEMO
;
2327 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2334 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2338 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2341 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2346 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen
);
2347 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2354 } // wxDb::ExecSql()
2356 /********** wxDb::GetNext() **********/
2357 bool wxDb::GetNext(void)
2359 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2363 DispAllErrors(henv
, hdbc
, hstmt
);
2367 } // wxDb::GetNext()
2370 /********** wxDb::GetData() **********/
2371 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SQLLEN FAR
*cbReturned
)
2374 wxASSERT(cbReturned
);
2376 long bufferSize
= maxLen
;
2378 if (cType
== SQL_C_WXCHAR
)
2379 bufferSize
= maxLen
* sizeof(wxChar
);
2381 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2385 DispAllErrors(henv
, hdbc
, hstmt
);
2389 } // wxDb::GetData()
2392 /********** wxDb::GetKeyFields() **********/
2393 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2395 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2396 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2398 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2399 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2405 * -----------------------------------------------------------------------
2406 * -- 19991224 : mj10777 : Create ------
2407 * -- : Three things are done and stored here : ------
2408 * -- : 1) which Column(s) is/are Primary Key(s) ------
2409 * -- : 2) which tables use this Key as a Foreign Key ------
2410 * -- : 3) which columns are Foreign Key and the name ------
2411 * -- : of the Table where the Key is the Primary Key -----
2412 * -- : Called from GetColumns(const wxString &tableName, ------
2413 * -- int *numCols,const wxChar *userID ) ------
2414 * -----------------------------------------------------------------------
2417 /*---------------------------------------------------------------------*/
2418 /* Get the names of the columns in the primary key. */
2419 /*---------------------------------------------------------------------*/
2420 retcode
= SQLPrimaryKeys(hstmt
,
2421 NULL
, 0, /* Catalog name */
2422 NULL
, 0, /* Schema name */
2423 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2425 /*---------------------------------------------------------------------*/
2426 /* Fetch and display the result set. This will be a list of the */
2427 /* columns in the primary key of the tableName table. */
2428 /*---------------------------------------------------------------------*/
2429 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2431 retcode
= SQLFetch(hstmt
);
2432 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2434 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2435 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2437 for (i
=0;i
<noCols
;i
++) // Find the Column name
2438 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2439 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2442 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2444 /*---------------------------------------------------------------------*/
2445 /* Get all the foreign keys that refer to tableName primary key. */
2446 /*---------------------------------------------------------------------*/
2447 retcode
= SQLForeignKeys(hstmt
,
2448 NULL
, 0, /* Primary catalog */
2449 NULL
, 0, /* Primary schema */
2450 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2451 NULL
, 0, /* Foreign catalog */
2452 NULL
, 0, /* Foreign schema */
2453 NULL
, 0); /* Foreign table */
2455 /*---------------------------------------------------------------------*/
2456 /* Fetch and display the result set. This will be all of the foreign */
2457 /* keys in other tables that refer to the tableName primary key. */
2458 /*---------------------------------------------------------------------*/
2461 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2463 retcode
= SQLFetch(hstmt
);
2464 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2466 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2467 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2468 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2469 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2470 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2471 tempStr
<< _T('[') << szFkTable
<< _T(']'); // [ ] in case there is a blank in the Table name
2475 tempStr
.Trim(); // Get rid of any unneeded blanks
2476 if (!tempStr
.empty())
2478 for (i
=0; i
<noCols
; i
++)
2479 { // Find the Column name
2480 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2482 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
2483 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2488 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2490 /*---------------------------------------------------------------------*/
2491 /* Get all the foreign keys in the tablename table. */
2492 /*---------------------------------------------------------------------*/
2493 retcode
= SQLForeignKeys(hstmt
,
2494 NULL
, 0, /* Primary catalog */
2495 NULL
, 0, /* Primary schema */
2496 NULL
, 0, /* Primary table */
2497 NULL
, 0, /* Foreign catalog */
2498 NULL
, 0, /* Foreign schema */
2499 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2501 /*---------------------------------------------------------------------*/
2502 /* Fetch and display the result set. This will be all of the */
2503 /* primary keys in other tables that are referred to by foreign */
2504 /* keys in the tableName table. */
2505 /*---------------------------------------------------------------------*/
2506 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2508 retcode
= SQLFetch(hstmt
);
2509 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2511 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2512 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2513 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2515 for (i
=0; i
<noCols
; i
++) // Find the Column name
2517 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2519 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2520 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2521 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2526 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2530 } // wxDb::GetKeyFields()
2534 /********** wxDb::GetColumns() **********/
2535 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2537 * 1) The last array element of the tableName[] argument must be zero (null).
2538 * This is how the end of the array is detected.
2539 * 2) This function returns an array of wxDbColInf structures. If no columns
2540 * were found, or an error occurred, this pointer will be zero (null). THE
2541 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2542 * IS FINISHED WITH IT. i.e.
2544 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2547 * // Use the column inf
2549 * // Destroy the memory
2553 * userID is evaluated in the following manner:
2554 * userID == NULL ... UserID is ignored
2555 * userID == "" ... UserID set equal to 'this->uid'
2556 * userID != "" ... UserID set equal to 'userID'
2558 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2559 * by this function. This function should use its own wxDb instance
2560 * to avoid undesired unbinding of columns.
2565 wxDbColInf
*colInf
= 0;
2573 convertUserID(userID
,UserID
);
2575 // Pass 1 - Determine how many columns there are.
2576 // Pass 2 - Allocate the wxDbColInf array and fill in
2577 // the array with the column information.
2579 for (pass
= 1; pass
<= 2; pass
++)
2583 if (noCols
== 0) // Probably a bogus table name(s)
2585 // Allocate n wxDbColInf objects to hold the column information
2586 colInf
= new wxDbColInf
[noCols
+1];
2589 // Mark the end of the array
2590 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2591 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2592 colInf
[noCols
].sqlDataType
= 0;
2594 // Loop through each table name
2596 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2598 TableName
= tableName
[tbl
];
2599 // Oracle and Interbase table names are uppercase only, so force
2600 // the name to uppercase just in case programmer forgot to do this
2601 if ((Dbms() == dbmsORACLE
) ||
2602 (Dbms() == dbmsFIREBIRD
) ||
2603 (Dbms() == dbmsINTERBASE
))
2604 TableName
= TableName
.Upper();
2606 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2608 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2609 // use the call below that leaves out the user name
2610 if (!UserID
.empty() &&
2611 Dbms() != dbmsMY_SQL
&&
2612 Dbms() != dbmsACCESS
&&
2613 Dbms() != dbmsMS_SQL_SERVER
)
2615 retcode
= SQLColumns(hstmt
,
2616 NULL
, 0, // All qualifiers
2617 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2618 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2619 NULL
, 0); // All columns
2623 retcode
= SQLColumns(hstmt
,
2624 NULL
, 0, // All qualifiers
2626 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2627 NULL
, 0); // All columns
2629 if (retcode
!= SQL_SUCCESS
)
2630 { // Error occurred, abort
2631 DispAllErrors(henv
, hdbc
, hstmt
);
2634 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2638 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2640 if (pass
== 1) // First pass, just add up the number of columns
2642 else // Pass 2; Fill in the array of structures
2644 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2646 // NOTE: Only the ODBC 1.x fields are retrieved
2647 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2648 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2649 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2650 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2651 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2652 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2653 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2654 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2655 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2656 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2657 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2658 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2660 // Determine the wxDb data type that is used to represent the native data type of this data source
2661 colInf
[colNo
].dbDataType
= 0;
2662 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2665 // IODBC does not return a correct columnLength, so we set
2666 // columnLength = bufferSize if no column length was returned
2667 // IODBC returns the columnLength in bufferSize. (bug)
2668 if (colInf
[colNo
].columnLength
< 1)
2670 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2673 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2675 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2676 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2677 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2678 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2679 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2680 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2681 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2682 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2687 if (retcode
!= SQL_NO_DATA_FOUND
)
2688 { // Error occurred, abort
2689 DispAllErrors(henv
, hdbc
, hstmt
);
2692 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2698 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2701 } // wxDb::GetColumns()
2704 /********** wxDb::GetColumns() **********/
2706 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2708 // Same as the above GetColumns() function except this one gets columns
2709 // only for a single table, and if 'numCols' is not NULL, the number of
2710 // columns stored in the returned wxDbColInf is set in '*numCols'
2712 // userID is evaluated in the following manner:
2713 // userID == NULL ... UserID is ignored
2714 // userID == "" ... UserID set equal to 'this->uid'
2715 // userID != "" ... UserID set equal to 'userID'
2717 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2718 // by this function. This function should use its own wxDb instance
2719 // to avoid undesired unbinding of columns.
2724 wxDbColInf
*colInf
= 0;
2732 convertUserID(userID
,UserID
);
2734 // Pass 1 - Determine how many columns there are.
2735 // Pass 2 - Allocate the wxDbColInf array and fill in
2736 // the array with the column information.
2738 for (pass
= 1; pass
<= 2; pass
++)
2742 if (noCols
== 0) // Probably a bogus table name(s)
2744 // Allocate n wxDbColInf objects to hold the column information
2745 colInf
= new wxDbColInf
[noCols
+1];
2748 // Mark the end of the array
2749 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2750 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2751 colInf
[noCols
].sqlDataType
= 0;
2754 TableName
= tableName
;
2755 // Oracle and Interbase table names are uppercase only, so force
2756 // the name to uppercase just in case programmer forgot to do this
2757 if ((Dbms() == dbmsORACLE
) ||
2758 (Dbms() == dbmsFIREBIRD
) ||
2759 (Dbms() == dbmsINTERBASE
))
2760 TableName
= TableName
.Upper();
2762 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2764 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2765 // use the call below that leaves out the user name
2766 if (!UserID
.empty() &&
2767 Dbms() != dbmsMY_SQL
&&
2768 Dbms() != dbmsACCESS
&&
2769 Dbms() != dbmsMS_SQL_SERVER
)
2771 retcode
= SQLColumns(hstmt
,
2772 NULL
, 0, // All qualifiers
2773 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2774 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2775 NULL
, 0); // All columns
2779 retcode
= SQLColumns(hstmt
,
2780 NULL
, 0, // All qualifiers
2782 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2783 NULL
, 0); // All columns
2785 if (retcode
!= SQL_SUCCESS
)
2786 { // Error occurred, abort
2787 DispAllErrors(henv
, hdbc
, hstmt
);
2790 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2796 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2798 if (pass
== 1) // First pass, just add up the number of columns
2800 else // Pass 2; Fill in the array of structures
2802 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2804 // NOTE: Only the ODBC 1.x fields are retrieved
2805 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2806 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2807 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2808 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2809 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2810 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2811 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2812 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2813 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2814 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2815 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2816 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2817 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2818 // Start Values for Primary/Foriegn Key (=No)
2819 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2820 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2821 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2822 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2824 // BJO 20000428 : Virtuoso returns type names with upper cases!
2825 if (Dbms() == dbmsVIRTUOSO
)
2827 wxString s
= colInf
[colNo
].typeName
;
2829 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2832 // Determine the wxDb data type that is used to represent the native data type of this data source
2833 colInf
[colNo
].dbDataType
= 0;
2834 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2837 // IODBC does not return a correct columnLength, so we set
2838 // columnLength = bufferSize if no column length was returned
2839 // IODBC returns the columnLength in bufferSize. (bug)
2840 if (colInf
[colNo
].columnLength
< 1)
2842 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2846 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2848 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2849 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2850 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2851 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2852 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2853 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2854 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2855 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2861 if (retcode
!= SQL_NO_DATA_FOUND
)
2862 { // Error occurred, abort
2863 DispAllErrors(henv
, hdbc
, hstmt
);
2866 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2873 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2875 // Store Primary and Foriegn Keys
2876 GetKeyFields(tableName
,colInf
,noCols
);
2882 } // wxDb::GetColumns()
2885 #else // New GetColumns
2890 These are tentative new GetColumns members which should be more database
2891 independent and which always returns the columns in the order they were
2894 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2895 wxChar* userID)) calls the second implementation for each separate table
2896 before merging the results. This makes the code easier to maintain as
2897 only one member (the second) makes the real work
2898 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2899 wxChar *userID) is a little bit improved
2900 - It doesn't anymore rely on the type-name to find out which database-type
2902 - It ends by sorting the columns, so that they are returned in the same
2903 order they were created
2913 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2916 // The last array element of the tableName[] argument must be zero (null).
2917 // This is how the end of the array is detected.
2921 // How many tables ?
2923 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2925 // Create a table to maintain the columns for each separate table
2926 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2929 for (i
= 0 ; i
< tbl
; i
++)
2932 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2933 if (TableColumns
[i
].colInf
== NULL
)
2935 noCols
+= TableColumns
[i
].noCols
;
2938 // Now merge all the separate table infos
2939 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2941 // Mark the end of the array
2942 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2943 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2944 colInf
[noCols
].sqlDataType
= 0;
2949 for (i
= 0 ; i
< tbl
; i
++)
2951 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2953 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2957 delete [] TableColumns
;
2960 } // wxDb::GetColumns() -- NEW
2963 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2965 // Same as the above GetColumns() function except this one gets columns
2966 // only for a single table, and if 'numCols' is not NULL, the number of
2967 // columns stored in the returned wxDbColInf is set in '*numCols'
2969 // userID is evaluated in the following manner:
2970 // userID == NULL ... UserID is ignored
2971 // userID == "" ... UserID set equal to 'this->uid'
2972 // userID != "" ... UserID set equal to 'userID'
2974 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2975 // by this function. This function should use its own wxDb instance
2976 // to avoid undesired unbinding of columns.
2980 wxDbColInf
*colInf
= 0;
2988 convertUserID(userID
,UserID
);
2990 // Pass 1 - Determine how many columns there are.
2991 // Pass 2 - Allocate the wxDbColInf array and fill in
2992 // the array with the column information.
2994 for (pass
= 1; pass
<= 2; pass
++)
2998 if (noCols
== 0) // Probably a bogus table name(s)
3000 // Allocate n wxDbColInf objects to hold the column information
3001 colInf
= new wxDbColInf
[noCols
+1];
3004 // Mark the end of the array
3005 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
3006 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
3007 colInf
[noCols
].sqlDataType
= 0;
3010 TableName
= tableName
;
3011 // Oracle and Interbase table names are uppercase only, so force
3012 // the name to uppercase just in case programmer forgot to do this
3013 if ((Dbms() == dbmsORACLE
) ||
3014 (Dbms() == dbmsFIREBIRD
) ||
3015 (Dbms() == dbmsINTERBASE
))
3016 TableName
= TableName
.Upper();
3018 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3020 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3021 // use the call below that leaves out the user name
3022 if (!UserID
.empty() &&
3023 Dbms() != dbmsMY_SQL
&&
3024 Dbms() != dbmsACCESS
&&
3025 Dbms() != dbmsMS_SQL_SERVER
)
3027 retcode
= SQLColumns(hstmt
,
3028 NULL
, 0, // All qualifiers
3029 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3030 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3031 NULL
, 0); // All columns
3035 retcode
= SQLColumns(hstmt
,
3036 NULL
, 0, // All qualifiers
3038 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3039 NULL
, 0); // All columns
3041 if (retcode
!= SQL_SUCCESS
)
3042 { // Error occurred, abort
3043 DispAllErrors(henv
, hdbc
, hstmt
);
3046 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3052 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3054 if (pass
== 1) // First pass, just add up the number of columns
3056 else // Pass 2; Fill in the array of structures
3058 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3060 // NOTE: Only the ODBC 1.x fields are retrieved
3061 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3062 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3063 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3064 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3065 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3066 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3067 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3068 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3069 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3070 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3071 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3072 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3073 // Start Values for Primary/Foriegn Key (=No)
3074 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3075 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3076 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3077 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3080 // IODBC does not return a correct columnLength, so we set
3081 // columnLength = bufferSize if no column length was returned
3082 // IODBC returns the columnLength in bufferSize. (bug)
3083 if (colInf
[colNo
].columnLength
< 1)
3085 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3089 // Determine the wxDb data type that is used to represent the native data type of this data source
3090 colInf
[colNo
].dbDataType
= 0;
3091 // Get the intern datatype
3092 switch (colInf
[colNo
].sqlDataType
)
3095 #if defined(SQL_WCHAR)
3098 #if defined(SQL_WVARCHAR)
3104 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3106 case SQL_LONGVARCHAR
:
3107 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_MEMO
;
3113 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3120 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3124 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3127 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3132 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3133 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3140 if (retcode
!= SQL_NO_DATA_FOUND
)
3141 { // Error occurred, abort
3142 DispAllErrors(henv
, hdbc
, hstmt
);
3145 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3152 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3154 // Store Primary and Foreign Keys
3155 GetKeyFields(tableName
,colInf
,noCols
);
3157 ///////////////////////////////////////////////////////////////////////////
3158 // Now sort the the columns in order to make them appear in the right order
3159 ///////////////////////////////////////////////////////////////////////////
3161 // Build a generic SELECT statement which returns 0 rows
3164 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3167 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3169 DispAllErrors(henv
, hdbc
, hstmt
);
3173 // Get the number of result columns
3174 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3176 DispAllErrors(henv
, hdbc
, hstmt
);
3180 if (noCols
== 0) // Probably a bogus table name
3189 for (colNum
= 0; colNum
< noCols
; colNum
++)
3191 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3193 &Sword
, &Sdword
) != SQL_SUCCESS
)
3195 DispAllErrors(henv
, hdbc
, hstmt
);
3199 wxString Name1
= name
;
3200 Name1
= Name1
.Upper();
3202 // Where is this name in the array ?
3203 for (i
= colNum
; i
< noCols
; i
++)
3205 wxString Name2
= colInf
[i
].colName
;
3206 Name2
= Name2
.Upper();
3209 if (colNum
!= i
) // swap to sort
3211 wxDbColInf tmpColInf
= colInf
[colNum
];
3212 colInf
[colNum
] = colInf
[i
];
3213 colInf
[i
] = tmpColInf
;
3219 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3221 ///////////////////////////////////////////////////////////////////////////
3223 ///////////////////////////////////////////////////////////////////////////
3229 } // wxDb::GetColumns()
3232 #endif // #else OLD_GETCOLUMNS
3235 /********** wxDb::GetColumnCount() **********/
3236 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3238 * Returns a count of how many columns are in a table.
3239 * If an error occurs in computing the number of columns
3240 * this function will return a -1 for the count
3242 * userID is evaluated in the following manner:
3243 * userID == NULL ... UserID is ignored
3244 * userID == "" ... UserID set equal to 'this->uid'
3245 * userID != "" ... UserID set equal to 'userID'
3247 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3248 * by this function. This function should use its own wxDb instance
3249 * to avoid undesired unbinding of columns.
3259 convertUserID(userID
,UserID
);
3261 TableName
= tableName
;
3262 // Oracle and Interbase table names are uppercase only, so force
3263 // the name to uppercase just in case programmer forgot to do this
3264 if ((Dbms() == dbmsORACLE
) ||
3265 (Dbms() == dbmsFIREBIRD
) ||
3266 (Dbms() == dbmsINTERBASE
))
3267 TableName
= TableName
.Upper();
3269 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3271 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3272 // use the call below that leaves out the user name
3273 if (!UserID
.empty() &&
3274 Dbms() != dbmsMY_SQL
&&
3275 Dbms() != dbmsACCESS
&&
3276 Dbms() != dbmsMS_SQL_SERVER
)
3278 retcode
= SQLColumns(hstmt
,
3279 NULL
, 0, // All qualifiers
3280 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3281 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3282 NULL
, 0); // All columns
3286 retcode
= SQLColumns(hstmt
,
3287 NULL
, 0, // All qualifiers
3289 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3290 NULL
, 0); // All columns
3292 if (retcode
!= SQL_SUCCESS
)
3293 { // Error occurred, abort
3294 DispAllErrors(henv
, hdbc
, hstmt
);
3295 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3299 // Count the columns
3300 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3303 if (retcode
!= SQL_NO_DATA_FOUND
)
3304 { // Error occurred, abort
3305 DispAllErrors(henv
, hdbc
, hstmt
);
3306 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3310 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3313 } // wxDb::GetColumnCount()
3316 /********** wxDb::GetCatalog() *******/
3317 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3319 * ---------------------------------------------------------------------
3320 * -- 19991203 : mj10777 : Create ------
3321 * -- : Creates a wxDbInf with Tables / Cols Array ------
3322 * -- : uses SQLTables and fills pTableInf; ------
3323 * -- : pColInf is set to NULL and numCols to 0; ------
3324 * -- : returns pDbInf (wxDbInf) ------
3325 * -- - if unsuccessful (pDbInf == NULL) ------
3326 * -- : pColInf can be filled with GetColumns(..); ------
3327 * -- : numCols can be filled with GetColumnCount(..); ------
3328 * ---------------------------------------------------------------------
3330 * userID is evaluated in the following manner:
3331 * userID == NULL ... UserID is ignored
3332 * userID == "" ... UserID set equal to 'this->uid'
3333 * userID != "" ... UserID set equal to 'userID'
3335 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3336 * by this function. This function should use its own wxDb instance
3337 * to avoid undesired unbinding of columns.
3340 int noTab
= 0; // Counter while filling table entries
3344 wxString tblNameSave
;
3347 convertUserID(userID
,UserID
);
3349 //-------------------------------------------------------------
3350 // Create the Database Array of catalog entries
3352 wxDbInf
*pDbInf
= new wxDbInf
;
3354 //-------------------------------------------------------------
3355 // Table Information
3356 // Pass 1 - Determine how many Tables there are.
3357 // Pass 2 - Create the Table array and fill it
3358 // - Create the Cols array = NULL
3359 //-------------------------------------------------------------
3361 for (pass
= 1; pass
<= 2; pass
++)
3363 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3364 tblNameSave
.Empty();
3366 if (!UserID
.empty() &&
3367 Dbms() != dbmsMY_SQL
&&
3368 Dbms() != dbmsACCESS
&&
3369 Dbms() != dbmsMS_SQL_SERVER
)
3371 retcode
= SQLTables(hstmt
,
3372 NULL
, 0, // All qualifiers
3373 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3374 NULL
, 0, // All tables
3375 NULL
, 0); // All columns
3379 retcode
= SQLTables(hstmt
,
3380 NULL
, 0, // All qualifiers
3381 NULL
, 0, // User specified
3382 NULL
, 0, // All tables
3383 NULL
, 0); // All columns
3386 if (retcode
!= SQL_SUCCESS
)
3388 DispAllErrors(henv
, hdbc
, hstmt
);
3390 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3394 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3396 if (pass
== 1) // First pass, just count the Tables
3398 if (pDbInf
->numTables
== 0)
3400 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3401 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3403 pDbInf
->numTables
++; // Counter for Tables
3405 if (pass
== 2) // Create and fill the Table entries
3407 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3408 { // no, then create the Array
3409 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3411 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3413 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3414 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3415 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3421 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3423 // Query how many columns are in each table
3424 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3426 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3431 } // wxDb::GetCatalog()
3434 /********** wxDb::Catalog() **********/
3435 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3437 * Creates the text file specified in 'filename' which will contain
3438 * a minimal data dictionary of all tables accessible by the user specified
3441 * userID is evaluated in the following manner:
3442 * userID == NULL ... UserID is ignored
3443 * userID == "" ... UserID set equal to 'this->uid'
3444 * userID != "" ... UserID set equal to 'userID'
3446 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3447 * by this function. This function should use its own wxDb instance
3448 * to avoid undesired unbinding of columns.
3451 wxASSERT(fileName
.length());
3455 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3456 wxString tblNameSave
;
3457 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3459 wxChar typeName
[30+1];
3460 SDWORD precision
, length
;
3462 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3466 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3469 convertUserID(userID
,UserID
);
3471 if (!UserID
.empty() &&
3472 Dbms() != dbmsMY_SQL
&&
3473 Dbms() != dbmsACCESS
&&
3474 Dbms() != dbmsFIREBIRD
&&
3475 Dbms() != dbmsINTERBASE
&&
3476 Dbms() != dbmsMS_SQL_SERVER
)
3478 retcode
= SQLColumns(hstmt
,
3479 NULL
, 0, // All qualifiers
3480 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3481 NULL
, 0, // All tables
3482 NULL
, 0); // All columns
3486 retcode
= SQLColumns(hstmt
,
3487 NULL
, 0, // All qualifiers
3488 NULL
, 0, // User specified
3489 NULL
, 0, // All tables
3490 NULL
, 0); // All columns
3492 if (retcode
!= SQL_SUCCESS
)
3494 DispAllErrors(henv
, hdbc
, hstmt
);
3500 tblNameSave
.Empty();
3505 retcode
= SQLFetch(hstmt
);
3506 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3509 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3510 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3511 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3512 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3513 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3514 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3516 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3519 wxFputs(wxT("\n"), fp
);
3520 wxFputs(wxT("================================ "), fp
);
3521 wxFputs(wxT("================================ "), fp
);
3522 wxFputs(wxT("===================== "), fp
);
3523 wxFputs(wxT("========= "), fp
);
3524 wxFputs(wxT("=========\n"), fp
);
3525 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3526 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3527 wxFputs(outStr
.c_str(), fp
);
3528 wxFputs(wxT("================================ "), fp
);
3529 wxFputs(wxT("================================ "), fp
);
3530 wxFputs(wxT("===================== "), fp
);
3531 wxFputs(wxT("========= "), fp
);
3532 wxFputs(wxT("=========\n"), fp
);
3533 tblNameSave
= tblName
;
3536 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3537 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3538 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3540 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3547 if (retcode
!= SQL_NO_DATA_FOUND
)
3548 DispAllErrors(henv
, hdbc
, hstmt
);
3550 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3553 return(retcode
== SQL_NO_DATA_FOUND
);
3555 } // wxDb::Catalog()
3558 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3560 * Table name can refer to a table, view, alias or synonym. Returns true
3561 * if the object exists in the database. This function does not indicate
3562 * whether or not the user has privleges to query or perform other functions
3565 * userID is evaluated in the following manner:
3566 * userID == NULL ... UserID is ignored
3567 * userID == "" ... UserID set equal to 'this->uid'
3568 * userID != "" ... UserID set equal to 'userID'
3571 wxASSERT(tableName
.length());
3575 if (Dbms() == dbmsDBASE
)
3578 if (tablePath
.length())
3579 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3581 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3584 exists
= wxFileExists(dbName
);
3589 convertUserID(userID
,UserID
);
3591 TableName
= tableName
;
3592 // Oracle and Interbase table names are uppercase only, so force
3593 // the name to uppercase just in case programmer forgot to do this
3594 if ((Dbms() == dbmsORACLE
) ||
3595 (Dbms() == dbmsFIREBIRD
) ||
3596 (Dbms() == dbmsINTERBASE
))
3597 TableName
= TableName
.Upper();
3599 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3602 // Some databases cannot accept a user name when looking up table names,
3603 // so we use the call below that leaves out the user name
3604 if (!UserID
.empty() &&
3605 Dbms() != dbmsMY_SQL
&&
3606 Dbms() != dbmsACCESS
&&
3607 Dbms() != dbmsMS_SQL_SERVER
&&
3608 Dbms() != dbmsDB2
&&
3609 Dbms() != dbmsFIREBIRD
&&
3610 Dbms() != dbmsINTERBASE
&&
3611 Dbms() != dbmsPERVASIVE_SQL
)
3613 retcode
= SQLTables(hstmt
,
3614 NULL
, 0, // All qualifiers
3615 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3616 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3617 NULL
, 0); // All table types
3621 retcode
= SQLTables(hstmt
,
3622 NULL
, 0, // All qualifiers
3623 NULL
, 0, // All owners
3624 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3625 NULL
, 0); // All table types
3627 if (retcode
!= SQL_SUCCESS
)
3628 return(DispAllErrors(henv
, hdbc
, hstmt
));
3630 retcode
= SQLFetch(hstmt
);
3631 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3633 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3634 return(DispAllErrors(henv
, hdbc
, hstmt
));
3637 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3641 } // wxDb::TableExists()
3644 /********** wxDb::TablePrivileges() **********/
3645 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3646 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3648 wxASSERT(tableName
.length());
3650 wxDbTablePrivilegeInfo result
;
3654 // We probably need to be able to dynamically set this based on
3655 // the driver type, and state.
3656 wxChar curRole
[]=wxT("public");
3660 wxString UserID
,Schema
;
3661 convertUserID(userID
,UserID
);
3662 convertUserID(schema
,Schema
);
3664 TableName
= tableName
;
3665 // Oracle and Interbase table names are uppercase only, so force
3666 // the name to uppercase just in case programmer forgot to do this
3667 if ((Dbms() == dbmsORACLE
) ||
3668 (Dbms() == dbmsFIREBIRD
) ||
3669 (Dbms() == dbmsINTERBASE
))
3670 TableName
= TableName
.Upper();
3672 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3674 // Some databases cannot accept a user name when looking up table names,
3675 // so we use the call below that leaves out the user name
3676 if (!Schema
.empty() &&
3677 Dbms() != dbmsMY_SQL
&&
3678 Dbms() != dbmsACCESS
&&
3679 Dbms() != dbmsMS_SQL_SERVER
)
3681 retcode
= SQLTablePrivileges(hstmt
,
3683 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3684 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3688 retcode
= SQLTablePrivileges(hstmt
,
3691 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3694 #ifdef DBDEBUG_CONSOLE
3695 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3698 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3699 return (DispAllErrors(henv
, hdbc
, hstmt
));
3701 bool failed
= false;
3702 retcode
= SQLFetch(hstmt
);
3703 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3705 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3708 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3711 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3714 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3717 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3720 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3723 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3728 return(DispAllErrors(henv
, hdbc
, hstmt
));
3730 #ifdef DBDEBUG_CONSOLE
3731 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3732 result
.privilege
,result
.tableOwner
,result
.tableName
,
3733 result
.grantor
, result
.grantee
);
3736 if (UserID
.IsSameAs(result
.tableOwner
,false))
3738 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3742 if (UserID
.IsSameAs(result
.grantee
,false) &&
3743 !wxStrcmp(result
.privilege
,priv
))
3745 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3749 if (!wxStrcmp(result
.grantee
,curRole
) &&
3750 !wxStrcmp(result
.privilege
,priv
))
3752 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3756 retcode
= SQLFetch(hstmt
);
3759 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3762 } // wxDb::TablePrivileges
3765 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3769 if (Dbms() == dbmsACCESS
)
3770 TableName
= _T("\"");
3771 TableName
+= tableName
;
3772 if (Dbms() == dbmsACCESS
)
3773 TableName
+= _T("\"");
3776 } // wxDb::SQLTableName()
3779 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3783 if (Dbms() == dbmsACCESS
)
3786 if (Dbms() == dbmsACCESS
)
3787 ColName
+= _T("\"");
3790 } // wxDb::SQLColumnName()
3793 /********** wxDb::SetSqlLogging() **********/
3794 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3796 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3797 wxASSERT(state
== sqlLogOFF
|| filename
.length());
3799 if (state
== sqlLogON
)
3803 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3804 if (fpSqlLog
== NULL
)
3812 if (fclose(fpSqlLog
))
3818 sqlLogState
= state
;
3821 } // wxDb::SetSqlLogging()
3824 /********** wxDb::WriteSqlLog() **********/
3825 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3827 wxASSERT(logMsg
.length());
3829 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3832 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3834 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3836 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3841 } // wxDb::WriteSqlLog()
3844 /********** wxDb::Dbms() **********/
3845 wxDBMS
wxDb::Dbms(void)
3847 * Be aware that not all database engines use the exact same syntax, and not
3848 * every ODBC compliant database is compliant to the same level of compliancy.
3849 * Some manufacturers support the minimum Level 1 compliancy, and others up
3850 * through Level 3. Others support subsets of features for levels above 1.
3852 * If you find an inconsistency between the wxDb class and a specific database
3853 * engine, and an identifier to this section, and special handle the database in
3854 * the area where behavior is non-conforming with the other databases.
3857 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3858 * ---------------------------------------------------
3861 * - Currently the only database supported by the class to support VIEWS
3864 * - Does not support the SQL_TIMESTAMP structure
3865 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3866 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3867 * is true. The user must create ALL indexes from their program.
3868 * - Table names can only be 8 characters long
3869 * - Column names can only be 10 characters long
3872 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3873 * after every table name involved in the query/join if that tables matching record(s)
3875 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3877 * SYBASE (Enterprise)
3878 * - If a column is part of the Primary Key, the column cannot be NULL
3879 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3882 * - If a column is part of the Primary Key, the column cannot be NULL
3883 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3884 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3885 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3886 * column definition if it is not defined correctly, but it is experimental
3887 * - Does not support sub-queries in SQL statements
3890 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3891 * - Does not support sub-queries in SQL statements
3894 * - Primary keys must be declared as NOT NULL
3895 * - Table and index names must not be longer than 13 characters in length (technically
3896 * table names can be up to 18 characters, but the primary index is created using the
3897 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3902 * - Columns that are part of primary keys must be defined as being NOT NULL
3903 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3904 * column definition if it is not defined correctly, but it is experimental
3907 // Should only need to do this once for each new database connection
3908 // so return the value we already determined it to be to save time
3909 // and lots of string comparisons
3910 if (dbmsType
!= dbmsUNIDENTIFIED
)
3913 #ifdef DBDEBUG_CONSOLE
3914 // When run in console mode, use standard out to display errors.
3915 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3916 #endif // DBDEBUG_CONSOLE
3918 wxLogDebug(wxT("Database connecting to: "));
3919 wxLogDebug(dbInf
.dbmsName
);
3921 wxChar baseName
[25+1];
3922 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3925 // RGG 20001025 : add support for Interbase
3926 // GT : Integrated to base classes on 20001121
3927 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3928 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3930 // BJO 20000428 : add support for Virtuoso
3931 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3932 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3934 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3935 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3937 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3938 // connected through an OpenLink driver.
3939 // Is it also returned by Sybase Adapatitve server?
3940 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3941 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3943 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3944 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3945 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3947 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3950 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3951 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3954 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3955 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3958 if (!wxStricmp(baseName
,wxT("Pervasive")))
3959 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3962 if (!wxStricmp(baseName
,wxT("Informix")))
3963 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3965 if (!wxStricmp(baseName
,wxT("Firebird")))
3966 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3969 if (!wxStricmp(baseName
,wxT("Oracle")))
3970 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3971 if (!wxStricmp(baseName
,wxT("ACCESS")))
3972 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3973 if (!wxStricmp(baseName
,wxT("Sybase")))
3974 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3977 if (!wxStricmp(baseName
,wxT("DBASE")))
3978 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3979 if (!wxStricmp(baseName
,wxT("xBase")))
3980 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3981 if (!wxStricmp(baseName
,wxT("MySQL")))
3982 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3983 if (!wxStricmp(baseName
,wxT("MaxDB")))
3984 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
3987 if (!wxStricmp(baseName
,wxT("DB2")))
3988 return((wxDBMS
)(dbmsType
= dbmsDB2
));
3990 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3995 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3996 int dataType
, ULONG columnLength
,
3997 const wxString
&optionalParam
)
3999 wxASSERT(tableName
.length());
4000 wxASSERT(columnName
.length());
4001 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
4002 dataType
!= DB_DATA_TYPE_VARCHAR
);
4004 // Must specify a columnLength if modifying a VARCHAR type column
4005 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
4008 wxString dataTypeName
;
4010 wxString alterSlashModify
;
4014 case DB_DATA_TYPE_VARCHAR
:
4015 dataTypeName
= typeInfVarchar
.TypeName
;
4017 case DB_DATA_TYPE_INTEGER
:
4018 dataTypeName
= typeInfInteger
.TypeName
;
4020 case DB_DATA_TYPE_FLOAT
:
4021 dataTypeName
= typeInfFloat
.TypeName
;
4023 case DB_DATA_TYPE_DATE
:
4024 dataTypeName
= typeInfDate
.TypeName
;
4026 case DB_DATA_TYPE_BLOB
:
4027 dataTypeName
= typeInfBlob
.TypeName
;
4033 // Set the modify or alter syntax depending on the type of database connected to
4037 alterSlashModify
= _T("MODIFY");
4039 case dbmsMS_SQL_SERVER
:
4040 alterSlashModify
= _T("ALTER COLUMN");
4042 case dbmsUNIDENTIFIED
:
4044 case dbmsSYBASE_ASA
:
4045 case dbmsSYBASE_ASE
:
4050 case dbmsXBASE_SEQUITER
:
4052 alterSlashModify
= _T("MODIFY");
4056 // create the SQL statement
4057 if ( Dbms() == dbmsMY_SQL
)
4059 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4060 columnName
.c_str(), dataTypeName
.c_str());
4064 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4065 columnName
.c_str(), dataTypeName
.c_str());
4068 // For varchars only, append the size of the column
4069 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4070 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4073 s
.Printf(wxT("(%lu)"), columnLength
);
4077 // for passing things like "NOT NULL"
4078 if (optionalParam
.length())
4080 sqlStmt
+= wxT(" ");
4081 sqlStmt
+= optionalParam
;
4084 return ExecSql(sqlStmt
);
4086 } // wxDb::ModifyColumn()
4089 /********** wxDbGetConnection() **********/
4090 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4094 // Used to keep a pointer to a DB connection that matches the requested
4095 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4096 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4097 // rather than having to re-query the datasource to get all the values
4098 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4099 wxDb
*matchingDbConnection
= NULL
;
4101 // Scan the linked list searching for an available database connection
4102 // that's already been opened but is currently not in use.
4103 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4105 // The database connection must be for the same datasource
4106 // name and must currently not be in use.
4108 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4110 if (pDbConfig
->UseConnectionStr())
4112 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4113 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4115 // Found a free connection
4116 pList
->Free
= false;
4117 return(pList
->PtrDb
);
4122 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4123 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4125 // Found a free connection
4126 pList
->Free
= false;
4127 return(pList
->PtrDb
);
4132 if (pDbConfig
->UseConnectionStr())
4134 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4135 matchingDbConnection
= pList
->PtrDb
;
4139 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4140 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4141 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4142 matchingDbConnection
= pList
->PtrDb
;
4146 // No available connections. A new connection must be made and
4147 // appended to the end of the linked list.
4150 // Find the end of the list
4151 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4152 // Append a new list item
4153 pList
->PtrNext
= new wxDbList
;
4154 pList
->PtrNext
->PtrPrev
= pList
;
4155 pList
= pList
->PtrNext
;
4159 // Create the first node on the list
4160 pList
= PtrBegDbList
= new wxDbList
;
4164 // Initialize new node in the linked list
4166 pList
->Free
= false;
4167 pList
->Dsn
= pDbConfig
->GetDsn();
4168 pList
->Uid
= pDbConfig
->GetUserID();
4169 pList
->AuthStr
= pDbConfig
->GetPassword();
4170 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4172 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4176 if (!matchingDbConnection
)
4178 if (pDbConfig
->UseConnectionStr())
4180 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4184 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4188 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4190 // Connect to the datasource
4193 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4194 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4195 return(pList
->PtrDb
);
4197 else // Unable to connect, destroy list item
4200 pList
->PtrPrev
->PtrNext
= 0;
4202 PtrBegDbList
= 0; // Empty list again
4204 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4205 pList
->PtrDb
->Close(); // Close the wxDb object
4206 delete pList
->PtrDb
; // Deletes the wxDb object
4207 delete pList
; // Deletes the linked list object
4211 } // wxDbGetConnection()
4214 /********** wxDbFreeConnection() **********/
4215 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4219 // Scan the linked list searching for the database connection
4220 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4222 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4223 return (pList
->Free
= true);
4226 // Never found the database object, return failure
4229 } // wxDbFreeConnection()
4232 /********** wxDbCloseConnections() **********/
4233 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4235 wxDbList
*pList
, *pNext
;
4237 // Traverse the linked list closing database connections and freeing memory as I go.
4238 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4240 pNext
= pList
->PtrNext
; // Save the pointer to next
4241 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4242 pList
->PtrDb
->Close(); // Close the wxDb object
4243 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4244 delete pList
->PtrDb
; // Deletes the wxDb object
4245 delete pList
; // Deletes the linked list object
4248 // Mark the list as empty
4251 } // wxDbCloseConnections()
4254 /********** wxDbConnectionsInUse() **********/
4255 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4260 // Scan the linked list counting db connections that are currently in use
4261 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4263 if (pList
->Free
== false)
4269 } // wxDbConnectionsInUse()
4273 /********** wxDbLogExtendedErrorMsg() **********/
4274 // DEBUG ONLY function
4275 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4277 const wxChar
*ErrFile
,
4280 static wxString msg
;
4285 if (ErrFile
|| ErrLine
)
4287 msg
+= wxT("File: ");
4289 msg
+= wxT(" Line: ");
4290 tStr
.Printf(wxT("%d"),ErrLine
);
4291 msg
+= tStr
.c_str();
4295 msg
.Append (wxT("\nODBC errors:\n"));
4298 // Display errors for this connection
4300 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4302 if (pDb
->errorList
[i
])
4304 msg
.Append(pDb
->errorList
[i
]);
4305 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4306 msg
.Append(wxT("\n"));
4307 // Clear the errmsg buffer so the next error will not
4308 // end up showing the previous error that have occurred
4309 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4314 wxLogDebug(msg
.c_str());
4317 } // wxDbLogExtendedErrorMsg()
4320 /********** wxDbSqlLog() **********/
4321 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4323 bool append
= false;
4326 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4328 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4333 SQLLOGstate
= state
;
4334 SQLLOGfn
= filename
;
4342 /********** wxDbCreateDataSource() **********/
4343 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4344 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4346 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4347 * Very rudimentary creation of an ODBC data source.
4349 * ODBC driver must be ODBC 3.0 compliant to use this function
4354 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4360 dsnLocation
= ODBC_ADD_SYS_DSN
;
4362 dsnLocation
= ODBC_ADD_DSN
;
4364 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4365 // so that is why I used it, as wxString does not deal well with
4366 // embedded nulls in strings
4367 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4369 // Replace the separator from above with the '\0' separator needed
4370 // by the SQLConfigDataSource() function
4374 k
= setupStr
.Find((wxChar
)2,true);
4375 if (k
!= wxNOT_FOUND
)
4376 setupStr
[(UINT
)k
] = wxT('\0');
4378 while (k
!= wxNOT_FOUND
);
4380 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4381 driverName
, setupStr
.c_str());
4383 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4385 // check for errors caused by ConfigDSN based functions
4388 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4389 errMsg
[0] = wxT('\0');
4391 // This function is only supported in ODBC drivers v3.0 compliant and above
4392 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4395 #ifdef DBDEBUG_CONSOLE
4396 // When run in console mode, use standard out to display errors.
4397 cout
<< errMsg
<< endl
;
4398 cout
<< wxT("Press any key to continue...") << endl
;
4400 #endif // DBDEBUG_CONSOLE
4403 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4404 #endif // __WXDEBUG__
4410 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4411 // necessary to use this function, so this function is not supported
4413 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4416 #endif // __VISUALC__
4420 } // wxDbCreateDataSource()
4424 /********** wxDbGetDataSource() **********/
4425 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4426 SWORD DsDescMaxLength
, UWORD direction
)
4428 * Dsn and DsDesc will contain the data source name and data source
4429 * description upon return
4433 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4434 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4436 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4437 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4442 } // wxDbGetDataSource()
4445 // Change this to 0 to remove use of all deprecated functions
4446 #if wxODBC_BACKWARD_COMPATABILITY
4447 /********************************************************************
4448 ********************************************************************
4450 * The following functions are all DEPRECATED and are included for
4451 * backward compatibility reasons only
4453 ********************************************************************
4454 ********************************************************************/
4455 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4457 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4459 /***** DEPRECATED: use wxGetDataSource() *****/
4460 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4463 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4465 /***** DEPRECATED: use wxDbGetConnection() *****/
4466 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4468 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4470 /***** DEPRECATED: use wxDbFreeConnection() *****/
4471 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4473 return wxDbFreeConnection(pDb
);
4475 /***** DEPRECATED: use wxDbCloseConnections() *****/
4476 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4478 wxDbCloseConnections();
4480 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4481 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4483 return wxDbConnectionsInUse();