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
];
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
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
291 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
292 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
293 i_dbDataType
= DB_DATA_TYPE_DATE
;
294 if (i_sqlDataType
== SQL_C_BIT
)
295 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
296 if (i_sqlDataType
== SQL_NUMERIC
)
297 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
298 if (i_sqlDataType
== SQL_REAL
)
299 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
300 if (i_sqlDataType
== SQL_C_BINARY
)
301 i_dbDataType
= DB_DATA_TYPE_BLOB
;
304 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
306 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
309 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
311 case DB_DATA_TYPE_VARCHAR
:
314 case DB_DATA_TYPE_INTEGER
:
317 case DB_DATA_TYPE_FLOAT
:
318 if (decimalDigits
== 0)
321 tempStr
.Printf(wxT("%s%d.%d"), tempStr
.c_str(),columnLength
, decimalDigits
);
322 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
324 case DB_DATA_TYPE_DATE
:
325 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
327 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
329 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
331 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
333 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
335 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
337 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
339 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
341 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
343 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
346 case DB_DATA_TYPE_BLOB
:
347 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
350 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
354 } // wxDbColFor::Format()
357 /********** wxDbColInf Constructor **********/
358 wxDbColInf::wxDbColInf()
361 } // wxDbColInf::wxDbColInf()
364 /********** wxDbColInf Destructor ********/
365 wxDbColInf::~wxDbColInf()
370 } // wxDbColInf::~wxDbColInf()
373 bool wxDbColInf::Initialize()
395 } // wxDbColInf::Initialize()
398 /********** wxDbTableInf Constructor ********/
399 wxDbTableInf::wxDbTableInf()
402 } // wxDbTableInf::wxDbTableInf()
405 /********** wxDbTableInf Constructor ********/
406 wxDbTableInf::~wxDbTableInf()
411 } // wxDbTableInf::~wxDbTableInf()
414 bool wxDbTableInf::Initialize()
423 } // wxDbTableInf::Initialize()
426 /********** wxDbInf Constructor *************/
430 } // wxDbInf::wxDbInf()
433 /********** wxDbInf Destructor *************/
439 } // wxDbInf::~wxDbInf()
442 /********** wxDbInf::Initialize() *************/
443 bool wxDbInf::Initialize()
451 } // wxDbInf::Initialize()
454 /********** wxDb Constructor **********/
455 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
457 // Copy the HENV into the db class
459 fwdOnlyCursors
= FwdOnlyCursors
;
465 /********** wxDb Destructor **********/
468 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
478 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
479 /********** wxDb::initialize() **********/
480 void wxDb::initialize()
482 * Private member function that sets all wxDb member variables to
483 * known values at creation of the wxDb
488 fpSqlLog
= 0; // Sql Log file pointer
489 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
491 dbmsType
= dbmsUNIDENTIFIED
;
493 wxStrcpy(sqlState
,wxEmptyString
);
494 wxStrcpy(errorMsg
,wxEmptyString
);
495 nativeError
= cbErrorMsg
= 0;
496 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
497 wxStrcpy(errorList
[i
], wxEmptyString
);
499 // Init typeInf structures
500 typeInfVarchar
.TypeName
.Empty();
501 typeInfVarchar
.FsqlType
= 0;
502 typeInfVarchar
.Precision
= 0;
503 typeInfVarchar
.CaseSensitive
= 0;
504 typeInfVarchar
.MaximumScale
= 0;
506 typeInfInteger
.TypeName
.Empty();
507 typeInfInteger
.FsqlType
= 0;
508 typeInfInteger
.Precision
= 0;
509 typeInfInteger
.CaseSensitive
= 0;
510 typeInfInteger
.MaximumScale
= 0;
512 typeInfFloat
.TypeName
.Empty();
513 typeInfFloat
.FsqlType
= 0;
514 typeInfFloat
.Precision
= 0;
515 typeInfFloat
.CaseSensitive
= 0;
516 typeInfFloat
.MaximumScale
= 0;
518 typeInfDate
.TypeName
.Empty();
519 typeInfDate
.FsqlType
= 0;
520 typeInfDate
.Precision
= 0;
521 typeInfDate
.CaseSensitive
= 0;
522 typeInfDate
.MaximumScale
= 0;
524 typeInfBlob
.TypeName
.Empty();
525 typeInfBlob
.FsqlType
= 0;
526 typeInfBlob
.Precision
= 0;
527 typeInfBlob
.CaseSensitive
= 0;
528 typeInfBlob
.MaximumScale
= 0;
530 // Error reporting is turned OFF by default
533 // Allocate a data source connection handle
534 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
537 // Initialize the db status flag
540 // Mark database as not open as of yet
543 dbOpenedWithConnectionString
= false;
544 } // wxDb::initialize()
547 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
549 // NOTE: Return value from this function MUST be copied
550 // immediately, as the value is not good after
551 // this function has left scope.
553 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
557 if (!wxStrlen(userID
))
565 // dBase does not use user names, and some drivers fail if you try to pass one
566 if ( Dbms() == dbmsDBASE
567 || Dbms() == dbmsXBASE_SEQUITER
)
570 // Oracle user names may only be in uppercase, so force
571 // the name to uppercase
572 if (Dbms() == dbmsORACLE
)
573 UserID
= UserID
.Upper();
575 return UserID
.c_str();
576 } // wxDb::convertUserID()
579 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
583 // These are the possible SQL types we check for use against the datasource we are connected
584 // to for the purpose of determining which data type to use for the basic character strings
587 // NOTE: The first type in this enumeration that is determined to be supported by the
588 // datasource/driver is the one that will be used.
589 SWORD PossibleSqlCharTypes
[] = {
590 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
594 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
600 // These are the possible SQL types we check for use against the datasource we are connected
601 // to for the purpose of determining which data type to use for the basic non-floating point
604 // NOTE: The first type in this enumeration that is determined to be supported by the
605 // datasource/driver is the one that will be used.
606 SWORD PossibleSqlIntegerTypes
[] = {
610 // These are the possible SQL types we check for use against the datasource we are connected
611 // to for the purpose of determining which data type to use for the basic floating point number
614 // NOTE: The first type in this enumeration that is determined to be supported by the
615 // datasource/driver is the one that will be used.
616 SWORD PossibleSqlFloatTypes
[] = {
624 // These are the possible SQL types we check for use agains the datasource we are connected
625 // to for the purpose of determining which data type to use for the date/time column types
627 // NOTE: The first type in this enumeration that is determined to be supported by the
628 // datasource/driver is the one that will be used.
629 SWORD PossibleSqlDateTypes
[] = {
637 // These are the possible SQL types we check for use agains the datasource we are connected
638 // to for the purpose of determining which data type to use for the BLOB column types.
640 // NOTE: The first type in this enumeration that is determined to be supported by the
641 // datasource/driver is the one that will be used.
642 SWORD PossibleSqlBlobTypes
[] = {
648 // Query the data source regarding data type information
651 // The way it was determined which SQL data types to use was by calling SQLGetInfo
652 // for all of the possible SQL data types to see which ones were supported. If
653 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
654 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
655 // types I've selected below will not always be what we want. These are just
656 // what happened to work against an Oracle 7/Intersolv combination. The following is
657 // a complete list of the results I got back against the Oracle 7 database:
659 // SQL_BIGINT SQL_NO_DATA_FOUND
660 // SQL_BINARY SQL_NO_DATA_FOUND
661 // SQL_BIT SQL_NO_DATA_FOUND
662 // SQL_CHAR type name = 'CHAR', Precision = 255
663 // SQL_DATE SQL_NO_DATA_FOUND
664 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
665 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
666 // SQL_FLOAT SQL_NO_DATA_FOUND
667 // SQL_INTEGER SQL_NO_DATA_FOUND
668 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
669 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
670 // SQL_NUMERIC SQL_NO_DATA_FOUND
671 // SQL_REAL SQL_NO_DATA_FOUND
672 // SQL_SMALLINT SQL_NO_DATA_FOUND
673 // SQL_TIME SQL_NO_DATA_FOUND
674 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
675 // SQL_VARBINARY type name = 'RAW', Precision = 255
676 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
677 // =====================================================================
678 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
680 // SQL_VARCHAR type name = 'TEXT', Precision = 255
681 // SQL_TIMESTAMP type name = 'DATETIME'
682 // SQL_DECIMAL SQL_NO_DATA_FOUND
683 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
684 // SQL_FLOAT SQL_NO_DATA_FOUND
685 // SQL_REAL type name = 'SINGLE', Precision = 7
686 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
687 // SQL_INTEGER type name = 'LONG', Precision = 10
689 // Query the data source for info about itself
690 if (!getDbInfo(failOnDataTypeUnsupported
))
693 // --------------- Varchar - (Variable length character string) ---------------
694 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
695 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
698 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
699 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
700 else if (failOnDataTypeUnsupported
)
703 // --------------- Float ---------------
704 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
705 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
708 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
709 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
710 else if (failOnDataTypeUnsupported
)
713 // --------------- Integer -------------
714 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
715 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
718 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
719 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
720 else if (failOnDataTypeUnsupported
)
722 // If no non-floating point data types are supported, we'll
723 // use the type assigned for floats to store integers as well
724 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
726 if (failOnDataTypeUnsupported
)
730 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
733 // --------------- Date/Time ---------------
734 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
735 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
738 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
739 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
740 else if (failOnDataTypeUnsupported
)
743 // --------------- BLOB ---------------
744 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
745 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
748 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
749 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
750 else if (failOnDataTypeUnsupported
)
754 } // wxDb::determineDataTypes
757 bool wxDb::open(bool failOnDataTypeUnsupported
)
760 If using Intersolv branded ODBC drivers, this is the place where you would substitute
761 your branded driver license information
763 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
764 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
767 // Mark database as open
770 // Allocate a statement handle for the database connection
771 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
772 return(DispAllErrors(henv
, hdbc
));
774 // Set Connection Options
775 if (!setConnectionOptions())
778 if (!determineDataTypes(failOnDataTypeUnsupported
))
781 #ifdef DBDEBUG_CONSOLE
782 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
783 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
784 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
785 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
786 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
790 // Completed Successfully
794 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
796 wxASSERT(inConnectStr
.Length());
803 if (!FwdOnlyCursors())
805 // Specify that the ODBC cursor library be used, if needed. This must be
806 // specified before the connection is made.
807 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
809 #ifdef DBDEBUG_CONSOLE
810 if (retcode
== SQL_SUCCESS
)
811 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
813 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
815 wxUnusedVar(retcode
);
819 // Connect to the data source
820 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
821 short outConnectBufferLen
;
823 inConnectionStr
= inConnectStr
;
825 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
826 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
827 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
829 if ((retcode
!= SQL_SUCCESS
) &&
830 (retcode
!= SQL_SUCCESS_WITH_INFO
))
831 return(DispAllErrors(henv
, hdbc
));
833 outConnectBuffer
[outConnectBufferLen
] = 0;
834 outConnectionStr
= outConnectBuffer
;
835 dbOpenedWithConnectionString
= true;
837 return open(failOnDataTypeUnsupported
);
840 /********** wxDb::Open() **********/
841 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
843 wxASSERT(Dsn
.Length());
848 inConnectionStr
= wxT("");
849 outConnectionStr
= wxT("");
853 if (!FwdOnlyCursors())
855 // Specify that the ODBC cursor library be used, if needed. This must be
856 // specified before the connection is made.
857 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
859 #ifdef DBDEBUG_CONSOLE
860 if (retcode
== SQL_SUCCESS
)
861 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
863 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
865 wxUnusedVar( retcode
);
869 // Connect to the data source
870 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
871 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
872 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
874 if ((retcode
!= SQL_SUCCESS
) &&
875 (retcode
!= SQL_SUCCESS_WITH_INFO
))
876 return(DispAllErrors(henv
, hdbc
));
878 return open(failOnDataTypeUnsupported
);
883 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
885 wxASSERT(dbConnectInf
);
887 // Use the connection string if one is present
888 if (dbConnectInf
->UseConnectionStr())
889 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
891 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
892 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
896 bool wxDb::Open(wxDb
*copyDb
)
898 dsn
= copyDb
->GetDatasourceName();
899 uid
= copyDb
->GetUsername();
900 authStr
= copyDb
->GetPassword();
901 inConnectionStr
= copyDb
->GetConnectionInStr();
902 outConnectionStr
= copyDb
->GetConnectionOutStr();
906 if (!FwdOnlyCursors())
908 // Specify that the ODBC cursor library be used, if needed. This must be
909 // specified before the connection is made.
910 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
912 #ifdef DBDEBUG_CONSOLE
913 if (retcode
== SQL_SUCCESS
)
914 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
916 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
918 wxUnusedVar( retcode
);
922 if (copyDb
->OpenedWithConnectionString())
924 // Connect to the data source
925 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
926 short outConnectBufferLen
;
928 inConnectionStr
= copyDb
->GetConnectionInStr();
930 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
931 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
932 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
934 if ((retcode
!= SQL_SUCCESS
) &&
935 (retcode
!= SQL_SUCCESS_WITH_INFO
))
936 return(DispAllErrors(henv
, hdbc
));
938 outConnectBuffer
[outConnectBufferLen
] = 0;
939 outConnectionStr
= outConnectBuffer
;
940 dbOpenedWithConnectionString
= true;
944 // Connect to the data source
945 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
946 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
947 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
950 if ((retcode
!= SQL_SUCCESS
) &&
951 (retcode
!= SQL_SUCCESS_WITH_INFO
))
952 return(DispAllErrors(henv
, hdbc
));
955 If using Intersolv branded ODBC drivers, this is the place where you would substitute
956 your branded driver license information
958 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
959 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
962 // Mark database as open
965 // Allocate a statement handle for the database connection
966 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
967 return(DispAllErrors(henv
, hdbc
));
969 // Set Connection Options
970 if (!setConnectionOptions())
973 // Instead of Querying the data source for info about itself, it can just be copied
974 // from the wxDb instance that was passed in (copyDb).
975 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
976 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
977 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
978 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
979 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
980 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
981 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
982 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
983 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
984 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
985 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
986 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
987 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
988 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
989 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
990 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
991 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
992 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
993 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
994 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
995 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
996 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
997 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
998 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
999 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1000 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1001 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1002 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1003 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1004 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1005 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1007 // VARCHAR = Variable length character string
1008 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1009 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1010 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1011 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1012 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1015 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1016 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1017 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1018 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1019 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1022 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1023 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1024 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1025 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1026 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1029 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1030 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1031 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1032 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1033 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1036 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1037 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1038 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1039 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1040 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1042 #ifdef DBDEBUG_CONSOLE
1043 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1044 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1045 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1046 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1047 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1051 // Completed Successfully
1056 /********** wxDb::setConnectionOptions() **********/
1057 bool wxDb::setConnectionOptions(void)
1059 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1064 // I need to get the DBMS name here, because some of the connection options
1065 // are database specific and need to call the Dbms() function.
1068 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1069 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1070 return(DispAllErrors(henv
, hdbc
));
1072 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1073 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1074 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1076 // By default, MS Sql Server closes cursors on commit and rollback. The following
1077 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1078 // after a transaction. This is a driver specific option and is not part of the
1079 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1080 // The database settings don't have any effect one way or the other.
1081 if (Dbms() == dbmsMS_SQL_SERVER
)
1083 const long SQL_PRESERVE_CURSORS
= 1204L;
1084 const long SQL_PC_ON
= 1L;
1085 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1088 // Display the connection options to verify them
1089 #ifdef DBDEBUG_CONSOLE
1091 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1093 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1094 if (retcode
!= SQL_SUCCESS
)
1095 return(DispAllErrors(henv
, hdbc
));
1096 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1098 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1099 if (retcode
!= SQL_SUCCESS
)
1100 return(DispAllErrors(henv
, hdbc
));
1101 cout
<< wxT("ODBC CURSORS: ");
1104 case(SQL_CUR_USE_IF_NEEDED
):
1105 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1107 case(SQL_CUR_USE_ODBC
):
1108 cout
<< wxT("SQL_CUR_USE_ODBC");
1110 case(SQL_CUR_USE_DRIVER
):
1111 cout
<< wxT("SQL_CUR_USE_DRIVER");
1116 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1117 if (retcode
!= SQL_SUCCESS
)
1118 return(DispAllErrors(henv
, hdbc
));
1119 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1124 // Completed Successfully
1127 } // wxDb::setConnectionOptions()
1130 /********** wxDb::getDbInfo() **********/
1131 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1136 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1137 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1139 DispAllErrors(henv
, hdbc
);
1140 if (failOnDataTypeUnsupported
)
1144 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1145 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1147 DispAllErrors(henv
, hdbc
);
1148 if (failOnDataTypeUnsupported
)
1152 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1153 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1155 DispAllErrors(henv
, hdbc
);
1156 if (failOnDataTypeUnsupported
)
1161 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1162 // causing database connectivity to fail in some cases.
1163 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1164 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1166 DispAllErrors(henv
, hdbc
);
1167 if (failOnDataTypeUnsupported
)
1171 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1172 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1174 DispAllErrors(henv
, hdbc
);
1175 if (failOnDataTypeUnsupported
)
1179 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1180 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1182 DispAllErrors(henv
, hdbc
);
1183 if (failOnDataTypeUnsupported
)
1187 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1188 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1190 DispAllErrors(henv
, hdbc
);
1191 if (failOnDataTypeUnsupported
)
1195 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1196 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1198 DispAllErrors(henv
, hdbc
);
1199 if (failOnDataTypeUnsupported
)
1203 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1204 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1206 DispAllErrors(henv
, hdbc
);
1207 if (failOnDataTypeUnsupported
)
1211 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1212 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1214 DispAllErrors(henv
, hdbc
);
1215 if (failOnDataTypeUnsupported
)
1219 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1220 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1222 DispAllErrors(henv
, hdbc
);
1223 if (failOnDataTypeUnsupported
)
1227 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1228 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1230 // Not all drivers support this call - Nick Gorham(unixODBC)
1231 dbInf
.cliConfLvl
= 0;
1232 DispAllErrors(henv
, hdbc
);
1233 if (failOnDataTypeUnsupported
)
1237 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1238 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1240 DispAllErrors(henv
, hdbc
);
1241 if (failOnDataTypeUnsupported
)
1245 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1246 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1248 DispAllErrors(henv
, hdbc
);
1249 if (failOnDataTypeUnsupported
)
1253 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1254 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1256 DispAllErrors(henv
, hdbc
);
1257 if (failOnDataTypeUnsupported
)
1261 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1262 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1264 DispAllErrors(henv
, hdbc
);
1265 if (failOnDataTypeUnsupported
)
1269 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1270 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1272 DispAllErrors(henv
, hdbc
);
1273 if (failOnDataTypeUnsupported
)
1277 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1278 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1280 DispAllErrors(henv
, hdbc
);
1281 if (failOnDataTypeUnsupported
)
1285 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1286 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1288 DispAllErrors(henv
, hdbc
);
1289 if (failOnDataTypeUnsupported
)
1293 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1294 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1296 DispAllErrors(henv
, hdbc
);
1297 if (failOnDataTypeUnsupported
)
1301 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1302 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1304 DispAllErrors(henv
, hdbc
);
1305 if (failOnDataTypeUnsupported
)
1309 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1310 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1312 DispAllErrors(henv
, hdbc
);
1313 if (failOnDataTypeUnsupported
)
1317 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1318 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1320 DispAllErrors(henv
, hdbc
);
1321 if (failOnDataTypeUnsupported
)
1325 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1326 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1328 DispAllErrors(henv
, hdbc
);
1329 if (failOnDataTypeUnsupported
)
1333 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1334 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1336 DispAllErrors(henv
, hdbc
);
1337 if (failOnDataTypeUnsupported
)
1341 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1342 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1344 DispAllErrors(henv
, hdbc
);
1345 if (failOnDataTypeUnsupported
)
1349 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1350 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1352 DispAllErrors(henv
, hdbc
);
1353 if (failOnDataTypeUnsupported
)
1357 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1358 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1360 DispAllErrors(henv
, hdbc
);
1361 if (failOnDataTypeUnsupported
)
1365 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1366 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1368 DispAllErrors(henv
, hdbc
);
1369 if (failOnDataTypeUnsupported
)
1373 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1374 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1376 DispAllErrors(henv
, hdbc
);
1377 if (failOnDataTypeUnsupported
)
1381 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1382 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1384 DispAllErrors(henv
, hdbc
);
1385 if (failOnDataTypeUnsupported
)
1389 #ifdef DBDEBUG_CONSOLE
1390 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1391 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1392 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1393 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1395 cout
<< wxT("API Conf. Level: ");
1396 switch(dbInf
.apiConfLvl
)
1398 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1399 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1400 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1404 cout
<< wxT("SAG CLI Conf. Level: ");
1405 switch(dbInf
.cliConfLvl
)
1407 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1408 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1412 cout
<< wxT("SQL Conf. Level: ");
1413 switch(dbInf
.sqlConfLvl
)
1415 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1416 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1417 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1421 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1422 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1423 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1424 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1425 cout
<< wxT("Cursor COMMIT Behavior: ");
1426 switch(dbInf
.cursorCommitBehavior
)
1428 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1429 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1430 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1434 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1435 switch(dbInf
.cursorRollbackBehavior
)
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("Support NOT NULL clause: ");
1444 switch(dbInf
.supportNotNullClause
)
1446 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1447 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1451 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1452 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1454 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1457 cout
<< wxT("Default Transaction Isolation: ";
1458 switch(dbInf
.txnIsolation
)
1460 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1461 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1462 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1463 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1465 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1470 cout
<< wxT("Transaction Isolation Options: ");
1471 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1472 cout
<< wxT("Read Uncommitted, ");
1473 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1474 cout
<< wxT("Read Committed, ");
1475 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1476 cout
<< wxT("Repeatable Read, ");
1477 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1478 cout
<< wxT("Serializable, ");
1480 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1481 cout
<< wxT("Versioning");
1485 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1486 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1487 cout
<< wxT("Next, ");
1488 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1489 cout
<< wxT("Prev, ");
1490 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1491 cout
<< wxT("First, ");
1492 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1493 cout
<< wxT("Last, ");
1494 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1495 cout
<< wxT("Absolute, ");
1496 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1497 cout
<< wxT("Relative, ");
1499 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1500 cout
<< wxT("Resume, ");
1502 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1503 cout
<< wxT("Bookmark");
1506 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1507 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1508 cout
<< wxT("No Change, ");
1509 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1510 cout
<< wxT("Exclusive, ");
1511 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1512 cout
<< wxT("UnLock");
1515 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1516 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1517 cout
<< wxT("Position, ");
1518 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1519 cout
<< wxT("Refresh, ");
1520 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1521 cout
<< wxT("Upd, "));
1522 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1523 cout
<< wxT("Del, ");
1524 if (dbInf
.posOperations
& SQL_POS_ADD
)
1528 cout
<< wxT("Positioned Statements Supported: ");
1529 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1530 cout
<< wxT("Pos delete, ");
1531 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1532 cout
<< wxT("Pos update, ");
1533 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1534 cout
<< wxT("Select for update");
1537 cout
<< wxT("Scroll Concurrency: ");
1538 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1539 cout
<< wxT("Read Only, ");
1540 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1541 cout
<< wxT("Lock, ");
1542 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1543 cout
<< wxT("Opt. Rowver, ");
1544 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1545 cout
<< wxT("Opt. Values");
1548 cout
<< wxT("Scroll Options: ");
1549 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1550 cout
<< wxT("Fwd Only, ");
1551 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1552 cout
<< wxT("Static, ");
1553 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1554 cout
<< wxT("Keyset Driven, ");
1555 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1556 cout
<< wxT("Dynamic, ");
1557 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1558 cout
<< wxT("Mixed");
1561 cout
<< wxT("Static Sensitivity: ");
1562 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1563 cout
<< wxT("Additions, ");
1564 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1565 cout
<< wxT("Deletions, ");
1566 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1567 cout
<< wxT("Updates");
1570 cout
<< wxT("Transaction Capable?: ");
1571 switch(dbInf
.txnCapable
)
1573 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1574 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1575 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1576 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1577 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1584 // Completed Successfully
1587 } // wxDb::getDbInfo()
1590 /********** wxDb::getDataTypeInfo() **********/
1591 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1594 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1595 * the data type inf. is gathered for.
1597 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1602 // Get information about the data type specified
1603 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1604 return(DispAllErrors(henv
, hdbc
, hstmt
));
1607 retcode
= SQLFetch(hstmt
);
1608 if (retcode
!= SQL_SUCCESS
)
1610 #ifdef DBDEBUG_CONSOLE
1611 if (retcode
== SQL_NO_DATA_FOUND
)
1612 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1614 DispAllErrors(henv
, hdbc
, hstmt
);
1615 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1619 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1621 // Obtain columns from the record
1622 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1623 return(DispAllErrors(henv
, hdbc
, hstmt
));
1625 structSQLTypeInfo
.TypeName
= typeName
;
1627 // BJO 20000503: no more needed with new GetColumns...
1630 if (Dbms() == dbmsMY_SQL
)
1632 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1633 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1634 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1635 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1636 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1637 structSQLTypeInfo
.TypeName
= wxT("int");
1638 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1639 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1640 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1641 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1642 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1643 structSQLTypeInfo
.TypeName
= wxT("char");
1646 // BJO 20000427 : OpenLink driver
1647 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1648 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1650 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1651 structSQLTypeInfo
.TypeName
= wxT("real");
1655 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1656 return(DispAllErrors(henv
, hdbc
, hstmt
));
1657 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1658 return(DispAllErrors(henv
, hdbc
, hstmt
));
1659 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1660 // return(DispAllErrors(henv, hdbc, hstmt));
1662 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1663 return(DispAllErrors(henv
, hdbc
, hstmt
));
1665 if (structSQLTypeInfo
.MaximumScale
< 0)
1666 structSQLTypeInfo
.MaximumScale
= 0;
1668 // Close the statement handle which closes open cursors
1669 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1670 return(DispAllErrors(henv
, hdbc
, hstmt
));
1672 // Completed Successfully
1675 } // wxDb::getDataTypeInfo()
1678 /********** wxDb::Close() **********/
1679 void wxDb::Close(void)
1681 // Close the Sql Log file
1688 // Free statement handle
1691 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1692 DispAllErrors(henv
, hdbc
);
1695 // Disconnect from the datasource
1696 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1697 DispAllErrors(henv
, hdbc
);
1699 // Free the connection to the datasource
1700 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1701 DispAllErrors(henv
, hdbc
);
1703 // There should be zero Ctable objects still connected to this db object
1704 wxASSERT(nTables
== 0);
1708 wxList::compatibility_iterator pNode
;
1709 pNode
= TablesInUse
.GetFirst();
1713 tiu
= (wxTablesInUse
*)pNode
->GetData();
1714 if (tiu
->pDb
== this)
1716 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1717 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1718 wxLogDebug(s
.c_str(),s2
.c_str());
1720 pNode
= pNode
->GetNext();
1724 // Copy the error messages to a global variable
1726 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1727 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1729 dbmsType
= dbmsUNIDENTIFIED
;
1735 /********** wxDb::CommitTrans() **********/
1736 bool wxDb::CommitTrans(void)
1740 // Commit the transaction
1741 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1742 return(DispAllErrors(henv
, hdbc
));
1745 // Completed successfully
1748 } // wxDb::CommitTrans()
1751 /********** wxDb::RollbackTrans() **********/
1752 bool wxDb::RollbackTrans(void)
1754 // Rollback the transaction
1755 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1756 return(DispAllErrors(henv
, hdbc
));
1758 // Completed successfully
1761 } // wxDb::RollbackTrans()
1764 /********** wxDb::DispAllErrors() **********/
1765 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1767 * This function is called internally whenever an error condition prevents the user's
1768 * request from being executed. This function will query the datasource as to the
1769 * actual error(s) that just occured on the previous request of the datasource.
1771 * The function will retrieve each error condition from the datasource and
1772 * Printf the codes/text values into a string which it then logs via logError().
1773 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1774 * window and program execution will be paused until the user presses a key.
1776 * This function always returns a false, so that functions which call this function
1777 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1778 * of the users request, so that the calling code can then process the error msg log
1781 wxString odbcErrMsg
;
1784 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1786 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1789 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1790 logError(odbcErrMsg
, sqlState
);
1793 #ifdef DBDEBUG_CONSOLE
1794 // When run in console mode, use standard out to display errors.
1795 cout
<< odbcErrMsg
.c_str() << endl
;
1796 cout
<< wxT("Press any key to continue...") << endl
;
1801 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1806 return false; // This function always returns false.
1808 } // wxDb::DispAllErrors()
1811 /********** wxDb::GetNextError() **********/
1812 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1815 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1817 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1823 } // wxDb::GetNextError()
1826 /********** wxDb::DispNextError() **********/
1827 void wxDb::DispNextError(void)
1829 wxString odbcErrMsg
;
1831 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1832 logError(odbcErrMsg
, sqlState
);
1837 #ifdef DBDEBUG_CONSOLE
1838 // When run in console mode, use standard out to display errors.
1839 cout
<< odbcErrMsg
.c_str() << endl
;
1840 cout
<< wxT("Press any key to continue...") << endl
;
1845 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1846 #endif // __WXDEBUG__
1848 } // wxDb::DispNextError()
1851 /********** wxDb::logError() **********/
1852 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1854 wxASSERT(errMsg
.Length());
1856 static int pLast
= -1;
1859 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1862 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1863 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1867 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1868 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1870 if (SQLState
.Length())
1871 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1872 DB_STATUS
= dbStatus
;
1874 // Add the errmsg to the sql log
1875 WriteSqlLog(errMsg
);
1877 } // wxDb::logError()
1880 /**********wxDb::TranslateSqlState() **********/
1881 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1883 if (!wxStrcmp(SQLState
, wxT("01000")))
1884 return(DB_ERR_GENERAL_WARNING
);
1885 if (!wxStrcmp(SQLState
, wxT("01002")))
1886 return(DB_ERR_DISCONNECT_ERROR
);
1887 if (!wxStrcmp(SQLState
, wxT("01004")))
1888 return(DB_ERR_DATA_TRUNCATED
);
1889 if (!wxStrcmp(SQLState
, wxT("01006")))
1890 return(DB_ERR_PRIV_NOT_REVOKED
);
1891 if (!wxStrcmp(SQLState
, wxT("01S00")))
1892 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1893 if (!wxStrcmp(SQLState
, wxT("01S01")))
1894 return(DB_ERR_ERROR_IN_ROW
);
1895 if (!wxStrcmp(SQLState
, wxT("01S02")))
1896 return(DB_ERR_OPTION_VALUE_CHANGED
);
1897 if (!wxStrcmp(SQLState
, wxT("01S03")))
1898 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1899 if (!wxStrcmp(SQLState
, wxT("01S04")))
1900 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1901 if (!wxStrcmp(SQLState
, wxT("07001")))
1902 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1903 if (!wxStrcmp(SQLState
, wxT("07006")))
1904 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1905 if (!wxStrcmp(SQLState
, wxT("08001")))
1906 return(DB_ERR_UNABLE_TO_CONNECT
);
1907 if (!wxStrcmp(SQLState
, wxT("08002")))
1908 return(DB_ERR_CONNECTION_IN_USE
);
1909 if (!wxStrcmp(SQLState
, wxT("08003")))
1910 return(DB_ERR_CONNECTION_NOT_OPEN
);
1911 if (!wxStrcmp(SQLState
, wxT("08004")))
1912 return(DB_ERR_REJECTED_CONNECTION
);
1913 if (!wxStrcmp(SQLState
, wxT("08007")))
1914 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1915 if (!wxStrcmp(SQLState
, wxT("08S01")))
1916 return(DB_ERR_COMM_LINK_FAILURE
);
1917 if (!wxStrcmp(SQLState
, wxT("21S01")))
1918 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1919 if (!wxStrcmp(SQLState
, wxT("21S02")))
1920 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1921 if (!wxStrcmp(SQLState
, wxT("22001")))
1922 return(DB_ERR_STRING_RIGHT_TRUNC
);
1923 if (!wxStrcmp(SQLState
, wxT("22003")))
1924 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1925 if (!wxStrcmp(SQLState
, wxT("22005")))
1926 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1927 if (!wxStrcmp(SQLState
, wxT("22008")))
1928 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1929 if (!wxStrcmp(SQLState
, wxT("22012")))
1930 return(DB_ERR_DIVIDE_BY_ZERO
);
1931 if (!wxStrcmp(SQLState
, wxT("22026")))
1932 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1933 if (!wxStrcmp(SQLState
, wxT("23000")))
1934 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1935 if (!wxStrcmp(SQLState
, wxT("24000")))
1936 return(DB_ERR_INVALID_CURSOR_STATE
);
1937 if (!wxStrcmp(SQLState
, wxT("25000")))
1938 return(DB_ERR_INVALID_TRANS_STATE
);
1939 if (!wxStrcmp(SQLState
, wxT("28000")))
1940 return(DB_ERR_INVALID_AUTH_SPEC
);
1941 if (!wxStrcmp(SQLState
, wxT("34000")))
1942 return(DB_ERR_INVALID_CURSOR_NAME
);
1943 if (!wxStrcmp(SQLState
, wxT("37000")))
1944 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1945 if (!wxStrcmp(SQLState
, wxT("3C000")))
1946 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1947 if (!wxStrcmp(SQLState
, wxT("40001")))
1948 return(DB_ERR_SERIALIZATION_FAILURE
);
1949 if (!wxStrcmp(SQLState
, wxT("42000")))
1950 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1951 if (!wxStrcmp(SQLState
, wxT("70100")))
1952 return(DB_ERR_OPERATION_ABORTED
);
1953 if (!wxStrcmp(SQLState
, wxT("IM001")))
1954 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1955 if (!wxStrcmp(SQLState
, wxT("IM002")))
1956 return(DB_ERR_NO_DATA_SOURCE
);
1957 if (!wxStrcmp(SQLState
, wxT("IM003")))
1958 return(DB_ERR_DRIVER_LOAD_ERROR
);
1959 if (!wxStrcmp(SQLState
, wxT("IM004")))
1960 return(DB_ERR_SQLALLOCENV_FAILED
);
1961 if (!wxStrcmp(SQLState
, wxT("IM005")))
1962 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1963 if (!wxStrcmp(SQLState
, wxT("IM006")))
1964 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1965 if (!wxStrcmp(SQLState
, wxT("IM007")))
1966 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1967 if (!wxStrcmp(SQLState
, wxT("IM008")))
1968 return(DB_ERR_DIALOG_FAILED
);
1969 if (!wxStrcmp(SQLState
, wxT("IM009")))
1970 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1971 if (!wxStrcmp(SQLState
, wxT("IM010")))
1972 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1973 if (!wxStrcmp(SQLState
, wxT("IM011")))
1974 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1975 if (!wxStrcmp(SQLState
, wxT("IM012")))
1976 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1977 if (!wxStrcmp(SQLState
, wxT("IM013")))
1978 return(DB_ERR_TRACE_FILE_ERROR
);
1979 if (!wxStrcmp(SQLState
, wxT("S0001")))
1980 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1981 if (!wxStrcmp(SQLState
, wxT("S0002")))
1982 return(DB_ERR_TABLE_NOT_FOUND
);
1983 if (!wxStrcmp(SQLState
, wxT("S0011")))
1984 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1985 if (!wxStrcmp(SQLState
, wxT("S0012")))
1986 return(DB_ERR_INDEX_NOT_FOUND
);
1987 if (!wxStrcmp(SQLState
, wxT("S0021")))
1988 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1989 if (!wxStrcmp(SQLState
, wxT("S0022")))
1990 return(DB_ERR_COLUMN_NOT_FOUND
);
1991 if (!wxStrcmp(SQLState
, wxT("S0023")))
1992 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1993 if (!wxStrcmp(SQLState
, wxT("S1000")))
1994 return(DB_ERR_GENERAL_ERROR
);
1995 if (!wxStrcmp(SQLState
, wxT("S1001")))
1996 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1997 if (!wxStrcmp(SQLState
, wxT("S1002")))
1998 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1999 if (!wxStrcmp(SQLState
, wxT("S1003")))
2000 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2001 if (!wxStrcmp(SQLState
, wxT("S1004")))
2002 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2003 if (!wxStrcmp(SQLState
, wxT("S1008")))
2004 return(DB_ERR_OPERATION_CANCELLED
);
2005 if (!wxStrcmp(SQLState
, wxT("S1009")))
2006 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2007 if (!wxStrcmp(SQLState
, wxT("S1010")))
2008 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2009 if (!wxStrcmp(SQLState
, wxT("S1011")))
2010 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2011 if (!wxStrcmp(SQLState
, wxT("S1012")))
2012 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2013 if (!wxStrcmp(SQLState
, wxT("S1015")))
2014 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2015 if (!wxStrcmp(SQLState
, wxT("S1090")))
2016 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2017 if (!wxStrcmp(SQLState
, wxT("S1091")))
2018 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2019 if (!wxStrcmp(SQLState
, wxT("S1092")))
2020 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2021 if (!wxStrcmp(SQLState
, wxT("S1093")))
2022 return(DB_ERR_INVALID_PARAM_NO
);
2023 if (!wxStrcmp(SQLState
, wxT("S1094")))
2024 return(DB_ERR_INVALID_SCALE_VALUE
);
2025 if (!wxStrcmp(SQLState
, wxT("S1095")))
2026 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2027 if (!wxStrcmp(SQLState
, wxT("S1096")))
2028 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2029 if (!wxStrcmp(SQLState
, wxT("S1097")))
2030 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2031 if (!wxStrcmp(SQLState
, wxT("S1098")))
2032 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2033 if (!wxStrcmp(SQLState
, wxT("S1099")))
2034 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2035 if (!wxStrcmp(SQLState
, wxT("S1100")))
2036 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2037 if (!wxStrcmp(SQLState
, wxT("S1101")))
2038 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2039 if (!wxStrcmp(SQLState
, wxT("S1103")))
2040 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2041 if (!wxStrcmp(SQLState
, wxT("S1104")))
2042 return(DB_ERR_INVALID_PRECISION_VALUE
);
2043 if (!wxStrcmp(SQLState
, wxT("S1105")))
2044 return(DB_ERR_INVALID_PARAM_TYPE
);
2045 if (!wxStrcmp(SQLState
, wxT("S1106")))
2046 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2047 if (!wxStrcmp(SQLState
, wxT("S1107")))
2048 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2049 if (!wxStrcmp(SQLState
, wxT("S1108")))
2050 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2051 if (!wxStrcmp(SQLState
, wxT("S1109")))
2052 return(DB_ERR_INVALID_CURSOR_POSITION
);
2053 if (!wxStrcmp(SQLState
, wxT("S1110")))
2054 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2055 if (!wxStrcmp(SQLState
, wxT("S1111")))
2056 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2057 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2058 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2059 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2060 return(DB_ERR_TIMEOUT_EXPIRED
);
2065 } // wxDb::TranslateSqlState()
2068 /********** wxDb::Grant() **********/
2069 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2073 // Build the grant statement
2074 sqlStmt
= wxT("GRANT ");
2075 if (privileges
== DB_GRANT_ALL
)
2076 sqlStmt
+= wxT("ALL");
2080 if (privileges
& DB_GRANT_SELECT
)
2082 sqlStmt
+= wxT("SELECT");
2085 if (privileges
& DB_GRANT_INSERT
)
2088 sqlStmt
+= wxT(", ");
2089 sqlStmt
+= wxT("INSERT");
2091 if (privileges
& DB_GRANT_UPDATE
)
2094 sqlStmt
+= wxT(", ");
2095 sqlStmt
+= wxT("UPDATE");
2097 if (privileges
& DB_GRANT_DELETE
)
2100 sqlStmt
+= wxT(", ");
2101 sqlStmt
+= wxT("DELETE");
2105 sqlStmt
+= wxT(" ON ");
2106 sqlStmt
+= SQLTableName(tableName
);
2107 sqlStmt
+= wxT(" TO ");
2108 sqlStmt
+= userList
;
2110 #ifdef DBDEBUG_CONSOLE
2111 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2114 WriteSqlLog(sqlStmt
);
2116 return(ExecSql(sqlStmt
));
2121 /********** wxDb::CreateView() **********/
2122 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2123 const wxString
&pSqlStmt
, bool attemptDrop
)
2127 // Drop the view first
2128 if (attemptDrop
&& !DropView(viewName
))
2131 // Build the create view statement
2132 sqlStmt
= wxT("CREATE VIEW ");
2133 sqlStmt
+= viewName
;
2135 if (colList
.Length())
2137 sqlStmt
+= wxT(" (");
2139 sqlStmt
+= wxT(")");
2142 sqlStmt
+= wxT(" AS ");
2143 sqlStmt
+= pSqlStmt
;
2145 WriteSqlLog(sqlStmt
);
2147 #ifdef DBDEBUG_CONSOLE
2148 cout
<< sqlStmt
.c_str() << endl
;
2151 return(ExecSql(sqlStmt
));
2153 } // wxDb::CreateView()
2156 /********** wxDb::DropView() **********/
2157 bool wxDb::DropView(const wxString
&viewName
)
2160 * NOTE: This function returns true if the View does not exist, but
2161 * only for identified databases. Code will need to be added
2162 * below for any other databases when those databases are defined
2163 * to handle this situation consistently
2167 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2169 WriteSqlLog(sqlStmt
);
2171 #ifdef DBDEBUG_CONSOLE
2172 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2175 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2177 // Check for "Base table not found" error and ignore
2178 GetNextError(henv
, hdbc
, hstmt
);
2179 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2181 // Check for product specific error codes
2182 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2185 DispAllErrors(henv
, hdbc
, hstmt
);
2192 // Commit the transaction
2198 } // wxDb::DropView()
2201 /********** wxDb::ExecSql() **********/
2202 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2206 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2208 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2209 if (retcode
== SQL_SUCCESS
||
2210 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2216 DispAllErrors(henv
, hdbc
, hstmt
);
2220 } // wxDb::ExecSql()
2223 /********** wxDb::ExecSql() with column info **********/
2224 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2226 //execute the statement first
2227 if (!ExecSql(pSqlStmt
))
2231 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2233 DispAllErrors(henv
, hdbc
, hstmt
);
2242 // Get column information
2244 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2247 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2249 // Fill in column information (name, datatype)
2250 for (colNum
= 0; colNum
< noCols
; colNum
++)
2252 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2254 &Sword
, &Sdword
) != SQL_SUCCESS
)
2256 DispAllErrors(henv
, hdbc
, hstmt
);
2261 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2262 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2264 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2265 NULL
, 0, &Sword
, &Sdword
) != SQL_SUCCESS
)
2267 DispAllErrors(henv
, hdbc
, hstmt
);
2276 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2282 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2289 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2293 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2296 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2301 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sdword
);
2302 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2309 } // wxDb::ExecSql()
2311 /********** wxDb::GetNext() **********/
2312 bool wxDb::GetNext(void)
2314 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2318 DispAllErrors(henv
, hdbc
, hstmt
);
2322 } // wxDb::GetNext()
2325 /********** wxDb::GetData() **********/
2326 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2329 wxASSERT(cbReturned
);
2331 long bufferSize
= maxLen
;
2333 if (cType
== SQL_C_WXCHAR
)
2334 bufferSize
= maxLen
* sizeof(wxChar
);
2336 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2340 DispAllErrors(henv
, hdbc
, hstmt
);
2344 } // wxDb::GetData()
2347 /********** wxDb::GetKeyFields() **********/
2348 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2350 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2351 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2353 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2354 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2360 * -----------------------------------------------------------------------
2361 * -- 19991224 : mj10777 : Create ------
2362 * -- : Three things are done and stored here : ------
2363 * -- : 1) which Column(s) is/are Primary Key(s) ------
2364 * -- : 2) which tables use this Key as a Foreign Key ------
2365 * -- : 3) which columns are Foreign Key and the name ------
2366 * -- : of the Table where the Key is the Primary Key -----
2367 * -- : Called from GetColumns(const wxString &tableName, ------
2368 * -- int *numCols,const wxChar *userID ) ------
2369 * -----------------------------------------------------------------------
2372 /*---------------------------------------------------------------------*/
2373 /* Get the names of the columns in the primary key. */
2374 /*---------------------------------------------------------------------*/
2375 retcode
= SQLPrimaryKeys(hstmt
,
2376 NULL
, 0, /* Catalog name */
2377 NULL
, 0, /* Schema name */
2378 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2380 /*---------------------------------------------------------------------*/
2381 /* Fetch and display the result set. This will be a list of the */
2382 /* columns in the primary key of the tableName table. */
2383 /*---------------------------------------------------------------------*/
2384 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2386 retcode
= SQLFetch(hstmt
);
2387 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2389 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2390 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2392 for (i
=0;i
<noCols
;i
++) // Find the Column name
2393 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2394 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2397 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2399 /*---------------------------------------------------------------------*/
2400 /* Get all the foreign keys that refer to tableName primary key. */
2401 /*---------------------------------------------------------------------*/
2402 retcode
= SQLForeignKeys(hstmt
,
2403 NULL
, 0, /* Primary catalog */
2404 NULL
, 0, /* Primary schema */
2405 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2406 NULL
, 0, /* Foreign catalog */
2407 NULL
, 0, /* Foreign schema */
2408 NULL
, 0); /* Foreign table */
2410 /*---------------------------------------------------------------------*/
2411 /* Fetch and display the result set. This will be all of the foreign */
2412 /* keys in other tables that refer to the tableName primary key. */
2413 /*---------------------------------------------------------------------*/
2416 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2418 retcode
= SQLFetch(hstmt
);
2419 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2421 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2422 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2423 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2424 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2425 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2426 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2430 tempStr
.Trim(); // Get rid of any unneeded blanks
2431 if (!tempStr
.empty())
2433 for (i
=0; i
<noCols
; i
++)
2434 { // Find the Column name
2435 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2437 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
2438 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2443 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2445 /*---------------------------------------------------------------------*/
2446 /* Get all the foreign keys in the tablename table. */
2447 /*---------------------------------------------------------------------*/
2448 retcode
= SQLForeignKeys(hstmt
,
2449 NULL
, 0, /* Primary catalog */
2450 NULL
, 0, /* Primary schema */
2451 NULL
, 0, /* Primary table */
2452 NULL
, 0, /* Foreign catalog */
2453 NULL
, 0, /* Foreign schema */
2454 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2456 /*---------------------------------------------------------------------*/
2457 /* Fetch and display the result set. This will be all of the */
2458 /* primary keys in other tables that are referred to by foreign */
2459 /* keys in the tableName table. */
2460 /*---------------------------------------------------------------------*/
2461 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2463 retcode
= SQLFetch(hstmt
);
2464 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2466 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2467 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2468 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2470 for (i
=0; i
<noCols
; i
++) // Find the Column name
2472 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2474 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2475 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2476 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2481 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2485 } // wxDb::GetKeyFields()
2489 /********** wxDb::GetColumns() **********/
2490 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2492 * 1) The last array element of the tableName[] argument must be zero (null).
2493 * This is how the end of the array is detected.
2494 * 2) This function returns an array of wxDbColInf structures. If no columns
2495 * were found, or an error occured, this pointer will be zero (null). THE
2496 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2497 * IS FINISHED WITH IT. i.e.
2499 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2502 * // Use the column inf
2504 * // Destroy the memory
2508 * userID is evaluated in the following manner:
2509 * userID == NULL ... UserID is ignored
2510 * userID == "" ... UserID set equal to 'this->uid'
2511 * userID != "" ... UserID set equal to 'userID'
2513 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2514 * by this function. This function should use its own wxDb instance
2515 * to avoid undesired unbinding of columns.
2520 wxDbColInf
*colInf
= 0;
2528 convertUserID(userID
,UserID
);
2530 // Pass 1 - Determine how many columns there are.
2531 // Pass 2 - Allocate the wxDbColInf array and fill in
2532 // the array with the column information.
2534 for (pass
= 1; pass
<= 2; pass
++)
2538 if (noCols
== 0) // Probably a bogus table name(s)
2540 // Allocate n wxDbColInf objects to hold the column information
2541 colInf
= new wxDbColInf
[noCols
+1];
2544 // Mark the end of the array
2545 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2546 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2547 colInf
[noCols
].sqlDataType
= 0;
2549 // Loop through each table name
2551 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2553 TableName
= tableName
[tbl
];
2554 // Oracle and Interbase table names are uppercase only, so force
2555 // the name to uppercase just in case programmer forgot to do this
2556 if ((Dbms() == dbmsORACLE
) ||
2557 (Dbms() == dbmsFIREBIRD
) ||
2558 (Dbms() == dbmsINTERBASE
))
2559 TableName
= TableName
.Upper();
2561 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2563 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2564 // use the call below that leaves out the user name
2565 if (!UserID
.empty() &&
2566 Dbms() != dbmsMY_SQL
&&
2567 Dbms() != dbmsACCESS
&&
2568 Dbms() != dbmsMS_SQL_SERVER
)
2570 retcode
= SQLColumns(hstmt
,
2571 NULL
, 0, // All qualifiers
2572 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2573 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2574 NULL
, 0); // All columns
2578 retcode
= SQLColumns(hstmt
,
2579 NULL
, 0, // All qualifiers
2581 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2582 NULL
, 0); // All columns
2584 if (retcode
!= SQL_SUCCESS
)
2585 { // Error occured, abort
2586 DispAllErrors(henv
, hdbc
, hstmt
);
2589 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2593 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2595 if (pass
== 1) // First pass, just add up the number of columns
2597 else // Pass 2; Fill in the array of structures
2599 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2601 // NOTE: Only the ODBC 1.x fields are retrieved
2602 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2603 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2604 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2605 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2606 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2607 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2608 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2609 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2610 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2611 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2612 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2613 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2615 // Determine the wxDb data type that is used to represent the native data type of this data source
2616 colInf
[colNo
].dbDataType
= 0;
2617 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2620 // IODBC does not return a correct columnLength, so we set
2621 // columnLength = bufferSize if no column length was returned
2622 // IODBC returns the columnLength in bufferSize. (bug)
2623 if (colInf
[colNo
].columnLength
< 1)
2625 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2628 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2630 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2631 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2632 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2633 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2634 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2635 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2636 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2637 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2642 if (retcode
!= SQL_NO_DATA_FOUND
)
2643 { // Error occured, abort
2644 DispAllErrors(henv
, hdbc
, hstmt
);
2647 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2653 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2656 } // wxDb::GetColumns()
2659 /********** wxDb::GetColumns() **********/
2661 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2663 // Same as the above GetColumns() function except this one gets columns
2664 // only for a single table, and if 'numCols' is not NULL, the number of
2665 // columns stored in the returned wxDbColInf is set in '*numCols'
2667 // userID is evaluated in the following manner:
2668 // userID == NULL ... UserID is ignored
2669 // userID == "" ... UserID set equal to 'this->uid'
2670 // userID != "" ... UserID set equal to 'userID'
2672 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2673 // by this function. This function should use its own wxDb instance
2674 // to avoid undesired unbinding of columns.
2679 wxDbColInf
*colInf
= 0;
2687 convertUserID(userID
,UserID
);
2689 // Pass 1 - Determine how many columns there are.
2690 // Pass 2 - Allocate the wxDbColInf array and fill in
2691 // the array with the column information.
2693 for (pass
= 1; pass
<= 2; pass
++)
2697 if (noCols
== 0) // Probably a bogus table name(s)
2699 // Allocate n wxDbColInf objects to hold the column information
2700 colInf
= new wxDbColInf
[noCols
+1];
2703 // Mark the end of the array
2704 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2705 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2706 colInf
[noCols
].sqlDataType
= 0;
2709 TableName
= tableName
;
2710 // Oracle and Interbase table names are uppercase only, so force
2711 // the name to uppercase just in case programmer forgot to do this
2712 if ((Dbms() == dbmsORACLE
) ||
2713 (Dbms() == dbmsFIREBIRD
) ||
2714 (Dbms() == dbmsINTERBASE
))
2715 TableName
= TableName
.Upper();
2717 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2719 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2720 // use the call below that leaves out the user name
2721 if (!UserID
.empty() &&
2722 Dbms() != dbmsMY_SQL
&&
2723 Dbms() != dbmsACCESS
&&
2724 Dbms() != dbmsMS_SQL_SERVER
)
2726 retcode
= SQLColumns(hstmt
,
2727 NULL
, 0, // All qualifiers
2728 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2729 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2730 NULL
, 0); // All columns
2734 retcode
= SQLColumns(hstmt
,
2735 NULL
, 0, // All qualifiers
2737 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2738 NULL
, 0); // All columns
2740 if (retcode
!= SQL_SUCCESS
)
2741 { // Error occured, abort
2742 DispAllErrors(henv
, hdbc
, hstmt
);
2745 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2751 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2753 if (pass
== 1) // First pass, just add up the number of columns
2755 else // Pass 2; Fill in the array of structures
2757 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2759 // NOTE: Only the ODBC 1.x fields are retrieved
2760 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2761 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2762 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2763 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2764 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2765 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2766 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2767 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2768 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2769 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2770 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2771 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2772 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2773 // Start Values for Primary/Foriegn Key (=No)
2774 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2775 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2776 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2777 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2779 // BJO 20000428 : Virtuoso returns type names with upper cases!
2780 if (Dbms() == dbmsVIRTUOSO
)
2782 wxString s
= colInf
[colNo
].typeName
;
2784 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2787 // Determine the wxDb data type that is used to represent the native data type of this data source
2788 colInf
[colNo
].dbDataType
= 0;
2789 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2792 // IODBC does not return a correct columnLength, so we set
2793 // columnLength = bufferSize if no column length was returned
2794 // IODBC returns the columnLength in bufferSize. (bug)
2795 if (colInf
[colNo
].columnLength
< 1)
2797 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2801 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2803 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2804 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2805 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2806 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2807 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2808 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2809 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2810 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2816 if (retcode
!= SQL_NO_DATA_FOUND
)
2817 { // Error occured, abort
2818 DispAllErrors(henv
, hdbc
, hstmt
);
2821 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2828 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2830 // Store Primary and Foriegn Keys
2831 GetKeyFields(tableName
,colInf
,noCols
);
2837 } // wxDb::GetColumns()
2840 #else // New GetColumns
2845 These are tentative new GetColumns members which should be more database
2846 independant and which always returns the columns in the order they were
2849 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2850 wxChar* userID)) calls the second implementation for each separate table
2851 before merging the results. This makes the code easier to maintain as
2852 only one member (the second) makes the real work
2853 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2854 wxChar *userID) is a little bit improved
2855 - It doesn't anymore rely on the type-name to find out which database-type
2857 - It ends by sorting the columns, so that they are returned in the same
2858 order they were created
2868 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2871 // The last array element of the tableName[] argument must be zero (null).
2872 // This is how the end of the array is detected.
2876 // How many tables ?
2878 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2880 // Create a table to maintain the columns for each separate table
2881 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2884 for (i
= 0 ; i
< tbl
; i
++)
2887 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2888 if (TableColumns
[i
].colInf
== NULL
)
2890 noCols
+= TableColumns
[i
].noCols
;
2893 // Now merge all the separate table infos
2894 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2896 // Mark the end of the array
2897 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2898 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2899 colInf
[noCols
].sqlDataType
= 0;
2904 for (i
= 0 ; i
< tbl
; i
++)
2906 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2908 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2912 delete [] TableColumns
;
2915 } // wxDb::GetColumns() -- NEW
2918 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2920 // Same as the above GetColumns() function except this one gets columns
2921 // only for a single table, and if 'numCols' is not NULL, the number of
2922 // columns stored in the returned wxDbColInf is set in '*numCols'
2924 // userID is evaluated in the following manner:
2925 // userID == NULL ... UserID is ignored
2926 // userID == "" ... UserID set equal to 'this->uid'
2927 // userID != "" ... UserID set equal to 'userID'
2929 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2930 // by this function. This function should use its own wxDb instance
2931 // to avoid undesired unbinding of columns.
2935 wxDbColInf
*colInf
= 0;
2943 convertUserID(userID
,UserID
);
2945 // Pass 1 - Determine how many columns there are.
2946 // Pass 2 - Allocate the wxDbColInf array and fill in
2947 // the array with the column information.
2949 for (pass
= 1; pass
<= 2; pass
++)
2953 if (noCols
== 0) // Probably a bogus table name(s)
2955 // Allocate n wxDbColInf objects to hold the column information
2956 colInf
= new wxDbColInf
[noCols
+1];
2959 // Mark the end of the array
2960 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2961 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2962 colInf
[noCols
].sqlDataType
= 0;
2965 TableName
= tableName
;
2966 // Oracle and Interbase table names are uppercase only, so force
2967 // the name to uppercase just in case programmer forgot to do this
2968 if ((Dbms() == dbmsORACLE
) ||
2969 (Dbms() == dbmsFIREBIRD
) ||
2970 (Dbms() == dbmsINTERBASE
))
2971 TableName
= TableName
.Upper();
2973 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2975 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2976 // use the call below that leaves out the user name
2977 if (!UserID
.empty() &&
2978 Dbms() != dbmsMY_SQL
&&
2979 Dbms() != dbmsACCESS
&&
2980 Dbms() != dbmsMS_SQL_SERVER
)
2982 retcode
= SQLColumns(hstmt
,
2983 NULL
, 0, // All qualifiers
2984 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2985 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2986 NULL
, 0); // All columns
2990 retcode
= SQLColumns(hstmt
,
2991 NULL
, 0, // All qualifiers
2993 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2994 NULL
, 0); // All columns
2996 if (retcode
!= SQL_SUCCESS
)
2997 { // Error occured, abort
2998 DispAllErrors(henv
, hdbc
, hstmt
);
3001 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3007 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3009 if (pass
== 1) // First pass, just add up the number of columns
3011 else // Pass 2; Fill in the array of structures
3013 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3015 // NOTE: Only the ODBC 1.x fields are retrieved
3016 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3017 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3018 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3019 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3020 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3021 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3022 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3023 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3024 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3025 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3026 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3027 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3028 // Start Values for Primary/Foriegn Key (=No)
3029 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3030 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3031 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3032 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3035 // IODBC does not return a correct columnLength, so we set
3036 // columnLength = bufferSize if no column length was returned
3037 // IODBC returns the columnLength in bufferSize. (bug)
3038 if (colInf
[colNo
].columnLength
< 1)
3040 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3044 // Determine the wxDb data type that is used to represent the native data type of this data source
3045 colInf
[colNo
].dbDataType
= 0;
3046 // Get the intern datatype
3047 switch (colInf
[colNo
].sqlDataType
)
3051 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3057 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3064 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3068 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3071 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3076 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3077 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3084 if (retcode
!= SQL_NO_DATA_FOUND
)
3085 { // Error occured, abort
3086 DispAllErrors(henv
, hdbc
, hstmt
);
3089 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3096 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3098 // Store Primary and Foreign Keys
3099 GetKeyFields(tableName
,colInf
,noCols
);
3101 ///////////////////////////////////////////////////////////////////////////
3102 // Now sort the the columns in order to make them appear in the right order
3103 ///////////////////////////////////////////////////////////////////////////
3105 // Build a generic SELECT statement which returns 0 rows
3108 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3111 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3113 DispAllErrors(henv
, hdbc
, hstmt
);
3117 // Get the number of result columns
3118 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3120 DispAllErrors(henv
, hdbc
, hstmt
);
3124 if (noCols
== 0) // Probably a bogus table name
3133 for (colNum
= 0; colNum
< noCols
; colNum
++)
3135 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3137 &Sword
, &Sdword
) != SQL_SUCCESS
)
3139 DispAllErrors(henv
, hdbc
, hstmt
);
3143 wxString Name1
= name
;
3144 Name1
= Name1
.Upper();
3146 // Where is this name in the array ?
3147 for (i
= colNum
; i
< noCols
; i
++)
3149 wxString Name2
= colInf
[i
].colName
;
3150 Name2
= Name2
.Upper();
3153 if (colNum
!= i
) // swap to sort
3155 wxDbColInf tmpColInf
= colInf
[colNum
];
3156 colInf
[colNum
] = colInf
[i
];
3157 colInf
[i
] = tmpColInf
;
3163 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3165 ///////////////////////////////////////////////////////////////////////////
3167 ///////////////////////////////////////////////////////////////////////////
3173 } // wxDb::GetColumns()
3176 #endif // #else OLD_GETCOLUMNS
3179 /********** wxDb::GetColumnCount() **********/
3180 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3182 * Returns a count of how many columns are in a table.
3183 * If an error occurs in computing the number of columns
3184 * this function will return a -1 for the count
3186 * userID is evaluated in the following manner:
3187 * userID == NULL ... UserID is ignored
3188 * userID == "" ... UserID set equal to 'this->uid'
3189 * userID != "" ... UserID set equal to 'userID'
3191 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3192 * by this function. This function should use its own wxDb instance
3193 * to avoid undesired unbinding of columns.
3203 convertUserID(userID
,UserID
);
3205 TableName
= tableName
;
3206 // Oracle and Interbase table names are uppercase only, so force
3207 // the name to uppercase just in case programmer forgot to do this
3208 if ((Dbms() == dbmsORACLE
) ||
3209 (Dbms() == dbmsFIREBIRD
) ||
3210 (Dbms() == dbmsINTERBASE
))
3211 TableName
= TableName
.Upper();
3213 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3215 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3216 // use the call below that leaves out the user name
3217 if (!UserID
.empty() &&
3218 Dbms() != dbmsMY_SQL
&&
3219 Dbms() != dbmsACCESS
&&
3220 Dbms() != dbmsMS_SQL_SERVER
)
3222 retcode
= SQLColumns(hstmt
,
3223 NULL
, 0, // All qualifiers
3224 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3225 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3226 NULL
, 0); // All columns
3230 retcode
= SQLColumns(hstmt
,
3231 NULL
, 0, // All qualifiers
3233 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3234 NULL
, 0); // All columns
3236 if (retcode
!= SQL_SUCCESS
)
3237 { // Error occured, abort
3238 DispAllErrors(henv
, hdbc
, hstmt
);
3239 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3243 // Count the columns
3244 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3247 if (retcode
!= SQL_NO_DATA_FOUND
)
3248 { // Error occured, abort
3249 DispAllErrors(henv
, hdbc
, hstmt
);
3250 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3254 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3257 } // wxDb::GetColumnCount()
3260 /********** wxDb::GetCatalog() *******/
3261 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3263 * ---------------------------------------------------------------------
3264 * -- 19991203 : mj10777 : Create ------
3265 * -- : Creates a wxDbInf with Tables / Cols Array ------
3266 * -- : uses SQLTables and fills pTableInf; ------
3267 * -- : pColInf is set to NULL and numCols to 0; ------
3268 * -- : returns pDbInf (wxDbInf) ------
3269 * -- - if unsuccesfull (pDbInf == NULL) ------
3270 * -- : pColInf can be filled with GetColumns(..); ------
3271 * -- : numCols can be filled with GetColumnCount(..); ------
3272 * ---------------------------------------------------------------------
3274 * userID is evaluated in the following manner:
3275 * userID == NULL ... UserID is ignored
3276 * userID == "" ... UserID set equal to 'this->uid'
3277 * userID != "" ... UserID set equal to 'userID'
3279 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3280 * by this function. This function should use its own wxDb instance
3281 * to avoid undesired unbinding of columns.
3284 int noTab
= 0; // Counter while filling table entries
3288 wxString tblNameSave
;
3291 convertUserID(userID
,UserID
);
3293 //-------------------------------------------------------------
3294 // Create the Database Array of catalog entries
3296 wxDbInf
*pDbInf
= new wxDbInf
;
3298 //-------------------------------------------------------------
3299 // Table Information
3300 // Pass 1 - Determine how many Tables there are.
3301 // Pass 2 - Create the Table array and fill it
3302 // - Create the Cols array = NULL
3303 //-------------------------------------------------------------
3305 for (pass
= 1; pass
<= 2; pass
++)
3307 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3308 tblNameSave
.Empty();
3310 if (!UserID
.empty() &&
3311 Dbms() != dbmsMY_SQL
&&
3312 Dbms() != dbmsACCESS
&&
3313 Dbms() != dbmsMS_SQL_SERVER
)
3315 retcode
= SQLTables(hstmt
,
3316 NULL
, 0, // All qualifiers
3317 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3318 NULL
, 0, // All tables
3319 NULL
, 0); // All columns
3323 retcode
= SQLTables(hstmt
,
3324 NULL
, 0, // All qualifiers
3325 NULL
, 0, // User specified
3326 NULL
, 0, // All tables
3327 NULL
, 0); // All columns
3330 if (retcode
!= SQL_SUCCESS
)
3332 DispAllErrors(henv
, hdbc
, hstmt
);
3334 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3338 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3340 if (pass
== 1) // First pass, just count the Tables
3342 if (pDbInf
->numTables
== 0)
3344 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3345 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3347 pDbInf
->numTables
++; // Counter for Tables
3349 if (pass
== 2) // Create and fill the Table entries
3351 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3352 { // no, then create the Array
3353 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3355 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3357 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3358 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3359 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3365 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3367 // Query how many columns are in each table
3368 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3370 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3375 } // wxDb::GetCatalog()
3378 /********** wxDb::Catalog() **********/
3379 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3381 * Creates the text file specified in 'filename' which will contain
3382 * a minimal data dictionary of all tables accessible by the user specified
3385 * userID is evaluated in the following manner:
3386 * userID == NULL ... UserID is ignored
3387 * userID == "" ... UserID set equal to 'this->uid'
3388 * userID != "" ... UserID set equal to 'userID'
3390 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3391 * by this function. This function should use its own wxDb instance
3392 * to avoid undesired unbinding of columns.
3395 wxASSERT(fileName
.Length());
3399 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3400 wxString tblNameSave
;
3401 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3403 wxChar typeName
[30+1];
3404 SDWORD precision
, length
;
3406 FILE *fp
= wxFopen(fileName
.fn_str(),wxT("wt"));
3410 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3413 convertUserID(userID
,UserID
);
3415 if (!UserID
.empty() &&
3416 Dbms() != dbmsMY_SQL
&&
3417 Dbms() != dbmsACCESS
&&
3418 Dbms() != dbmsFIREBIRD
&&
3419 Dbms() != dbmsINTERBASE
&&
3420 Dbms() != dbmsMS_SQL_SERVER
)
3422 retcode
= SQLColumns(hstmt
,
3423 NULL
, 0, // All qualifiers
3424 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3425 NULL
, 0, // All tables
3426 NULL
, 0); // All columns
3430 retcode
= SQLColumns(hstmt
,
3431 NULL
, 0, // All qualifiers
3432 NULL
, 0, // User specified
3433 NULL
, 0, // All tables
3434 NULL
, 0); // All columns
3436 if (retcode
!= SQL_SUCCESS
)
3438 DispAllErrors(henv
, hdbc
, hstmt
);
3444 tblNameSave
.Empty();
3449 retcode
= SQLFetch(hstmt
);
3450 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3453 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3454 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3455 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3456 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3457 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3458 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3460 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3463 wxFputs(wxT("\n"), fp
);
3464 wxFputs(wxT("================================ "), fp
);
3465 wxFputs(wxT("================================ "), fp
);
3466 wxFputs(wxT("===================== "), fp
);
3467 wxFputs(wxT("========= "), fp
);
3468 wxFputs(wxT("=========\n"), fp
);
3469 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3470 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3471 wxFputs(outStr
.c_str(), fp
);
3472 wxFputs(wxT("================================ "), fp
);
3473 wxFputs(wxT("================================ "), fp
);
3474 wxFputs(wxT("===================== "), fp
);
3475 wxFputs(wxT("========= "), fp
);
3476 wxFputs(wxT("=========\n"), fp
);
3477 tblNameSave
= tblName
;
3480 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3481 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3482 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3484 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3491 if (retcode
!= SQL_NO_DATA_FOUND
)
3492 DispAllErrors(henv
, hdbc
, hstmt
);
3494 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3497 return(retcode
== SQL_NO_DATA_FOUND
);
3499 } // wxDb::Catalog()
3502 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3504 * Table name can refer to a table, view, alias or synonym. Returns true
3505 * if the object exists in the database. This function does not indicate
3506 * whether or not the user has privleges to query or perform other functions
3509 * userID is evaluated in the following manner:
3510 * userID == NULL ... UserID is ignored
3511 * userID == "" ... UserID set equal to 'this->uid'
3512 * userID != "" ... UserID set equal to 'userID'
3515 wxASSERT(tableName
.Length());
3519 if (Dbms() == dbmsDBASE
)
3522 if (tablePath
.Length())
3523 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3525 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3528 exists
= wxFileExists(dbName
);
3533 convertUserID(userID
,UserID
);
3535 TableName
= tableName
;
3536 // Oracle and Interbase table names are uppercase only, so force
3537 // the name to uppercase just in case programmer forgot to do this
3538 if ((Dbms() == dbmsORACLE
) ||
3539 (Dbms() == dbmsFIREBIRD
) ||
3540 (Dbms() == dbmsINTERBASE
))
3541 TableName
= TableName
.Upper();
3543 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3546 // Some databases cannot accept a user name when looking up table names,
3547 // so we use the call below that leaves out the user name
3548 if (!UserID
.empty() &&
3549 Dbms() != dbmsMY_SQL
&&
3550 Dbms() != dbmsACCESS
&&
3551 Dbms() != dbmsMS_SQL_SERVER
&&
3552 Dbms() != dbmsDB2
&&
3553 Dbms() != dbmsFIREBIRD
&&
3554 Dbms() != dbmsINTERBASE
&&
3555 Dbms() != dbmsPERVASIVE_SQL
)
3557 retcode
= SQLTables(hstmt
,
3558 NULL
, 0, // All qualifiers
3559 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3560 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3561 NULL
, 0); // All table types
3565 retcode
= SQLTables(hstmt
,
3566 NULL
, 0, // All qualifiers
3567 NULL
, 0, // All owners
3568 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3569 NULL
, 0); // All table types
3571 if (retcode
!= SQL_SUCCESS
)
3572 return(DispAllErrors(henv
, hdbc
, hstmt
));
3574 retcode
= SQLFetch(hstmt
);
3575 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3577 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3578 return(DispAllErrors(henv
, hdbc
, hstmt
));
3581 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3585 } // wxDb::TableExists()
3588 /********** wxDb::TablePrivileges() **********/
3589 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3590 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3592 wxASSERT(tableName
.Length());
3594 wxDbTablePrivilegeInfo result
;
3598 // We probably need to be able to dynamically set this based on
3599 // the driver type, and state.
3600 wxChar curRole
[]=wxT("public");
3604 wxString UserID
,Schema
;
3605 convertUserID(userID
,UserID
);
3606 convertUserID(schema
,Schema
);
3608 TableName
= tableName
;
3609 // Oracle and Interbase table names are uppercase only, so force
3610 // the name to uppercase just in case programmer forgot to do this
3611 if ((Dbms() == dbmsORACLE
) ||
3612 (Dbms() == dbmsFIREBIRD
) ||
3613 (Dbms() == dbmsINTERBASE
))
3614 TableName
= TableName
.Upper();
3616 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3618 // Some databases cannot accept a user name when looking up table names,
3619 // so we use the call below that leaves out the user name
3620 if (!Schema
.empty() &&
3621 Dbms() != dbmsMY_SQL
&&
3622 Dbms() != dbmsACCESS
&&
3623 Dbms() != dbmsMS_SQL_SERVER
)
3625 retcode
= SQLTablePrivileges(hstmt
,
3627 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3628 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3632 retcode
= SQLTablePrivileges(hstmt
,
3635 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3638 #ifdef DBDEBUG_CONSOLE
3639 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3642 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3643 return (DispAllErrors(henv
, hdbc
, hstmt
));
3645 bool failed
= false;
3646 retcode
= SQLFetch(hstmt
);
3647 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3649 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3652 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3655 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3658 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3661 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3664 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3667 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3672 return(DispAllErrors(henv
, hdbc
, hstmt
));
3674 #ifdef DBDEBUG_CONSOLE
3675 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3676 result
.privilege
,result
.tableOwner
,result
.tableName
,
3677 result
.grantor
, result
.grantee
);
3680 if (UserID
.IsSameAs(result
.tableOwner
,false))
3682 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3686 if (UserID
.IsSameAs(result
.grantee
,false) &&
3687 !wxStrcmp(result
.privilege
,priv
))
3689 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3693 if (!wxStrcmp(result
.grantee
,curRole
) &&
3694 !wxStrcmp(result
.privilege
,priv
))
3696 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3700 retcode
= SQLFetch(hstmt
);
3703 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3706 } // wxDb::TablePrivileges
3709 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3713 if (Dbms() == dbmsACCESS
)
3714 TableName
= _T("\"");
3715 TableName
+= tableName
;
3716 if (Dbms() == dbmsACCESS
)
3717 TableName
+= _T("\"");
3720 } // wxDb::SQLTableName()
3723 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3727 if (Dbms() == dbmsACCESS
)
3730 if (Dbms() == dbmsACCESS
)
3731 ColName
+= _T("\"");
3734 } // wxDb::SQLColumnName()
3737 /********** wxDb::SetSqlLogging() **********/
3738 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3740 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3741 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3743 if (state
== sqlLogON
)
3747 fpSqlLog
= wxFopen(filename
.fn_str(), (append
? wxT("at") : wxT("wt")));
3748 if (fpSqlLog
== NULL
)
3756 if (fclose(fpSqlLog
))
3762 sqlLogState
= state
;
3765 } // wxDb::SetSqlLogging()
3768 /********** wxDb::WriteSqlLog() **********/
3769 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3771 wxASSERT(logMsg
.Length());
3773 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3776 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3778 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3780 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3785 } // wxDb::WriteSqlLog()
3788 /********** wxDb::Dbms() **********/
3789 wxDBMS
wxDb::Dbms(void)
3791 * Be aware that not all database engines use the exact same syntax, and not
3792 * every ODBC compliant database is compliant to the same level of compliancy.
3793 * Some manufacturers support the minimum Level 1 compliancy, and others up
3794 * through Level 3. Others support subsets of features for levels above 1.
3796 * If you find an inconsistency between the wxDb class and a specific database
3797 * engine, and an identifier to this section, and special handle the database in
3798 * the area where behavior is non-conforming with the other databases.
3801 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3802 * ---------------------------------------------------
3805 * - Currently the only database supported by the class to support VIEWS
3808 * - Does not support the SQL_TIMESTAMP structure
3809 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3810 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3811 * is true. The user must create ALL indexes from their program.
3812 * - Table names can only be 8 characters long
3813 * - Column names can only be 10 characters long
3816 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3817 * after every table name involved in the query/join if that tables matching record(s)
3819 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3821 * SYBASE (Enterprise)
3822 * - If a column is part of the Primary Key, the column cannot be NULL
3823 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3826 * - If a column is part of the Primary Key, the column cannot be NULL
3827 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3828 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3829 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3830 * column definition if it is not defined correctly, but it is experimental
3831 * - Does not support sub-queries in SQL statements
3834 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3835 * - Does not support sub-queries in SQL statements
3838 * - Primary keys must be declared as NOT NULL
3839 * - Table and index names must not be longer than 13 characters in length (technically
3840 * table names can be up to 18 characters, but the primary index is created using the
3841 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3846 * - Columns that are part of primary keys must be defined as being NOT NULL
3847 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3848 * column definition if it is not defined correctly, but it is experimental
3851 // Should only need to do this once for each new database connection
3852 // so return the value we already determined it to be to save time
3853 // and lots of string comparisons
3854 if (dbmsType
!= dbmsUNIDENTIFIED
)
3857 #ifdef DBDEBUG_CONSOLE
3858 // When run in console mode, use standard out to display errors.
3859 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3860 #endif // DBDEBUG_CONSOLE
3862 wxLogDebug(wxT("Database connecting to: "));
3863 wxLogDebug(dbInf
.dbmsName
);
3865 wxChar baseName
[25+1];
3866 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3869 // RGG 20001025 : add support for Interbase
3870 // GT : Integrated to base classes on 20001121
3871 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3872 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3874 // BJO 20000428 : add support for Virtuoso
3875 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3876 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3878 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3879 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3881 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3882 // connected through an OpenLink driver.
3883 // Is it also returned by Sybase Adapatitve server?
3884 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3885 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3887 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3888 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3889 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3891 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3894 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3895 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3898 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3899 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3902 if (!wxStricmp(baseName
,wxT("Pervasive")))
3903 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3906 if (!wxStricmp(baseName
,wxT("Informix")))
3907 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3909 if (!wxStricmp(baseName
,wxT("Firebird")))
3910 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3913 if (!wxStricmp(baseName
,wxT("Oracle")))
3914 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3915 if (!wxStricmp(baseName
,wxT("ACCESS")))
3916 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3917 if (!wxStricmp(baseName
,wxT("Sybase")))
3918 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3921 if (!wxStricmp(baseName
,wxT("DBASE")))
3922 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3923 if (!wxStricmp(baseName
,wxT("xBase")))
3924 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3925 if (!wxStricmp(baseName
,wxT("MySQL")))
3926 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3929 if (!wxStricmp(baseName
,wxT("DB2")))
3930 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3932 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3937 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3938 int dataType
, ULONG columnLength
,
3939 const wxString
&optionalParam
)
3941 wxASSERT(tableName
.Length());
3942 wxASSERT(columnName
.Length());
3943 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3944 dataType
!= DB_DATA_TYPE_VARCHAR
);
3946 // Must specify a columnLength if modifying a VARCHAR type column
3947 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3950 wxString dataTypeName
;
3952 wxString alterSlashModify
;
3956 case DB_DATA_TYPE_VARCHAR
:
3957 dataTypeName
= typeInfVarchar
.TypeName
;
3959 case DB_DATA_TYPE_INTEGER
:
3960 dataTypeName
= typeInfInteger
.TypeName
;
3962 case DB_DATA_TYPE_FLOAT
:
3963 dataTypeName
= typeInfFloat
.TypeName
;
3965 case DB_DATA_TYPE_DATE
:
3966 dataTypeName
= typeInfDate
.TypeName
;
3968 case DB_DATA_TYPE_BLOB
:
3969 dataTypeName
= typeInfBlob
.TypeName
;
3975 // Set the modify or alter syntax depending on the type of database connected to
3979 alterSlashModify
= _T("MODIFY");
3981 case dbmsMS_SQL_SERVER
:
3982 alterSlashModify
= _T("ALTER COLUMN");
3984 case dbmsUNIDENTIFIED
:
3986 case dbmsSYBASE_ASA
:
3987 case dbmsSYBASE_ASE
:
3992 case dbmsXBASE_SEQUITER
:
3994 alterSlashModify
= _T("MODIFY");
3998 // create the SQL statement
3999 if ( Dbms() == dbmsMY_SQL
)
4001 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4002 columnName
.c_str(), dataTypeName
.c_str());
4006 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4007 columnName
.c_str(), dataTypeName
.c_str());
4010 // For varchars only, append the size of the column
4011 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4012 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4015 s
.Printf(wxT("(%lu)"), columnLength
);
4019 // for passing things like "NOT NULL"
4020 if (optionalParam
.Length())
4022 sqlStmt
+= wxT(" ");
4023 sqlStmt
+= optionalParam
;
4026 return ExecSql(sqlStmt
);
4028 } // wxDb::ModifyColumn()
4031 /********** wxDbGetConnection() **********/
4032 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4036 // Used to keep a pointer to a DB connection that matches the requested
4037 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4038 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4039 // rather than having to re-query the datasource to get all the values
4040 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4041 wxDb
*matchingDbConnection
= NULL
;
4043 // Scan the linked list searching for an available database connection
4044 // that's already been opened but is currently not in use.
4045 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4047 // The database connection must be for the same datasource
4048 // name and must currently not be in use.
4050 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4052 if (pDbConfig
->UseConnectionStr())
4054 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4055 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4057 // Found a free connection
4058 pList
->Free
= false;
4059 return(pList
->PtrDb
);
4064 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4065 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4067 // Found a free connection
4068 pList
->Free
= false;
4069 return(pList
->PtrDb
);
4074 if (pDbConfig
->UseConnectionStr())
4076 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4077 matchingDbConnection
= pList
->PtrDb
;
4081 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4082 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4083 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4084 matchingDbConnection
= pList
->PtrDb
;
4088 // No available connections. A new connection must be made and
4089 // appended to the end of the linked list.
4092 // Find the end of the list
4093 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4094 // Append a new list item
4095 pList
->PtrNext
= new wxDbList
;
4096 pList
->PtrNext
->PtrPrev
= pList
;
4097 pList
= pList
->PtrNext
;
4101 // Create the first node on the list
4102 pList
= PtrBegDbList
= new wxDbList
;
4106 // Initialize new node in the linked list
4108 pList
->Free
= false;
4109 pList
->Dsn
= pDbConfig
->GetDsn();
4110 pList
->Uid
= pDbConfig
->GetUserID();
4111 pList
->AuthStr
= pDbConfig
->GetPassword();
4112 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4114 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4118 if (!matchingDbConnection
)
4120 if (pDbConfig
->UseConnectionStr())
4122 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4126 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4130 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4132 // Connect to the datasource
4135 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4136 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4137 return(pList
->PtrDb
);
4139 else // Unable to connect, destroy list item
4142 pList
->PtrPrev
->PtrNext
= 0;
4144 PtrBegDbList
= 0; // Empty list again
4146 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4147 pList
->PtrDb
->Close(); // Close the wxDb object
4148 delete pList
->PtrDb
; // Deletes the wxDb object
4149 delete pList
; // Deletes the linked list object
4153 } // wxDbGetConnection()
4156 /********** wxDbFreeConnection() **********/
4157 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4161 // Scan the linked list searching for the database connection
4162 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4164 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4165 return (pList
->Free
= true);
4168 // Never found the database object, return failure
4171 } // wxDbFreeConnection()
4174 /********** wxDbCloseConnections() **********/
4175 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4177 wxDbList
*pList
, *pNext
;
4179 // Traverse the linked list closing database connections and freeing memory as I go.
4180 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4182 pNext
= pList
->PtrNext
; // Save the pointer to next
4183 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4184 pList
->PtrDb
->Close(); // Close the wxDb object
4185 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4186 delete pList
->PtrDb
; // Deletes the wxDb object
4187 delete pList
; // Deletes the linked list object
4190 // Mark the list as empty
4193 } // wxDbCloseConnections()
4196 /********** wxDbConnectionsInUse() **********/
4197 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4202 // Scan the linked list counting db connections that are currently in use
4203 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4205 if (pList
->Free
== false)
4211 } // wxDbConnectionsInUse()
4215 /********** wxDbLogExtendedErrorMsg() **********/
4216 // DEBUG ONLY function
4217 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4219 const wxChar
*ErrFile
,
4222 static wxString msg
;
4227 if (ErrFile
|| ErrLine
)
4229 msg
+= wxT("File: ");
4231 msg
+= wxT(" Line: ");
4232 tStr
.Printf(wxT("%d"),ErrLine
);
4233 msg
+= tStr
.c_str();
4237 msg
.Append (wxT("\nODBC errors:\n"));
4240 // Display errors for this connection
4242 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4244 if (pDb
->errorList
[i
])
4246 msg
.Append(pDb
->errorList
[i
]);
4247 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4248 msg
.Append(wxT("\n"));
4249 // Clear the errmsg buffer so the next error will not
4250 // end up showing the previous error that have occurred
4251 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4256 wxLogDebug(msg
.c_str());
4259 } // wxDbLogExtendedErrorMsg()
4262 /********** wxDbSqlLog() **********/
4263 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4265 bool append
= false;
4268 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4270 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4275 SQLLOGstate
= state
;
4276 SQLLOGfn
= filename
;
4284 /********** wxDbCreateDataSource() **********/
4285 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4286 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4288 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4289 * Very rudimentary creation of an ODBC data source.
4291 * ODBC driver must be ODBC 3.0 compliant to use this function
4296 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4302 dsnLocation
= ODBC_ADD_SYS_DSN
;
4304 dsnLocation
= ODBC_ADD_DSN
;
4306 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4307 // so that is why I used it, as wxString does not deal well with
4308 // embedded nulls in strings
4309 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4311 // Replace the separator from above with the '\0' seperator needed
4312 // by the SQLConfigDataSource() function
4316 k
= setupStr
.Find((wxChar
)2,true);
4317 if (k
!= wxNOT_FOUND
)
4318 setupStr
[(UINT
)k
] = wxT('\0');
4320 while (k
!= wxNOT_FOUND
);
4322 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4323 driverName
, setupStr
.c_str());
4325 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4327 // check for errors caused by ConfigDSN based functions
4330 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4331 errMsg
[0] = wxT('\0');
4333 // This function is only supported in ODBC drivers v3.0 compliant and above
4334 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4337 #ifdef DBDEBUG_CONSOLE
4338 // When run in console mode, use standard out to display errors.
4339 cout
<< errMsg
<< endl
;
4340 cout
<< wxT("Press any key to continue...") << endl
;
4342 #endif // DBDEBUG_CONSOLE
4345 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4346 #endif // __WXDEBUG__
4352 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4353 // necessary to use this function, so this function is not supported
4355 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4358 #endif // __VISUALC__
4362 } // wxDbCreateDataSource()
4366 /********** wxDbGetDataSource() **********/
4367 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4368 SWORD DsDescMaxLength
, UWORD direction
)
4370 * Dsn and DsDesc will contain the data source name and data source
4371 * description upon return
4375 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4376 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4378 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4379 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4384 } // wxDbGetDataSource()
4387 // Change this to 0 to remove use of all deprecated functions
4388 #if wxODBC_BACKWARD_COMPATABILITY
4389 /********************************************************************
4390 ********************************************************************
4392 * The following functions are all DEPRECATED and are included for
4393 * backward compatability reasons only
4395 ********************************************************************
4396 ********************************************************************/
4397 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4399 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4401 /***** DEPRECATED: use wxGetDataSource() *****/
4402 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4405 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4407 /***** DEPRECATED: use wxDbGetConnection() *****/
4408 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4410 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4412 /***** DEPRECATED: use wxDbFreeConnection() *****/
4413 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4415 return wxDbFreeConnection(pDb
);
4417 /***** DEPRECATED: use wxDbCloseConnections() *****/
4418 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4420 wxDbCloseConnections();
4422 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4423 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4425 return wxDbConnectionsInUse();