1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
28 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
29 #pragma implementation "db.h"
32 #include "wx/wxprec.h"
38 #ifdef DBDEBUG_CONSOLE
39 #include "wx/ioswrap.h"
43 #include "wx/string.h"
44 #include "wx/object.h"
49 #include "wx/filefn.h"
50 #include "wx/wxchar.h"
62 // DLL options compatibility check:
64 WX_CHECK_BUILD_OPTIONS("wxODBC")
66 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
68 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
69 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
72 extern wxList TablesInUse
;
75 // SQL Log defaults to be used by GetDbConnection
76 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
78 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
80 // The wxDb::errorList is copied to this variable when the wxDb object
81 // is closed. This way, the error list is still available after the
82 // database object is closed. This is necessary if the database
83 // connection fails so the calling application can show the operator
84 // why the connection failed. Note: as each wxDb object is closed, it
85 // will overwrite the errors of the previously destroyed wxDb object in
86 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
88 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
91 // This type defines the return row-struct form
92 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
95 wxChar tableQual
[128+1];
96 wxChar tableOwner
[128+1];
97 wxChar tableName
[128+1];
98 wxChar grantor
[128+1];
99 wxChar grantee
[128+1];
100 wxChar privilege
[128+1];
101 wxChar grantable
[3+1];
102 } wxDbTablePrivilegeInfo
;
105 /********** wxDbConnectInf Constructor - form 1 **********/
106 wxDbConnectInf::wxDbConnectInf()
109 freeHenvOnDestroy
= false;
115 /********** wxDbConnectInf Constructor - form 2 **********/
116 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
117 const wxString
&password
, const wxString
&defaultDir
,
118 const wxString
&fileType
, const wxString
&description
)
121 freeHenvOnDestroy
= false;
132 SetPassword(password
);
133 SetDescription(description
);
134 SetFileType(fileType
);
135 SetDefaultDir(defaultDir
);
136 } // wxDbConnectInf Constructor
139 wxDbConnectInf::~wxDbConnectInf()
141 if (freeHenvOnDestroy
)
145 } // wxDbConnectInf Destructor
149 /********** wxDbConnectInf::Initialize() **********/
150 bool wxDbConnectInf::Initialize()
152 freeHenvOnDestroy
= false;
154 if (freeHenvOnDestroy
&& Henv
)
161 ConnectionStr
[0] = 0;
166 useConnectionStr
= false;
169 } // wxDbConnectInf::Initialize()
172 /********** wxDbConnectInf::AllocHenv() **********/
173 bool wxDbConnectInf::AllocHenv()
175 // This is here to help trap if you are getting a new henv
176 // without releasing an existing henv
179 // Initialize the ODBC Environment for Database Operations
180 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
182 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
186 freeHenvOnDestroy
= true;
189 } // wxDbConnectInf::AllocHenv()
192 void wxDbConnectInf::FreeHenv()
200 freeHenvOnDestroy
= false;
202 } // wxDbConnectInf::FreeHenv()
205 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
207 wxASSERT(dsn
.Length() < sizeof(Dsn
));
209 wxStrncpy(Dsn
, dsn
, sizeof(Dsn
)-1);
210 Dsn
[sizeof(Dsn
)-1] = 0; // Prevent buffer overrun
211 } // wxDbConnectInf::SetDsn()
214 void wxDbConnectInf::SetUserID(const wxString
&uid
)
216 wxASSERT(uid
.Length() < sizeof(Uid
));
217 wxStrncpy(Uid
, uid
, sizeof(Uid
)-1);
218 Uid
[sizeof(Uid
)-1] = 0; // Prevent buffer overrun
219 } // wxDbConnectInf::SetUserID()
222 void wxDbConnectInf::SetPassword(const wxString
&password
)
224 wxASSERT(password
.Length() < sizeof(AuthStr
));
226 wxStrncpy(AuthStr
, password
, sizeof(AuthStr
)-1);
227 AuthStr
[sizeof(AuthStr
)-1] = 0; // Prevent buffer overrun
228 } // wxDbConnectInf::SetPassword()
230 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
232 wxASSERT(connectStr
.Length() < sizeof(ConnectionStr
));
234 useConnectionStr
= wxStrlen(connectStr
) > 0;
236 wxStrncpy(ConnectionStr
, connectStr
, sizeof(ConnectionStr
)-1);
237 ConnectionStr
[sizeof(ConnectionStr
)-1] = 0; // Prevent buffer overrun
238 } // wxDbConnectInf::SetConnectionStr()
241 /********** wxDbColFor Constructor **********/
242 wxDbColFor::wxDbColFor()
245 } // wxDbColFor::wxDbColFor()
248 /********** wxDbColFor::Initialize() **********/
249 void wxDbColFor::Initialize()
259 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
262 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
263 } // wxDbColFor::Initialize()
266 /********** wxDbColFor::Format() **********/
267 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
268 short columnLength
, short decimalDigits
)
270 // ----------------------------------------------------------------------------------------
271 // -- 19991224 : mj10777 : Create
272 // There is still a lot of work to do here, but it is a start
273 // It handles all the basic data-types that I have run into up to now
274 // The main work will have be with Dates and float Formatting
275 // (US 1,000.00 ; EU 1.000,00)
276 // There are wxWindow plans for locale support and the new wxDateTime. If
277 // they define some constants (wxEUROPEAN) that can be gloably used,
278 // they should be used here.
279 // ----------------------------------------------------------------------------------------
280 // There should also be a function to scan in a string to fill the variable
281 // ----------------------------------------------------------------------------------------
283 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
284 i_dbDataType
= dbDataType
;
285 i_sqlDataType
= sqlDataType
;
286 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
288 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
290 if ((i_sqlDataType
== SQL_VARCHAR
)
292 #if defined(SQL_WCHAR)
293 || (i_sqlDataType
== SQL_WCHAR
)
295 #if defined(SQL_WVARCHAR)
296 || (i_sqlDataType
== SQL_WVARCHAR
)
299 || (i_sqlDataType
== SQL_LONGVARCHAR
))
300 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
301 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
302 i_dbDataType
= DB_DATA_TYPE_DATE
;
303 if (i_sqlDataType
== SQL_C_BIT
)
304 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
305 if (i_sqlDataType
== SQL_NUMERIC
)
306 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
307 if (i_sqlDataType
== SQL_REAL
)
308 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
309 if (i_sqlDataType
== SQL_C_BINARY
)
310 i_dbDataType
= DB_DATA_TYPE_BLOB
;
313 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
315 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
318 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
320 case DB_DATA_TYPE_VARCHAR
:
323 case DB_DATA_TYPE_INTEGER
:
326 case DB_DATA_TYPE_FLOAT
:
327 if (decimalDigits
== 0)
330 tempStr
.Printf(wxT("%s%d.%d"), tempStr
.c_str(),columnLength
, decimalDigits
);
331 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
333 case DB_DATA_TYPE_DATE
:
334 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
336 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
338 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
340 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
342 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
344 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
346 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
348 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
350 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
352 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
355 case DB_DATA_TYPE_BLOB
:
356 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
359 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
363 } // wxDbColFor::Format()
366 /********** wxDbColInf Constructor **********/
367 wxDbColInf::wxDbColInf()
370 } // wxDbColInf::wxDbColInf()
373 /********** wxDbColInf Destructor ********/
374 wxDbColInf::~wxDbColInf()
379 } // wxDbColInf::~wxDbColInf()
382 bool wxDbColInf::Initialize()
404 } // wxDbColInf::Initialize()
407 /********** wxDbTableInf Constructor ********/
408 wxDbTableInf::wxDbTableInf()
411 } // wxDbTableInf::wxDbTableInf()
414 /********** wxDbTableInf Constructor ********/
415 wxDbTableInf::~wxDbTableInf()
420 } // wxDbTableInf::~wxDbTableInf()
423 bool wxDbTableInf::Initialize()
432 } // wxDbTableInf::Initialize()
435 /********** wxDbInf Constructor *************/
439 } // wxDbInf::wxDbInf()
442 /********** wxDbInf Destructor *************/
448 } // wxDbInf::~wxDbInf()
451 /********** wxDbInf::Initialize() *************/
452 bool wxDbInf::Initialize()
460 } // wxDbInf::Initialize()
463 /********** wxDb Constructor **********/
464 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
466 // Copy the HENV into the db class
468 fwdOnlyCursors
= FwdOnlyCursors
;
474 /********** wxDb Destructor **********/
477 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
487 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
488 /********** wxDb::initialize() **********/
489 void wxDb::initialize()
491 * Private member function that sets all wxDb member variables to
492 * known values at creation of the wxDb
497 fpSqlLog
= 0; // Sql Log file pointer
498 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
500 dbmsType
= dbmsUNIDENTIFIED
;
502 wxStrcpy(sqlState
,wxEmptyString
);
503 wxStrcpy(errorMsg
,wxEmptyString
);
504 nativeError
= cbErrorMsg
= 0;
505 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
506 wxStrcpy(errorList
[i
], wxEmptyString
);
508 // Init typeInf structures
509 typeInfVarchar
.TypeName
.Empty();
510 typeInfVarchar
.FsqlType
= 0;
511 typeInfVarchar
.Precision
= 0;
512 typeInfVarchar
.CaseSensitive
= 0;
513 typeInfVarchar
.MaximumScale
= 0;
515 typeInfInteger
.TypeName
.Empty();
516 typeInfInteger
.FsqlType
= 0;
517 typeInfInteger
.Precision
= 0;
518 typeInfInteger
.CaseSensitive
= 0;
519 typeInfInteger
.MaximumScale
= 0;
521 typeInfFloat
.TypeName
.Empty();
522 typeInfFloat
.FsqlType
= 0;
523 typeInfFloat
.Precision
= 0;
524 typeInfFloat
.CaseSensitive
= 0;
525 typeInfFloat
.MaximumScale
= 0;
527 typeInfDate
.TypeName
.Empty();
528 typeInfDate
.FsqlType
= 0;
529 typeInfDate
.Precision
= 0;
530 typeInfDate
.CaseSensitive
= 0;
531 typeInfDate
.MaximumScale
= 0;
533 typeInfBlob
.TypeName
.Empty();
534 typeInfBlob
.FsqlType
= 0;
535 typeInfBlob
.Precision
= 0;
536 typeInfBlob
.CaseSensitive
= 0;
537 typeInfBlob
.MaximumScale
= 0;
539 // Error reporting is turned OFF by default
542 // Allocate a data source connection handle
543 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
546 // Initialize the db status flag
549 // Mark database as not open as of yet
552 dbOpenedWithConnectionString
= false;
553 } // wxDb::initialize()
556 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
558 // NOTE: Return value from this function MUST be copied
559 // immediately, as the value is not good after
560 // this function has left scope.
562 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
566 if (!wxStrlen(userID
))
574 // dBase does not use user names, and some drivers fail if you try to pass one
575 if ( Dbms() == dbmsDBASE
576 || Dbms() == dbmsXBASE_SEQUITER
)
579 // Some databases require user names to be specified in uppercase,
580 // so force the name to uppercase
581 if ((Dbms() == dbmsORACLE
) ||
582 (Dbms() == dbmsMAXDB
))
583 UserID
= UserID
.Upper();
585 return UserID
.c_str();
586 } // wxDb::convertUserID()
589 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
593 // These are the possible SQL types we check for use against the datasource we are connected
594 // to for the purpose of determining which data type to use for the basic character strings
597 // NOTE: The first type in this enumeration that is determined to be supported by the
598 // datasource/driver is the one that will be used.
599 SWORD PossibleSqlCharTypes
[] = {
600 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
604 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
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 non-floating point
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 PossibleSqlIntegerTypes
[] = {
620 // These are the possible SQL types we check for use against the datasource we are connected
621 // to for the purpose of determining which data type to use for the basic floating point number
624 // NOTE: The first type in this enumeration that is determined to be supported by the
625 // datasource/driver is the one that will be used.
626 SWORD PossibleSqlFloatTypes
[] = {
634 // These are the possible SQL types we check for use agains the datasource we are connected
635 // to for the purpose of determining which data type to use for the date/time column types
637 // NOTE: The first type in this enumeration that is determined to be supported by the
638 // datasource/driver is the one that will be used.
639 SWORD PossibleSqlDateTypes
[] = {
647 // These are the possible SQL types we check for use agains the datasource we are connected
648 // to for the purpose of determining which data type to use for the BLOB column types.
650 // NOTE: The first type in this enumeration that is determined to be supported by the
651 // datasource/driver is the one that will be used.
652 SWORD PossibleSqlBlobTypes
[] = {
658 // Query the data source regarding data type information
661 // The way it was determined which SQL data types to use was by calling SQLGetInfo
662 // for all of the possible SQL data types to see which ones were supported. If
663 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
664 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
665 // types I've selected below will not always be what we want. These are just
666 // what happened to work against an Oracle 7/Intersolv combination. The following is
667 // a complete list of the results I got back against the Oracle 7 database:
669 // SQL_BIGINT SQL_NO_DATA_FOUND
670 // SQL_BINARY SQL_NO_DATA_FOUND
671 // SQL_BIT SQL_NO_DATA_FOUND
672 // SQL_CHAR type name = 'CHAR', Precision = 255
673 // SQL_DATE SQL_NO_DATA_FOUND
674 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
675 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
676 // SQL_FLOAT SQL_NO_DATA_FOUND
677 // SQL_INTEGER SQL_NO_DATA_FOUND
678 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
679 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
680 // SQL_NUMERIC SQL_NO_DATA_FOUND
681 // SQL_REAL SQL_NO_DATA_FOUND
682 // SQL_SMALLINT SQL_NO_DATA_FOUND
683 // SQL_TIME SQL_NO_DATA_FOUND
684 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
685 // SQL_VARBINARY type name = 'RAW', Precision = 255
686 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
687 // =====================================================================
688 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
690 // SQL_VARCHAR type name = 'TEXT', Precision = 255
691 // SQL_TIMESTAMP type name = 'DATETIME'
692 // SQL_DECIMAL SQL_NO_DATA_FOUND
693 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
694 // SQL_FLOAT SQL_NO_DATA_FOUND
695 // SQL_REAL type name = 'SINGLE', Precision = 7
696 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
697 // SQL_INTEGER type name = 'LONG', Precision = 10
699 // Query the data source for info about itself
700 if (!getDbInfo(failOnDataTypeUnsupported
))
703 // --------------- Varchar - (Variable length character string) ---------------
704 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
705 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
708 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
709 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
710 else if (failOnDataTypeUnsupported
)
713 // --------------- Float ---------------
714 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
715 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
718 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
719 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
720 else if (failOnDataTypeUnsupported
)
723 // --------------- Integer -------------
724 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
725 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
728 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
729 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
730 else if (failOnDataTypeUnsupported
)
732 // If no non-floating point data types are supported, we'll
733 // use the type assigned for floats to store integers as well
734 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
736 if (failOnDataTypeUnsupported
)
740 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
743 // --------------- Date/Time ---------------
744 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
745 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
748 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
749 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
750 else if (failOnDataTypeUnsupported
)
753 // --------------- BLOB ---------------
754 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
755 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
758 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
759 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
760 else if (failOnDataTypeUnsupported
)
764 } // wxDb::determineDataTypes
767 bool wxDb::open(bool failOnDataTypeUnsupported
)
770 If using Intersolv branded ODBC drivers, this is the place where you would substitute
771 your branded driver license information
773 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
774 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
777 // Mark database as open
780 // Allocate a statement handle for the database connection
781 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
782 return(DispAllErrors(henv
, hdbc
));
784 // Set Connection Options
785 if (!setConnectionOptions())
788 if (!determineDataTypes(failOnDataTypeUnsupported
))
791 #ifdef DBDEBUG_CONSOLE
792 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
793 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
794 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
795 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
796 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
800 // Completed Successfully
804 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
806 wxASSERT(inConnectStr
.Length());
813 if (!FwdOnlyCursors())
815 // Specify that the ODBC cursor library be used, if needed. This must be
816 // specified before the connection is made.
817 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
819 #ifdef DBDEBUG_CONSOLE
820 if (retcode
== SQL_SUCCESS
)
821 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
823 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
825 wxUnusedVar(retcode
);
829 // Connect to the data source
830 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
831 short outConnectBufferLen
;
833 inConnectionStr
= inConnectStr
;
835 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
836 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
837 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
839 if ((retcode
!= SQL_SUCCESS
) &&
840 (retcode
!= SQL_SUCCESS_WITH_INFO
))
841 return(DispAllErrors(henv
, hdbc
));
843 outConnectBuffer
[outConnectBufferLen
] = 0;
844 outConnectionStr
= outConnectBuffer
;
845 dbOpenedWithConnectionString
= true;
847 return open(failOnDataTypeUnsupported
);
850 /********** wxDb::Open() **********/
851 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
853 wxASSERT(Dsn
.Length());
858 inConnectionStr
= wxT("");
859 outConnectionStr
= wxT("");
863 if (!FwdOnlyCursors())
865 // Specify that the ODBC cursor library be used, if needed. This must be
866 // specified before the connection is made.
867 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
869 #ifdef DBDEBUG_CONSOLE
870 if (retcode
== SQL_SUCCESS
)
871 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
873 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
875 wxUnusedVar( retcode
);
879 // Connect to the data source
880 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
881 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
882 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
884 if ((retcode
!= SQL_SUCCESS
) &&
885 (retcode
!= SQL_SUCCESS_WITH_INFO
))
886 return(DispAllErrors(henv
, hdbc
));
888 return open(failOnDataTypeUnsupported
);
893 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
895 wxASSERT(dbConnectInf
);
897 // Use the connection string if one is present
898 if (dbConnectInf
->UseConnectionStr())
899 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
901 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
902 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
906 bool wxDb::Open(wxDb
*copyDb
)
908 dsn
= copyDb
->GetDatasourceName();
909 uid
= copyDb
->GetUsername();
910 authStr
= copyDb
->GetPassword();
911 inConnectionStr
= copyDb
->GetConnectionInStr();
912 outConnectionStr
= copyDb
->GetConnectionOutStr();
916 if (!FwdOnlyCursors())
918 // Specify that the ODBC cursor library be used, if needed. This must be
919 // specified before the connection is made.
920 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
922 #ifdef DBDEBUG_CONSOLE
923 if (retcode
== SQL_SUCCESS
)
924 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
926 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
928 wxUnusedVar( retcode
);
932 if (copyDb
->OpenedWithConnectionString())
934 // Connect to the data source
935 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
936 short outConnectBufferLen
;
938 inConnectionStr
= copyDb
->GetConnectionInStr();
940 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
941 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
942 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
944 if ((retcode
!= SQL_SUCCESS
) &&
945 (retcode
!= SQL_SUCCESS_WITH_INFO
))
946 return(DispAllErrors(henv
, hdbc
));
948 outConnectBuffer
[outConnectBufferLen
] = 0;
949 outConnectionStr
= outConnectBuffer
;
950 dbOpenedWithConnectionString
= true;
954 // Connect to the data source
955 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
956 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
957 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
960 if ((retcode
!= SQL_SUCCESS
) &&
961 (retcode
!= SQL_SUCCESS_WITH_INFO
))
962 return(DispAllErrors(henv
, hdbc
));
965 If using Intersolv branded ODBC drivers, this is the place where you would substitute
966 your branded driver license information
968 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
969 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
972 // Mark database as open
975 // Allocate a statement handle for the database connection
976 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
977 return(DispAllErrors(henv
, hdbc
));
979 // Set Connection Options
980 if (!setConnectionOptions())
983 // Instead of Querying the data source for info about itself, it can just be copied
984 // from the wxDb instance that was passed in (copyDb).
985 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
986 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
987 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
988 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
989 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
990 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
991 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
992 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
993 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
994 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
995 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
996 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
997 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
998 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
999 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
1000 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1001 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1002 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1003 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1004 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1005 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1006 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1007 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1008 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1009 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1010 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1011 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1012 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1013 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1014 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1015 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1017 // VARCHAR = Variable length character string
1018 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1019 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1020 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1021 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1022 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1025 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1026 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1027 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1028 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1029 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1032 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1033 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1034 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1035 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1036 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1039 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1040 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1041 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1042 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1043 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1046 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1047 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1048 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1049 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1050 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1052 #ifdef DBDEBUG_CONSOLE
1053 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1054 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1055 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1056 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1057 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1061 // Completed Successfully
1066 /********** wxDb::setConnectionOptions() **********/
1067 bool wxDb::setConnectionOptions(void)
1069 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1074 // I need to get the DBMS name here, because some of the connection options
1075 // are database specific and need to call the Dbms() function.
1078 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1079 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1080 return(DispAllErrors(henv
, hdbc
));
1082 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1083 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1084 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1086 // By default, MS Sql Server closes cursors on commit and rollback. The following
1087 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1088 // after a transaction. This is a driver specific option and is not part of the
1089 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1090 // The database settings don't have any effect one way or the other.
1091 if (Dbms() == dbmsMS_SQL_SERVER
)
1093 const long SQL_PRESERVE_CURSORS
= 1204L;
1094 const long SQL_PC_ON
= 1L;
1095 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1098 // Display the connection options to verify them
1099 #ifdef DBDEBUG_CONSOLE
1101 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1103 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1104 if (retcode
!= SQL_SUCCESS
)
1105 return(DispAllErrors(henv
, hdbc
));
1106 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1108 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1109 if (retcode
!= SQL_SUCCESS
)
1110 return(DispAllErrors(henv
, hdbc
));
1111 cout
<< wxT("ODBC CURSORS: ");
1114 case(SQL_CUR_USE_IF_NEEDED
):
1115 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1117 case(SQL_CUR_USE_ODBC
):
1118 cout
<< wxT("SQL_CUR_USE_ODBC");
1120 case(SQL_CUR_USE_DRIVER
):
1121 cout
<< wxT("SQL_CUR_USE_DRIVER");
1126 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1127 if (retcode
!= SQL_SUCCESS
)
1128 return(DispAllErrors(henv
, hdbc
));
1129 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1134 // Completed Successfully
1137 } // wxDb::setConnectionOptions()
1140 /********** wxDb::getDbInfo() **********/
1141 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1146 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1147 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1149 DispAllErrors(henv
, hdbc
);
1150 if (failOnDataTypeUnsupported
)
1154 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1155 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1157 DispAllErrors(henv
, hdbc
);
1158 if (failOnDataTypeUnsupported
)
1162 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1163 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1165 DispAllErrors(henv
, hdbc
);
1166 if (failOnDataTypeUnsupported
)
1171 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1172 // causing database connectivity to fail in some cases.
1173 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1174 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1176 DispAllErrors(henv
, hdbc
);
1177 if (failOnDataTypeUnsupported
)
1181 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1182 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1184 DispAllErrors(henv
, hdbc
);
1185 if (failOnDataTypeUnsupported
)
1189 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1190 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1192 DispAllErrors(henv
, hdbc
);
1193 if (failOnDataTypeUnsupported
)
1197 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1198 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1200 DispAllErrors(henv
, hdbc
);
1201 if (failOnDataTypeUnsupported
)
1205 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1206 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1208 DispAllErrors(henv
, hdbc
);
1209 if (failOnDataTypeUnsupported
)
1213 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1214 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1216 DispAllErrors(henv
, hdbc
);
1217 if (failOnDataTypeUnsupported
)
1221 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1222 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1224 DispAllErrors(henv
, hdbc
);
1225 if (failOnDataTypeUnsupported
)
1229 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1230 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1232 DispAllErrors(henv
, hdbc
);
1233 if (failOnDataTypeUnsupported
)
1237 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1238 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1240 // Not all drivers support this call - Nick Gorham(unixODBC)
1241 dbInf
.cliConfLvl
= 0;
1242 DispAllErrors(henv
, hdbc
);
1243 if (failOnDataTypeUnsupported
)
1247 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1248 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1250 DispAllErrors(henv
, hdbc
);
1251 if (failOnDataTypeUnsupported
)
1255 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1256 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1258 DispAllErrors(henv
, hdbc
);
1259 if (failOnDataTypeUnsupported
)
1263 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1264 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1266 DispAllErrors(henv
, hdbc
);
1267 if (failOnDataTypeUnsupported
)
1271 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1272 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1274 DispAllErrors(henv
, hdbc
);
1275 if (failOnDataTypeUnsupported
)
1279 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1280 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1282 DispAllErrors(henv
, hdbc
);
1283 if (failOnDataTypeUnsupported
)
1287 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1288 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1290 DispAllErrors(henv
, hdbc
);
1291 if (failOnDataTypeUnsupported
)
1295 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1296 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1298 DispAllErrors(henv
, hdbc
);
1299 if (failOnDataTypeUnsupported
)
1303 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1304 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1306 DispAllErrors(henv
, hdbc
);
1307 if (failOnDataTypeUnsupported
)
1311 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1312 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1314 DispAllErrors(henv
, hdbc
);
1315 if (failOnDataTypeUnsupported
)
1319 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1320 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1322 DispAllErrors(henv
, hdbc
);
1323 if (failOnDataTypeUnsupported
)
1327 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1328 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1330 DispAllErrors(henv
, hdbc
);
1331 if (failOnDataTypeUnsupported
)
1335 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1336 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1338 DispAllErrors(henv
, hdbc
);
1339 if (failOnDataTypeUnsupported
)
1343 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1344 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1346 DispAllErrors(henv
, hdbc
);
1347 if (failOnDataTypeUnsupported
)
1351 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1352 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1354 DispAllErrors(henv
, hdbc
);
1355 if (failOnDataTypeUnsupported
)
1359 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1360 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1362 DispAllErrors(henv
, hdbc
);
1363 if (failOnDataTypeUnsupported
)
1367 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1368 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1370 DispAllErrors(henv
, hdbc
);
1371 if (failOnDataTypeUnsupported
)
1375 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1376 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1378 DispAllErrors(henv
, hdbc
);
1379 if (failOnDataTypeUnsupported
)
1383 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1384 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1386 DispAllErrors(henv
, hdbc
);
1387 if (failOnDataTypeUnsupported
)
1391 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1392 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1394 DispAllErrors(henv
, hdbc
);
1395 if (failOnDataTypeUnsupported
)
1399 #ifdef DBDEBUG_CONSOLE
1400 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1401 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1402 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1403 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1405 cout
<< wxT("API Conf. Level: ");
1406 switch(dbInf
.apiConfLvl
)
1408 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1409 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1410 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1414 cout
<< wxT("SAG CLI Conf. Level: ");
1415 switch(dbInf
.cliConfLvl
)
1417 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1418 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1422 cout
<< wxT("SQL Conf. Level: ");
1423 switch(dbInf
.sqlConfLvl
)
1425 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1426 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1427 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1431 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1432 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1433 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1434 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1435 cout
<< wxT("Cursor COMMIT Behavior: ");
1436 switch(dbInf
.cursorCommitBehavior
)
1438 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1439 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1440 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1444 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1445 switch(dbInf
.cursorRollbackBehavior
)
1447 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1448 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1449 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1453 cout
<< wxT("Support NOT NULL clause: ");
1454 switch(dbInf
.supportNotNullClause
)
1456 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1457 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1461 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1462 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1464 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1467 cout
<< wxT("Default Transaction Isolation: ";
1468 switch(dbInf
.txnIsolation
)
1470 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1471 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1472 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1473 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1475 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1480 cout
<< wxT("Transaction Isolation Options: ");
1481 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1482 cout
<< wxT("Read Uncommitted, ");
1483 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1484 cout
<< wxT("Read Committed, ");
1485 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1486 cout
<< wxT("Repeatable Read, ");
1487 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1488 cout
<< wxT("Serializable, ");
1490 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1491 cout
<< wxT("Versioning");
1495 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1496 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1497 cout
<< wxT("Next, ");
1498 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1499 cout
<< wxT("Prev, ");
1500 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1501 cout
<< wxT("First, ");
1502 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1503 cout
<< wxT("Last, ");
1504 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1505 cout
<< wxT("Absolute, ");
1506 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1507 cout
<< wxT("Relative, ");
1509 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1510 cout
<< wxT("Resume, ");
1512 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1513 cout
<< wxT("Bookmark");
1516 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1517 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1518 cout
<< wxT("No Change, ");
1519 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1520 cout
<< wxT("Exclusive, ");
1521 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1522 cout
<< wxT("UnLock");
1525 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1526 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1527 cout
<< wxT("Position, ");
1528 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1529 cout
<< wxT("Refresh, ");
1530 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1531 cout
<< wxT("Upd, "));
1532 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1533 cout
<< wxT("Del, ");
1534 if (dbInf
.posOperations
& SQL_POS_ADD
)
1538 cout
<< wxT("Positioned Statements Supported: ");
1539 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1540 cout
<< wxT("Pos delete, ");
1541 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1542 cout
<< wxT("Pos update, ");
1543 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1544 cout
<< wxT("Select for update");
1547 cout
<< wxT("Scroll Concurrency: ");
1548 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1549 cout
<< wxT("Read Only, ");
1550 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1551 cout
<< wxT("Lock, ");
1552 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1553 cout
<< wxT("Opt. Rowver, ");
1554 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1555 cout
<< wxT("Opt. Values");
1558 cout
<< wxT("Scroll Options: ");
1559 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1560 cout
<< wxT("Fwd Only, ");
1561 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1562 cout
<< wxT("Static, ");
1563 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1564 cout
<< wxT("Keyset Driven, ");
1565 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1566 cout
<< wxT("Dynamic, ");
1567 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1568 cout
<< wxT("Mixed");
1571 cout
<< wxT("Static Sensitivity: ");
1572 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1573 cout
<< wxT("Additions, ");
1574 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1575 cout
<< wxT("Deletions, ");
1576 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1577 cout
<< wxT("Updates");
1580 cout
<< wxT("Transaction Capable?: ");
1581 switch(dbInf
.txnCapable
)
1583 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1584 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1585 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1586 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1587 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1594 // Completed Successfully
1597 } // wxDb::getDbInfo()
1600 /********** wxDb::getDataTypeInfo() **********/
1601 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1604 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1605 * the data type inf. is gathered for.
1607 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1612 // Get information about the data type specified
1613 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1614 return(DispAllErrors(henv
, hdbc
, hstmt
));
1617 retcode
= SQLFetch(hstmt
);
1618 if (retcode
!= SQL_SUCCESS
)
1620 #ifdef DBDEBUG_CONSOLE
1621 if (retcode
== SQL_NO_DATA_FOUND
)
1622 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1624 DispAllErrors(henv
, hdbc
, hstmt
);
1625 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1629 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1631 // Obtain columns from the record
1632 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1633 return(DispAllErrors(henv
, hdbc
, hstmt
));
1635 structSQLTypeInfo
.TypeName
= typeName
;
1637 // BJO 20000503: no more needed with new GetColumns...
1640 if (Dbms() == dbmsMY_SQL
)
1642 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1643 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1644 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1645 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1646 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1647 structSQLTypeInfo
.TypeName
= wxT("int");
1648 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1649 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1650 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1651 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1652 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1653 structSQLTypeInfo
.TypeName
= wxT("char");
1656 // BJO 20000427 : OpenLink driver
1657 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1658 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1660 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1661 structSQLTypeInfo
.TypeName
= wxT("real");
1665 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1666 return(DispAllErrors(henv
, hdbc
, hstmt
));
1667 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1668 return(DispAllErrors(henv
, hdbc
, hstmt
));
1669 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1670 // return(DispAllErrors(henv, hdbc, hstmt));
1672 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1673 return(DispAllErrors(henv
, hdbc
, hstmt
));
1675 if (structSQLTypeInfo
.MaximumScale
< 0)
1676 structSQLTypeInfo
.MaximumScale
= 0;
1678 // Close the statement handle which closes open cursors
1679 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1680 return(DispAllErrors(henv
, hdbc
, hstmt
));
1682 // Completed Successfully
1685 } // wxDb::getDataTypeInfo()
1688 /********** wxDb::Close() **********/
1689 void wxDb::Close(void)
1691 // Close the Sql Log file
1698 // Free statement handle
1701 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1702 DispAllErrors(henv
, hdbc
);
1705 // Disconnect from the datasource
1706 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1707 DispAllErrors(henv
, hdbc
);
1709 // Free the connection to the datasource
1710 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1711 DispAllErrors(henv
, hdbc
);
1713 // There should be zero Ctable objects still connected to this db object
1714 wxASSERT(nTables
== 0);
1718 wxList::compatibility_iterator pNode
;
1719 pNode
= TablesInUse
.GetFirst();
1723 tiu
= (wxTablesInUse
*)pNode
->GetData();
1724 if (tiu
->pDb
== this)
1726 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1727 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1728 wxLogDebug(s
.c_str(),s2
.c_str());
1730 pNode
= pNode
->GetNext();
1734 // Copy the error messages to a global variable
1736 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1737 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1739 dbmsType
= dbmsUNIDENTIFIED
;
1745 /********** wxDb::CommitTrans() **********/
1746 bool wxDb::CommitTrans(void)
1750 // Commit the transaction
1751 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1752 return(DispAllErrors(henv
, hdbc
));
1755 // Completed successfully
1758 } // wxDb::CommitTrans()
1761 /********** wxDb::RollbackTrans() **********/
1762 bool wxDb::RollbackTrans(void)
1764 // Rollback the transaction
1765 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1766 return(DispAllErrors(henv
, hdbc
));
1768 // Completed successfully
1771 } // wxDb::RollbackTrans()
1774 /********** wxDb::DispAllErrors() **********/
1775 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1777 * This function is called internally whenever an error condition prevents the user's
1778 * request from being executed. This function will query the datasource as to the
1779 * actual error(s) that just occured on the previous request of the datasource.
1781 * The function will retrieve each error condition from the datasource and
1782 * Printf the codes/text values into a string which it then logs via logError().
1783 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1784 * window and program execution will be paused until the user presses a key.
1786 * This function always returns a false, so that functions which call this function
1787 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1788 * of the users request, so that the calling code can then process the error msg log
1791 wxString odbcErrMsg
;
1793 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1795 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1796 logError(odbcErrMsg
, sqlState
);
1799 #ifdef DBDEBUG_CONSOLE
1800 // When run in console mode, use standard out to display errors.
1801 cout
<< odbcErrMsg
.c_str() << endl
;
1802 cout
<< wxT("Press any key to continue...") << endl
;
1807 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1812 return false; // This function always returns false.
1814 } // wxDb::DispAllErrors()
1817 /********** wxDb::GetNextError() **********/
1818 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1820 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1825 } // wxDb::GetNextError()
1828 /********** wxDb::DispNextError() **********/
1829 void wxDb::DispNextError(void)
1831 wxString odbcErrMsg
;
1833 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1834 logError(odbcErrMsg
, sqlState
);
1839 #ifdef DBDEBUG_CONSOLE
1840 // When run in console mode, use standard out to display errors.
1841 cout
<< odbcErrMsg
.c_str() << endl
;
1842 cout
<< wxT("Press any key to continue...") << endl
;
1847 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1848 #endif // __WXDEBUG__
1850 } // wxDb::DispNextError()
1853 /********** wxDb::logError() **********/
1854 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1856 wxASSERT(errMsg
.Length());
1858 static int pLast
= -1;
1861 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1864 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1865 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1869 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1870 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1872 if (SQLState
.Length())
1873 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1874 DB_STATUS
= dbStatus
;
1876 // Add the errmsg to the sql log
1877 WriteSqlLog(errMsg
);
1879 } // wxDb::logError()
1882 /**********wxDb::TranslateSqlState() **********/
1883 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1885 if (!wxStrcmp(SQLState
, wxT("01000")))
1886 return(DB_ERR_GENERAL_WARNING
);
1887 if (!wxStrcmp(SQLState
, wxT("01002")))
1888 return(DB_ERR_DISCONNECT_ERROR
);
1889 if (!wxStrcmp(SQLState
, wxT("01004")))
1890 return(DB_ERR_DATA_TRUNCATED
);
1891 if (!wxStrcmp(SQLState
, wxT("01006")))
1892 return(DB_ERR_PRIV_NOT_REVOKED
);
1893 if (!wxStrcmp(SQLState
, wxT("01S00")))
1894 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1895 if (!wxStrcmp(SQLState
, wxT("01S01")))
1896 return(DB_ERR_ERROR_IN_ROW
);
1897 if (!wxStrcmp(SQLState
, wxT("01S02")))
1898 return(DB_ERR_OPTION_VALUE_CHANGED
);
1899 if (!wxStrcmp(SQLState
, wxT("01S03")))
1900 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1901 if (!wxStrcmp(SQLState
, wxT("01S04")))
1902 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1903 if (!wxStrcmp(SQLState
, wxT("07001")))
1904 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1905 if (!wxStrcmp(SQLState
, wxT("07006")))
1906 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1907 if (!wxStrcmp(SQLState
, wxT("08001")))
1908 return(DB_ERR_UNABLE_TO_CONNECT
);
1909 if (!wxStrcmp(SQLState
, wxT("08002")))
1910 return(DB_ERR_CONNECTION_IN_USE
);
1911 if (!wxStrcmp(SQLState
, wxT("08003")))
1912 return(DB_ERR_CONNECTION_NOT_OPEN
);
1913 if (!wxStrcmp(SQLState
, wxT("08004")))
1914 return(DB_ERR_REJECTED_CONNECTION
);
1915 if (!wxStrcmp(SQLState
, wxT("08007")))
1916 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1917 if (!wxStrcmp(SQLState
, wxT("08S01")))
1918 return(DB_ERR_COMM_LINK_FAILURE
);
1919 if (!wxStrcmp(SQLState
, wxT("21S01")))
1920 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1921 if (!wxStrcmp(SQLState
, wxT("21S02")))
1922 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1923 if (!wxStrcmp(SQLState
, wxT("22001")))
1924 return(DB_ERR_STRING_RIGHT_TRUNC
);
1925 if (!wxStrcmp(SQLState
, wxT("22003")))
1926 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1927 if (!wxStrcmp(SQLState
, wxT("22005")))
1928 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1929 if (!wxStrcmp(SQLState
, wxT("22008")))
1930 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1931 if (!wxStrcmp(SQLState
, wxT("22012")))
1932 return(DB_ERR_DIVIDE_BY_ZERO
);
1933 if (!wxStrcmp(SQLState
, wxT("22026")))
1934 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1935 if (!wxStrcmp(SQLState
, wxT("23000")))
1936 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1937 if (!wxStrcmp(SQLState
, wxT("24000")))
1938 return(DB_ERR_INVALID_CURSOR_STATE
);
1939 if (!wxStrcmp(SQLState
, wxT("25000")))
1940 return(DB_ERR_INVALID_TRANS_STATE
);
1941 if (!wxStrcmp(SQLState
, wxT("28000")))
1942 return(DB_ERR_INVALID_AUTH_SPEC
);
1943 if (!wxStrcmp(SQLState
, wxT("34000")))
1944 return(DB_ERR_INVALID_CURSOR_NAME
);
1945 if (!wxStrcmp(SQLState
, wxT("37000")))
1946 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1947 if (!wxStrcmp(SQLState
, wxT("3C000")))
1948 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1949 if (!wxStrcmp(SQLState
, wxT("40001")))
1950 return(DB_ERR_SERIALIZATION_FAILURE
);
1951 if (!wxStrcmp(SQLState
, wxT("42000")))
1952 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1953 if (!wxStrcmp(SQLState
, wxT("70100")))
1954 return(DB_ERR_OPERATION_ABORTED
);
1955 if (!wxStrcmp(SQLState
, wxT("IM001")))
1956 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1957 if (!wxStrcmp(SQLState
, wxT("IM002")))
1958 return(DB_ERR_NO_DATA_SOURCE
);
1959 if (!wxStrcmp(SQLState
, wxT("IM003")))
1960 return(DB_ERR_DRIVER_LOAD_ERROR
);
1961 if (!wxStrcmp(SQLState
, wxT("IM004")))
1962 return(DB_ERR_SQLALLOCENV_FAILED
);
1963 if (!wxStrcmp(SQLState
, wxT("IM005")))
1964 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1965 if (!wxStrcmp(SQLState
, wxT("IM006")))
1966 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1967 if (!wxStrcmp(SQLState
, wxT("IM007")))
1968 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1969 if (!wxStrcmp(SQLState
, wxT("IM008")))
1970 return(DB_ERR_DIALOG_FAILED
);
1971 if (!wxStrcmp(SQLState
, wxT("IM009")))
1972 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1973 if (!wxStrcmp(SQLState
, wxT("IM010")))
1974 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1975 if (!wxStrcmp(SQLState
, wxT("IM011")))
1976 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1977 if (!wxStrcmp(SQLState
, wxT("IM012")))
1978 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1979 if (!wxStrcmp(SQLState
, wxT("IM013")))
1980 return(DB_ERR_TRACE_FILE_ERROR
);
1981 if (!wxStrcmp(SQLState
, wxT("S0001")))
1982 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1983 if (!wxStrcmp(SQLState
, wxT("S0002")))
1984 return(DB_ERR_TABLE_NOT_FOUND
);
1985 if (!wxStrcmp(SQLState
, wxT("S0011")))
1986 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1987 if (!wxStrcmp(SQLState
, wxT("S0012")))
1988 return(DB_ERR_INDEX_NOT_FOUND
);
1989 if (!wxStrcmp(SQLState
, wxT("S0021")))
1990 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1991 if (!wxStrcmp(SQLState
, wxT("S0022")))
1992 return(DB_ERR_COLUMN_NOT_FOUND
);
1993 if (!wxStrcmp(SQLState
, wxT("S0023")))
1994 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1995 if (!wxStrcmp(SQLState
, wxT("S1000")))
1996 return(DB_ERR_GENERAL_ERROR
);
1997 if (!wxStrcmp(SQLState
, wxT("S1001")))
1998 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1999 if (!wxStrcmp(SQLState
, wxT("S1002")))
2000 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2001 if (!wxStrcmp(SQLState
, wxT("S1003")))
2002 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2003 if (!wxStrcmp(SQLState
, wxT("S1004")))
2004 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2005 if (!wxStrcmp(SQLState
, wxT("S1008")))
2006 return(DB_ERR_OPERATION_CANCELLED
);
2007 if (!wxStrcmp(SQLState
, wxT("S1009")))
2008 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2009 if (!wxStrcmp(SQLState
, wxT("S1010")))
2010 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2011 if (!wxStrcmp(SQLState
, wxT("S1011")))
2012 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2013 if (!wxStrcmp(SQLState
, wxT("S1012")))
2014 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2015 if (!wxStrcmp(SQLState
, wxT("S1015")))
2016 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2017 if (!wxStrcmp(SQLState
, wxT("S1090")))
2018 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2019 if (!wxStrcmp(SQLState
, wxT("S1091")))
2020 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2021 if (!wxStrcmp(SQLState
, wxT("S1092")))
2022 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2023 if (!wxStrcmp(SQLState
, wxT("S1093")))
2024 return(DB_ERR_INVALID_PARAM_NO
);
2025 if (!wxStrcmp(SQLState
, wxT("S1094")))
2026 return(DB_ERR_INVALID_SCALE_VALUE
);
2027 if (!wxStrcmp(SQLState
, wxT("S1095")))
2028 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2029 if (!wxStrcmp(SQLState
, wxT("S1096")))
2030 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2031 if (!wxStrcmp(SQLState
, wxT("S1097")))
2032 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2033 if (!wxStrcmp(SQLState
, wxT("S1098")))
2034 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2035 if (!wxStrcmp(SQLState
, wxT("S1099")))
2036 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2037 if (!wxStrcmp(SQLState
, wxT("S1100")))
2038 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2039 if (!wxStrcmp(SQLState
, wxT("S1101")))
2040 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2041 if (!wxStrcmp(SQLState
, wxT("S1103")))
2042 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2043 if (!wxStrcmp(SQLState
, wxT("S1104")))
2044 return(DB_ERR_INVALID_PRECISION_VALUE
);
2045 if (!wxStrcmp(SQLState
, wxT("S1105")))
2046 return(DB_ERR_INVALID_PARAM_TYPE
);
2047 if (!wxStrcmp(SQLState
, wxT("S1106")))
2048 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2049 if (!wxStrcmp(SQLState
, wxT("S1107")))
2050 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2051 if (!wxStrcmp(SQLState
, wxT("S1108")))
2052 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2053 if (!wxStrcmp(SQLState
, wxT("S1109")))
2054 return(DB_ERR_INVALID_CURSOR_POSITION
);
2055 if (!wxStrcmp(SQLState
, wxT("S1110")))
2056 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2057 if (!wxStrcmp(SQLState
, wxT("S1111")))
2058 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2059 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2060 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2061 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2062 return(DB_ERR_TIMEOUT_EXPIRED
);
2067 } // wxDb::TranslateSqlState()
2070 /********** wxDb::Grant() **********/
2071 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2075 // Build the grant statement
2076 sqlStmt
= wxT("GRANT ");
2077 if (privileges
== DB_GRANT_ALL
)
2078 sqlStmt
+= wxT("ALL");
2082 if (privileges
& DB_GRANT_SELECT
)
2084 sqlStmt
+= wxT("SELECT");
2087 if (privileges
& DB_GRANT_INSERT
)
2090 sqlStmt
+= wxT(", ");
2091 sqlStmt
+= wxT("INSERT");
2093 if (privileges
& DB_GRANT_UPDATE
)
2096 sqlStmt
+= wxT(", ");
2097 sqlStmt
+= wxT("UPDATE");
2099 if (privileges
& DB_GRANT_DELETE
)
2102 sqlStmt
+= wxT(", ");
2103 sqlStmt
+= wxT("DELETE");
2107 sqlStmt
+= wxT(" ON ");
2108 sqlStmt
+= SQLTableName(tableName
);
2109 sqlStmt
+= wxT(" TO ");
2110 sqlStmt
+= userList
;
2112 #ifdef DBDEBUG_CONSOLE
2113 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2116 WriteSqlLog(sqlStmt
);
2118 return(ExecSql(sqlStmt
));
2123 /********** wxDb::CreateView() **********/
2124 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2125 const wxString
&pSqlStmt
, bool attemptDrop
)
2129 // Drop the view first
2130 if (attemptDrop
&& !DropView(viewName
))
2133 // Build the create view statement
2134 sqlStmt
= wxT("CREATE VIEW ");
2135 sqlStmt
+= viewName
;
2137 if (colList
.Length())
2139 sqlStmt
+= wxT(" (");
2141 sqlStmt
+= wxT(")");
2144 sqlStmt
+= wxT(" AS ");
2145 sqlStmt
+= pSqlStmt
;
2147 WriteSqlLog(sqlStmt
);
2149 #ifdef DBDEBUG_CONSOLE
2150 cout
<< sqlStmt
.c_str() << endl
;
2153 return(ExecSql(sqlStmt
));
2155 } // wxDb::CreateView()
2158 /********** wxDb::DropView() **********/
2159 bool wxDb::DropView(const wxString
&viewName
)
2162 * NOTE: This function returns true if the View does not exist, but
2163 * only for identified databases. Code will need to be added
2164 * below for any other databases when those databases are defined
2165 * to handle this situation consistently
2169 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2171 WriteSqlLog(sqlStmt
);
2173 #ifdef DBDEBUG_CONSOLE
2174 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2177 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2179 // Check for "Base table not found" error and ignore
2180 GetNextError(henv
, hdbc
, hstmt
);
2181 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2183 // Check for product specific error codes
2184 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2187 DispAllErrors(henv
, hdbc
, hstmt
);
2194 // Commit the transaction
2200 } // wxDb::DropView()
2203 /********** wxDb::ExecSql() **********/
2204 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2208 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2210 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2211 if (retcode
== SQL_SUCCESS
||
2212 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2218 DispAllErrors(henv
, hdbc
, hstmt
);
2222 } // wxDb::ExecSql()
2225 /********** wxDb::ExecSql() with column info **********/
2226 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2228 //execute the statement first
2229 if (!ExecSql(pSqlStmt
))
2233 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2235 DispAllErrors(henv
, hdbc
, hstmt
);
2244 // Get column information
2246 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2249 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2251 // Fill in column information (name, datatype)
2252 for (colNum
= 0; colNum
< noCols
; colNum
++)
2254 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2256 &Sword
, &Sdword
) != SQL_SUCCESS
)
2258 DispAllErrors(henv
, hdbc
, hstmt
);
2263 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2264 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2266 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2267 NULL
, 0, &Sword
, &Sdword
) != SQL_SUCCESS
)
2269 DispAllErrors(henv
, hdbc
, hstmt
);
2277 #if defined(SQL_WCHAR)
2280 #if defined(SQL_WVARCHAR)
2286 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2292 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2299 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2303 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2306 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2311 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sdword
);
2312 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2319 } // wxDb::ExecSql()
2321 /********** wxDb::GetNext() **********/
2322 bool wxDb::GetNext(void)
2324 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2328 DispAllErrors(henv
, hdbc
, hstmt
);
2332 } // wxDb::GetNext()
2335 /********** wxDb::GetData() **********/
2336 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2339 wxASSERT(cbReturned
);
2341 long bufferSize
= maxLen
;
2343 if (cType
== SQL_C_WXCHAR
)
2344 bufferSize
= maxLen
* sizeof(wxChar
);
2346 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2350 DispAllErrors(henv
, hdbc
, hstmt
);
2354 } // wxDb::GetData()
2357 /********** wxDb::GetKeyFields() **********/
2358 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2360 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2361 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2363 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2364 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2370 * -----------------------------------------------------------------------
2371 * -- 19991224 : mj10777 : Create ------
2372 * -- : Three things are done and stored here : ------
2373 * -- : 1) which Column(s) is/are Primary Key(s) ------
2374 * -- : 2) which tables use this Key as a Foreign Key ------
2375 * -- : 3) which columns are Foreign Key and the name ------
2376 * -- : of the Table where the Key is the Primary Key -----
2377 * -- : Called from GetColumns(const wxString &tableName, ------
2378 * -- int *numCols,const wxChar *userID ) ------
2379 * -----------------------------------------------------------------------
2382 /*---------------------------------------------------------------------*/
2383 /* Get the names of the columns in the primary key. */
2384 /*---------------------------------------------------------------------*/
2385 retcode
= SQLPrimaryKeys(hstmt
,
2386 NULL
, 0, /* Catalog name */
2387 NULL
, 0, /* Schema name */
2388 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2390 /*---------------------------------------------------------------------*/
2391 /* Fetch and display the result set. This will be a list of the */
2392 /* columns in the primary key of the tableName table. */
2393 /*---------------------------------------------------------------------*/
2394 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2396 retcode
= SQLFetch(hstmt
);
2397 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2399 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2400 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2402 for (i
=0;i
<noCols
;i
++) // Find the Column name
2403 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2404 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2407 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2409 /*---------------------------------------------------------------------*/
2410 /* Get all the foreign keys that refer to tableName primary key. */
2411 /*---------------------------------------------------------------------*/
2412 retcode
= SQLForeignKeys(hstmt
,
2413 NULL
, 0, /* Primary catalog */
2414 NULL
, 0, /* Primary schema */
2415 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2416 NULL
, 0, /* Foreign catalog */
2417 NULL
, 0, /* Foreign schema */
2418 NULL
, 0); /* Foreign table */
2420 /*---------------------------------------------------------------------*/
2421 /* Fetch and display the result set. This will be all of the foreign */
2422 /* keys in other tables that refer to the tableName primary key. */
2423 /*---------------------------------------------------------------------*/
2426 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2428 retcode
= SQLFetch(hstmt
);
2429 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2431 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2432 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2433 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2434 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2435 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2436 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2440 tempStr
.Trim(); // Get rid of any unneeded blanks
2441 if (!tempStr
.empty())
2443 for (i
=0; i
<noCols
; i
++)
2444 { // Find the Column name
2445 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2447 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
2448 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2453 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2455 /*---------------------------------------------------------------------*/
2456 /* Get all the foreign keys in the tablename table. */
2457 /*---------------------------------------------------------------------*/
2458 retcode
= SQLForeignKeys(hstmt
,
2459 NULL
, 0, /* Primary catalog */
2460 NULL
, 0, /* Primary schema */
2461 NULL
, 0, /* Primary table */
2462 NULL
, 0, /* Foreign catalog */
2463 NULL
, 0, /* Foreign schema */
2464 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2466 /*---------------------------------------------------------------------*/
2467 /* Fetch and display the result set. This will be all of the */
2468 /* primary keys in other tables that are referred to by foreign */
2469 /* keys in the tableName table. */
2470 /*---------------------------------------------------------------------*/
2471 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2473 retcode
= SQLFetch(hstmt
);
2474 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2476 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2477 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2478 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2480 for (i
=0; i
<noCols
; i
++) // Find the Column name
2482 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2484 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2485 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2486 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2491 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2495 } // wxDb::GetKeyFields()
2499 /********** wxDb::GetColumns() **********/
2500 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2502 * 1) The last array element of the tableName[] argument must be zero (null).
2503 * This is how the end of the array is detected.
2504 * 2) This function returns an array of wxDbColInf structures. If no columns
2505 * were found, or an error occured, this pointer will be zero (null). THE
2506 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2507 * IS FINISHED WITH IT. i.e.
2509 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2512 * // Use the column inf
2514 * // Destroy the memory
2518 * userID is evaluated in the following manner:
2519 * userID == NULL ... UserID is ignored
2520 * userID == "" ... UserID set equal to 'this->uid'
2521 * userID != "" ... UserID set equal to 'userID'
2523 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2524 * by this function. This function should use its own wxDb instance
2525 * to avoid undesired unbinding of columns.
2530 wxDbColInf
*colInf
= 0;
2538 convertUserID(userID
,UserID
);
2540 // Pass 1 - Determine how many columns there are.
2541 // Pass 2 - Allocate the wxDbColInf array and fill in
2542 // the array with the column information.
2544 for (pass
= 1; pass
<= 2; pass
++)
2548 if (noCols
== 0) // Probably a bogus table name(s)
2550 // Allocate n wxDbColInf objects to hold the column information
2551 colInf
= new wxDbColInf
[noCols
+1];
2554 // Mark the end of the array
2555 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2556 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2557 colInf
[noCols
].sqlDataType
= 0;
2559 // Loop through each table name
2561 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2563 TableName
= tableName
[tbl
];
2564 // Oracle and Interbase table names are uppercase only, so force
2565 // the name to uppercase just in case programmer forgot to do this
2566 if ((Dbms() == dbmsORACLE
) ||
2567 (Dbms() == dbmsFIREBIRD
) ||
2568 (Dbms() == dbmsINTERBASE
))
2569 TableName
= TableName
.Upper();
2571 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2573 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2574 // use the call below that leaves out the user name
2575 if (!UserID
.empty() &&
2576 Dbms() != dbmsMY_SQL
&&
2577 Dbms() != dbmsACCESS
&&
2578 Dbms() != dbmsMS_SQL_SERVER
)
2580 retcode
= SQLColumns(hstmt
,
2581 NULL
, 0, // All qualifiers
2582 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2583 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2584 NULL
, 0); // All columns
2588 retcode
= SQLColumns(hstmt
,
2589 NULL
, 0, // All qualifiers
2591 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2592 NULL
, 0); // All columns
2594 if (retcode
!= SQL_SUCCESS
)
2595 { // Error occured, abort
2596 DispAllErrors(henv
, hdbc
, hstmt
);
2599 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2603 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2605 if (pass
== 1) // First pass, just add up the number of columns
2607 else // Pass 2; Fill in the array of structures
2609 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2611 // NOTE: Only the ODBC 1.x fields are retrieved
2612 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2613 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2614 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2615 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2616 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2617 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2618 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2619 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2620 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2621 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2622 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2623 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2625 // Determine the wxDb data type that is used to represent the native data type of this data source
2626 colInf
[colNo
].dbDataType
= 0;
2627 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2630 // IODBC does not return a correct columnLength, so we set
2631 // columnLength = bufferSize if no column length was returned
2632 // IODBC returns the columnLength in bufferSize. (bug)
2633 if (colInf
[colNo
].columnLength
< 1)
2635 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2638 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2640 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2641 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2642 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2643 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2644 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2646 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2647 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2652 if (retcode
!= SQL_NO_DATA_FOUND
)
2653 { // Error occured, abort
2654 DispAllErrors(henv
, hdbc
, hstmt
);
2657 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2663 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2666 } // wxDb::GetColumns()
2669 /********** wxDb::GetColumns() **********/
2671 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2673 // Same as the above GetColumns() function except this one gets columns
2674 // only for a single table, and if 'numCols' is not NULL, the number of
2675 // columns stored in the returned wxDbColInf is set in '*numCols'
2677 // userID is evaluated in the following manner:
2678 // userID == NULL ... UserID is ignored
2679 // userID == "" ... UserID set equal to 'this->uid'
2680 // userID != "" ... UserID set equal to 'userID'
2682 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2683 // by this function. This function should use its own wxDb instance
2684 // to avoid undesired unbinding of columns.
2689 wxDbColInf
*colInf
= 0;
2697 convertUserID(userID
,UserID
);
2699 // Pass 1 - Determine how many columns there are.
2700 // Pass 2 - Allocate the wxDbColInf array and fill in
2701 // the array with the column information.
2703 for (pass
= 1; pass
<= 2; pass
++)
2707 if (noCols
== 0) // Probably a bogus table name(s)
2709 // Allocate n wxDbColInf objects to hold the column information
2710 colInf
= new wxDbColInf
[noCols
+1];
2713 // Mark the end of the array
2714 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2715 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2716 colInf
[noCols
].sqlDataType
= 0;
2719 TableName
= tableName
;
2720 // Oracle and Interbase table names are uppercase only, so force
2721 // the name to uppercase just in case programmer forgot to do this
2722 if ((Dbms() == dbmsORACLE
) ||
2723 (Dbms() == dbmsFIREBIRD
) ||
2724 (Dbms() == dbmsINTERBASE
))
2725 TableName
= TableName
.Upper();
2727 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2729 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2730 // use the call below that leaves out the user name
2731 if (!UserID
.empty() &&
2732 Dbms() != dbmsMY_SQL
&&
2733 Dbms() != dbmsACCESS
&&
2734 Dbms() != dbmsMS_SQL_SERVER
)
2736 retcode
= SQLColumns(hstmt
,
2737 NULL
, 0, // All qualifiers
2738 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2739 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2740 NULL
, 0); // All columns
2744 retcode
= SQLColumns(hstmt
,
2745 NULL
, 0, // All qualifiers
2747 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2748 NULL
, 0); // All columns
2750 if (retcode
!= SQL_SUCCESS
)
2751 { // Error occured, abort
2752 DispAllErrors(henv
, hdbc
, hstmt
);
2755 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2761 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2763 if (pass
== 1) // First pass, just add up the number of columns
2765 else // Pass 2; Fill in the array of structures
2767 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2769 // NOTE: Only the ODBC 1.x fields are retrieved
2770 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2771 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2772 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2773 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2774 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2775 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2776 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2777 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2778 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2779 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2780 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2781 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2782 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2783 // Start Values for Primary/Foriegn Key (=No)
2784 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2785 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2786 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2787 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2789 // BJO 20000428 : Virtuoso returns type names with upper cases!
2790 if (Dbms() == dbmsVIRTUOSO
)
2792 wxString s
= colInf
[colNo
].typeName
;
2794 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2797 // Determine the wxDb data type that is used to represent the native data type of this data source
2798 colInf
[colNo
].dbDataType
= 0;
2799 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2802 // IODBC does not return a correct columnLength, so we set
2803 // columnLength = bufferSize if no column length was returned
2804 // IODBC returns the columnLength in bufferSize. (bug)
2805 if (colInf
[colNo
].columnLength
< 1)
2807 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2811 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2813 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2814 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2815 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2816 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2817 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2818 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2819 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2820 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2826 if (retcode
!= SQL_NO_DATA_FOUND
)
2827 { // Error occured, abort
2828 DispAllErrors(henv
, hdbc
, hstmt
);
2831 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2838 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2840 // Store Primary and Foriegn Keys
2841 GetKeyFields(tableName
,colInf
,noCols
);
2847 } // wxDb::GetColumns()
2850 #else // New GetColumns
2855 These are tentative new GetColumns members which should be more database
2856 independent and which always returns the columns in the order they were
2859 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2860 wxChar* userID)) calls the second implementation for each separate table
2861 before merging the results. This makes the code easier to maintain as
2862 only one member (the second) makes the real work
2863 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2864 wxChar *userID) is a little bit improved
2865 - It doesn't anymore rely on the type-name to find out which database-type
2867 - It ends by sorting the columns, so that they are returned in the same
2868 order they were created
2878 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2881 // The last array element of the tableName[] argument must be zero (null).
2882 // This is how the end of the array is detected.
2886 // How many tables ?
2888 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2890 // Create a table to maintain the columns for each separate table
2891 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2894 for (i
= 0 ; i
< tbl
; i
++)
2897 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2898 if (TableColumns
[i
].colInf
== NULL
)
2900 noCols
+= TableColumns
[i
].noCols
;
2903 // Now merge all the separate table infos
2904 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2906 // Mark the end of the array
2907 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2908 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2909 colInf
[noCols
].sqlDataType
= 0;
2914 for (i
= 0 ; i
< tbl
; i
++)
2916 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2918 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2922 delete [] TableColumns
;
2925 } // wxDb::GetColumns() -- NEW
2928 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2930 // Same as the above GetColumns() function except this one gets columns
2931 // only for a single table, and if 'numCols' is not NULL, the number of
2932 // columns stored in the returned wxDbColInf is set in '*numCols'
2934 // userID is evaluated in the following manner:
2935 // userID == NULL ... UserID is ignored
2936 // userID == "" ... UserID set equal to 'this->uid'
2937 // userID != "" ... UserID set equal to 'userID'
2939 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2940 // by this function. This function should use its own wxDb instance
2941 // to avoid undesired unbinding of columns.
2945 wxDbColInf
*colInf
= 0;
2953 convertUserID(userID
,UserID
);
2955 // Pass 1 - Determine how many columns there are.
2956 // Pass 2 - Allocate the wxDbColInf array and fill in
2957 // the array with the column information.
2959 for (pass
= 1; pass
<= 2; pass
++)
2963 if (noCols
== 0) // Probably a bogus table name(s)
2965 // Allocate n wxDbColInf objects to hold the column information
2966 colInf
= new wxDbColInf
[noCols
+1];
2969 // Mark the end of the array
2970 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2971 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2972 colInf
[noCols
].sqlDataType
= 0;
2975 TableName
= tableName
;
2976 // Oracle and Interbase table names are uppercase only, so force
2977 // the name to uppercase just in case programmer forgot to do this
2978 if ((Dbms() == dbmsORACLE
) ||
2979 (Dbms() == dbmsFIREBIRD
) ||
2980 (Dbms() == dbmsINTERBASE
))
2981 TableName
= TableName
.Upper();
2983 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2985 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2986 // use the call below that leaves out the user name
2987 if (!UserID
.empty() &&
2988 Dbms() != dbmsMY_SQL
&&
2989 Dbms() != dbmsACCESS
&&
2990 Dbms() != dbmsMS_SQL_SERVER
)
2992 retcode
= SQLColumns(hstmt
,
2993 NULL
, 0, // All qualifiers
2994 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2995 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2996 NULL
, 0); // All columns
3000 retcode
= SQLColumns(hstmt
,
3001 NULL
, 0, // All qualifiers
3003 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3004 NULL
, 0); // All columns
3006 if (retcode
!= SQL_SUCCESS
)
3007 { // Error occured, abort
3008 DispAllErrors(henv
, hdbc
, hstmt
);
3011 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3017 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3019 if (pass
== 1) // First pass, just add up the number of columns
3021 else // Pass 2; Fill in the array of structures
3023 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3025 // NOTE: Only the ODBC 1.x fields are retrieved
3026 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3027 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3028 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3029 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3030 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3031 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3032 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3033 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3034 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3035 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3036 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3037 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3038 // Start Values for Primary/Foriegn Key (=No)
3039 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3040 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3041 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3042 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3045 // IODBC does not return a correct columnLength, so we set
3046 // columnLength = bufferSize if no column length was returned
3047 // IODBC returns the columnLength in bufferSize. (bug)
3048 if (colInf
[colNo
].columnLength
< 1)
3050 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3054 // Determine the wxDb data type that is used to represent the native data type of this data source
3055 colInf
[colNo
].dbDataType
= 0;
3056 // Get the intern datatype
3057 switch (colInf
[colNo
].sqlDataType
)
3060 #if defined(SQL_WCHAR)
3063 #if defined(SQL_WVARCHAR)
3069 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3075 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3082 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3086 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3089 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3094 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3095 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3102 if (retcode
!= SQL_NO_DATA_FOUND
)
3103 { // Error occured, abort
3104 DispAllErrors(henv
, hdbc
, hstmt
);
3107 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3114 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3116 // Store Primary and Foreign Keys
3117 GetKeyFields(tableName
,colInf
,noCols
);
3119 ///////////////////////////////////////////////////////////////////////////
3120 // Now sort the the columns in order to make them appear in the right order
3121 ///////////////////////////////////////////////////////////////////////////
3123 // Build a generic SELECT statement which returns 0 rows
3126 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3129 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3131 DispAllErrors(henv
, hdbc
, hstmt
);
3135 // Get the number of result columns
3136 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3138 DispAllErrors(henv
, hdbc
, hstmt
);
3142 if (noCols
== 0) // Probably a bogus table name
3151 for (colNum
= 0; colNum
< noCols
; colNum
++)
3153 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3155 &Sword
, &Sdword
) != SQL_SUCCESS
)
3157 DispAllErrors(henv
, hdbc
, hstmt
);
3161 wxString Name1
= name
;
3162 Name1
= Name1
.Upper();
3164 // Where is this name in the array ?
3165 for (i
= colNum
; i
< noCols
; i
++)
3167 wxString Name2
= colInf
[i
].colName
;
3168 Name2
= Name2
.Upper();
3171 if (colNum
!= i
) // swap to sort
3173 wxDbColInf tmpColInf
= colInf
[colNum
];
3174 colInf
[colNum
] = colInf
[i
];
3175 colInf
[i
] = tmpColInf
;
3181 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3183 ///////////////////////////////////////////////////////////////////////////
3185 ///////////////////////////////////////////////////////////////////////////
3191 } // wxDb::GetColumns()
3194 #endif // #else OLD_GETCOLUMNS
3197 /********** wxDb::GetColumnCount() **********/
3198 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3200 * Returns a count of how many columns are in a table.
3201 * If an error occurs in computing the number of columns
3202 * this function will return a -1 for the count
3204 * userID is evaluated in the following manner:
3205 * userID == NULL ... UserID is ignored
3206 * userID == "" ... UserID set equal to 'this->uid'
3207 * userID != "" ... UserID set equal to 'userID'
3209 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3210 * by this function. This function should use its own wxDb instance
3211 * to avoid undesired unbinding of columns.
3221 convertUserID(userID
,UserID
);
3223 TableName
= tableName
;
3224 // Oracle and Interbase table names are uppercase only, so force
3225 // the name to uppercase just in case programmer forgot to do this
3226 if ((Dbms() == dbmsORACLE
) ||
3227 (Dbms() == dbmsFIREBIRD
) ||
3228 (Dbms() == dbmsINTERBASE
))
3229 TableName
= TableName
.Upper();
3231 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3233 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3234 // use the call below that leaves out the user name
3235 if (!UserID
.empty() &&
3236 Dbms() != dbmsMY_SQL
&&
3237 Dbms() != dbmsACCESS
&&
3238 Dbms() != dbmsMS_SQL_SERVER
)
3240 retcode
= SQLColumns(hstmt
,
3241 NULL
, 0, // All qualifiers
3242 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3243 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3244 NULL
, 0); // All columns
3248 retcode
= SQLColumns(hstmt
,
3249 NULL
, 0, // All qualifiers
3251 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3252 NULL
, 0); // All columns
3254 if (retcode
!= SQL_SUCCESS
)
3255 { // Error occured, abort
3256 DispAllErrors(henv
, hdbc
, hstmt
);
3257 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3261 // Count the columns
3262 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3265 if (retcode
!= SQL_NO_DATA_FOUND
)
3266 { // Error occured, abort
3267 DispAllErrors(henv
, hdbc
, hstmt
);
3268 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3272 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3275 } // wxDb::GetColumnCount()
3278 /********** wxDb::GetCatalog() *******/
3279 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3281 * ---------------------------------------------------------------------
3282 * -- 19991203 : mj10777 : Create ------
3283 * -- : Creates a wxDbInf with Tables / Cols Array ------
3284 * -- : uses SQLTables and fills pTableInf; ------
3285 * -- : pColInf is set to NULL and numCols to 0; ------
3286 * -- : returns pDbInf (wxDbInf) ------
3287 * -- - if unsuccesfull (pDbInf == NULL) ------
3288 * -- : pColInf can be filled with GetColumns(..); ------
3289 * -- : numCols can be filled with GetColumnCount(..); ------
3290 * ---------------------------------------------------------------------
3292 * userID is evaluated in the following manner:
3293 * userID == NULL ... UserID is ignored
3294 * userID == "" ... UserID set equal to 'this->uid'
3295 * userID != "" ... UserID set equal to 'userID'
3297 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3298 * by this function. This function should use its own wxDb instance
3299 * to avoid undesired unbinding of columns.
3302 int noTab
= 0; // Counter while filling table entries
3306 wxString tblNameSave
;
3309 convertUserID(userID
,UserID
);
3311 //-------------------------------------------------------------
3312 // Create the Database Array of catalog entries
3314 wxDbInf
*pDbInf
= new wxDbInf
;
3316 //-------------------------------------------------------------
3317 // Table Information
3318 // Pass 1 - Determine how many Tables there are.
3319 // Pass 2 - Create the Table array and fill it
3320 // - Create the Cols array = NULL
3321 //-------------------------------------------------------------
3323 for (pass
= 1; pass
<= 2; pass
++)
3325 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3326 tblNameSave
.Empty();
3328 if (!UserID
.empty() &&
3329 Dbms() != dbmsMY_SQL
&&
3330 Dbms() != dbmsACCESS
&&
3331 Dbms() != dbmsMS_SQL_SERVER
)
3333 retcode
= SQLTables(hstmt
,
3334 NULL
, 0, // All qualifiers
3335 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3336 NULL
, 0, // All tables
3337 NULL
, 0); // All columns
3341 retcode
= SQLTables(hstmt
,
3342 NULL
, 0, // All qualifiers
3343 NULL
, 0, // User specified
3344 NULL
, 0, // All tables
3345 NULL
, 0); // All columns
3348 if (retcode
!= SQL_SUCCESS
)
3350 DispAllErrors(henv
, hdbc
, hstmt
);
3352 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3356 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3358 if (pass
== 1) // First pass, just count the Tables
3360 if (pDbInf
->numTables
== 0)
3362 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3363 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3365 pDbInf
->numTables
++; // Counter for Tables
3367 if (pass
== 2) // Create and fill the Table entries
3369 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3370 { // no, then create the Array
3371 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3373 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3375 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3376 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3377 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3383 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3385 // Query how many columns are in each table
3386 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3388 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3393 } // wxDb::GetCatalog()
3396 /********** wxDb::Catalog() **********/
3397 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3399 * Creates the text file specified in 'filename' which will contain
3400 * a minimal data dictionary of all tables accessible by the user specified
3403 * userID is evaluated in the following manner:
3404 * userID == NULL ... UserID is ignored
3405 * userID == "" ... UserID set equal to 'this->uid'
3406 * userID != "" ... UserID set equal to 'userID'
3408 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3409 * by this function. This function should use its own wxDb instance
3410 * to avoid undesired unbinding of columns.
3413 wxASSERT(fileName
.Length());
3417 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3418 wxString tblNameSave
;
3419 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3421 wxChar typeName
[30+1];
3422 SDWORD precision
, length
;
3424 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3428 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3431 convertUserID(userID
,UserID
);
3433 if (!UserID
.empty() &&
3434 Dbms() != dbmsMY_SQL
&&
3435 Dbms() != dbmsACCESS
&&
3436 Dbms() != dbmsFIREBIRD
&&
3437 Dbms() != dbmsINTERBASE
&&
3438 Dbms() != dbmsMS_SQL_SERVER
)
3440 retcode
= SQLColumns(hstmt
,
3441 NULL
, 0, // All qualifiers
3442 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3443 NULL
, 0, // All tables
3444 NULL
, 0); // All columns
3448 retcode
= SQLColumns(hstmt
,
3449 NULL
, 0, // All qualifiers
3450 NULL
, 0, // User specified
3451 NULL
, 0, // All tables
3452 NULL
, 0); // All columns
3454 if (retcode
!= SQL_SUCCESS
)
3456 DispAllErrors(henv
, hdbc
, hstmt
);
3462 tblNameSave
.Empty();
3467 retcode
= SQLFetch(hstmt
);
3468 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3471 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3472 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3473 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3474 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3475 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3476 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3478 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3481 wxFputs(wxT("\n"), fp
);
3482 wxFputs(wxT("================================ "), fp
);
3483 wxFputs(wxT("================================ "), fp
);
3484 wxFputs(wxT("===================== "), fp
);
3485 wxFputs(wxT("========= "), fp
);
3486 wxFputs(wxT("=========\n"), fp
);
3487 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3488 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3489 wxFputs(outStr
.c_str(), fp
);
3490 wxFputs(wxT("================================ "), fp
);
3491 wxFputs(wxT("================================ "), fp
);
3492 wxFputs(wxT("===================== "), fp
);
3493 wxFputs(wxT("========= "), fp
);
3494 wxFputs(wxT("=========\n"), fp
);
3495 tblNameSave
= tblName
;
3498 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3499 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3500 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3502 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3509 if (retcode
!= SQL_NO_DATA_FOUND
)
3510 DispAllErrors(henv
, hdbc
, hstmt
);
3512 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3515 return(retcode
== SQL_NO_DATA_FOUND
);
3517 } // wxDb::Catalog()
3520 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3522 * Table name can refer to a table, view, alias or synonym. Returns true
3523 * if the object exists in the database. This function does not indicate
3524 * whether or not the user has privleges to query or perform other functions
3527 * userID is evaluated in the following manner:
3528 * userID == NULL ... UserID is ignored
3529 * userID == "" ... UserID set equal to 'this->uid'
3530 * userID != "" ... UserID set equal to 'userID'
3533 wxASSERT(tableName
.Length());
3537 if (Dbms() == dbmsDBASE
)
3540 if (tablePath
.Length())
3541 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3543 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3546 exists
= wxFileExists(dbName
);
3551 convertUserID(userID
,UserID
);
3553 TableName
= tableName
;
3554 // Oracle and Interbase table names are uppercase only, so force
3555 // the name to uppercase just in case programmer forgot to do this
3556 if ((Dbms() == dbmsORACLE
) ||
3557 (Dbms() == dbmsFIREBIRD
) ||
3558 (Dbms() == dbmsINTERBASE
))
3559 TableName
= TableName
.Upper();
3561 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3564 // Some databases cannot accept a user name when looking up table names,
3565 // so we use the call below that leaves out the user name
3566 if (!UserID
.empty() &&
3567 Dbms() != dbmsMY_SQL
&&
3568 Dbms() != dbmsACCESS
&&
3569 Dbms() != dbmsMS_SQL_SERVER
&&
3570 Dbms() != dbmsDB2
&&
3571 Dbms() != dbmsFIREBIRD
&&
3572 Dbms() != dbmsINTERBASE
&&
3573 Dbms() != dbmsPERVASIVE_SQL
)
3575 retcode
= SQLTables(hstmt
,
3576 NULL
, 0, // All qualifiers
3577 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3578 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3579 NULL
, 0); // All table types
3583 retcode
= SQLTables(hstmt
,
3584 NULL
, 0, // All qualifiers
3585 NULL
, 0, // All owners
3586 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3587 NULL
, 0); // All table types
3589 if (retcode
!= SQL_SUCCESS
)
3590 return(DispAllErrors(henv
, hdbc
, hstmt
));
3592 retcode
= SQLFetch(hstmt
);
3593 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3595 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3596 return(DispAllErrors(henv
, hdbc
, hstmt
));
3599 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3603 } // wxDb::TableExists()
3606 /********** wxDb::TablePrivileges() **********/
3607 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3608 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3610 wxASSERT(tableName
.Length());
3612 wxDbTablePrivilegeInfo result
;
3616 // We probably need to be able to dynamically set this based on
3617 // the driver type, and state.
3618 wxChar curRole
[]=wxT("public");
3622 wxString UserID
,Schema
;
3623 convertUserID(userID
,UserID
);
3624 convertUserID(schema
,Schema
);
3626 TableName
= tableName
;
3627 // Oracle and Interbase table names are uppercase only, so force
3628 // the name to uppercase just in case programmer forgot to do this
3629 if ((Dbms() == dbmsORACLE
) ||
3630 (Dbms() == dbmsFIREBIRD
) ||
3631 (Dbms() == dbmsINTERBASE
))
3632 TableName
= TableName
.Upper();
3634 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3636 // Some databases cannot accept a user name when looking up table names,
3637 // so we use the call below that leaves out the user name
3638 if (!Schema
.empty() &&
3639 Dbms() != dbmsMY_SQL
&&
3640 Dbms() != dbmsACCESS
&&
3641 Dbms() != dbmsMS_SQL_SERVER
)
3643 retcode
= SQLTablePrivileges(hstmt
,
3645 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3646 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3650 retcode
= SQLTablePrivileges(hstmt
,
3653 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3656 #ifdef DBDEBUG_CONSOLE
3657 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3660 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3661 return (DispAllErrors(henv
, hdbc
, hstmt
));
3663 bool failed
= false;
3664 retcode
= SQLFetch(hstmt
);
3665 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3667 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3670 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3673 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3676 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3679 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3682 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3685 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3690 return(DispAllErrors(henv
, hdbc
, hstmt
));
3692 #ifdef DBDEBUG_CONSOLE
3693 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3694 result
.privilege
,result
.tableOwner
,result
.tableName
,
3695 result
.grantor
, result
.grantee
);
3698 if (UserID
.IsSameAs(result
.tableOwner
,false))
3700 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3704 if (UserID
.IsSameAs(result
.grantee
,false) &&
3705 !wxStrcmp(result
.privilege
,priv
))
3707 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3711 if (!wxStrcmp(result
.grantee
,curRole
) &&
3712 !wxStrcmp(result
.privilege
,priv
))
3714 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3718 retcode
= SQLFetch(hstmt
);
3721 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3724 } // wxDb::TablePrivileges
3727 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3731 if (Dbms() == dbmsACCESS
)
3732 TableName
= _T("\"");
3733 TableName
+= tableName
;
3734 if (Dbms() == dbmsACCESS
)
3735 TableName
+= _T("\"");
3738 } // wxDb::SQLTableName()
3741 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3745 if (Dbms() == dbmsACCESS
)
3748 if (Dbms() == dbmsACCESS
)
3749 ColName
+= _T("\"");
3752 } // wxDb::SQLColumnName()
3755 /********** wxDb::SetSqlLogging() **********/
3756 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3758 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3759 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3761 if (state
== sqlLogON
)
3765 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3766 if (fpSqlLog
== NULL
)
3774 if (fclose(fpSqlLog
))
3780 sqlLogState
= state
;
3783 } // wxDb::SetSqlLogging()
3786 /********** wxDb::WriteSqlLog() **********/
3787 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3789 wxASSERT(logMsg
.Length());
3791 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3794 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3796 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3798 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3803 } // wxDb::WriteSqlLog()
3806 /********** wxDb::Dbms() **********/
3807 wxDBMS
wxDb::Dbms(void)
3809 * Be aware that not all database engines use the exact same syntax, and not
3810 * every ODBC compliant database is compliant to the same level of compliancy.
3811 * Some manufacturers support the minimum Level 1 compliancy, and others up
3812 * through Level 3. Others support subsets of features for levels above 1.
3814 * If you find an inconsistency between the wxDb class and a specific database
3815 * engine, and an identifier to this section, and special handle the database in
3816 * the area where behavior is non-conforming with the other databases.
3819 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3820 * ---------------------------------------------------
3823 * - Currently the only database supported by the class to support VIEWS
3826 * - Does not support the SQL_TIMESTAMP structure
3827 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3828 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3829 * is true. The user must create ALL indexes from their program.
3830 * - Table names can only be 8 characters long
3831 * - Column names can only be 10 characters long
3834 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3835 * after every table name involved in the query/join if that tables matching record(s)
3837 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3839 * SYBASE (Enterprise)
3840 * - If a column is part of the Primary Key, the column cannot be NULL
3841 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3844 * - If a column is part of the Primary Key, the column cannot be NULL
3845 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3846 * - Columns that are part of primary or secondary 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
3849 * - Does not support sub-queries in SQL statements
3852 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3853 * - Does not support sub-queries in SQL statements
3856 * - Primary keys must be declared as NOT NULL
3857 * - Table and index names must not be longer than 13 characters in length (technically
3858 * table names can be up to 18 characters, but the primary index is created using the
3859 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3864 * - Columns that are part of primary keys must be defined as being NOT NULL
3865 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3866 * column definition if it is not defined correctly, but it is experimental
3869 // Should only need to do this once for each new database connection
3870 // so return the value we already determined it to be to save time
3871 // and lots of string comparisons
3872 if (dbmsType
!= dbmsUNIDENTIFIED
)
3875 #ifdef DBDEBUG_CONSOLE
3876 // When run in console mode, use standard out to display errors.
3877 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3878 #endif // DBDEBUG_CONSOLE
3880 wxLogDebug(wxT("Database connecting to: "));
3881 wxLogDebug(dbInf
.dbmsName
);
3883 wxChar baseName
[25+1];
3884 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3887 // RGG 20001025 : add support for Interbase
3888 // GT : Integrated to base classes on 20001121
3889 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3890 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3892 // BJO 20000428 : add support for Virtuoso
3893 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3894 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3896 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3897 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3899 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3900 // connected through an OpenLink driver.
3901 // Is it also returned by Sybase Adapatitve server?
3902 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3903 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3905 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3906 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3907 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3909 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3912 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3913 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3916 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3917 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3920 if (!wxStricmp(baseName
,wxT("Pervasive")))
3921 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3924 if (!wxStricmp(baseName
,wxT("Informix")))
3925 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3927 if (!wxStricmp(baseName
,wxT("Firebird")))
3928 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3931 if (!wxStricmp(baseName
,wxT("Oracle")))
3932 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3933 if (!wxStricmp(baseName
,wxT("ACCESS")))
3934 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3935 if (!wxStricmp(baseName
,wxT("Sybase")))
3936 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3939 if (!wxStricmp(baseName
,wxT("DBASE")))
3940 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3941 if (!wxStricmp(baseName
,wxT("xBase")))
3942 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3943 if (!wxStricmp(baseName
,wxT("MySQL")))
3944 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3945 if (!wxStricmp(baseName
,wxT("MaxDB")))
3946 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
3949 if (!wxStricmp(baseName
,wxT("DB2")))
3950 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3952 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3957 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3958 int dataType
, ULONG columnLength
,
3959 const wxString
&optionalParam
)
3961 wxASSERT(tableName
.Length());
3962 wxASSERT(columnName
.Length());
3963 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3964 dataType
!= DB_DATA_TYPE_VARCHAR
);
3966 // Must specify a columnLength if modifying a VARCHAR type column
3967 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3970 wxString dataTypeName
;
3972 wxString alterSlashModify
;
3976 case DB_DATA_TYPE_VARCHAR
:
3977 dataTypeName
= typeInfVarchar
.TypeName
;
3979 case DB_DATA_TYPE_INTEGER
:
3980 dataTypeName
= typeInfInteger
.TypeName
;
3982 case DB_DATA_TYPE_FLOAT
:
3983 dataTypeName
= typeInfFloat
.TypeName
;
3985 case DB_DATA_TYPE_DATE
:
3986 dataTypeName
= typeInfDate
.TypeName
;
3988 case DB_DATA_TYPE_BLOB
:
3989 dataTypeName
= typeInfBlob
.TypeName
;
3995 // Set the modify or alter syntax depending on the type of database connected to
3999 alterSlashModify
= _T("MODIFY");
4001 case dbmsMS_SQL_SERVER
:
4002 alterSlashModify
= _T("ALTER COLUMN");
4004 case dbmsUNIDENTIFIED
:
4006 case dbmsSYBASE_ASA
:
4007 case dbmsSYBASE_ASE
:
4012 case dbmsXBASE_SEQUITER
:
4014 alterSlashModify
= _T("MODIFY");
4018 // create the SQL statement
4019 if ( Dbms() == dbmsMY_SQL
)
4021 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4022 columnName
.c_str(), dataTypeName
.c_str());
4026 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4027 columnName
.c_str(), dataTypeName
.c_str());
4030 // For varchars only, append the size of the column
4031 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4032 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4035 s
.Printf(wxT("(%lu)"), columnLength
);
4039 // for passing things like "NOT NULL"
4040 if (optionalParam
.Length())
4042 sqlStmt
+= wxT(" ");
4043 sqlStmt
+= optionalParam
;
4046 return ExecSql(sqlStmt
);
4048 } // wxDb::ModifyColumn()
4051 /********** wxDbGetConnection() **********/
4052 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4056 // Used to keep a pointer to a DB connection that matches the requested
4057 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4058 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4059 // rather than having to re-query the datasource to get all the values
4060 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4061 wxDb
*matchingDbConnection
= NULL
;
4063 // Scan the linked list searching for an available database connection
4064 // that's already been opened but is currently not in use.
4065 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4067 // The database connection must be for the same datasource
4068 // name and must currently not be in use.
4070 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4072 if (pDbConfig
->UseConnectionStr())
4074 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4075 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4077 // Found a free connection
4078 pList
->Free
= false;
4079 return(pList
->PtrDb
);
4084 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4085 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4087 // Found a free connection
4088 pList
->Free
= false;
4089 return(pList
->PtrDb
);
4094 if (pDbConfig
->UseConnectionStr())
4096 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4097 matchingDbConnection
= pList
->PtrDb
;
4101 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4102 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4103 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4104 matchingDbConnection
= pList
->PtrDb
;
4108 // No available connections. A new connection must be made and
4109 // appended to the end of the linked list.
4112 // Find the end of the list
4113 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4114 // Append a new list item
4115 pList
->PtrNext
= new wxDbList
;
4116 pList
->PtrNext
->PtrPrev
= pList
;
4117 pList
= pList
->PtrNext
;
4121 // Create the first node on the list
4122 pList
= PtrBegDbList
= new wxDbList
;
4126 // Initialize new node in the linked list
4128 pList
->Free
= false;
4129 pList
->Dsn
= pDbConfig
->GetDsn();
4130 pList
->Uid
= pDbConfig
->GetUserID();
4131 pList
->AuthStr
= pDbConfig
->GetPassword();
4132 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4134 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4138 if (!matchingDbConnection
)
4140 if (pDbConfig
->UseConnectionStr())
4142 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4146 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4150 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4152 // Connect to the datasource
4155 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4156 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4157 return(pList
->PtrDb
);
4159 else // Unable to connect, destroy list item
4162 pList
->PtrPrev
->PtrNext
= 0;
4164 PtrBegDbList
= 0; // Empty list again
4166 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4167 pList
->PtrDb
->Close(); // Close the wxDb object
4168 delete pList
->PtrDb
; // Deletes the wxDb object
4169 delete pList
; // Deletes the linked list object
4173 } // wxDbGetConnection()
4176 /********** wxDbFreeConnection() **********/
4177 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4181 // Scan the linked list searching for the database connection
4182 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4184 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4185 return (pList
->Free
= true);
4188 // Never found the database object, return failure
4191 } // wxDbFreeConnection()
4194 /********** wxDbCloseConnections() **********/
4195 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4197 wxDbList
*pList
, *pNext
;
4199 // Traverse the linked list closing database connections and freeing memory as I go.
4200 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4202 pNext
= pList
->PtrNext
; // Save the pointer to next
4203 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4204 pList
->PtrDb
->Close(); // Close the wxDb object
4205 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4206 delete pList
->PtrDb
; // Deletes the wxDb object
4207 delete pList
; // Deletes the linked list object
4210 // Mark the list as empty
4213 } // wxDbCloseConnections()
4216 /********** wxDbConnectionsInUse() **********/
4217 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4222 // Scan the linked list counting db connections that are currently in use
4223 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4225 if (pList
->Free
== false)
4231 } // wxDbConnectionsInUse()
4235 /********** wxDbLogExtendedErrorMsg() **********/
4236 // DEBUG ONLY function
4237 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4239 const wxChar
*ErrFile
,
4242 static wxString msg
;
4247 if (ErrFile
|| ErrLine
)
4249 msg
+= wxT("File: ");
4251 msg
+= wxT(" Line: ");
4252 tStr
.Printf(wxT("%d"),ErrLine
);
4253 msg
+= tStr
.c_str();
4257 msg
.Append (wxT("\nODBC errors:\n"));
4260 // Display errors for this connection
4262 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4264 if (pDb
->errorList
[i
])
4266 msg
.Append(pDb
->errorList
[i
]);
4267 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4268 msg
.Append(wxT("\n"));
4269 // Clear the errmsg buffer so the next error will not
4270 // end up showing the previous error that have occurred
4271 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4276 wxLogDebug(msg
.c_str());
4279 } // wxDbLogExtendedErrorMsg()
4282 /********** wxDbSqlLog() **********/
4283 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4285 bool append
= false;
4288 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4290 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4295 SQLLOGstate
= state
;
4296 SQLLOGfn
= filename
;
4304 /********** wxDbCreateDataSource() **********/
4305 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4306 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4308 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4309 * Very rudimentary creation of an ODBC data source.
4311 * ODBC driver must be ODBC 3.0 compliant to use this function
4316 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4322 dsnLocation
= ODBC_ADD_SYS_DSN
;
4324 dsnLocation
= ODBC_ADD_DSN
;
4326 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4327 // so that is why I used it, as wxString does not deal well with
4328 // embedded nulls in strings
4329 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4331 // Replace the separator from above with the '\0' seperator needed
4332 // by the SQLConfigDataSource() function
4336 k
= setupStr
.Find((wxChar
)2,true);
4337 if (k
!= wxNOT_FOUND
)
4338 setupStr
[(UINT
)k
] = wxT('\0');
4340 while (k
!= wxNOT_FOUND
);
4342 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4343 driverName
, setupStr
.c_str());
4345 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4347 // check for errors caused by ConfigDSN based functions
4350 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4351 errMsg
[0] = wxT('\0');
4353 // This function is only supported in ODBC drivers v3.0 compliant and above
4354 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4357 #ifdef DBDEBUG_CONSOLE
4358 // When run in console mode, use standard out to display errors.
4359 cout
<< errMsg
<< endl
;
4360 cout
<< wxT("Press any key to continue...") << endl
;
4362 #endif // DBDEBUG_CONSOLE
4365 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4366 #endif // __WXDEBUG__
4372 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4373 // necessary to use this function, so this function is not supported
4375 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4378 #endif // __VISUALC__
4382 } // wxDbCreateDataSource()
4386 /********** wxDbGetDataSource() **********/
4387 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4388 SWORD DsDescMaxLength
, UWORD direction
)
4390 * Dsn and DsDesc will contain the data source name and data source
4391 * description upon return
4395 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4396 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4398 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4399 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4404 } // wxDbGetDataSource()
4407 // Change this to 0 to remove use of all deprecated functions
4408 #if wxODBC_BACKWARD_COMPATABILITY
4409 /********************************************************************
4410 ********************************************************************
4412 * The following functions are all DEPRECATED and are included for
4413 * backward compatability reasons only
4415 ********************************************************************
4416 ********************************************************************/
4417 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4419 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4421 /***** DEPRECATED: use wxGetDataSource() *****/
4422 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4425 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4427 /***** DEPRECATED: use wxDbGetConnection() *****/
4428 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4430 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4432 /***** DEPRECATED: use wxDbFreeConnection() *****/
4433 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4435 return wxDbFreeConnection(pDb
);
4437 /***** DEPRECATED: use wxDbCloseConnections() *****/
4438 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4440 wxDbCloseConnections();
4442 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4443 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4445 return wxDbConnectionsInUse();