1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
28 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
29 #pragma implementation "db.h"
32 #include "wx/wxprec.h"
38 #ifdef DBDEBUG_CONSOLE
39 #include "wx/ioswrap.h"
43 #include "wx/string.h"
44 #include "wx/object.h"
49 #include "wx/filefn.h"
50 #include "wx/wxchar.h"
62 // DLL options compatibility check:
64 WX_CHECK_BUILD_OPTIONS("wxODBC")
66 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
68 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
69 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
72 extern wxList TablesInUse
;
75 // SQL Log defaults to be used by GetDbConnection
76 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
78 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
80 // The wxDb::errorList is copied to this variable when the wxDb object
81 // is closed. This way, the error list is still available after the
82 // database object is closed. This is necessary if the database
83 // connection fails so the calling application can show the operator
84 // why the connection failed. Note: as each wxDb object is closed, it
85 // will overwrite the errors of the previously destroyed wxDb object in
86 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
88 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
91 // This type defines the return row-struct form
92 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
95 wxChar tableQual
[128+1];
96 wxChar tableOwner
[128+1];
97 wxChar tableName
[128+1];
98 wxChar grantor
[128+1];
99 wxChar grantee
[128+1];
100 wxChar privilege
[128+1];
101 wxChar grantable
[3+1];
102 } wxDbTablePrivilegeInfo
;
105 /********** wxDbConnectInf Constructor - form 1 **********/
106 wxDbConnectInf::wxDbConnectInf()
109 freeHenvOnDestroy
= false;
115 /********** wxDbConnectInf Constructor - form 2 **********/
116 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
117 const wxString
&password
, const wxString
&defaultDir
,
118 const wxString
&fileType
, const wxString
&description
)
121 freeHenvOnDestroy
= false;
132 SetPassword(password
);
133 SetDescription(description
);
134 SetFileType(fileType
);
135 SetDefaultDir(defaultDir
);
136 } // wxDbConnectInf Constructor
139 wxDbConnectInf::~wxDbConnectInf()
141 if (freeHenvOnDestroy
)
145 } // wxDbConnectInf Destructor
149 /********** wxDbConnectInf::Initialize() **********/
150 bool wxDbConnectInf::Initialize()
152 freeHenvOnDestroy
= false;
154 if (freeHenvOnDestroy
&& Henv
)
161 ConnectionStr
[0] = 0;
166 useConnectionStr
= false;
169 } // wxDbConnectInf::Initialize()
172 /********** wxDbConnectInf::AllocHenv() **********/
173 bool wxDbConnectInf::AllocHenv()
175 // This is here to help trap if you are getting a new henv
176 // without releasing an existing henv
179 // Initialize the ODBC Environment for Database Operations
180 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
182 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
186 freeHenvOnDestroy
= true;
189 } // wxDbConnectInf::AllocHenv()
192 void wxDbConnectInf::FreeHenv()
200 freeHenvOnDestroy
= false;
202 } // wxDbConnectInf::FreeHenv()
205 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
207 wxASSERT(dsn
.Length() < sizeof(Dsn
));
209 wxStrncpy(Dsn
, dsn
, sizeof(Dsn
)-1);
210 Dsn
[sizeof(Dsn
)-1] = 0; // Prevent buffer overrun
211 } // wxDbConnectInf::SetDsn()
214 void wxDbConnectInf::SetUserID(const wxString
&uid
)
216 wxASSERT(uid
.Length() < sizeof(Uid
));
217 wxStrncpy(Uid
, uid
, sizeof(Uid
)-1);
218 Uid
[sizeof(Uid
)-1] = 0; // Prevent buffer overrun
219 } // wxDbConnectInf::SetUserID()
222 void wxDbConnectInf::SetPassword(const wxString
&password
)
224 wxASSERT(password
.Length() < sizeof(AuthStr
));
226 wxStrncpy(AuthStr
, password
, sizeof(AuthStr
)-1);
227 AuthStr
[sizeof(AuthStr
)-1] = 0; // Prevent buffer overrun
228 } // wxDbConnectInf::SetPassword()
230 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
232 wxASSERT(connectStr
.Length() < sizeof(ConnectionStr
));
234 useConnectionStr
= wxStrlen(connectStr
) > 0;
236 wxStrncpy(ConnectionStr
, connectStr
, sizeof(ConnectionStr
)-1);
237 ConnectionStr
[sizeof(ConnectionStr
)-1] = 0; // Prevent buffer overrun
238 } // wxDbConnectInf::SetConnectionStr()
241 /********** wxDbColFor Constructor **********/
242 wxDbColFor::wxDbColFor()
245 } // wxDbColFor::wxDbColFor()
248 /********** wxDbColFor::Initialize() **********/
249 void wxDbColFor::Initialize()
259 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
262 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
263 } // wxDbColFor::Initialize()
266 /********** wxDbColFor::Format() **********/
267 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
268 short columnLength
, short decimalDigits
)
270 // ----------------------------------------------------------------------------------------
271 // -- 19991224 : mj10777 : Create
272 // There is still a lot of work to do here, but it is a start
273 // It handles all the basic data-types that I have run into up to now
274 // The main work will have be with Dates and float Formatting
275 // (US 1,000.00 ; EU 1.000,00)
276 // There are wxWindow plans for locale support and the new wxDateTime. If
277 // they define some constants (wxEUROPEAN) that can be gloably used,
278 // they should be used here.
279 // ----------------------------------------------------------------------------------------
280 // There should also be a function to scan in a string to fill the variable
281 // ----------------------------------------------------------------------------------------
283 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
284 i_dbDataType
= dbDataType
;
285 i_sqlDataType
= sqlDataType
;
286 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
288 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
290 if ((i_sqlDataType
== SQL_VARCHAR
)
292 #if defined(SQL_WCHAR)
293 || (i_sqlDataType
== SQL_WCHAR
)
295 #if defined(SQL_WVARCHAR)
296 || (i_sqlDataType
== SQL_WVARCHAR
)
299 || (i_sqlDataType
== SQL_LONGVARCHAR
))
300 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
301 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
302 i_dbDataType
= DB_DATA_TYPE_DATE
;
303 if (i_sqlDataType
== SQL_C_BIT
)
304 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
305 if (i_sqlDataType
== SQL_NUMERIC
)
306 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
307 if (i_sqlDataType
== SQL_REAL
)
308 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
309 if (i_sqlDataType
== SQL_C_BINARY
)
310 i_dbDataType
= DB_DATA_TYPE_BLOB
;
313 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
315 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
318 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
320 case DB_DATA_TYPE_VARCHAR
:
323 case DB_DATA_TYPE_INTEGER
:
326 case DB_DATA_TYPE_FLOAT
:
327 if (decimalDigits
== 0)
330 tempStr
.Printf(wxT("%s%d.%d"), tempStr
.c_str(),columnLength
, decimalDigits
);
331 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
333 case DB_DATA_TYPE_DATE
:
334 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
336 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
338 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
340 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
342 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
344 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
346 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
348 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
350 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
352 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
355 case DB_DATA_TYPE_BLOB
:
356 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
359 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
363 } // wxDbColFor::Format()
366 /********** wxDbColInf Constructor **********/
367 wxDbColInf::wxDbColInf()
370 } // wxDbColInf::wxDbColInf()
373 /********** wxDbColInf Destructor ********/
374 wxDbColInf::~wxDbColInf()
379 } // wxDbColInf::~wxDbColInf()
382 bool wxDbColInf::Initialize()
404 } // wxDbColInf::Initialize()
407 /********** wxDbTableInf Constructor ********/
408 wxDbTableInf::wxDbTableInf()
411 } // wxDbTableInf::wxDbTableInf()
414 /********** wxDbTableInf Constructor ********/
415 wxDbTableInf::~wxDbTableInf()
420 } // wxDbTableInf::~wxDbTableInf()
423 bool wxDbTableInf::Initialize()
432 } // wxDbTableInf::Initialize()
435 /********** wxDbInf Constructor *************/
439 } // wxDbInf::wxDbInf()
442 /********** wxDbInf Destructor *************/
448 } // wxDbInf::~wxDbInf()
451 /********** wxDbInf::Initialize() *************/
452 bool wxDbInf::Initialize()
460 } // wxDbInf::Initialize()
463 /********** wxDb Constructor **********/
464 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
466 // Copy the HENV into the db class
468 fwdOnlyCursors
= FwdOnlyCursors
;
474 /********** wxDb Destructor **********/
477 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
487 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
488 /********** wxDb::initialize() **********/
489 void wxDb::initialize()
491 * Private member function that sets all wxDb member variables to
492 * known values at creation of the wxDb
497 fpSqlLog
= 0; // Sql Log file pointer
498 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
500 dbmsType
= dbmsUNIDENTIFIED
;
502 wxStrcpy(sqlState
,wxEmptyString
);
503 wxStrcpy(errorMsg
,wxEmptyString
);
504 nativeError
= cbErrorMsg
= 0;
505 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
506 wxStrcpy(errorList
[i
], wxEmptyString
);
508 // Init typeInf structures
509 typeInfVarchar
.TypeName
.Empty();
510 typeInfVarchar
.FsqlType
= 0;
511 typeInfVarchar
.Precision
= 0;
512 typeInfVarchar
.CaseSensitive
= 0;
513 typeInfVarchar
.MaximumScale
= 0;
515 typeInfInteger
.TypeName
.Empty();
516 typeInfInteger
.FsqlType
= 0;
517 typeInfInteger
.Precision
= 0;
518 typeInfInteger
.CaseSensitive
= 0;
519 typeInfInteger
.MaximumScale
= 0;
521 typeInfFloat
.TypeName
.Empty();
522 typeInfFloat
.FsqlType
= 0;
523 typeInfFloat
.Precision
= 0;
524 typeInfFloat
.CaseSensitive
= 0;
525 typeInfFloat
.MaximumScale
= 0;
527 typeInfDate
.TypeName
.Empty();
528 typeInfDate
.FsqlType
= 0;
529 typeInfDate
.Precision
= 0;
530 typeInfDate
.CaseSensitive
= 0;
531 typeInfDate
.MaximumScale
= 0;
533 typeInfBlob
.TypeName
.Empty();
534 typeInfBlob
.FsqlType
= 0;
535 typeInfBlob
.Precision
= 0;
536 typeInfBlob
.CaseSensitive
= 0;
537 typeInfBlob
.MaximumScale
= 0;
539 // Error reporting is turned OFF by default
542 // Allocate a data source connection handle
543 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
546 // Initialize the db status flag
549 // Mark database as not open as of yet
552 dbOpenedWithConnectionString
= false;
553 } // wxDb::initialize()
556 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
558 // NOTE: Return value from this function MUST be copied
559 // immediately, as the value is not good after
560 // this function has left scope.
562 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
566 if (!wxStrlen(userID
))
574 // dBase does not use user names, and some drivers fail if you try to pass one
575 if ( Dbms() == dbmsDBASE
576 || Dbms() == dbmsXBASE_SEQUITER
)
579 // Oracle user names may only be in uppercase, so force
580 // the name to uppercase
581 if (Dbms() == dbmsORACLE
)
582 UserID
= UserID
.Upper();
584 return UserID
.c_str();
585 } // wxDb::convertUserID()
588 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
592 // These are the possible SQL types we check for use against the datasource we are connected
593 // to for the purpose of determining which data type to use for the basic character strings
596 // NOTE: The first type in this enumeration that is determined to be supported by the
597 // datasource/driver is the one that will be used.
598 SWORD PossibleSqlCharTypes
[] = {
599 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
603 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
609 // These are the possible SQL types we check for use against the datasource we are connected
610 // to for the purpose of determining which data type to use for the basic non-floating point
613 // NOTE: The first type in this enumeration that is determined to be supported by the
614 // datasource/driver is the one that will be used.
615 SWORD PossibleSqlIntegerTypes
[] = {
619 // These are the possible SQL types we check for use against the datasource we are connected
620 // to for the purpose of determining which data type to use for the basic floating point number
623 // NOTE: The first type in this enumeration that is determined to be supported by the
624 // datasource/driver is the one that will be used.
625 SWORD PossibleSqlFloatTypes
[] = {
633 // These are the possible SQL types we check for use agains the datasource we are connected
634 // to for the purpose of determining which data type to use for the date/time column types
636 // NOTE: The first type in this enumeration that is determined to be supported by the
637 // datasource/driver is the one that will be used.
638 SWORD PossibleSqlDateTypes
[] = {
646 // These are the possible SQL types we check for use agains the datasource we are connected
647 // to for the purpose of determining which data type to use for the BLOB column types.
649 // NOTE: The first type in this enumeration that is determined to be supported by the
650 // datasource/driver is the one that will be used.
651 SWORD PossibleSqlBlobTypes
[] = {
657 // Query the data source regarding data type information
660 // The way it was determined which SQL data types to use was by calling SQLGetInfo
661 // for all of the possible SQL data types to see which ones were supported. If
662 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
663 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
664 // types I've selected below will not always be what we want. These are just
665 // what happened to work against an Oracle 7/Intersolv combination. The following is
666 // a complete list of the results I got back against the Oracle 7 database:
668 // SQL_BIGINT SQL_NO_DATA_FOUND
669 // SQL_BINARY SQL_NO_DATA_FOUND
670 // SQL_BIT SQL_NO_DATA_FOUND
671 // SQL_CHAR type name = 'CHAR', Precision = 255
672 // SQL_DATE SQL_NO_DATA_FOUND
673 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
674 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
675 // SQL_FLOAT SQL_NO_DATA_FOUND
676 // SQL_INTEGER SQL_NO_DATA_FOUND
677 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
678 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
679 // SQL_NUMERIC SQL_NO_DATA_FOUND
680 // SQL_REAL SQL_NO_DATA_FOUND
681 // SQL_SMALLINT SQL_NO_DATA_FOUND
682 // SQL_TIME SQL_NO_DATA_FOUND
683 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
684 // SQL_VARBINARY type name = 'RAW', Precision = 255
685 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
686 // =====================================================================
687 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
689 // SQL_VARCHAR type name = 'TEXT', Precision = 255
690 // SQL_TIMESTAMP type name = 'DATETIME'
691 // SQL_DECIMAL SQL_NO_DATA_FOUND
692 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
693 // SQL_FLOAT SQL_NO_DATA_FOUND
694 // SQL_REAL type name = 'SINGLE', Precision = 7
695 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
696 // SQL_INTEGER type name = 'LONG', Precision = 10
698 // Query the data source for info about itself
699 if (!getDbInfo(failOnDataTypeUnsupported
))
702 // --------------- Varchar - (Variable length character string) ---------------
703 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
704 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
707 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
708 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
709 else if (failOnDataTypeUnsupported
)
712 // --------------- Float ---------------
713 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
714 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
717 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
718 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
719 else if (failOnDataTypeUnsupported
)
722 // --------------- Integer -------------
723 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
724 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
727 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
728 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
729 else if (failOnDataTypeUnsupported
)
731 // If no non-floating point data types are supported, we'll
732 // use the type assigned for floats to store integers as well
733 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
735 if (failOnDataTypeUnsupported
)
739 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
742 // --------------- Date/Time ---------------
743 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
744 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
747 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
748 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
749 else if (failOnDataTypeUnsupported
)
752 // --------------- BLOB ---------------
753 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
754 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
757 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
758 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
759 else if (failOnDataTypeUnsupported
)
763 } // wxDb::determineDataTypes
766 bool wxDb::open(bool failOnDataTypeUnsupported
)
769 If using Intersolv branded ODBC drivers, this is the place where you would substitute
770 your branded driver license information
772 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
773 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
776 // Mark database as open
779 // Allocate a statement handle for the database connection
780 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
781 return(DispAllErrors(henv
, hdbc
));
783 // Set Connection Options
784 if (!setConnectionOptions())
787 if (!determineDataTypes(failOnDataTypeUnsupported
))
790 #ifdef DBDEBUG_CONSOLE
791 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
792 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
793 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
794 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
795 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
799 // Completed Successfully
803 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
805 wxASSERT(inConnectStr
.Length());
812 if (!FwdOnlyCursors())
814 // Specify that the ODBC cursor library be used, if needed. This must be
815 // specified before the connection is made.
816 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
818 #ifdef DBDEBUG_CONSOLE
819 if (retcode
== SQL_SUCCESS
)
820 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
822 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
824 wxUnusedVar(retcode
);
828 // Connect to the data source
829 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
830 short outConnectBufferLen
;
832 inConnectionStr
= inConnectStr
;
834 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
835 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
836 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
838 if ((retcode
!= SQL_SUCCESS
) &&
839 (retcode
!= SQL_SUCCESS_WITH_INFO
))
840 return(DispAllErrors(henv
, hdbc
));
842 outConnectBuffer
[outConnectBufferLen
] = 0;
843 outConnectionStr
= outConnectBuffer
;
844 dbOpenedWithConnectionString
= true;
846 return open(failOnDataTypeUnsupported
);
849 /********** wxDb::Open() **********/
850 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
852 wxASSERT(Dsn
.Length());
857 inConnectionStr
= wxT("");
858 outConnectionStr
= wxT("");
862 if (!FwdOnlyCursors())
864 // Specify that the ODBC cursor library be used, if needed. This must be
865 // specified before the connection is made.
866 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
868 #ifdef DBDEBUG_CONSOLE
869 if (retcode
== SQL_SUCCESS
)
870 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
872 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
874 wxUnusedVar( retcode
);
878 // Connect to the data source
879 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
880 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
881 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
883 if ((retcode
!= SQL_SUCCESS
) &&
884 (retcode
!= SQL_SUCCESS_WITH_INFO
))
885 return(DispAllErrors(henv
, hdbc
));
887 return open(failOnDataTypeUnsupported
);
892 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
894 wxASSERT(dbConnectInf
);
896 // Use the connection string if one is present
897 if (dbConnectInf
->UseConnectionStr())
898 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
900 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
901 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
905 bool wxDb::Open(wxDb
*copyDb
)
907 dsn
= copyDb
->GetDatasourceName();
908 uid
= copyDb
->GetUsername();
909 authStr
= copyDb
->GetPassword();
910 inConnectionStr
= copyDb
->GetConnectionInStr();
911 outConnectionStr
= copyDb
->GetConnectionOutStr();
915 if (!FwdOnlyCursors())
917 // Specify that the ODBC cursor library be used, if needed. This must be
918 // specified before the connection is made.
919 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
921 #ifdef DBDEBUG_CONSOLE
922 if (retcode
== SQL_SUCCESS
)
923 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
925 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
927 wxUnusedVar( retcode
);
931 if (copyDb
->OpenedWithConnectionString())
933 // Connect to the data source
934 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
935 short outConnectBufferLen
;
937 inConnectionStr
= copyDb
->GetConnectionInStr();
939 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
940 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
941 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
943 if ((retcode
!= SQL_SUCCESS
) &&
944 (retcode
!= SQL_SUCCESS_WITH_INFO
))
945 return(DispAllErrors(henv
, hdbc
));
947 outConnectBuffer
[outConnectBufferLen
] = 0;
948 outConnectionStr
= outConnectBuffer
;
949 dbOpenedWithConnectionString
= true;
953 // Connect to the data source
954 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
955 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
956 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
959 if ((retcode
!= SQL_SUCCESS
) &&
960 (retcode
!= SQL_SUCCESS_WITH_INFO
))
961 return(DispAllErrors(henv
, hdbc
));
964 If using Intersolv branded ODBC drivers, this is the place where you would substitute
965 your branded driver license information
967 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
968 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
971 // Mark database as open
974 // Allocate a statement handle for the database connection
975 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
976 return(DispAllErrors(henv
, hdbc
));
978 // Set Connection Options
979 if (!setConnectionOptions())
982 // Instead of Querying the data source for info about itself, it can just be copied
983 // from the wxDb instance that was passed in (copyDb).
984 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
985 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
986 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
987 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
988 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
989 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
990 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
991 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
992 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
993 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
994 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
995 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
996 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
997 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
998 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
999 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1000 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1001 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1002 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1003 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1004 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1005 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1006 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1007 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1008 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1009 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1010 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1011 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1012 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1013 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1014 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1016 // VARCHAR = Variable length character string
1017 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1018 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1019 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1020 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1021 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1024 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1025 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1026 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1027 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1028 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1031 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1032 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1033 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1034 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1035 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1038 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1039 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1040 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1041 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1042 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1045 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1046 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1047 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1048 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1049 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1051 #ifdef DBDEBUG_CONSOLE
1052 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1053 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1054 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1055 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1056 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1060 // Completed Successfully
1065 /********** wxDb::setConnectionOptions() **********/
1066 bool wxDb::setConnectionOptions(void)
1068 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1073 // I need to get the DBMS name here, because some of the connection options
1074 // are database specific and need to call the Dbms() function.
1077 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1078 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1079 return(DispAllErrors(henv
, hdbc
));
1081 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1082 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1083 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1085 // By default, MS Sql Server closes cursors on commit and rollback. The following
1086 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1087 // after a transaction. This is a driver specific option and is not part of the
1088 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1089 // The database settings don't have any effect one way or the other.
1090 if (Dbms() == dbmsMS_SQL_SERVER
)
1092 const long SQL_PRESERVE_CURSORS
= 1204L;
1093 const long SQL_PC_ON
= 1L;
1094 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1097 // Display the connection options to verify them
1098 #ifdef DBDEBUG_CONSOLE
1100 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1102 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1103 if (retcode
!= SQL_SUCCESS
)
1104 return(DispAllErrors(henv
, hdbc
));
1105 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1107 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1108 if (retcode
!= SQL_SUCCESS
)
1109 return(DispAllErrors(henv
, hdbc
));
1110 cout
<< wxT("ODBC CURSORS: ");
1113 case(SQL_CUR_USE_IF_NEEDED
):
1114 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1116 case(SQL_CUR_USE_ODBC
):
1117 cout
<< wxT("SQL_CUR_USE_ODBC");
1119 case(SQL_CUR_USE_DRIVER
):
1120 cout
<< wxT("SQL_CUR_USE_DRIVER");
1125 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1126 if (retcode
!= SQL_SUCCESS
)
1127 return(DispAllErrors(henv
, hdbc
));
1128 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1133 // Completed Successfully
1136 } // wxDb::setConnectionOptions()
1139 /********** wxDb::getDbInfo() **********/
1140 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1145 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1146 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1148 DispAllErrors(henv
, hdbc
);
1149 if (failOnDataTypeUnsupported
)
1153 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1154 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1156 DispAllErrors(henv
, hdbc
);
1157 if (failOnDataTypeUnsupported
)
1161 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1162 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1164 DispAllErrors(henv
, hdbc
);
1165 if (failOnDataTypeUnsupported
)
1170 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1171 // causing database connectivity to fail in some cases.
1172 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1173 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1175 DispAllErrors(henv
, hdbc
);
1176 if (failOnDataTypeUnsupported
)
1180 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1181 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1183 DispAllErrors(henv
, hdbc
);
1184 if (failOnDataTypeUnsupported
)
1188 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1189 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1191 DispAllErrors(henv
, hdbc
);
1192 if (failOnDataTypeUnsupported
)
1196 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1197 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1199 DispAllErrors(henv
, hdbc
);
1200 if (failOnDataTypeUnsupported
)
1204 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1205 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1207 DispAllErrors(henv
, hdbc
);
1208 if (failOnDataTypeUnsupported
)
1212 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1213 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1215 DispAllErrors(henv
, hdbc
);
1216 if (failOnDataTypeUnsupported
)
1220 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1221 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1223 DispAllErrors(henv
, hdbc
);
1224 if (failOnDataTypeUnsupported
)
1228 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1229 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1231 DispAllErrors(henv
, hdbc
);
1232 if (failOnDataTypeUnsupported
)
1236 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1237 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1239 // Not all drivers support this call - Nick Gorham(unixODBC)
1240 dbInf
.cliConfLvl
= 0;
1241 DispAllErrors(henv
, hdbc
);
1242 if (failOnDataTypeUnsupported
)
1246 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1247 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1249 DispAllErrors(henv
, hdbc
);
1250 if (failOnDataTypeUnsupported
)
1254 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1255 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1257 DispAllErrors(henv
, hdbc
);
1258 if (failOnDataTypeUnsupported
)
1262 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1263 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1265 DispAllErrors(henv
, hdbc
);
1266 if (failOnDataTypeUnsupported
)
1270 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1271 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1273 DispAllErrors(henv
, hdbc
);
1274 if (failOnDataTypeUnsupported
)
1278 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1279 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1281 DispAllErrors(henv
, hdbc
);
1282 if (failOnDataTypeUnsupported
)
1286 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1287 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1289 DispAllErrors(henv
, hdbc
);
1290 if (failOnDataTypeUnsupported
)
1294 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1295 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1297 DispAllErrors(henv
, hdbc
);
1298 if (failOnDataTypeUnsupported
)
1302 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1303 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1305 DispAllErrors(henv
, hdbc
);
1306 if (failOnDataTypeUnsupported
)
1310 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1311 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1313 DispAllErrors(henv
, hdbc
);
1314 if (failOnDataTypeUnsupported
)
1318 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1319 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1321 DispAllErrors(henv
, hdbc
);
1322 if (failOnDataTypeUnsupported
)
1326 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1327 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1329 DispAllErrors(henv
, hdbc
);
1330 if (failOnDataTypeUnsupported
)
1334 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1335 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1337 DispAllErrors(henv
, hdbc
);
1338 if (failOnDataTypeUnsupported
)
1342 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1343 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1345 DispAllErrors(henv
, hdbc
);
1346 if (failOnDataTypeUnsupported
)
1350 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1351 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1353 DispAllErrors(henv
, hdbc
);
1354 if (failOnDataTypeUnsupported
)
1358 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1359 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1361 DispAllErrors(henv
, hdbc
);
1362 if (failOnDataTypeUnsupported
)
1366 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1367 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1369 DispAllErrors(henv
, hdbc
);
1370 if (failOnDataTypeUnsupported
)
1374 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1375 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1377 DispAllErrors(henv
, hdbc
);
1378 if (failOnDataTypeUnsupported
)
1382 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1383 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1385 DispAllErrors(henv
, hdbc
);
1386 if (failOnDataTypeUnsupported
)
1390 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1391 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1393 DispAllErrors(henv
, hdbc
);
1394 if (failOnDataTypeUnsupported
)
1398 #ifdef DBDEBUG_CONSOLE
1399 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1400 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1401 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1402 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1404 cout
<< wxT("API Conf. Level: ");
1405 switch(dbInf
.apiConfLvl
)
1407 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1408 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1409 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1413 cout
<< wxT("SAG CLI Conf. Level: ");
1414 switch(dbInf
.cliConfLvl
)
1416 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1417 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1421 cout
<< wxT("SQL Conf. Level: ");
1422 switch(dbInf
.sqlConfLvl
)
1424 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1425 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1426 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1430 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1431 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1432 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1433 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1434 cout
<< wxT("Cursor COMMIT Behavior: ");
1435 switch(dbInf
.cursorCommitBehavior
)
1437 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1438 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1439 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1443 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1444 switch(dbInf
.cursorRollbackBehavior
)
1446 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1447 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1448 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1452 cout
<< wxT("Support NOT NULL clause: ");
1453 switch(dbInf
.supportNotNullClause
)
1455 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1456 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1460 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1461 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1463 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1466 cout
<< wxT("Default Transaction Isolation: ";
1467 switch(dbInf
.txnIsolation
)
1469 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1470 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1471 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1472 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1474 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1479 cout
<< wxT("Transaction Isolation Options: ");
1480 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1481 cout
<< wxT("Read Uncommitted, ");
1482 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1483 cout
<< wxT("Read Committed, ");
1484 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1485 cout
<< wxT("Repeatable Read, ");
1486 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1487 cout
<< wxT("Serializable, ");
1489 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1490 cout
<< wxT("Versioning");
1494 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1495 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1496 cout
<< wxT("Next, ");
1497 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1498 cout
<< wxT("Prev, ");
1499 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1500 cout
<< wxT("First, ");
1501 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1502 cout
<< wxT("Last, ");
1503 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1504 cout
<< wxT("Absolute, ");
1505 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1506 cout
<< wxT("Relative, ");
1508 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1509 cout
<< wxT("Resume, ");
1511 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1512 cout
<< wxT("Bookmark");
1515 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1516 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1517 cout
<< wxT("No Change, ");
1518 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1519 cout
<< wxT("Exclusive, ");
1520 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1521 cout
<< wxT("UnLock");
1524 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1525 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1526 cout
<< wxT("Position, ");
1527 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1528 cout
<< wxT("Refresh, ");
1529 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1530 cout
<< wxT("Upd, "));
1531 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1532 cout
<< wxT("Del, ");
1533 if (dbInf
.posOperations
& SQL_POS_ADD
)
1537 cout
<< wxT("Positioned Statements Supported: ");
1538 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1539 cout
<< wxT("Pos delete, ");
1540 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1541 cout
<< wxT("Pos update, ");
1542 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1543 cout
<< wxT("Select for update");
1546 cout
<< wxT("Scroll Concurrency: ");
1547 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1548 cout
<< wxT("Read Only, ");
1549 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1550 cout
<< wxT("Lock, ");
1551 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1552 cout
<< wxT("Opt. Rowver, ");
1553 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1554 cout
<< wxT("Opt. Values");
1557 cout
<< wxT("Scroll Options: ");
1558 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1559 cout
<< wxT("Fwd Only, ");
1560 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1561 cout
<< wxT("Static, ");
1562 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1563 cout
<< wxT("Keyset Driven, ");
1564 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1565 cout
<< wxT("Dynamic, ");
1566 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1567 cout
<< wxT("Mixed");
1570 cout
<< wxT("Static Sensitivity: ");
1571 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1572 cout
<< wxT("Additions, ");
1573 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1574 cout
<< wxT("Deletions, ");
1575 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1576 cout
<< wxT("Updates");
1579 cout
<< wxT("Transaction Capable?: ");
1580 switch(dbInf
.txnCapable
)
1582 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1583 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1584 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1585 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1586 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1593 // Completed Successfully
1596 } // wxDb::getDbInfo()
1599 /********** wxDb::getDataTypeInfo() **********/
1600 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1603 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1604 * the data type inf. is gathered for.
1606 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1611 // Get information about the data type specified
1612 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1613 return(DispAllErrors(henv
, hdbc
, hstmt
));
1616 retcode
= SQLFetch(hstmt
);
1617 if (retcode
!= SQL_SUCCESS
)
1619 #ifdef DBDEBUG_CONSOLE
1620 if (retcode
== SQL_NO_DATA_FOUND
)
1621 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1623 DispAllErrors(henv
, hdbc
, hstmt
);
1624 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1628 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1630 // Obtain columns from the record
1631 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1632 return(DispAllErrors(henv
, hdbc
, hstmt
));
1634 structSQLTypeInfo
.TypeName
= typeName
;
1636 // BJO 20000503: no more needed with new GetColumns...
1639 if (Dbms() == dbmsMY_SQL
)
1641 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1642 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1643 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1644 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1645 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1646 structSQLTypeInfo
.TypeName
= wxT("int");
1647 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1648 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1649 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1650 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1651 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1652 structSQLTypeInfo
.TypeName
= wxT("char");
1655 // BJO 20000427 : OpenLink driver
1656 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1657 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1659 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1660 structSQLTypeInfo
.TypeName
= wxT("real");
1664 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1665 return(DispAllErrors(henv
, hdbc
, hstmt
));
1666 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1667 return(DispAllErrors(henv
, hdbc
, hstmt
));
1668 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1669 // return(DispAllErrors(henv, hdbc, hstmt));
1671 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1672 return(DispAllErrors(henv
, hdbc
, hstmt
));
1674 if (structSQLTypeInfo
.MaximumScale
< 0)
1675 structSQLTypeInfo
.MaximumScale
= 0;
1677 // Close the statement handle which closes open cursors
1678 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1679 return(DispAllErrors(henv
, hdbc
, hstmt
));
1681 // Completed Successfully
1684 } // wxDb::getDataTypeInfo()
1687 /********** wxDb::Close() **********/
1688 void wxDb::Close(void)
1690 // Close the Sql Log file
1697 // Free statement handle
1700 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1701 DispAllErrors(henv
, hdbc
);
1704 // Disconnect from the datasource
1705 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1706 DispAllErrors(henv
, hdbc
);
1708 // Free the connection to the datasource
1709 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1710 DispAllErrors(henv
, hdbc
);
1712 // There should be zero Ctable objects still connected to this db object
1713 wxASSERT(nTables
== 0);
1717 wxList::compatibility_iterator pNode
;
1718 pNode
= TablesInUse
.GetFirst();
1722 tiu
= (wxTablesInUse
*)pNode
->GetData();
1723 if (tiu
->pDb
== this)
1725 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1726 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1727 wxLogDebug(s
.c_str(),s2
.c_str());
1729 pNode
= pNode
->GetNext();
1733 // Copy the error messages to a global variable
1735 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1736 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1738 dbmsType
= dbmsUNIDENTIFIED
;
1744 /********** wxDb::CommitTrans() **********/
1745 bool wxDb::CommitTrans(void)
1749 // Commit the transaction
1750 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1751 return(DispAllErrors(henv
, hdbc
));
1754 // Completed successfully
1757 } // wxDb::CommitTrans()
1760 /********** wxDb::RollbackTrans() **********/
1761 bool wxDb::RollbackTrans(void)
1763 // Rollback the transaction
1764 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1765 return(DispAllErrors(henv
, hdbc
));
1767 // Completed successfully
1770 } // wxDb::RollbackTrans()
1773 /********** wxDb::DispAllErrors() **********/
1774 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1776 * This function is called internally whenever an error condition prevents the user's
1777 * request from being executed. This function will query the datasource as to the
1778 * actual error(s) that just occured on the previous request of the datasource.
1780 * The function will retrieve each error condition from the datasource and
1781 * Printf the codes/text values into a string which it then logs via logError().
1782 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1783 * window and program execution will be paused until the user presses a key.
1785 * This function always returns a false, so that functions which call this function
1786 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1787 * of the users request, so that the calling code can then process the error msg log
1790 wxString odbcErrMsg
;
1793 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1795 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1798 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1799 logError(odbcErrMsg
, sqlState
);
1802 #ifdef DBDEBUG_CONSOLE
1803 // When run in console mode, use standard out to display errors.
1804 cout
<< odbcErrMsg
.c_str() << endl
;
1805 cout
<< wxT("Press any key to continue...") << endl
;
1810 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1815 return false; // This function always returns false.
1817 } // wxDb::DispAllErrors()
1820 /********** wxDb::GetNextError() **********/
1821 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1824 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1826 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1832 } // wxDb::GetNextError()
1835 /********** wxDb::DispNextError() **********/
1836 void wxDb::DispNextError(void)
1838 wxString odbcErrMsg
;
1840 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1841 logError(odbcErrMsg
, sqlState
);
1846 #ifdef DBDEBUG_CONSOLE
1847 // When run in console mode, use standard out to display errors.
1848 cout
<< odbcErrMsg
.c_str() << endl
;
1849 cout
<< wxT("Press any key to continue...") << endl
;
1854 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1855 #endif // __WXDEBUG__
1857 } // wxDb::DispNextError()
1860 /********** wxDb::logError() **********/
1861 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1863 wxASSERT(errMsg
.Length());
1865 static int pLast
= -1;
1868 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1871 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1872 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1876 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1877 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1879 if (SQLState
.Length())
1880 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1881 DB_STATUS
= dbStatus
;
1883 // Add the errmsg to the sql log
1884 WriteSqlLog(errMsg
);
1886 } // wxDb::logError()
1889 /**********wxDb::TranslateSqlState() **********/
1890 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1892 if (!wxStrcmp(SQLState
, wxT("01000")))
1893 return(DB_ERR_GENERAL_WARNING
);
1894 if (!wxStrcmp(SQLState
, wxT("01002")))
1895 return(DB_ERR_DISCONNECT_ERROR
);
1896 if (!wxStrcmp(SQLState
, wxT("01004")))
1897 return(DB_ERR_DATA_TRUNCATED
);
1898 if (!wxStrcmp(SQLState
, wxT("01006")))
1899 return(DB_ERR_PRIV_NOT_REVOKED
);
1900 if (!wxStrcmp(SQLState
, wxT("01S00")))
1901 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1902 if (!wxStrcmp(SQLState
, wxT("01S01")))
1903 return(DB_ERR_ERROR_IN_ROW
);
1904 if (!wxStrcmp(SQLState
, wxT("01S02")))
1905 return(DB_ERR_OPTION_VALUE_CHANGED
);
1906 if (!wxStrcmp(SQLState
, wxT("01S03")))
1907 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1908 if (!wxStrcmp(SQLState
, wxT("01S04")))
1909 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1910 if (!wxStrcmp(SQLState
, wxT("07001")))
1911 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1912 if (!wxStrcmp(SQLState
, wxT("07006")))
1913 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1914 if (!wxStrcmp(SQLState
, wxT("08001")))
1915 return(DB_ERR_UNABLE_TO_CONNECT
);
1916 if (!wxStrcmp(SQLState
, wxT("08002")))
1917 return(DB_ERR_CONNECTION_IN_USE
);
1918 if (!wxStrcmp(SQLState
, wxT("08003")))
1919 return(DB_ERR_CONNECTION_NOT_OPEN
);
1920 if (!wxStrcmp(SQLState
, wxT("08004")))
1921 return(DB_ERR_REJECTED_CONNECTION
);
1922 if (!wxStrcmp(SQLState
, wxT("08007")))
1923 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1924 if (!wxStrcmp(SQLState
, wxT("08S01")))
1925 return(DB_ERR_COMM_LINK_FAILURE
);
1926 if (!wxStrcmp(SQLState
, wxT("21S01")))
1927 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1928 if (!wxStrcmp(SQLState
, wxT("21S02")))
1929 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1930 if (!wxStrcmp(SQLState
, wxT("22001")))
1931 return(DB_ERR_STRING_RIGHT_TRUNC
);
1932 if (!wxStrcmp(SQLState
, wxT("22003")))
1933 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1934 if (!wxStrcmp(SQLState
, wxT("22005")))
1935 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1936 if (!wxStrcmp(SQLState
, wxT("22008")))
1937 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1938 if (!wxStrcmp(SQLState
, wxT("22012")))
1939 return(DB_ERR_DIVIDE_BY_ZERO
);
1940 if (!wxStrcmp(SQLState
, wxT("22026")))
1941 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1942 if (!wxStrcmp(SQLState
, wxT("23000")))
1943 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1944 if (!wxStrcmp(SQLState
, wxT("24000")))
1945 return(DB_ERR_INVALID_CURSOR_STATE
);
1946 if (!wxStrcmp(SQLState
, wxT("25000")))
1947 return(DB_ERR_INVALID_TRANS_STATE
);
1948 if (!wxStrcmp(SQLState
, wxT("28000")))
1949 return(DB_ERR_INVALID_AUTH_SPEC
);
1950 if (!wxStrcmp(SQLState
, wxT("34000")))
1951 return(DB_ERR_INVALID_CURSOR_NAME
);
1952 if (!wxStrcmp(SQLState
, wxT("37000")))
1953 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1954 if (!wxStrcmp(SQLState
, wxT("3C000")))
1955 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1956 if (!wxStrcmp(SQLState
, wxT("40001")))
1957 return(DB_ERR_SERIALIZATION_FAILURE
);
1958 if (!wxStrcmp(SQLState
, wxT("42000")))
1959 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1960 if (!wxStrcmp(SQLState
, wxT("70100")))
1961 return(DB_ERR_OPERATION_ABORTED
);
1962 if (!wxStrcmp(SQLState
, wxT("IM001")))
1963 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1964 if (!wxStrcmp(SQLState
, wxT("IM002")))
1965 return(DB_ERR_NO_DATA_SOURCE
);
1966 if (!wxStrcmp(SQLState
, wxT("IM003")))
1967 return(DB_ERR_DRIVER_LOAD_ERROR
);
1968 if (!wxStrcmp(SQLState
, wxT("IM004")))
1969 return(DB_ERR_SQLALLOCENV_FAILED
);
1970 if (!wxStrcmp(SQLState
, wxT("IM005")))
1971 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1972 if (!wxStrcmp(SQLState
, wxT("IM006")))
1973 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1974 if (!wxStrcmp(SQLState
, wxT("IM007")))
1975 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1976 if (!wxStrcmp(SQLState
, wxT("IM008")))
1977 return(DB_ERR_DIALOG_FAILED
);
1978 if (!wxStrcmp(SQLState
, wxT("IM009")))
1979 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1980 if (!wxStrcmp(SQLState
, wxT("IM010")))
1981 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1982 if (!wxStrcmp(SQLState
, wxT("IM011")))
1983 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1984 if (!wxStrcmp(SQLState
, wxT("IM012")))
1985 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1986 if (!wxStrcmp(SQLState
, wxT("IM013")))
1987 return(DB_ERR_TRACE_FILE_ERROR
);
1988 if (!wxStrcmp(SQLState
, wxT("S0001")))
1989 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1990 if (!wxStrcmp(SQLState
, wxT("S0002")))
1991 return(DB_ERR_TABLE_NOT_FOUND
);
1992 if (!wxStrcmp(SQLState
, wxT("S0011")))
1993 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1994 if (!wxStrcmp(SQLState
, wxT("S0012")))
1995 return(DB_ERR_INDEX_NOT_FOUND
);
1996 if (!wxStrcmp(SQLState
, wxT("S0021")))
1997 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1998 if (!wxStrcmp(SQLState
, wxT("S0022")))
1999 return(DB_ERR_COLUMN_NOT_FOUND
);
2000 if (!wxStrcmp(SQLState
, wxT("S0023")))
2001 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2002 if (!wxStrcmp(SQLState
, wxT("S1000")))
2003 return(DB_ERR_GENERAL_ERROR
);
2004 if (!wxStrcmp(SQLState
, wxT("S1001")))
2005 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2006 if (!wxStrcmp(SQLState
, wxT("S1002")))
2007 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2008 if (!wxStrcmp(SQLState
, wxT("S1003")))
2009 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2010 if (!wxStrcmp(SQLState
, wxT("S1004")))
2011 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2012 if (!wxStrcmp(SQLState
, wxT("S1008")))
2013 return(DB_ERR_OPERATION_CANCELLED
);
2014 if (!wxStrcmp(SQLState
, wxT("S1009")))
2015 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2016 if (!wxStrcmp(SQLState
, wxT("S1010")))
2017 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2018 if (!wxStrcmp(SQLState
, wxT("S1011")))
2019 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2020 if (!wxStrcmp(SQLState
, wxT("S1012")))
2021 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2022 if (!wxStrcmp(SQLState
, wxT("S1015")))
2023 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2024 if (!wxStrcmp(SQLState
, wxT("S1090")))
2025 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2026 if (!wxStrcmp(SQLState
, wxT("S1091")))
2027 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2028 if (!wxStrcmp(SQLState
, wxT("S1092")))
2029 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2030 if (!wxStrcmp(SQLState
, wxT("S1093")))
2031 return(DB_ERR_INVALID_PARAM_NO
);
2032 if (!wxStrcmp(SQLState
, wxT("S1094")))
2033 return(DB_ERR_INVALID_SCALE_VALUE
);
2034 if (!wxStrcmp(SQLState
, wxT("S1095")))
2035 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2036 if (!wxStrcmp(SQLState
, wxT("S1096")))
2037 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2038 if (!wxStrcmp(SQLState
, wxT("S1097")))
2039 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2040 if (!wxStrcmp(SQLState
, wxT("S1098")))
2041 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2042 if (!wxStrcmp(SQLState
, wxT("S1099")))
2043 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2044 if (!wxStrcmp(SQLState
, wxT("S1100")))
2045 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2046 if (!wxStrcmp(SQLState
, wxT("S1101")))
2047 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2048 if (!wxStrcmp(SQLState
, wxT("S1103")))
2049 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2050 if (!wxStrcmp(SQLState
, wxT("S1104")))
2051 return(DB_ERR_INVALID_PRECISION_VALUE
);
2052 if (!wxStrcmp(SQLState
, wxT("S1105")))
2053 return(DB_ERR_INVALID_PARAM_TYPE
);
2054 if (!wxStrcmp(SQLState
, wxT("S1106")))
2055 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2056 if (!wxStrcmp(SQLState
, wxT("S1107")))
2057 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2058 if (!wxStrcmp(SQLState
, wxT("S1108")))
2059 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2060 if (!wxStrcmp(SQLState
, wxT("S1109")))
2061 return(DB_ERR_INVALID_CURSOR_POSITION
);
2062 if (!wxStrcmp(SQLState
, wxT("S1110")))
2063 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2064 if (!wxStrcmp(SQLState
, wxT("S1111")))
2065 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2066 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2067 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2068 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2069 return(DB_ERR_TIMEOUT_EXPIRED
);
2074 } // wxDb::TranslateSqlState()
2077 /********** wxDb::Grant() **********/
2078 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2082 // Build the grant statement
2083 sqlStmt
= wxT("GRANT ");
2084 if (privileges
== DB_GRANT_ALL
)
2085 sqlStmt
+= wxT("ALL");
2089 if (privileges
& DB_GRANT_SELECT
)
2091 sqlStmt
+= wxT("SELECT");
2094 if (privileges
& DB_GRANT_INSERT
)
2097 sqlStmt
+= wxT(", ");
2098 sqlStmt
+= wxT("INSERT");
2100 if (privileges
& DB_GRANT_UPDATE
)
2103 sqlStmt
+= wxT(", ");
2104 sqlStmt
+= wxT("UPDATE");
2106 if (privileges
& DB_GRANT_DELETE
)
2109 sqlStmt
+= wxT(", ");
2110 sqlStmt
+= wxT("DELETE");
2114 sqlStmt
+= wxT(" ON ");
2115 sqlStmt
+= SQLTableName(tableName
);
2116 sqlStmt
+= wxT(" TO ");
2117 sqlStmt
+= userList
;
2119 #ifdef DBDEBUG_CONSOLE
2120 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2123 WriteSqlLog(sqlStmt
);
2125 return(ExecSql(sqlStmt
));
2130 /********** wxDb::CreateView() **********/
2131 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2132 const wxString
&pSqlStmt
, bool attemptDrop
)
2136 // Drop the view first
2137 if (attemptDrop
&& !DropView(viewName
))
2140 // Build the create view statement
2141 sqlStmt
= wxT("CREATE VIEW ");
2142 sqlStmt
+= viewName
;
2144 if (colList
.Length())
2146 sqlStmt
+= wxT(" (");
2148 sqlStmt
+= wxT(")");
2151 sqlStmt
+= wxT(" AS ");
2152 sqlStmt
+= pSqlStmt
;
2154 WriteSqlLog(sqlStmt
);
2156 #ifdef DBDEBUG_CONSOLE
2157 cout
<< sqlStmt
.c_str() << endl
;
2160 return(ExecSql(sqlStmt
));
2162 } // wxDb::CreateView()
2165 /********** wxDb::DropView() **********/
2166 bool wxDb::DropView(const wxString
&viewName
)
2169 * NOTE: This function returns true if the View does not exist, but
2170 * only for identified databases. Code will need to be added
2171 * below for any other databases when those databases are defined
2172 * to handle this situation consistently
2176 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2178 WriteSqlLog(sqlStmt
);
2180 #ifdef DBDEBUG_CONSOLE
2181 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2184 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2186 // Check for "Base table not found" error and ignore
2187 GetNextError(henv
, hdbc
, hstmt
);
2188 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2190 // Check for product specific error codes
2191 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2194 DispAllErrors(henv
, hdbc
, hstmt
);
2201 // Commit the transaction
2207 } // wxDb::DropView()
2210 /********** wxDb::ExecSql() **********/
2211 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2215 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2217 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2218 if (retcode
== SQL_SUCCESS
||
2219 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2225 DispAllErrors(henv
, hdbc
, hstmt
);
2229 } // wxDb::ExecSql()
2232 /********** wxDb::ExecSql() with column info **********/
2233 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2235 //execute the statement first
2236 if (!ExecSql(pSqlStmt
))
2240 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2242 DispAllErrors(henv
, hdbc
, hstmt
);
2251 // Get column information
2253 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2256 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2258 // Fill in column information (name, datatype)
2259 for (colNum
= 0; colNum
< noCols
; colNum
++)
2261 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2263 &Sword
, &Sdword
) != SQL_SUCCESS
)
2265 DispAllErrors(henv
, hdbc
, hstmt
);
2270 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2271 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2273 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2274 NULL
, 0, &Sword
, &Sdword
) != SQL_SUCCESS
)
2276 DispAllErrors(henv
, hdbc
, hstmt
);
2284 #if defined(SQL_WCHAR)
2287 #if defined(SQL_WVARCHAR)
2293 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2299 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2306 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2310 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2313 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2318 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sdword
);
2319 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2326 } // wxDb::ExecSql()
2328 /********** wxDb::GetNext() **********/
2329 bool wxDb::GetNext(void)
2331 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2335 DispAllErrors(henv
, hdbc
, hstmt
);
2339 } // wxDb::GetNext()
2342 /********** wxDb::GetData() **********/
2343 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2346 wxASSERT(cbReturned
);
2348 long bufferSize
= maxLen
;
2350 if (cType
== SQL_C_WXCHAR
)
2351 bufferSize
= maxLen
* sizeof(wxChar
);
2353 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2357 DispAllErrors(henv
, hdbc
, hstmt
);
2361 } // wxDb::GetData()
2364 /********** wxDb::GetKeyFields() **********/
2365 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2367 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2368 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2370 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2371 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2377 * -----------------------------------------------------------------------
2378 * -- 19991224 : mj10777 : Create ------
2379 * -- : Three things are done and stored here : ------
2380 * -- : 1) which Column(s) is/are Primary Key(s) ------
2381 * -- : 2) which tables use this Key as a Foreign Key ------
2382 * -- : 3) which columns are Foreign Key and the name ------
2383 * -- : of the Table where the Key is the Primary Key -----
2384 * -- : Called from GetColumns(const wxString &tableName, ------
2385 * -- int *numCols,const wxChar *userID ) ------
2386 * -----------------------------------------------------------------------
2389 /*---------------------------------------------------------------------*/
2390 /* Get the names of the columns in the primary key. */
2391 /*---------------------------------------------------------------------*/
2392 retcode
= SQLPrimaryKeys(hstmt
,
2393 NULL
, 0, /* Catalog name */
2394 NULL
, 0, /* Schema name */
2395 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2397 /*---------------------------------------------------------------------*/
2398 /* Fetch and display the result set. This will be a list of the */
2399 /* columns in the primary key of the tableName table. */
2400 /*---------------------------------------------------------------------*/
2401 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2403 retcode
= SQLFetch(hstmt
);
2404 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2406 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2407 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2409 for (i
=0;i
<noCols
;i
++) // Find the Column name
2410 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2411 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2414 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2416 /*---------------------------------------------------------------------*/
2417 /* Get all the foreign keys that refer to tableName primary key. */
2418 /*---------------------------------------------------------------------*/
2419 retcode
= SQLForeignKeys(hstmt
,
2420 NULL
, 0, /* Primary catalog */
2421 NULL
, 0, /* Primary schema */
2422 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2423 NULL
, 0, /* Foreign catalog */
2424 NULL
, 0, /* Foreign schema */
2425 NULL
, 0); /* Foreign table */
2427 /*---------------------------------------------------------------------*/
2428 /* Fetch and display the result set. This will be all of the foreign */
2429 /* keys in other tables that refer to the tableName primary key. */
2430 /*---------------------------------------------------------------------*/
2433 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2435 retcode
= SQLFetch(hstmt
);
2436 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2438 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2439 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2440 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2441 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2442 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2443 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2447 tempStr
.Trim(); // Get rid of any unneeded blanks
2448 if (!tempStr
.empty())
2450 for (i
=0; i
<noCols
; i
++)
2451 { // Find the Column name
2452 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2454 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
2455 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2460 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2462 /*---------------------------------------------------------------------*/
2463 /* Get all the foreign keys in the tablename table. */
2464 /*---------------------------------------------------------------------*/
2465 retcode
= SQLForeignKeys(hstmt
,
2466 NULL
, 0, /* Primary catalog */
2467 NULL
, 0, /* Primary schema */
2468 NULL
, 0, /* Primary table */
2469 NULL
, 0, /* Foreign catalog */
2470 NULL
, 0, /* Foreign schema */
2471 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2473 /*---------------------------------------------------------------------*/
2474 /* Fetch and display the result set. This will be all of the */
2475 /* primary keys in other tables that are referred to by foreign */
2476 /* keys in the tableName table. */
2477 /*---------------------------------------------------------------------*/
2478 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2480 retcode
= SQLFetch(hstmt
);
2481 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2483 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2484 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2485 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2487 for (i
=0; i
<noCols
; i
++) // Find the Column name
2489 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2491 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2492 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2493 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2498 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2502 } // wxDb::GetKeyFields()
2506 /********** wxDb::GetColumns() **********/
2507 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2509 * 1) The last array element of the tableName[] argument must be zero (null).
2510 * This is how the end of the array is detected.
2511 * 2) This function returns an array of wxDbColInf structures. If no columns
2512 * were found, or an error occured, this pointer will be zero (null). THE
2513 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2514 * IS FINISHED WITH IT. i.e.
2516 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2519 * // Use the column inf
2521 * // Destroy the memory
2525 * userID is evaluated in the following manner:
2526 * userID == NULL ... UserID is ignored
2527 * userID == "" ... UserID set equal to 'this->uid'
2528 * userID != "" ... UserID set equal to 'userID'
2530 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2531 * by this function. This function should use its own wxDb instance
2532 * to avoid undesired unbinding of columns.
2537 wxDbColInf
*colInf
= 0;
2545 convertUserID(userID
,UserID
);
2547 // Pass 1 - Determine how many columns there are.
2548 // Pass 2 - Allocate the wxDbColInf array and fill in
2549 // the array with the column information.
2551 for (pass
= 1; pass
<= 2; pass
++)
2555 if (noCols
== 0) // Probably a bogus table name(s)
2557 // Allocate n wxDbColInf objects to hold the column information
2558 colInf
= new wxDbColInf
[noCols
+1];
2561 // Mark the end of the array
2562 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2563 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2564 colInf
[noCols
].sqlDataType
= 0;
2566 // Loop through each table name
2568 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2570 TableName
= tableName
[tbl
];
2571 // Oracle and Interbase table names are uppercase only, so force
2572 // the name to uppercase just in case programmer forgot to do this
2573 if ((Dbms() == dbmsORACLE
) ||
2574 (Dbms() == dbmsFIREBIRD
) ||
2575 (Dbms() == dbmsINTERBASE
))
2576 TableName
= TableName
.Upper();
2578 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2580 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2581 // use the call below that leaves out the user name
2582 if (!UserID
.empty() &&
2583 Dbms() != dbmsMY_SQL
&&
2584 Dbms() != dbmsACCESS
&&
2585 Dbms() != dbmsMS_SQL_SERVER
)
2587 retcode
= SQLColumns(hstmt
,
2588 NULL
, 0, // All qualifiers
2589 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2590 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2591 NULL
, 0); // All columns
2595 retcode
= SQLColumns(hstmt
,
2596 NULL
, 0, // All qualifiers
2598 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2599 NULL
, 0); // All columns
2601 if (retcode
!= SQL_SUCCESS
)
2602 { // Error occured, abort
2603 DispAllErrors(henv
, hdbc
, hstmt
);
2606 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2610 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2612 if (pass
== 1) // First pass, just add up the number of columns
2614 else // Pass 2; Fill in the array of structures
2616 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2618 // NOTE: Only the ODBC 1.x fields are retrieved
2619 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2620 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2621 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2622 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2623 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2624 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2625 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2626 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2627 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2628 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2629 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2630 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2632 // Determine the wxDb data type that is used to represent the native data type of this data source
2633 colInf
[colNo
].dbDataType
= 0;
2634 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2637 // IODBC does not return a correct columnLength, so we set
2638 // columnLength = bufferSize if no column length was returned
2639 // IODBC returns the columnLength in bufferSize. (bug)
2640 if (colInf
[colNo
].columnLength
< 1)
2642 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2647 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2648 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2649 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2650 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2651 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2652 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2653 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2654 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2659 if (retcode
!= SQL_NO_DATA_FOUND
)
2660 { // Error occured, abort
2661 DispAllErrors(henv
, hdbc
, hstmt
);
2664 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2670 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2673 } // wxDb::GetColumns()
2676 /********** wxDb::GetColumns() **********/
2678 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2680 // Same as the above GetColumns() function except this one gets columns
2681 // only for a single table, and if 'numCols' is not NULL, the number of
2682 // columns stored in the returned wxDbColInf is set in '*numCols'
2684 // userID is evaluated in the following manner:
2685 // userID == NULL ... UserID is ignored
2686 // userID == "" ... UserID set equal to 'this->uid'
2687 // userID != "" ... UserID set equal to 'userID'
2689 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2690 // by this function. This function should use its own wxDb instance
2691 // to avoid undesired unbinding of columns.
2696 wxDbColInf
*colInf
= 0;
2704 convertUserID(userID
,UserID
);
2706 // Pass 1 - Determine how many columns there are.
2707 // Pass 2 - Allocate the wxDbColInf array and fill in
2708 // the array with the column information.
2710 for (pass
= 1; pass
<= 2; pass
++)
2714 if (noCols
== 0) // Probably a bogus table name(s)
2716 // Allocate n wxDbColInf objects to hold the column information
2717 colInf
= new wxDbColInf
[noCols
+1];
2720 // Mark the end of the array
2721 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2722 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2723 colInf
[noCols
].sqlDataType
= 0;
2726 TableName
= tableName
;
2727 // Oracle and Interbase table names are uppercase only, so force
2728 // the name to uppercase just in case programmer forgot to do this
2729 if ((Dbms() == dbmsORACLE
) ||
2730 (Dbms() == dbmsFIREBIRD
) ||
2731 (Dbms() == dbmsINTERBASE
))
2732 TableName
= TableName
.Upper();
2734 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2736 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2737 // use the call below that leaves out the user name
2738 if (!UserID
.empty() &&
2739 Dbms() != dbmsMY_SQL
&&
2740 Dbms() != dbmsACCESS
&&
2741 Dbms() != dbmsMS_SQL_SERVER
)
2743 retcode
= SQLColumns(hstmt
,
2744 NULL
, 0, // All qualifiers
2745 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2746 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2747 NULL
, 0); // All columns
2751 retcode
= SQLColumns(hstmt
,
2752 NULL
, 0, // All qualifiers
2754 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2755 NULL
, 0); // All columns
2757 if (retcode
!= SQL_SUCCESS
)
2758 { // Error occured, abort
2759 DispAllErrors(henv
, hdbc
, hstmt
);
2762 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2768 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2770 if (pass
== 1) // First pass, just add up the number of columns
2772 else // Pass 2; Fill in the array of structures
2774 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2776 // NOTE: Only the ODBC 1.x fields are retrieved
2777 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2778 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2779 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2780 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2781 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2782 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2783 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2784 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2785 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2786 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2787 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2788 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2789 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2790 // Start Values for Primary/Foriegn Key (=No)
2791 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2792 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2793 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2794 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2796 // BJO 20000428 : Virtuoso returns type names with upper cases!
2797 if (Dbms() == dbmsVIRTUOSO
)
2799 wxString s
= colInf
[colNo
].typeName
;
2801 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2804 // Determine the wxDb data type that is used to represent the native data type of this data source
2805 colInf
[colNo
].dbDataType
= 0;
2806 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2809 // IODBC does not return a correct columnLength, so we set
2810 // columnLength = bufferSize if no column length was returned
2811 // IODBC returns the columnLength in bufferSize. (bug)
2812 if (colInf
[colNo
].columnLength
< 1)
2814 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2818 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2820 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2821 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2822 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2823 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2824 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2825 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2826 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2827 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2833 if (retcode
!= SQL_NO_DATA_FOUND
)
2834 { // Error occured, abort
2835 DispAllErrors(henv
, hdbc
, hstmt
);
2838 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2845 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2847 // Store Primary and Foriegn Keys
2848 GetKeyFields(tableName
,colInf
,noCols
);
2854 } // wxDb::GetColumns()
2857 #else // New GetColumns
2862 These are tentative new GetColumns members which should be more database
2863 independant and which always returns the columns in the order they were
2866 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2867 wxChar* userID)) calls the second implementation for each separate table
2868 before merging the results. This makes the code easier to maintain as
2869 only one member (the second) makes the real work
2870 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2871 wxChar *userID) is a little bit improved
2872 - It doesn't anymore rely on the type-name to find out which database-type
2874 - It ends by sorting the columns, so that they are returned in the same
2875 order they were created
2885 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2888 // The last array element of the tableName[] argument must be zero (null).
2889 // This is how the end of the array is detected.
2893 // How many tables ?
2895 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2897 // Create a table to maintain the columns for each separate table
2898 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2901 for (i
= 0 ; i
< tbl
; i
++)
2904 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2905 if (TableColumns
[i
].colInf
== NULL
)
2907 noCols
+= TableColumns
[i
].noCols
;
2910 // Now merge all the separate table infos
2911 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2913 // Mark the end of the array
2914 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2915 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2916 colInf
[noCols
].sqlDataType
= 0;
2921 for (i
= 0 ; i
< tbl
; i
++)
2923 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2925 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2929 delete [] TableColumns
;
2932 } // wxDb::GetColumns() -- NEW
2935 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2937 // Same as the above GetColumns() function except this one gets columns
2938 // only for a single table, and if 'numCols' is not NULL, the number of
2939 // columns stored in the returned wxDbColInf is set in '*numCols'
2941 // userID is evaluated in the following manner:
2942 // userID == NULL ... UserID is ignored
2943 // userID == "" ... UserID set equal to 'this->uid'
2944 // userID != "" ... UserID set equal to 'userID'
2946 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2947 // by this function. This function should use its own wxDb instance
2948 // to avoid undesired unbinding of columns.
2952 wxDbColInf
*colInf
= 0;
2960 convertUserID(userID
,UserID
);
2962 // Pass 1 - Determine how many columns there are.
2963 // Pass 2 - Allocate the wxDbColInf array and fill in
2964 // the array with the column information.
2966 for (pass
= 1; pass
<= 2; pass
++)
2970 if (noCols
== 0) // Probably a bogus table name(s)
2972 // Allocate n wxDbColInf objects to hold the column information
2973 colInf
= new wxDbColInf
[noCols
+1];
2976 // Mark the end of the array
2977 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2978 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2979 colInf
[noCols
].sqlDataType
= 0;
2982 TableName
= tableName
;
2983 // Oracle and Interbase table names are uppercase only, so force
2984 // the name to uppercase just in case programmer forgot to do this
2985 if ((Dbms() == dbmsORACLE
) ||
2986 (Dbms() == dbmsFIREBIRD
) ||
2987 (Dbms() == dbmsINTERBASE
))
2988 TableName
= TableName
.Upper();
2990 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2992 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2993 // use the call below that leaves out the user name
2994 if (!UserID
.empty() &&
2995 Dbms() != dbmsMY_SQL
&&
2996 Dbms() != dbmsACCESS
&&
2997 Dbms() != dbmsMS_SQL_SERVER
)
2999 retcode
= SQLColumns(hstmt
,
3000 NULL
, 0, // All qualifiers
3001 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3002 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3003 NULL
, 0); // All columns
3007 retcode
= SQLColumns(hstmt
,
3008 NULL
, 0, // All qualifiers
3010 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3011 NULL
, 0); // All columns
3013 if (retcode
!= SQL_SUCCESS
)
3014 { // Error occured, abort
3015 DispAllErrors(henv
, hdbc
, hstmt
);
3018 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3024 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3026 if (pass
== 1) // First pass, just add up the number of columns
3028 else // Pass 2; Fill in the array of structures
3030 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3032 // NOTE: Only the ODBC 1.x fields are retrieved
3033 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3034 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3035 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3036 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3037 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3038 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3039 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3040 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3041 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3042 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3043 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3044 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3045 // Start Values for Primary/Foriegn Key (=No)
3046 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3047 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3048 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3049 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3052 // IODBC does not return a correct columnLength, so we set
3053 // columnLength = bufferSize if no column length was returned
3054 // IODBC returns the columnLength in bufferSize. (bug)
3055 if (colInf
[colNo
].columnLength
< 1)
3057 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3061 // Determine the wxDb data type that is used to represent the native data type of this data source
3062 colInf
[colNo
].dbDataType
= 0;
3063 // Get the intern datatype
3064 switch (colInf
[colNo
].sqlDataType
)
3067 #if defined(SQL_WCHAR)
3070 #if defined(SQL_WVARCHAR)
3076 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3082 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3089 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3093 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3096 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3101 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3102 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3109 if (retcode
!= SQL_NO_DATA_FOUND
)
3110 { // Error occured, abort
3111 DispAllErrors(henv
, hdbc
, hstmt
);
3114 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3121 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3123 // Store Primary and Foreign Keys
3124 GetKeyFields(tableName
,colInf
,noCols
);
3126 ///////////////////////////////////////////////////////////////////////////
3127 // Now sort the the columns in order to make them appear in the right order
3128 ///////////////////////////////////////////////////////////////////////////
3130 // Build a generic SELECT statement which returns 0 rows
3133 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3136 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3138 DispAllErrors(henv
, hdbc
, hstmt
);
3142 // Get the number of result columns
3143 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3145 DispAllErrors(henv
, hdbc
, hstmt
);
3149 if (noCols
== 0) // Probably a bogus table name
3158 for (colNum
= 0; colNum
< noCols
; colNum
++)
3160 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3162 &Sword
, &Sdword
) != SQL_SUCCESS
)
3164 DispAllErrors(henv
, hdbc
, hstmt
);
3168 wxString Name1
= name
;
3169 Name1
= Name1
.Upper();
3171 // Where is this name in the array ?
3172 for (i
= colNum
; i
< noCols
; i
++)
3174 wxString Name2
= colInf
[i
].colName
;
3175 Name2
= Name2
.Upper();
3178 if (colNum
!= i
) // swap to sort
3180 wxDbColInf tmpColInf
= colInf
[colNum
];
3181 colInf
[colNum
] = colInf
[i
];
3182 colInf
[i
] = tmpColInf
;
3188 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3190 ///////////////////////////////////////////////////////////////////////////
3192 ///////////////////////////////////////////////////////////////////////////
3198 } // wxDb::GetColumns()
3201 #endif // #else OLD_GETCOLUMNS
3204 /********** wxDb::GetColumnCount() **********/
3205 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3207 * Returns a count of how many columns are in a table.
3208 * If an error occurs in computing the number of columns
3209 * this function will return a -1 for the count
3211 * userID is evaluated in the following manner:
3212 * userID == NULL ... UserID is ignored
3213 * userID == "" ... UserID set equal to 'this->uid'
3214 * userID != "" ... UserID set equal to 'userID'
3216 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3217 * by this function. This function should use its own wxDb instance
3218 * to avoid undesired unbinding of columns.
3228 convertUserID(userID
,UserID
);
3230 TableName
= tableName
;
3231 // Oracle and Interbase table names are uppercase only, so force
3232 // the name to uppercase just in case programmer forgot to do this
3233 if ((Dbms() == dbmsORACLE
) ||
3234 (Dbms() == dbmsFIREBIRD
) ||
3235 (Dbms() == dbmsINTERBASE
))
3236 TableName
= TableName
.Upper();
3238 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3240 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3241 // use the call below that leaves out the user name
3242 if (!UserID
.empty() &&
3243 Dbms() != dbmsMY_SQL
&&
3244 Dbms() != dbmsACCESS
&&
3245 Dbms() != dbmsMS_SQL_SERVER
)
3247 retcode
= SQLColumns(hstmt
,
3248 NULL
, 0, // All qualifiers
3249 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3250 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3251 NULL
, 0); // All columns
3255 retcode
= SQLColumns(hstmt
,
3256 NULL
, 0, // All qualifiers
3258 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3259 NULL
, 0); // All columns
3261 if (retcode
!= SQL_SUCCESS
)
3262 { // Error occured, abort
3263 DispAllErrors(henv
, hdbc
, hstmt
);
3264 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3268 // Count the columns
3269 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3272 if (retcode
!= SQL_NO_DATA_FOUND
)
3273 { // Error occured, abort
3274 DispAllErrors(henv
, hdbc
, hstmt
);
3275 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3279 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3282 } // wxDb::GetColumnCount()
3285 /********** wxDb::GetCatalog() *******/
3286 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3288 * ---------------------------------------------------------------------
3289 * -- 19991203 : mj10777 : Create ------
3290 * -- : Creates a wxDbInf with Tables / Cols Array ------
3291 * -- : uses SQLTables and fills pTableInf; ------
3292 * -- : pColInf is set to NULL and numCols to 0; ------
3293 * -- : returns pDbInf (wxDbInf) ------
3294 * -- - if unsuccesfull (pDbInf == NULL) ------
3295 * -- : pColInf can be filled with GetColumns(..); ------
3296 * -- : numCols can be filled with GetColumnCount(..); ------
3297 * ---------------------------------------------------------------------
3299 * userID is evaluated in the following manner:
3300 * userID == NULL ... UserID is ignored
3301 * userID == "" ... UserID set equal to 'this->uid'
3302 * userID != "" ... UserID set equal to 'userID'
3304 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3305 * by this function. This function should use its own wxDb instance
3306 * to avoid undesired unbinding of columns.
3309 int noTab
= 0; // Counter while filling table entries
3313 wxString tblNameSave
;
3316 convertUserID(userID
,UserID
);
3318 //-------------------------------------------------------------
3319 // Create the Database Array of catalog entries
3321 wxDbInf
*pDbInf
= new wxDbInf
;
3323 //-------------------------------------------------------------
3324 // Table Information
3325 // Pass 1 - Determine how many Tables there are.
3326 // Pass 2 - Create the Table array and fill it
3327 // - Create the Cols array = NULL
3328 //-------------------------------------------------------------
3330 for (pass
= 1; pass
<= 2; pass
++)
3332 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3333 tblNameSave
.Empty();
3335 if (!UserID
.empty() &&
3336 Dbms() != dbmsMY_SQL
&&
3337 Dbms() != dbmsACCESS
&&
3338 Dbms() != dbmsMS_SQL_SERVER
)
3340 retcode
= SQLTables(hstmt
,
3341 NULL
, 0, // All qualifiers
3342 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3343 NULL
, 0, // All tables
3344 NULL
, 0); // All columns
3348 retcode
= SQLTables(hstmt
,
3349 NULL
, 0, // All qualifiers
3350 NULL
, 0, // User specified
3351 NULL
, 0, // All tables
3352 NULL
, 0); // All columns
3355 if (retcode
!= SQL_SUCCESS
)
3357 DispAllErrors(henv
, hdbc
, hstmt
);
3359 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3363 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3365 if (pass
== 1) // First pass, just count the Tables
3367 if (pDbInf
->numTables
== 0)
3369 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3370 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3372 pDbInf
->numTables
++; // Counter for Tables
3374 if (pass
== 2) // Create and fill the Table entries
3376 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3377 { // no, then create the Array
3378 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3380 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3382 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3383 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3384 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3390 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3392 // Query how many columns are in each table
3393 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3395 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3400 } // wxDb::GetCatalog()
3403 /********** wxDb::Catalog() **********/
3404 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3406 * Creates the text file specified in 'filename' which will contain
3407 * a minimal data dictionary of all tables accessible by the user specified
3410 * userID is evaluated in the following manner:
3411 * userID == NULL ... UserID is ignored
3412 * userID == "" ... UserID set equal to 'this->uid'
3413 * userID != "" ... UserID set equal to 'userID'
3415 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3416 * by this function. This function should use its own wxDb instance
3417 * to avoid undesired unbinding of columns.
3420 wxASSERT(fileName
.Length());
3424 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3425 wxString tblNameSave
;
3426 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3428 wxChar typeName
[30+1];
3429 SDWORD precision
, length
;
3431 FILE *fp
= wxFopen(fileName
.fn_str(),wxT("wt"));
3435 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3438 convertUserID(userID
,UserID
);
3440 if (!UserID
.empty() &&
3441 Dbms() != dbmsMY_SQL
&&
3442 Dbms() != dbmsACCESS
&&
3443 Dbms() != dbmsFIREBIRD
&&
3444 Dbms() != dbmsINTERBASE
&&
3445 Dbms() != dbmsMS_SQL_SERVER
)
3447 retcode
= SQLColumns(hstmt
,
3448 NULL
, 0, // All qualifiers
3449 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3450 NULL
, 0, // All tables
3451 NULL
, 0); // All columns
3455 retcode
= SQLColumns(hstmt
,
3456 NULL
, 0, // All qualifiers
3457 NULL
, 0, // User specified
3458 NULL
, 0, // All tables
3459 NULL
, 0); // All columns
3461 if (retcode
!= SQL_SUCCESS
)
3463 DispAllErrors(henv
, hdbc
, hstmt
);
3469 tblNameSave
.Empty();
3474 retcode
= SQLFetch(hstmt
);
3475 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3478 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3479 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3480 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3481 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3482 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3483 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3485 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3488 wxFputs(wxT("\n"), fp
);
3489 wxFputs(wxT("================================ "), fp
);
3490 wxFputs(wxT("================================ "), fp
);
3491 wxFputs(wxT("===================== "), fp
);
3492 wxFputs(wxT("========= "), fp
);
3493 wxFputs(wxT("=========\n"), fp
);
3494 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3495 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3496 wxFputs(outStr
.c_str(), fp
);
3497 wxFputs(wxT("================================ "), fp
);
3498 wxFputs(wxT("================================ "), fp
);
3499 wxFputs(wxT("===================== "), fp
);
3500 wxFputs(wxT("========= "), fp
);
3501 wxFputs(wxT("=========\n"), fp
);
3502 tblNameSave
= tblName
;
3505 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3506 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3507 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3509 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3516 if (retcode
!= SQL_NO_DATA_FOUND
)
3517 DispAllErrors(henv
, hdbc
, hstmt
);
3519 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3522 return(retcode
== SQL_NO_DATA_FOUND
);
3524 } // wxDb::Catalog()
3527 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3529 * Table name can refer to a table, view, alias or synonym. Returns true
3530 * if the object exists in the database. This function does not indicate
3531 * whether or not the user has privleges to query or perform other functions
3534 * userID is evaluated in the following manner:
3535 * userID == NULL ... UserID is ignored
3536 * userID == "" ... UserID set equal to 'this->uid'
3537 * userID != "" ... UserID set equal to 'userID'
3540 wxASSERT(tableName
.Length());
3544 if (Dbms() == dbmsDBASE
)
3547 if (tablePath
.Length())
3548 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3550 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3553 exists
= wxFileExists(dbName
);
3558 convertUserID(userID
,UserID
);
3560 TableName
= tableName
;
3561 // Oracle and Interbase table names are uppercase only, so force
3562 // the name to uppercase just in case programmer forgot to do this
3563 if ((Dbms() == dbmsORACLE
) ||
3564 (Dbms() == dbmsFIREBIRD
) ||
3565 (Dbms() == dbmsINTERBASE
))
3566 TableName
= TableName
.Upper();
3568 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3571 // Some databases cannot accept a user name when looking up table names,
3572 // so we use the call below that leaves out the user name
3573 if (!UserID
.empty() &&
3574 Dbms() != dbmsMY_SQL
&&
3575 Dbms() != dbmsACCESS
&&
3576 Dbms() != dbmsMS_SQL_SERVER
&&
3577 Dbms() != dbmsDB2
&&
3578 Dbms() != dbmsFIREBIRD
&&
3579 Dbms() != dbmsINTERBASE
&&
3580 Dbms() != dbmsPERVASIVE_SQL
)
3582 retcode
= SQLTables(hstmt
,
3583 NULL
, 0, // All qualifiers
3584 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3585 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3586 NULL
, 0); // All table types
3590 retcode
= SQLTables(hstmt
,
3591 NULL
, 0, // All qualifiers
3592 NULL
, 0, // All owners
3593 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3594 NULL
, 0); // All table types
3596 if (retcode
!= SQL_SUCCESS
)
3597 return(DispAllErrors(henv
, hdbc
, hstmt
));
3599 retcode
= SQLFetch(hstmt
);
3600 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3602 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3603 return(DispAllErrors(henv
, hdbc
, hstmt
));
3606 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3610 } // wxDb::TableExists()
3613 /********** wxDb::TablePrivileges() **********/
3614 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3615 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3617 wxASSERT(tableName
.Length());
3619 wxDbTablePrivilegeInfo result
;
3623 // We probably need to be able to dynamically set this based on
3624 // the driver type, and state.
3625 wxChar curRole
[]=wxT("public");
3629 wxString UserID
,Schema
;
3630 convertUserID(userID
,UserID
);
3631 convertUserID(schema
,Schema
);
3633 TableName
= tableName
;
3634 // Oracle and Interbase table names are uppercase only, so force
3635 // the name to uppercase just in case programmer forgot to do this
3636 if ((Dbms() == dbmsORACLE
) ||
3637 (Dbms() == dbmsFIREBIRD
) ||
3638 (Dbms() == dbmsINTERBASE
))
3639 TableName
= TableName
.Upper();
3641 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3643 // Some databases cannot accept a user name when looking up table names,
3644 // so we use the call below that leaves out the user name
3645 if (!Schema
.empty() &&
3646 Dbms() != dbmsMY_SQL
&&
3647 Dbms() != dbmsACCESS
&&
3648 Dbms() != dbmsMS_SQL_SERVER
)
3650 retcode
= SQLTablePrivileges(hstmt
,
3652 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3653 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3657 retcode
= SQLTablePrivileges(hstmt
,
3660 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3663 #ifdef DBDEBUG_CONSOLE
3664 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3667 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3668 return (DispAllErrors(henv
, hdbc
, hstmt
));
3670 bool failed
= false;
3671 retcode
= SQLFetch(hstmt
);
3672 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3674 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3677 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3680 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3683 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3686 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3689 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3692 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3697 return(DispAllErrors(henv
, hdbc
, hstmt
));
3699 #ifdef DBDEBUG_CONSOLE
3700 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3701 result
.privilege
,result
.tableOwner
,result
.tableName
,
3702 result
.grantor
, result
.grantee
);
3705 if (UserID
.IsSameAs(result
.tableOwner
,false))
3707 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3711 if (UserID
.IsSameAs(result
.grantee
,false) &&
3712 !wxStrcmp(result
.privilege
,priv
))
3714 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3718 if (!wxStrcmp(result
.grantee
,curRole
) &&
3719 !wxStrcmp(result
.privilege
,priv
))
3721 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3725 retcode
= SQLFetch(hstmt
);
3728 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3731 } // wxDb::TablePrivileges
3734 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3738 if (Dbms() == dbmsACCESS
)
3739 TableName
= _T("\"");
3740 TableName
+= tableName
;
3741 if (Dbms() == dbmsACCESS
)
3742 TableName
+= _T("\"");
3745 } // wxDb::SQLTableName()
3748 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3752 if (Dbms() == dbmsACCESS
)
3755 if (Dbms() == dbmsACCESS
)
3756 ColName
+= _T("\"");
3759 } // wxDb::SQLColumnName()
3762 /********** wxDb::SetSqlLogging() **********/
3763 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3765 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3766 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3768 if (state
== sqlLogON
)
3772 fpSqlLog
= wxFopen(filename
.fn_str(), (append
? wxT("at") : wxT("wt")));
3773 if (fpSqlLog
== NULL
)
3781 if (fclose(fpSqlLog
))
3787 sqlLogState
= state
;
3790 } // wxDb::SetSqlLogging()
3793 /********** wxDb::WriteSqlLog() **********/
3794 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3796 wxASSERT(logMsg
.Length());
3798 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3801 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3803 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3805 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3810 } // wxDb::WriteSqlLog()
3813 /********** wxDb::Dbms() **********/
3814 wxDBMS
wxDb::Dbms(void)
3816 * Be aware that not all database engines use the exact same syntax, and not
3817 * every ODBC compliant database is compliant to the same level of compliancy.
3818 * Some manufacturers support the minimum Level 1 compliancy, and others up
3819 * through Level 3. Others support subsets of features for levels above 1.
3821 * If you find an inconsistency between the wxDb class and a specific database
3822 * engine, and an identifier to this section, and special handle the database in
3823 * the area where behavior is non-conforming with the other databases.
3826 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3827 * ---------------------------------------------------
3830 * - Currently the only database supported by the class to support VIEWS
3833 * - Does not support the SQL_TIMESTAMP structure
3834 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3835 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3836 * is true. The user must create ALL indexes from their program.
3837 * - Table names can only be 8 characters long
3838 * - Column names can only be 10 characters long
3841 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3842 * after every table name involved in the query/join if that tables matching record(s)
3844 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3846 * SYBASE (Enterprise)
3847 * - If a column is part of the Primary Key, the column cannot be NULL
3848 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3851 * - If a column is part of the Primary Key, the column cannot be NULL
3852 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3853 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3854 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3855 * column definition if it is not defined correctly, but it is experimental
3856 * - Does not support sub-queries in SQL statements
3859 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3860 * - Does not support sub-queries in SQL statements
3863 * - Primary keys must be declared as NOT NULL
3864 * - Table and index names must not be longer than 13 characters in length (technically
3865 * table names can be up to 18 characters, but the primary index is created using the
3866 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3871 * - Columns that are part of primary keys must be defined as being NOT NULL
3872 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3873 * column definition if it is not defined correctly, but it is experimental
3876 // Should only need to do this once for each new database connection
3877 // so return the value we already determined it to be to save time
3878 // and lots of string comparisons
3879 if (dbmsType
!= dbmsUNIDENTIFIED
)
3882 #ifdef DBDEBUG_CONSOLE
3883 // When run in console mode, use standard out to display errors.
3884 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3885 #endif // DBDEBUG_CONSOLE
3887 wxLogDebug(wxT("Database connecting to: "));
3888 wxLogDebug(dbInf
.dbmsName
);
3890 wxChar baseName
[25+1];
3891 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3894 // RGG 20001025 : add support for Interbase
3895 // GT : Integrated to base classes on 20001121
3896 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3897 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3899 // BJO 20000428 : add support for Virtuoso
3900 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3901 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3903 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3904 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3906 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3907 // connected through an OpenLink driver.
3908 // Is it also returned by Sybase Adapatitve server?
3909 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3910 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3912 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3913 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3914 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3916 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3919 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3920 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3923 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3924 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3927 if (!wxStricmp(baseName
,wxT("Pervasive")))
3928 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3931 if (!wxStricmp(baseName
,wxT("Informix")))
3932 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3934 if (!wxStricmp(baseName
,wxT("Firebird")))
3935 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3938 if (!wxStricmp(baseName
,wxT("Oracle")))
3939 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3940 if (!wxStricmp(baseName
,wxT("ACCESS")))
3941 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3942 if (!wxStricmp(baseName
,wxT("Sybase")))
3943 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3946 if (!wxStricmp(baseName
,wxT("DBASE")))
3947 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3948 if (!wxStricmp(baseName
,wxT("xBase")))
3949 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3950 if (!wxStricmp(baseName
,wxT("MySQL")))
3951 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3954 if (!wxStricmp(baseName
,wxT("DB2")))
3955 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3957 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3962 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3963 int dataType
, ULONG columnLength
,
3964 const wxString
&optionalParam
)
3966 wxASSERT(tableName
.Length());
3967 wxASSERT(columnName
.Length());
3968 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3969 dataType
!= DB_DATA_TYPE_VARCHAR
);
3971 // Must specify a columnLength if modifying a VARCHAR type column
3972 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3975 wxString dataTypeName
;
3977 wxString alterSlashModify
;
3981 case DB_DATA_TYPE_VARCHAR
:
3982 dataTypeName
= typeInfVarchar
.TypeName
;
3984 case DB_DATA_TYPE_INTEGER
:
3985 dataTypeName
= typeInfInteger
.TypeName
;
3987 case DB_DATA_TYPE_FLOAT
:
3988 dataTypeName
= typeInfFloat
.TypeName
;
3990 case DB_DATA_TYPE_DATE
:
3991 dataTypeName
= typeInfDate
.TypeName
;
3993 case DB_DATA_TYPE_BLOB
:
3994 dataTypeName
= typeInfBlob
.TypeName
;
4000 // Set the modify or alter syntax depending on the type of database connected to
4004 alterSlashModify
= _T("MODIFY");
4006 case dbmsMS_SQL_SERVER
:
4007 alterSlashModify
= _T("ALTER COLUMN");
4009 case dbmsUNIDENTIFIED
:
4011 case dbmsSYBASE_ASA
:
4012 case dbmsSYBASE_ASE
:
4017 case dbmsXBASE_SEQUITER
:
4019 alterSlashModify
= _T("MODIFY");
4023 // create the SQL statement
4024 if ( Dbms() == dbmsMY_SQL
)
4026 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4027 columnName
.c_str(), dataTypeName
.c_str());
4031 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4032 columnName
.c_str(), dataTypeName
.c_str());
4035 // For varchars only, append the size of the column
4036 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4037 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4040 s
.Printf(wxT("(%lu)"), columnLength
);
4044 // for passing things like "NOT NULL"
4045 if (optionalParam
.Length())
4047 sqlStmt
+= wxT(" ");
4048 sqlStmt
+= optionalParam
;
4051 return ExecSql(sqlStmt
);
4053 } // wxDb::ModifyColumn()
4056 /********** wxDbGetConnection() **********/
4057 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4061 // Used to keep a pointer to a DB connection that matches the requested
4062 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4063 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4064 // rather than having to re-query the datasource to get all the values
4065 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4066 wxDb
*matchingDbConnection
= NULL
;
4068 // Scan the linked list searching for an available database connection
4069 // that's already been opened but is currently not in use.
4070 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4072 // The database connection must be for the same datasource
4073 // name and must currently not be in use.
4075 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4077 if (pDbConfig
->UseConnectionStr())
4079 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4080 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4082 // Found a free connection
4083 pList
->Free
= false;
4084 return(pList
->PtrDb
);
4089 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4090 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4092 // Found a free connection
4093 pList
->Free
= false;
4094 return(pList
->PtrDb
);
4099 if (pDbConfig
->UseConnectionStr())
4101 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4102 matchingDbConnection
= pList
->PtrDb
;
4106 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4107 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4108 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4109 matchingDbConnection
= pList
->PtrDb
;
4113 // No available connections. A new connection must be made and
4114 // appended to the end of the linked list.
4117 // Find the end of the list
4118 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4119 // Append a new list item
4120 pList
->PtrNext
= new wxDbList
;
4121 pList
->PtrNext
->PtrPrev
= pList
;
4122 pList
= pList
->PtrNext
;
4126 // Create the first node on the list
4127 pList
= PtrBegDbList
= new wxDbList
;
4131 // Initialize new node in the linked list
4133 pList
->Free
= false;
4134 pList
->Dsn
= pDbConfig
->GetDsn();
4135 pList
->Uid
= pDbConfig
->GetUserID();
4136 pList
->AuthStr
= pDbConfig
->GetPassword();
4137 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4139 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4143 if (!matchingDbConnection
)
4145 if (pDbConfig
->UseConnectionStr())
4147 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4151 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4155 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4157 // Connect to the datasource
4160 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4161 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4162 return(pList
->PtrDb
);
4164 else // Unable to connect, destroy list item
4167 pList
->PtrPrev
->PtrNext
= 0;
4169 PtrBegDbList
= 0; // Empty list again
4171 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4172 pList
->PtrDb
->Close(); // Close the wxDb object
4173 delete pList
->PtrDb
; // Deletes the wxDb object
4174 delete pList
; // Deletes the linked list object
4178 } // wxDbGetConnection()
4181 /********** wxDbFreeConnection() **********/
4182 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4186 // Scan the linked list searching for the database connection
4187 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4189 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4190 return (pList
->Free
= true);
4193 // Never found the database object, return failure
4196 } // wxDbFreeConnection()
4199 /********** wxDbCloseConnections() **********/
4200 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4202 wxDbList
*pList
, *pNext
;
4204 // Traverse the linked list closing database connections and freeing memory as I go.
4205 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4207 pNext
= pList
->PtrNext
; // Save the pointer to next
4208 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4209 pList
->PtrDb
->Close(); // Close the wxDb object
4210 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4211 delete pList
->PtrDb
; // Deletes the wxDb object
4212 delete pList
; // Deletes the linked list object
4215 // Mark the list as empty
4218 } // wxDbCloseConnections()
4221 /********** wxDbConnectionsInUse() **********/
4222 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4227 // Scan the linked list counting db connections that are currently in use
4228 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4230 if (pList
->Free
== false)
4236 } // wxDbConnectionsInUse()
4240 /********** wxDbLogExtendedErrorMsg() **********/
4241 // DEBUG ONLY function
4242 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4244 const wxChar
*ErrFile
,
4247 static wxString msg
;
4252 if (ErrFile
|| ErrLine
)
4254 msg
+= wxT("File: ");
4256 msg
+= wxT(" Line: ");
4257 tStr
.Printf(wxT("%d"),ErrLine
);
4258 msg
+= tStr
.c_str();
4262 msg
.Append (wxT("\nODBC errors:\n"));
4265 // Display errors for this connection
4267 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4269 if (pDb
->errorList
[i
])
4271 msg
.Append(pDb
->errorList
[i
]);
4272 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4273 msg
.Append(wxT("\n"));
4274 // Clear the errmsg buffer so the next error will not
4275 // end up showing the previous error that have occurred
4276 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4281 wxLogDebug(msg
.c_str());
4284 } // wxDbLogExtendedErrorMsg()
4287 /********** wxDbSqlLog() **********/
4288 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4290 bool append
= false;
4293 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4295 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4300 SQLLOGstate
= state
;
4301 SQLLOGfn
= filename
;
4309 /********** wxDbCreateDataSource() **********/
4310 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4311 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4313 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4314 * Very rudimentary creation of an ODBC data source.
4316 * ODBC driver must be ODBC 3.0 compliant to use this function
4321 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4327 dsnLocation
= ODBC_ADD_SYS_DSN
;
4329 dsnLocation
= ODBC_ADD_DSN
;
4331 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4332 // so that is why I used it, as wxString does not deal well with
4333 // embedded nulls in strings
4334 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4336 // Replace the separator from above with the '\0' seperator needed
4337 // by the SQLConfigDataSource() function
4341 k
= setupStr
.Find((wxChar
)2,true);
4342 if (k
!= wxNOT_FOUND
)
4343 setupStr
[(UINT
)k
] = wxT('\0');
4345 while (k
!= wxNOT_FOUND
);
4347 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4348 driverName
, setupStr
.c_str());
4350 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4352 // check for errors caused by ConfigDSN based functions
4355 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4356 errMsg
[0] = wxT('\0');
4358 // This function is only supported in ODBC drivers v3.0 compliant and above
4359 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4362 #ifdef DBDEBUG_CONSOLE
4363 // When run in console mode, use standard out to display errors.
4364 cout
<< errMsg
<< endl
;
4365 cout
<< wxT("Press any key to continue...") << endl
;
4367 #endif // DBDEBUG_CONSOLE
4370 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4371 #endif // __WXDEBUG__
4377 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4378 // necessary to use this function, so this function is not supported
4380 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4383 #endif // __VISUALC__
4387 } // wxDbCreateDataSource()
4391 /********** wxDbGetDataSource() **********/
4392 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4393 SWORD DsDescMaxLength
, UWORD direction
)
4395 * Dsn and DsDesc will contain the data source name and data source
4396 * description upon return
4400 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4401 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4403 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4404 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4409 } // wxDbGetDataSource()
4412 // Change this to 0 to remove use of all deprecated functions
4413 #if wxODBC_BACKWARD_COMPATABILITY
4414 /********************************************************************
4415 ********************************************************************
4417 * The following functions are all DEPRECATED and are included for
4418 * backward compatability reasons only
4420 ********************************************************************
4421 ********************************************************************/
4422 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4424 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4426 /***** DEPRECATED: use wxGetDataSource() *****/
4427 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4430 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4432 /***** DEPRECATED: use wxDbGetConnection() *****/
4433 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4435 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4437 /***** DEPRECATED: use wxDbFreeConnection() *****/
4438 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4440 return wxDbFreeConnection(pDb
);
4442 /***** DEPRECATED: use wxDbCloseConnections() *****/
4443 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4445 wxDbCloseConnections();
4447 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4448 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4450 return wxDbConnectionsInUse();