1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/db.cpp
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
24 #include "wx/wxprec.h"
33 #include "wx/object.h"
35 #include "wx/string.h"
41 #ifdef DBDEBUG_CONSOLE
42 #include "wx/ioswrap.h"
45 #include "wx/filefn.h"
46 #include "wx/wxchar.h"
56 // DLL options compatibility check:
57 WX_CHECK_BUILD_OPTIONS("wxODBC")
59 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
61 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
62 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
65 extern wxList TablesInUse
;
66 extern wxCriticalSection csTablesInUse
;
69 // SQL Log defaults to be used by GetDbConnection
70 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
72 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
74 // The wxDb::errorList is copied to this variable when the wxDb object
75 // is closed. This way, the error list is still available after the
76 // database object is closed. This is necessary if the database
77 // connection fails so the calling application can show the operator
78 // why the connection failed. Note: as each wxDb object is closed, it
79 // will overwrite the errors of the previously destroyed wxDb object in
80 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
82 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
+1];
85 // This type defines the return row-struct form
86 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
89 wxChar tableQual
[128+1];
90 wxChar tableOwner
[128+1];
91 wxChar tableName
[128+1];
92 wxChar grantor
[128+1];
93 wxChar grantee
[128+1];
94 wxChar privilege
[128+1];
95 wxChar grantable
[3+1];
96 } wxDbTablePrivilegeInfo
;
99 /********** wxDbConnectInf Constructor - form 1 **********/
100 wxDbConnectInf::wxDbConnectInf()
103 freeHenvOnDestroy
= false;
109 /********** wxDbConnectInf Constructor - form 2 **********/
110 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
111 const wxString
&password
, const wxString
&defaultDir
,
112 const wxString
&fileType
, const wxString
&description
)
115 freeHenvOnDestroy
= false;
126 SetPassword(password
);
127 SetDescription(description
);
128 SetFileType(fileType
);
129 SetDefaultDir(defaultDir
);
130 } // wxDbConnectInf Constructor
133 wxDbConnectInf::~wxDbConnectInf()
135 if (freeHenvOnDestroy
)
139 } // wxDbConnectInf Destructor
143 /********** wxDbConnectInf::Initialize() **********/
144 bool wxDbConnectInf::Initialize()
146 freeHenvOnDestroy
= false;
148 if (freeHenvOnDestroy
&& Henv
)
155 ConnectionStr
[0] = 0;
160 useConnectionStr
= false;
163 } // wxDbConnectInf::Initialize()
166 /********** wxDbConnectInf::AllocHenv() **********/
167 bool wxDbConnectInf::AllocHenv()
169 // This is here to help trap if you are getting a new henv
170 // without releasing an existing henv
173 // Initialize the ODBC Environment for Database Operations
174 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
176 wxLogDebug(wxT("A problem occurred while trying to get a connection to the data source"));
180 freeHenvOnDestroy
= true;
183 } // wxDbConnectInf::AllocHenv()
186 void wxDbConnectInf::FreeHenv()
194 freeHenvOnDestroy
= false;
196 } // wxDbConnectInf::FreeHenv()
199 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
201 wxASSERT(dsn
.length() < WXSIZEOF(Dsn
));
203 wxStrncpy(Dsn
, dsn
, WXSIZEOF(Dsn
)-1);
204 Dsn
[WXSIZEOF(Dsn
)-1] = 0; // Prevent buffer overrun
205 } // wxDbConnectInf::SetDsn()
208 void wxDbConnectInf::SetUserID(const wxString
&uid
)
210 wxASSERT(uid
.length() < WXSIZEOF(Uid
));
211 wxStrncpy(Uid
, uid
, WXSIZEOF(Uid
)-1);
212 Uid
[WXSIZEOF(Uid
)-1] = 0; // Prevent buffer overrun
213 } // wxDbConnectInf::SetUserID()
216 void wxDbConnectInf::SetPassword(const wxString
&password
)
218 wxASSERT(password
.length() < WXSIZEOF(AuthStr
));
220 wxStrncpy(AuthStr
, password
, WXSIZEOF(AuthStr
)-1);
221 AuthStr
[WXSIZEOF(AuthStr
)-1] = 0; // Prevent buffer overrun
222 } // wxDbConnectInf::SetPassword()
224 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
226 wxASSERT(connectStr
.length() < WXSIZEOF(ConnectionStr
));
228 useConnectionStr
= wxStrlen(connectStr
) > 0;
230 wxStrncpy(ConnectionStr
, connectStr
, WXSIZEOF(ConnectionStr
)-1);
231 ConnectionStr
[WXSIZEOF(ConnectionStr
)-1] = 0; // Prevent buffer overrun
232 } // wxDbConnectInf::SetConnectionStr()
235 /********** wxDbColFor Constructor **********/
236 wxDbColFor::wxDbColFor()
239 } // wxDbColFor::wxDbColFor()
242 /********** wxDbColFor::Initialize() **********/
243 void wxDbColFor::Initialize()
253 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
256 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
257 } // wxDbColFor::Initialize()
260 /********** wxDbColFor::Format() **********/
261 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
262 short columnLength
, short decimalDigits
)
264 // ----------------------------------------------------------------------------------------
265 // -- 19991224 : mj10777 : Create
266 // There is still a lot of work to do here, but it is a start
267 // It handles all the basic data-types that I have run into up to now
268 // The main work will have be with Dates and float Formatting
269 // (US 1,000.00 ; EU 1.000,00)
270 // There are wxWindow plans for locale support and the new wxDateTime. If
271 // they define some constants (wxEUROPEAN) that can be gloably used,
272 // they should be used here.
273 // ----------------------------------------------------------------------------------------
274 // There should also be a function to scan in a string to fill the variable
275 // ----------------------------------------------------------------------------------------
277 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
278 i_dbDataType
= dbDataType
;
279 i_sqlDataType
= sqlDataType
;
280 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
282 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
284 if ((i_sqlDataType
== SQL_VARCHAR
)
286 #if defined(SQL_WCHAR)
287 || (i_sqlDataType
== SQL_WCHAR
)
289 #if defined(SQL_WVARCHAR)
290 || (i_sqlDataType
== SQL_WVARCHAR
)
293 || (i_sqlDataType
== SQL_LONGVARCHAR
))
294 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
295 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
296 i_dbDataType
= DB_DATA_TYPE_DATE
;
297 if (i_sqlDataType
== SQL_C_BIT
)
298 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
299 if (i_sqlDataType
== SQL_NUMERIC
)
300 i_dbDataType
= DB_DATA_TYPE_VARCHAR
; // glt - ??? is this right?
301 if (i_sqlDataType
== SQL_REAL
)
302 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
303 if (i_sqlDataType
== SQL_C_BINARY
)
304 i_dbDataType
= DB_DATA_TYPE_BLOB
;
307 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
309 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
312 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
314 case DB_DATA_TYPE_VARCHAR
:
317 case DB_DATA_TYPE_INTEGER
:
320 case DB_DATA_TYPE_FLOAT
:
321 if (decimalDigits
== 0)
323 tempStr
.Printf(wxT("%%%d.%d"), columnLength
, decimalDigits
);
324 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
326 case DB_DATA_TYPE_DATE
:
327 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
329 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
331 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
333 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
335 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
337 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
339 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
341 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
343 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
345 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
348 case DB_DATA_TYPE_BLOB
:
349 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
352 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
356 } // wxDbColFor::Format()
359 /********** wxDbColInf Constructor **********/
360 wxDbColInf::wxDbColInf()
363 } // wxDbColInf::wxDbColInf()
366 /********** wxDbColInf Destructor ********/
367 wxDbColInf::~wxDbColInf()
372 } // wxDbColInf::~wxDbColInf()
375 bool wxDbColInf::Initialize()
397 } // wxDbColInf::Initialize()
400 /********** wxDbTableInf Constructor ********/
401 wxDbTableInf::wxDbTableInf()
404 } // wxDbTableInf::wxDbTableInf()
407 /********** wxDbTableInf Constructor ********/
408 wxDbTableInf::~wxDbTableInf()
413 } // wxDbTableInf::~wxDbTableInf()
416 bool wxDbTableInf::Initialize()
425 } // wxDbTableInf::Initialize()
428 /********** wxDbInf Constructor *************/
432 } // wxDbInf::wxDbInf()
435 /********** wxDbInf Destructor *************/
441 } // wxDbInf::~wxDbInf()
444 /********** wxDbInf::Initialize() *************/
445 bool wxDbInf::Initialize()
453 } // wxDbInf::Initialize()
456 /********** wxDb Constructor **********/
457 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
459 // Copy the HENV into the db class
461 fwdOnlyCursors
= FwdOnlyCursors
;
467 /********** wxDb Destructor **********/
470 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
480 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
481 /********** wxDb::initialize() **********/
482 void wxDb::initialize()
484 * Private member function that sets all wxDb member variables to
485 * known values at creation of the wxDb
490 fpSqlLog
= 0; // Sql Log file pointer
491 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
493 dbmsType
= dbmsUNIDENTIFIED
;
495 wxStrcpy(sqlState
,wxEmptyString
);
496 wxStrcpy(errorMsg
,wxEmptyString
);
497 nativeError
= cbErrorMsg
= 0;
498 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
499 wxStrcpy(errorList
[i
], wxEmptyString
);
501 // Init typeInf structures
502 typeInfVarchar
.TypeName
.Empty();
503 typeInfVarchar
.FsqlType
= 0;
504 typeInfVarchar
.Precision
= 0;
505 typeInfVarchar
.CaseSensitive
= 0;
506 typeInfVarchar
.MaximumScale
= 0;
508 typeInfInteger
.TypeName
.Empty();
509 typeInfInteger
.FsqlType
= 0;
510 typeInfInteger
.Precision
= 0;
511 typeInfInteger
.CaseSensitive
= 0;
512 typeInfInteger
.MaximumScale
= 0;
514 typeInfFloat
.TypeName
.Empty();
515 typeInfFloat
.FsqlType
= 0;
516 typeInfFloat
.Precision
= 0;
517 typeInfFloat
.CaseSensitive
= 0;
518 typeInfFloat
.MaximumScale
= 0;
520 typeInfDate
.TypeName
.Empty();
521 typeInfDate
.FsqlType
= 0;
522 typeInfDate
.Precision
= 0;
523 typeInfDate
.CaseSensitive
= 0;
524 typeInfDate
.MaximumScale
= 0;
526 typeInfBlob
.TypeName
.Empty();
527 typeInfBlob
.FsqlType
= 0;
528 typeInfBlob
.Precision
= 0;
529 typeInfBlob
.CaseSensitive
= 0;
530 typeInfBlob
.MaximumScale
= 0;
532 typeInfMemo
.TypeName
.Empty();
533 typeInfMemo
.FsqlType
= 0;
534 typeInfMemo
.Precision
= 0;
535 typeInfMemo
.CaseSensitive
= 0;
536 typeInfMemo
.MaximumScale
= 0;
538 // Error reporting is turned OFF by default
541 // Allocate a data source connection handle
542 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
545 // Initialize the db status flag
548 // Mark database as not open as of yet
551 dbOpenedWithConnectionString
= false;
552 } // wxDb::initialize()
555 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
557 // NOTE: Return value from this function MUST be copied
558 // immediately, as the value is not good after
559 // this function has left scope.
561 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
565 if (!wxStrlen(userID
))
573 // dBase does not use user names, and some drivers fail if you try to pass one
574 if ( Dbms() == dbmsDBASE
575 || Dbms() == dbmsXBASE_SEQUITER
)
578 // Some databases require user names to be specified in uppercase,
579 // so force the name to uppercase
580 if ((Dbms() == dbmsORACLE
) ||
581 (Dbms() == dbmsMAXDB
))
582 UserID
= UserID
.Upper();
584 return UserID
.c_str();
585 } // wxDb::convertUserID()
588 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported
)
592 // These are the possible SQL types we check for use against the datasource we are connected
593 // to for the purpose of determining which data type to use for the basic character strings
596 // NOTE: The first type in this enumeration that is determined to be supported by the
597 // datasource/driver is the one that will be used.
598 SWORD PossibleSqlCharTypes
[] = {
599 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
603 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
609 // These are the possible SQL types we check for use against the datasource we are connected
610 // to for the purpose of determining which data type to use for the basic non-floating point
613 // NOTE: The first type in this enumeration that is determined to be supported by the
614 // datasource/driver is the one that will be used.
615 SWORD PossibleSqlIntegerTypes
[] = {
619 // These are the possible SQL types we check for use against the datasource we are connected
620 // to for the purpose of determining which data type to use for the basic floating point number
623 // NOTE: The first type in this enumeration that is determined to be supported by the
624 // datasource/driver is the one that will be used.
625 SWORD PossibleSqlFloatTypes
[] = {
633 // These are the possible SQL types we check for use agains the datasource we are connected
634 // to for the purpose of determining which data type to use for the date/time column types
636 // NOTE: The first type in this enumeration that is determined to be supported by the
637 // datasource/driver is the one that will be used.
638 SWORD PossibleSqlDateTypes
[] = {
646 // These are the possible SQL types we check for use agains the datasource we are connected
647 // to for the purpose of determining which data type to use for the BLOB column types.
649 // NOTE: The first type in this enumeration that is determined to be supported by the
650 // datasource/driver is the one that will be used.
651 SWORD PossibleSqlBlobTypes
[] = {
656 // These are the possible SQL types we check for use agains the datasource we are connected
657 // to for the purpose of determining which data type to use for the MEMO column types
658 // (a type which allow to store large strings; like VARCHAR just with a bigger precision)
660 // NOTE: The first type in this enumeration that is determined to be supported by the
661 // datasource/driver is the one that will be used.
662 SWORD PossibleSqlMemoTypes
[] = {
667 // Query the data source regarding data type information
670 // The way it was determined which SQL data types to use was by calling SQLGetInfo
671 // for all of the possible SQL data types to see which ones were supported. If
672 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
673 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
674 // types I've selected below will not always be what we want. These are just
675 // what happened to work against an Oracle 7/Intersolv combination. The following is
676 // a complete list of the results I got back against the Oracle 7 database:
678 // SQL_BIGINT SQL_NO_DATA_FOUND
679 // SQL_BINARY SQL_NO_DATA_FOUND
680 // SQL_BIT SQL_NO_DATA_FOUND
681 // SQL_CHAR type name = 'CHAR', Precision = 255
682 // SQL_DATE SQL_NO_DATA_FOUND
683 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
684 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
685 // SQL_FLOAT SQL_NO_DATA_FOUND
686 // SQL_INTEGER SQL_NO_DATA_FOUND
687 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
688 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
689 // SQL_NUMERIC SQL_NO_DATA_FOUND
690 // SQL_REAL SQL_NO_DATA_FOUND
691 // SQL_SMALLINT SQL_NO_DATA_FOUND
692 // SQL_TIME SQL_NO_DATA_FOUND
693 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
694 // SQL_VARBINARY type name = 'RAW', Precision = 255
695 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
696 // =====================================================================
697 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
699 // SQL_VARCHAR type name = 'TEXT', Precision = 255
700 // SQL_TIMESTAMP type name = 'DATETIME'
701 // SQL_DECIMAL SQL_NO_DATA_FOUND
702 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
703 // SQL_FLOAT SQL_NO_DATA_FOUND
704 // SQL_REAL type name = 'SINGLE', Precision = 7
705 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
706 // SQL_INTEGER type name = 'LONG', Precision = 10
708 // Query the data source for info about itself
709 if (!getDbInfo(failOnDataTypeUnsupported
))
712 // --------------- Varchar - (Variable length character string) ---------------
713 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlCharTypes
) &&
714 !getDataTypeInfo(PossibleSqlCharTypes
[iIndex
], typeInfVarchar
); ++iIndex
)
717 if (iIndex
< WXSIZEOF(PossibleSqlCharTypes
))
718 typeInfVarchar
.FsqlType
= PossibleSqlCharTypes
[iIndex
];
719 else if (failOnDataTypeUnsupported
)
722 // --------------- Float ---------------
723 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlFloatTypes
) &&
724 !getDataTypeInfo(PossibleSqlFloatTypes
[iIndex
], typeInfFloat
); ++iIndex
)
727 if (iIndex
< WXSIZEOF(PossibleSqlFloatTypes
))
728 typeInfFloat
.FsqlType
= PossibleSqlFloatTypes
[iIndex
];
729 else if (failOnDataTypeUnsupported
)
732 // --------------- Integer -------------
733 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
) &&
734 !getDataTypeInfo(PossibleSqlIntegerTypes
[iIndex
], typeInfInteger
); ++iIndex
)
737 if (iIndex
< WXSIZEOF(PossibleSqlIntegerTypes
))
738 typeInfInteger
.FsqlType
= PossibleSqlIntegerTypes
[iIndex
];
739 else if (failOnDataTypeUnsupported
)
741 // If no non-floating point data types are supported, we'll
742 // use the type assigned for floats to store integers as well
743 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
745 if (failOnDataTypeUnsupported
)
749 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
752 // --------------- Date/Time ---------------
753 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlDateTypes
) &&
754 !getDataTypeInfo(PossibleSqlDateTypes
[iIndex
], typeInfDate
); ++iIndex
)
757 if (iIndex
< WXSIZEOF(PossibleSqlDateTypes
))
758 typeInfDate
.FsqlType
= PossibleSqlDateTypes
[iIndex
];
759 else if (failOnDataTypeUnsupported
)
762 // --------------- BLOB ---------------
763 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlBlobTypes
) &&
764 !getDataTypeInfo(PossibleSqlBlobTypes
[iIndex
], typeInfBlob
); ++iIndex
)
767 if (iIndex
< WXSIZEOF(PossibleSqlBlobTypes
))
768 typeInfBlob
.FsqlType
= PossibleSqlBlobTypes
[iIndex
];
769 else if (failOnDataTypeUnsupported
)
772 // --------------- MEMO ---------------
773 for (iIndex
= 0; iIndex
< WXSIZEOF(PossibleSqlMemoTypes
) &&
774 !getDataTypeInfo(PossibleSqlMemoTypes
[iIndex
], typeInfMemo
); ++iIndex
)
777 if (iIndex
< WXSIZEOF(PossibleSqlMemoTypes
))
778 typeInfMemo
.FsqlType
= PossibleSqlMemoTypes
[iIndex
];
779 else if (failOnDataTypeUnsupported
)
783 } // wxDb::determineDataTypes
786 bool wxDb::open(bool failOnDataTypeUnsupported
)
789 If using Intersolv branded ODBC drivers, this is the place where you would substitute
790 your branded driver license information
792 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
793 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
796 // Mark database as open
799 // Allocate a statement handle for the database connection
800 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
801 return(DispAllErrors(henv
, hdbc
));
803 // Set Connection Options
804 if (!setConnectionOptions())
807 if (!determineDataTypes(failOnDataTypeUnsupported
))
810 #ifdef DBDEBUG_CONSOLE
811 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
812 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
813 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
814 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
815 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
816 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
820 // Completed Successfully
824 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
826 wxASSERT(inConnectStr
.length());
827 return Open(inConnectStr
, NULL
, failOnDataTypeUnsupported
);
830 bool wxDb::Open(const wxString
& inConnectStr
, SQLHWND parentWnd
, bool failOnDataTypeUnsupported
)
834 authStr
= wxEmptyString
;
838 if (!FwdOnlyCursors())
840 // Specify that the ODBC cursor library be used, if needed. This must be
841 // specified before the connection is made.
842 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
844 #ifdef DBDEBUG_CONSOLE
845 if (retcode
== SQL_SUCCESS
)
846 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
848 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
850 wxUnusedVar(retcode
);
854 // Connect to the data source
855 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
856 short outConnectBufferLen
;
858 inConnectionStr
= inConnectStr
;
860 retcode
= SQLDriverConnect(hdbc
, parentWnd
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
861 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
862 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
864 if ((retcode
!= SQL_SUCCESS
) &&
865 (retcode
!= SQL_SUCCESS_WITH_INFO
))
866 return(DispAllErrors(henv
, hdbc
));
868 outConnectBuffer
[outConnectBufferLen
] = 0;
869 outConnectionStr
= outConnectBuffer
;
870 dbOpenedWithConnectionString
= true;
872 return open(failOnDataTypeUnsupported
);
875 /********** wxDb::Open() **********/
876 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
878 wxASSERT(!Dsn
.empty());
883 inConnectionStr
= wxEmptyString
;
884 outConnectionStr
= wxEmptyString
;
888 if (!FwdOnlyCursors())
890 // Specify that the ODBC cursor library be used, if needed. This must be
891 // specified before the connection is made.
892 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
894 #ifdef DBDEBUG_CONSOLE
895 if (retcode
== SQL_SUCCESS
)
896 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
898 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
900 wxUnusedVar( retcode
);
904 // Connect to the data source
905 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
906 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
907 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
909 if ((retcode
!= SQL_SUCCESS
) &&
910 (retcode
!= SQL_SUCCESS_WITH_INFO
))
911 return(DispAllErrors(henv
, hdbc
));
913 return open(failOnDataTypeUnsupported
);
918 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
920 wxASSERT(dbConnectInf
);
922 // Use the connection string if one is present
923 if (dbConnectInf
->UseConnectionStr())
924 return Open(dbConnectInf
->GetConnectionStr(), failOnDataTypeUnsupported
);
926 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
927 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
931 bool wxDb::Open(wxDb
*copyDb
)
933 dsn
= copyDb
->GetDatasourceName();
934 uid
= copyDb
->GetUsername();
935 authStr
= copyDb
->GetPassword();
936 inConnectionStr
= copyDb
->GetConnectionInStr();
937 outConnectionStr
= copyDb
->GetConnectionOutStr();
941 if (!FwdOnlyCursors())
943 // Specify that the ODBC cursor library be used, if needed. This must be
944 // specified before the connection is made.
945 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
947 #ifdef DBDEBUG_CONSOLE
948 if (retcode
== SQL_SUCCESS
)
949 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
951 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
953 wxUnusedVar( retcode
);
957 if (copyDb
->OpenedWithConnectionString())
959 // Connect to the data source
960 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
961 short outConnectBufferLen
;
963 inConnectionStr
= copyDb
->GetConnectionInStr();
965 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
966 (SWORD
)inConnectionStr
.length(), (SQLTCHAR FAR
*)outConnectBuffer
,
967 WXSIZEOF(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
969 if ((retcode
!= SQL_SUCCESS
) &&
970 (retcode
!= SQL_SUCCESS_WITH_INFO
))
971 return(DispAllErrors(henv
, hdbc
));
973 outConnectBuffer
[outConnectBufferLen
] = 0;
974 outConnectionStr
= outConnectBuffer
;
975 dbOpenedWithConnectionString
= true;
979 // Connect to the data source
980 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
981 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
982 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
985 if ((retcode
!= SQL_SUCCESS
) &&
986 (retcode
!= SQL_SUCCESS_WITH_INFO
))
987 return(DispAllErrors(henv
, hdbc
));
990 If using Intersolv branded ODBC drivers, this is the place where you would substitute
991 your branded driver license information
993 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
994 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
997 // Mark database as open
1000 // Allocate a statement handle for the database connection
1001 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
1002 return(DispAllErrors(henv
, hdbc
));
1004 // Set Connection Options
1005 if (!setConnectionOptions())
1008 // Instead of Querying the data source for info about itself, it can just be copied
1009 // from the wxDb instance that was passed in (copyDb).
1010 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
1011 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
1012 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
1013 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
1014 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
1015 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
1016 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
1017 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
1018 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
1019 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
1020 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
1021 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
1022 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
1023 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
1024 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
1025 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
1026 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
1027 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
1028 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
1029 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
1030 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
1031 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
1032 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
1033 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
1034 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
1035 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
1036 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
1037 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
1038 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
1039 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
1040 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
1042 // VARCHAR = Variable length character string
1043 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
1044 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
1045 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
1046 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
1047 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
1050 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
1051 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
1052 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
1053 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
1054 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
1057 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
1058 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
1059 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
1060 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
1061 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
1064 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
1065 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
1066 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
1067 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
1068 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
1071 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
1072 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
1073 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
1074 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
1075 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1078 typeInfMemo
.FsqlType
= copyDb
->typeInfMemo
.FsqlType
;
1079 typeInfMemo
.TypeName
= copyDb
->typeInfMemo
.TypeName
;
1080 typeInfMemo
.Precision
= copyDb
->typeInfMemo
.Precision
;
1081 typeInfMemo
.CaseSensitive
= copyDb
->typeInfMemo
.CaseSensitive
;
1082 typeInfMemo
.MaximumScale
= copyDb
->typeInfMemo
.MaximumScale
;
1084 #ifdef DBDEBUG_CONSOLE
1085 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1086 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1087 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1088 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1089 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1090 cout
<< wxT("MEMO DATA TYPE: ") << typeInfMemo
.TypeName
<< endl
;
1094 // Completed Successfully
1099 /********** wxDb::setConnectionOptions() **********/
1100 bool wxDb::setConnectionOptions(void)
1102 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1107 // I need to get the DBMS name here, because some of the connection options
1108 // are database specific and need to call the Dbms() function.
1111 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1112 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1113 return(DispAllErrors(henv
, hdbc
));
1115 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1116 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1117 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1119 // By default, MS Sql Server closes cursors on commit and rollback. The following
1120 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1121 // after a transaction. This is a driver specific option and is not part of the
1122 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1123 // The database settings don't have any effect one way or the other.
1124 if (Dbms() == dbmsMS_SQL_SERVER
)
1126 const long SQL_PRESERVE_CURSORS
= 1204L;
1127 const long SQL_PC_ON
= 1L;
1128 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1131 // Display the connection options to verify them
1132 #ifdef DBDEBUG_CONSOLE
1134 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1136 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1137 if (retcode
!= SQL_SUCCESS
)
1138 return(DispAllErrors(henv
, hdbc
));
1139 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1141 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1142 if (retcode
!= SQL_SUCCESS
)
1143 return(DispAllErrors(henv
, hdbc
));
1144 cout
<< wxT("ODBC CURSORS: ");
1147 case(SQL_CUR_USE_IF_NEEDED
):
1148 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1150 case(SQL_CUR_USE_ODBC
):
1151 cout
<< wxT("SQL_CUR_USE_ODBC");
1153 case(SQL_CUR_USE_DRIVER
):
1154 cout
<< wxT("SQL_CUR_USE_DRIVER");
1159 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1160 if (retcode
!= SQL_SUCCESS
)
1161 return(DispAllErrors(henv
, hdbc
));
1162 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1167 // Completed Successfully
1170 } // wxDb::setConnectionOptions()
1173 /********** wxDb::getDbInfo() **********/
1174 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1179 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1180 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1182 DispAllErrors(henv
, hdbc
);
1183 if (failOnDataTypeUnsupported
)
1187 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1188 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1190 DispAllErrors(henv
, hdbc
);
1191 if (failOnDataTypeUnsupported
)
1195 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1196 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1198 DispAllErrors(henv
, hdbc
);
1199 if (failOnDataTypeUnsupported
)
1204 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1205 // causing database connectivity to fail in some cases.
1206 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1207 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1209 DispAllErrors(henv
, hdbc
);
1210 if (failOnDataTypeUnsupported
)
1214 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1215 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1217 DispAllErrors(henv
, hdbc
);
1218 if (failOnDataTypeUnsupported
)
1222 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1223 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1225 DispAllErrors(henv
, hdbc
);
1226 if (failOnDataTypeUnsupported
)
1230 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1231 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1233 DispAllErrors(henv
, hdbc
);
1234 if (failOnDataTypeUnsupported
)
1238 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1239 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1241 DispAllErrors(henv
, hdbc
);
1242 if (failOnDataTypeUnsupported
)
1246 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1247 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1249 DispAllErrors(henv
, hdbc
);
1250 if (failOnDataTypeUnsupported
)
1254 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1255 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1257 DispAllErrors(henv
, hdbc
);
1258 if (failOnDataTypeUnsupported
)
1262 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1263 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1265 DispAllErrors(henv
, hdbc
);
1266 if (failOnDataTypeUnsupported
)
1270 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1271 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1273 // Not all drivers support this call - Nick Gorham(unixODBC)
1274 dbInf
.cliConfLvl
= 0;
1275 DispAllErrors(henv
, hdbc
);
1276 if (failOnDataTypeUnsupported
)
1280 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1281 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1283 DispAllErrors(henv
, hdbc
);
1284 if (failOnDataTypeUnsupported
)
1288 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1289 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1291 DispAllErrors(henv
, hdbc
);
1292 if (failOnDataTypeUnsupported
)
1296 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1297 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1299 DispAllErrors(henv
, hdbc
);
1300 if (failOnDataTypeUnsupported
)
1304 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1305 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1307 DispAllErrors(henv
, hdbc
);
1308 if (failOnDataTypeUnsupported
)
1312 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1313 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1315 DispAllErrors(henv
, hdbc
);
1316 if (failOnDataTypeUnsupported
)
1320 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1321 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1323 DispAllErrors(henv
, hdbc
);
1324 if (failOnDataTypeUnsupported
)
1328 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1329 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1331 DispAllErrors(henv
, hdbc
);
1332 if (failOnDataTypeUnsupported
)
1336 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1337 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1339 DispAllErrors(henv
, hdbc
);
1340 if (failOnDataTypeUnsupported
)
1344 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1345 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1347 DispAllErrors(henv
, hdbc
);
1348 if (failOnDataTypeUnsupported
)
1352 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1353 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1355 DispAllErrors(henv
, hdbc
);
1356 if (failOnDataTypeUnsupported
)
1360 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1361 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1363 DispAllErrors(henv
, hdbc
);
1364 if (failOnDataTypeUnsupported
)
1368 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1369 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1371 DispAllErrors(henv
, hdbc
);
1372 if (failOnDataTypeUnsupported
)
1376 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1377 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1379 DispAllErrors(henv
, hdbc
);
1380 if (failOnDataTypeUnsupported
)
1384 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1385 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1387 DispAllErrors(henv
, hdbc
);
1388 if (failOnDataTypeUnsupported
)
1392 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1393 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1395 DispAllErrors(henv
, hdbc
);
1396 if (failOnDataTypeUnsupported
)
1400 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1401 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1403 DispAllErrors(henv
, hdbc
);
1404 if (failOnDataTypeUnsupported
)
1408 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1409 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1411 DispAllErrors(henv
, hdbc
);
1412 if (failOnDataTypeUnsupported
)
1416 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1417 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1419 DispAllErrors(henv
, hdbc
);
1420 if (failOnDataTypeUnsupported
)
1424 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1425 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1427 DispAllErrors(henv
, hdbc
);
1428 if (failOnDataTypeUnsupported
)
1432 #ifdef DBDEBUG_CONSOLE
1433 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1434 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1435 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1436 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1438 cout
<< wxT("API Conf. Level: ");
1439 switch(dbInf
.apiConfLvl
)
1441 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1442 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1443 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1447 cout
<< wxT("SAG CLI Conf. Level: ");
1448 switch(dbInf
.cliConfLvl
)
1450 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1451 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1455 cout
<< wxT("SQL Conf. Level: ");
1456 switch(dbInf
.sqlConfLvl
)
1458 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1459 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1460 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1464 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1465 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1466 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1467 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1468 cout
<< wxT("Cursor COMMIT Behavior: ");
1469 switch(dbInf
.cursorCommitBehavior
)
1471 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1472 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1473 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1477 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1478 switch(dbInf
.cursorRollbackBehavior
)
1480 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1481 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1482 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1486 cout
<< wxT("Support NOT NULL clause: ");
1487 switch(dbInf
.supportNotNullClause
)
1489 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1490 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1494 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1495 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1497 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1500 cout
<< wxT("Default Transaction Isolation: ";
1501 switch(dbInf
.txnIsolation
)
1503 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1504 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1505 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1506 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1508 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1513 cout
<< wxT("Transaction Isolation Options: ");
1514 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1515 cout
<< wxT("Read Uncommitted, ");
1516 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1517 cout
<< wxT("Read Committed, ");
1518 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1519 cout
<< wxT("Repeatable Read, ");
1520 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1521 cout
<< wxT("Serializable, ");
1523 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1524 cout
<< wxT("Versioning");
1528 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1529 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1530 cout
<< wxT("Next, ");
1531 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1532 cout
<< wxT("Prev, ");
1533 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1534 cout
<< wxT("First, ");
1535 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1536 cout
<< wxT("Last, ");
1537 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1538 cout
<< wxT("Absolute, ");
1539 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1540 cout
<< wxT("Relative, ");
1542 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1543 cout
<< wxT("Resume, ");
1545 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1546 cout
<< wxT("Bookmark");
1549 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1550 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1551 cout
<< wxT("No Change, ");
1552 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1553 cout
<< wxT("Exclusive, ");
1554 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1555 cout
<< wxT("UnLock");
1558 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1559 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1560 cout
<< wxT("Position, ");
1561 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1562 cout
<< wxT("Refresh, ");
1563 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1564 cout
<< wxT("Upd, "));
1565 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1566 cout
<< wxT("Del, ");
1567 if (dbInf
.posOperations
& SQL_POS_ADD
)
1571 cout
<< wxT("Positioned Statements Supported: ");
1572 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1573 cout
<< wxT("Pos delete, ");
1574 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1575 cout
<< wxT("Pos update, ");
1576 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1577 cout
<< wxT("Select for update");
1580 cout
<< wxT("Scroll Concurrency: ");
1581 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1582 cout
<< wxT("Read Only, ");
1583 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1584 cout
<< wxT("Lock, ");
1585 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1586 cout
<< wxT("Opt. Rowver, ");
1587 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1588 cout
<< wxT("Opt. Values");
1591 cout
<< wxT("Scroll Options: ");
1592 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1593 cout
<< wxT("Fwd Only, ");
1594 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1595 cout
<< wxT("Static, ");
1596 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1597 cout
<< wxT("Keyset Driven, ");
1598 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1599 cout
<< wxT("Dynamic, ");
1600 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1601 cout
<< wxT("Mixed");
1604 cout
<< wxT("Static Sensitivity: ");
1605 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1606 cout
<< wxT("Additions, ");
1607 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1608 cout
<< wxT("Deletions, ");
1609 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1610 cout
<< wxT("Updates");
1613 cout
<< wxT("Transaction Capable?: ");
1614 switch(dbInf
.txnCapable
)
1616 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1617 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1618 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1619 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1620 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1627 // Completed Successfully
1630 } // wxDb::getDbInfo()
1633 /********** wxDb::getDataTypeInfo() **********/
1634 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1637 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1638 * the data type inf. is gathered for.
1640 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1645 // Get information about the data type specified
1646 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1647 return(DispAllErrors(henv
, hdbc
, hstmt
));
1650 retcode
= SQLFetch(hstmt
);
1651 if (retcode
!= SQL_SUCCESS
)
1653 #ifdef DBDEBUG_CONSOLE
1654 if (retcode
== SQL_NO_DATA_FOUND
)
1655 cout
<< wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl
;
1657 DispAllErrors(henv
, hdbc
, hstmt
);
1658 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1662 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1664 // Obtain columns from the record
1665 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1666 return(DispAllErrors(henv
, hdbc
, hstmt
));
1668 structSQLTypeInfo
.TypeName
= typeName
;
1670 // BJO 20000503: no more needed with new GetColumns...
1673 if (Dbms() == dbmsMY_SQL
)
1675 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1676 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1677 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1678 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1679 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1680 structSQLTypeInfo
.TypeName
= wxT("int");
1681 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1682 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1683 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1684 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1685 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1686 structSQLTypeInfo
.TypeName
= wxT("char");
1689 // BJO 20000427 : OpenLink driver
1690 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1691 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1693 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1694 structSQLTypeInfo
.TypeName
= wxT("real");
1698 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1699 return(DispAllErrors(henv
, hdbc
, hstmt
));
1700 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1701 return(DispAllErrors(henv
, hdbc
, hstmt
));
1702 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1703 // return(DispAllErrors(henv, hdbc, hstmt));
1705 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1706 return(DispAllErrors(henv
, hdbc
, hstmt
));
1708 if (structSQLTypeInfo
.MaximumScale
< 0)
1709 structSQLTypeInfo
.MaximumScale
= 0;
1711 // Close the statement handle which closes open cursors
1712 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1713 return(DispAllErrors(henv
, hdbc
, hstmt
));
1715 // Completed Successfully
1718 } // wxDb::getDataTypeInfo()
1721 /********** wxDb::Close() **********/
1722 void wxDb::Close(void)
1724 // Close the Sql Log file
1731 // Free statement handle
1734 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1735 DispAllErrors(henv
, hdbc
);
1738 // Disconnect from the datasource
1739 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1740 DispAllErrors(henv
, hdbc
);
1742 // Free the connection to the datasource
1743 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1744 DispAllErrors(henv
, hdbc
);
1746 // There should be zero Ctable objects still connected to this db object
1747 wxASSERT(nTables
== 0);
1751 wxCriticalSectionLocker
lock(csTablesInUse
);
1753 wxList::compatibility_iterator pNode
;
1754 pNode
= TablesInUse
.GetFirst();
1758 tiu
= (wxTablesInUse
*)pNode
->GetData();
1759 if (tiu
->pDb
== this)
1761 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"),
1762 tiu
->tableName
, tiu
->tableID
, wx_static_cast(void*, tiu
->pDb
));
1763 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"), wx_static_cast(void*, this));
1764 wxLogDebug(s
.c_str(),s2
.c_str());
1766 pNode
= pNode
->GetNext();
1771 // Copy the error messages to a global variable
1773 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1774 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1776 dbmsType
= dbmsUNIDENTIFIED
;
1782 /********** wxDb::CommitTrans() **********/
1783 bool wxDb::CommitTrans(void)
1787 // Commit the transaction
1788 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1789 return(DispAllErrors(henv
, hdbc
));
1792 // Completed successfully
1795 } // wxDb::CommitTrans()
1798 /********** wxDb::RollbackTrans() **********/
1799 bool wxDb::RollbackTrans(void)
1801 // Rollback the transaction
1802 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1803 return(DispAllErrors(henv
, hdbc
));
1805 // Completed successfully
1808 } // wxDb::RollbackTrans()
1811 /********** wxDb::DispAllErrors() **********/
1812 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1814 * This function is called internally whenever an error condition prevents the user's
1815 * request from being executed. This function will query the datasource as to the
1816 * actual error(s) that just occurred on the previous request of the datasource.
1818 * The function will retrieve each error condition from the datasource and
1819 * Printf the codes/text values into a string which it then logs via logError().
1820 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1821 * window and program execution will be paused until the user presses a key.
1823 * This function always returns false, so that functions which call this function
1824 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1825 * of the user's request, so that the calling code can then process the error message log.
1828 wxString odbcErrMsg
;
1830 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1832 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1833 sqlState
, (long)nativeError
, errorMsg
);
1834 logError(odbcErrMsg
, sqlState
);
1837 #ifdef DBDEBUG_CONSOLE
1838 // When run in console mode, use standard out to display errors.
1839 cout
<< odbcErrMsg
.c_str() << endl
;
1840 cout
<< wxT("Press any key to continue...") << endl
;
1845 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1850 return false; // This function always returns false.
1852 } // wxDb::DispAllErrors()
1855 /********** wxDb::GetNextError() **********/
1856 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1858 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1863 } // wxDb::GetNextError()
1866 /********** wxDb::DispNextError() **********/
1867 void wxDb::DispNextError(void)
1869 wxString odbcErrMsg
;
1871 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"),
1872 sqlState
, (long)nativeError
, errorMsg
);
1873 logError(odbcErrMsg
, sqlState
);
1878 #ifdef DBDEBUG_CONSOLE
1879 // When run in console mode, use standard out to display errors.
1880 cout
<< odbcErrMsg
.c_str() << endl
;
1881 cout
<< wxT("Press any key to continue...") << endl
;
1886 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1887 #endif // __WXDEBUG__
1889 } // wxDb::DispNextError()
1892 /********** wxDb::logError() **********/
1893 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1895 wxASSERT(errMsg
.length());
1897 static int pLast
= -1;
1900 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1903 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
-1; i
++)
1904 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1908 wxStrncpy(errorList
[pLast
], errMsg
, DB_MAX_ERROR_MSG_LEN
);
1909 errorList
[pLast
][DB_MAX_ERROR_MSG_LEN
] = 0;
1911 if (SQLState
.length())
1912 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1913 DB_STATUS
= dbStatus
;
1915 // Add the errmsg to the sql log
1916 WriteSqlLog(errMsg
);
1918 } // wxDb::logError()
1921 /**********wxDb::TranslateSqlState() **********/
1922 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1924 if (!wxStrcmp(SQLState
, wxT("01000")))
1925 return(DB_ERR_GENERAL_WARNING
);
1926 if (!wxStrcmp(SQLState
, wxT("01002")))
1927 return(DB_ERR_DISCONNECT_ERROR
);
1928 if (!wxStrcmp(SQLState
, wxT("01004")))
1929 return(DB_ERR_DATA_TRUNCATED
);
1930 if (!wxStrcmp(SQLState
, wxT("01006")))
1931 return(DB_ERR_PRIV_NOT_REVOKED
);
1932 if (!wxStrcmp(SQLState
, wxT("01S00")))
1933 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1934 if (!wxStrcmp(SQLState
, wxT("01S01")))
1935 return(DB_ERR_ERROR_IN_ROW
);
1936 if (!wxStrcmp(SQLState
, wxT("01S02")))
1937 return(DB_ERR_OPTION_VALUE_CHANGED
);
1938 if (!wxStrcmp(SQLState
, wxT("01S03")))
1939 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1940 if (!wxStrcmp(SQLState
, wxT("01S04")))
1941 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1942 if (!wxStrcmp(SQLState
, wxT("07001")))
1943 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1944 if (!wxStrcmp(SQLState
, wxT("07006")))
1945 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1946 if (!wxStrcmp(SQLState
, wxT("08001")))
1947 return(DB_ERR_UNABLE_TO_CONNECT
);
1948 if (!wxStrcmp(SQLState
, wxT("08002")))
1949 return(DB_ERR_CONNECTION_IN_USE
);
1950 if (!wxStrcmp(SQLState
, wxT("08003")))
1951 return(DB_ERR_CONNECTION_NOT_OPEN
);
1952 if (!wxStrcmp(SQLState
, wxT("08004")))
1953 return(DB_ERR_REJECTED_CONNECTION
);
1954 if (!wxStrcmp(SQLState
, wxT("08007")))
1955 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1956 if (!wxStrcmp(SQLState
, wxT("08S01")))
1957 return(DB_ERR_COMM_LINK_FAILURE
);
1958 if (!wxStrcmp(SQLState
, wxT("21S01")))
1959 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1960 if (!wxStrcmp(SQLState
, wxT("21S02")))
1961 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1962 if (!wxStrcmp(SQLState
, wxT("22001")))
1963 return(DB_ERR_STRING_RIGHT_TRUNC
);
1964 if (!wxStrcmp(SQLState
, wxT("22003")))
1965 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1966 if (!wxStrcmp(SQLState
, wxT("22005")))
1967 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1968 if (!wxStrcmp(SQLState
, wxT("22008")))
1969 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1970 if (!wxStrcmp(SQLState
, wxT("22012")))
1971 return(DB_ERR_DIVIDE_BY_ZERO
);
1972 if (!wxStrcmp(SQLState
, wxT("22026")))
1973 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1974 if (!wxStrcmp(SQLState
, wxT("23000")))
1975 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1976 if (!wxStrcmp(SQLState
, wxT("24000")))
1977 return(DB_ERR_INVALID_CURSOR_STATE
);
1978 if (!wxStrcmp(SQLState
, wxT("25000")))
1979 return(DB_ERR_INVALID_TRANS_STATE
);
1980 if (!wxStrcmp(SQLState
, wxT("28000")))
1981 return(DB_ERR_INVALID_AUTH_SPEC
);
1982 if (!wxStrcmp(SQLState
, wxT("34000")))
1983 return(DB_ERR_INVALID_CURSOR_NAME
);
1984 if (!wxStrcmp(SQLState
, wxT("37000")))
1985 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1986 if (!wxStrcmp(SQLState
, wxT("3C000")))
1987 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1988 if (!wxStrcmp(SQLState
, wxT("40001")))
1989 return(DB_ERR_SERIALIZATION_FAILURE
);
1990 if (!wxStrcmp(SQLState
, wxT("42000")))
1991 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1992 if (!wxStrcmp(SQLState
, wxT("70100")))
1993 return(DB_ERR_OPERATION_ABORTED
);
1994 if (!wxStrcmp(SQLState
, wxT("IM001")))
1995 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1996 if (!wxStrcmp(SQLState
, wxT("IM002")))
1997 return(DB_ERR_NO_DATA_SOURCE
);
1998 if (!wxStrcmp(SQLState
, wxT("IM003")))
1999 return(DB_ERR_DRIVER_LOAD_ERROR
);
2000 if (!wxStrcmp(SQLState
, wxT("IM004")))
2001 return(DB_ERR_SQLALLOCENV_FAILED
);
2002 if (!wxStrcmp(SQLState
, wxT("IM005")))
2003 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
2004 if (!wxStrcmp(SQLState
, wxT("IM006")))
2005 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
2006 if (!wxStrcmp(SQLState
, wxT("IM007")))
2007 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
2008 if (!wxStrcmp(SQLState
, wxT("IM008")))
2009 return(DB_ERR_DIALOG_FAILED
);
2010 if (!wxStrcmp(SQLState
, wxT("IM009")))
2011 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
2012 if (!wxStrcmp(SQLState
, wxT("IM010")))
2013 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
2014 if (!wxStrcmp(SQLState
, wxT("IM011")))
2015 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
2016 if (!wxStrcmp(SQLState
, wxT("IM012")))
2017 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
2018 if (!wxStrcmp(SQLState
, wxT("IM013")))
2019 return(DB_ERR_TRACE_FILE_ERROR
);
2020 if (!wxStrcmp(SQLState
, wxT("S0001")))
2021 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
2022 if (!wxStrcmp(SQLState
, wxT("S0002")))
2023 return(DB_ERR_TABLE_NOT_FOUND
);
2024 if (!wxStrcmp(SQLState
, wxT("S0011")))
2025 return(DB_ERR_INDEX_ALREADY_EXISTS
);
2026 if (!wxStrcmp(SQLState
, wxT("S0012")))
2027 return(DB_ERR_INDEX_NOT_FOUND
);
2028 if (!wxStrcmp(SQLState
, wxT("S0021")))
2029 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
2030 if (!wxStrcmp(SQLState
, wxT("S0022")))
2031 return(DB_ERR_COLUMN_NOT_FOUND
);
2032 if (!wxStrcmp(SQLState
, wxT("S0023")))
2033 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
2034 if (!wxStrcmp(SQLState
, wxT("S1000")))
2035 return(DB_ERR_GENERAL_ERROR
);
2036 if (!wxStrcmp(SQLState
, wxT("S1001")))
2037 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
2038 if (!wxStrcmp(SQLState
, wxT("S1002")))
2039 return(DB_ERR_INVALID_COLUMN_NUMBER
);
2040 if (!wxStrcmp(SQLState
, wxT("S1003")))
2041 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
2042 if (!wxStrcmp(SQLState
, wxT("S1004")))
2043 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
2044 if (!wxStrcmp(SQLState
, wxT("S1008")))
2045 return(DB_ERR_OPERATION_CANCELLED
);
2046 if (!wxStrcmp(SQLState
, wxT("S1009")))
2047 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
2048 if (!wxStrcmp(SQLState
, wxT("S1010")))
2049 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
2050 if (!wxStrcmp(SQLState
, wxT("S1011")))
2051 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
2052 if (!wxStrcmp(SQLState
, wxT("S1012")))
2053 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
2054 if (!wxStrcmp(SQLState
, wxT("S1015")))
2055 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
2056 if (!wxStrcmp(SQLState
, wxT("S1090")))
2057 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
2058 if (!wxStrcmp(SQLState
, wxT("S1091")))
2059 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
2060 if (!wxStrcmp(SQLState
, wxT("S1092")))
2061 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
2062 if (!wxStrcmp(SQLState
, wxT("S1093")))
2063 return(DB_ERR_INVALID_PARAM_NO
);
2064 if (!wxStrcmp(SQLState
, wxT("S1094")))
2065 return(DB_ERR_INVALID_SCALE_VALUE
);
2066 if (!wxStrcmp(SQLState
, wxT("S1095")))
2067 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
2068 if (!wxStrcmp(SQLState
, wxT("S1096")))
2069 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
2070 if (!wxStrcmp(SQLState
, wxT("S1097")))
2071 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
2072 if (!wxStrcmp(SQLState
, wxT("S1098")))
2073 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
2074 if (!wxStrcmp(SQLState
, wxT("S1099")))
2075 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
2076 if (!wxStrcmp(SQLState
, wxT("S1100")))
2077 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
2078 if (!wxStrcmp(SQLState
, wxT("S1101")))
2079 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
2080 if (!wxStrcmp(SQLState
, wxT("S1103")))
2081 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
2082 if (!wxStrcmp(SQLState
, wxT("S1104")))
2083 return(DB_ERR_INVALID_PRECISION_VALUE
);
2084 if (!wxStrcmp(SQLState
, wxT("S1105")))
2085 return(DB_ERR_INVALID_PARAM_TYPE
);
2086 if (!wxStrcmp(SQLState
, wxT("S1106")))
2087 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
2088 if (!wxStrcmp(SQLState
, wxT("S1107")))
2089 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
2090 if (!wxStrcmp(SQLState
, wxT("S1108")))
2091 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
2092 if (!wxStrcmp(SQLState
, wxT("S1109")))
2093 return(DB_ERR_INVALID_CURSOR_POSITION
);
2094 if (!wxStrcmp(SQLState
, wxT("S1110")))
2095 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
2096 if (!wxStrcmp(SQLState
, wxT("S1111")))
2097 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2098 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2099 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2100 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2101 return(DB_ERR_TIMEOUT_EXPIRED
);
2106 } // wxDb::TranslateSqlState()
2109 /********** wxDb::Grant() **********/
2110 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2114 // Build the grant statement
2115 sqlStmt
= wxT("GRANT ");
2116 if (privileges
== DB_GRANT_ALL
)
2117 sqlStmt
+= wxT("ALL");
2121 if (privileges
& DB_GRANT_SELECT
)
2123 sqlStmt
+= wxT("SELECT");
2126 if (privileges
& DB_GRANT_INSERT
)
2129 sqlStmt
+= wxT(", ");
2130 sqlStmt
+= wxT("INSERT");
2132 if (privileges
& DB_GRANT_UPDATE
)
2135 sqlStmt
+= wxT(", ");
2136 sqlStmt
+= wxT("UPDATE");
2138 if (privileges
& DB_GRANT_DELETE
)
2141 sqlStmt
+= wxT(", ");
2142 sqlStmt
+= wxT("DELETE");
2146 sqlStmt
+= wxT(" ON ");
2147 sqlStmt
+= SQLTableName(tableName
);
2148 sqlStmt
+= wxT(" TO ");
2149 sqlStmt
+= userList
;
2151 #ifdef DBDEBUG_CONSOLE
2152 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2155 WriteSqlLog(sqlStmt
);
2157 return(ExecSql(sqlStmt
));
2162 /********** wxDb::CreateView() **********/
2163 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2164 const wxString
&pSqlStmt
, bool attemptDrop
)
2168 // Drop the view first
2169 if (attemptDrop
&& !DropView(viewName
))
2172 // Build the create view statement
2173 sqlStmt
= wxT("CREATE VIEW ");
2174 sqlStmt
+= viewName
;
2176 if (colList
.length())
2178 sqlStmt
+= wxT(" (");
2180 sqlStmt
+= wxT(")");
2183 sqlStmt
+= wxT(" AS ");
2184 sqlStmt
+= pSqlStmt
;
2186 WriteSqlLog(sqlStmt
);
2188 #ifdef DBDEBUG_CONSOLE
2189 cout
<< sqlStmt
.c_str() << endl
;
2192 return(ExecSql(sqlStmt
));
2194 } // wxDb::CreateView()
2197 /********** wxDb::DropView() **********/
2198 bool wxDb::DropView(const wxString
&viewName
)
2201 * NOTE: This function returns true if the View does not exist, but
2202 * only for identified databases. Code will need to be added
2203 * below for any other databases when those databases are defined
2204 * to handle this situation consistently
2208 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2210 WriteSqlLog(sqlStmt
);
2212 #ifdef DBDEBUG_CONSOLE
2213 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2216 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2218 // Check for "Base table not found" error and ignore
2219 GetNextError(henv
, hdbc
, hstmt
);
2220 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2222 // Check for product specific error codes
2223 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2226 DispAllErrors(henv
, hdbc
, hstmt
);
2233 // Commit the transaction
2239 } // wxDb::DropView()
2242 /********** wxDb::ExecSql() **********/
2243 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2247 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2249 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2250 if (retcode
== SQL_SUCCESS
||
2251 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2257 DispAllErrors(henv
, hdbc
, hstmt
);
2261 } // wxDb::ExecSql()
2264 /********** wxDb::ExecSql() with column info **********/
2265 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2267 //execute the statement first
2268 if (!ExecSql(pSqlStmt
))
2272 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2274 DispAllErrors(henv
, hdbc
, hstmt
);
2283 // Get column information
2285 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2288 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2290 // Fill in column information (name, datatype)
2291 for (colNum
= 0; colNum
< noCols
; colNum
++)
2293 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2295 &Sword
, &Sqllen
) != SQL_SUCCESS
)
2297 DispAllErrors(henv
, hdbc
, hstmt
);
2302 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2303 pColInf
[colNum
].colName
[DB_MAX_COLUMN_NAME_LEN
] = 0; // Prevent buffer overrun
2305 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2306 NULL
, 0, &Sword
, &Sqllen
) != SQL_SUCCESS
)
2308 DispAllErrors(henv
, hdbc
, hstmt
);
2316 #if defined(SQL_WCHAR)
2319 #if defined(SQL_WVARCHAR)
2325 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2327 case SQL_LONGVARCHAR
:
2328 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_MEMO
;
2334 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2341 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2345 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2348 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2353 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen
);
2354 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2361 } // wxDb::ExecSql()
2363 /********** wxDb::GetNext() **********/
2364 bool wxDb::GetNext(void)
2366 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2370 DispAllErrors(henv
, hdbc
, hstmt
);
2374 } // wxDb::GetNext()
2377 /********** wxDb::GetData() **********/
2378 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SQLLEN FAR
*cbReturned
)
2381 wxASSERT(cbReturned
);
2383 long bufferSize
= maxLen
;
2385 if (cType
== SQL_C_WXCHAR
)
2386 bufferSize
= maxLen
* sizeof(wxChar
);
2388 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2392 DispAllErrors(henv
, hdbc
, hstmt
);
2396 } // wxDb::GetData()
2399 /********** wxDb::GetKeyFields() **********/
2400 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2402 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2403 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2405 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2406 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2412 * -----------------------------------------------------------------------
2413 * -- 19991224 : mj10777 : Create ------
2414 * -- : Three things are done and stored here : ------
2415 * -- : 1) which Column(s) is/are Primary Key(s) ------
2416 * -- : 2) which tables use this Key as a Foreign Key ------
2417 * -- : 3) which columns are Foreign Key and the name ------
2418 * -- : of the Table where the Key is the Primary Key -----
2419 * -- : Called from GetColumns(const wxString &tableName, ------
2420 * -- int *numCols,const wxChar *userID ) ------
2421 * -----------------------------------------------------------------------
2424 /*---------------------------------------------------------------------*/
2425 /* Get the names of the columns in the primary key. */
2426 /*---------------------------------------------------------------------*/
2427 retcode
= SQLPrimaryKeys(hstmt
,
2428 NULL
, 0, /* Catalog name */
2429 NULL
, 0, /* Schema name */
2430 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2432 /*---------------------------------------------------------------------*/
2433 /* Fetch and display the result set. This will be a list of the */
2434 /* columns in the primary key of the tableName table. */
2435 /*---------------------------------------------------------------------*/
2436 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2438 retcode
= SQLFetch(hstmt
);
2439 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2441 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2442 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2444 for (i
=0;i
<noCols
;i
++) // Find the Column name
2445 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2446 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2449 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2451 /*---------------------------------------------------------------------*/
2452 /* Get all the foreign keys that refer to tableName primary key. */
2453 /*---------------------------------------------------------------------*/
2454 retcode
= SQLForeignKeys(hstmt
,
2455 NULL
, 0, /* Primary catalog */
2456 NULL
, 0, /* Primary schema */
2457 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2458 NULL
, 0, /* Foreign catalog */
2459 NULL
, 0, /* Foreign schema */
2460 NULL
, 0); /* Foreign table */
2462 /*---------------------------------------------------------------------*/
2463 /* Fetch and display the result set. This will be all of the foreign */
2464 /* keys in other tables that refer to the tableName primary key. */
2465 /*---------------------------------------------------------------------*/
2468 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2470 retcode
= SQLFetch(hstmt
);
2471 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2473 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2474 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2475 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2476 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2477 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2478 tempStr
<< _T('[') << szFkTable
<< _T(']'); // [ ] in case there is a blank in the Table name
2482 tempStr
.Trim(); // Get rid of any unneeded blanks
2483 if (!tempStr
.empty())
2485 for (i
=0; i
<noCols
; i
++)
2486 { // Find the Column name
2487 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2489 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
2490 colInf
[i
].PkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2495 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2497 /*---------------------------------------------------------------------*/
2498 /* Get all the foreign keys in the tablename table. */
2499 /*---------------------------------------------------------------------*/
2500 retcode
= SQLForeignKeys(hstmt
,
2501 NULL
, 0, /* Primary catalog */
2502 NULL
, 0, /* Primary schema */
2503 NULL
, 0, /* Primary table */
2504 NULL
, 0, /* Foreign catalog */
2505 NULL
, 0, /* Foreign schema */
2506 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2508 /*---------------------------------------------------------------------*/
2509 /* Fetch and display the result set. This will be all of the */
2510 /* primary keys in other tables that are referred to by foreign */
2511 /* keys in the tableName table. */
2512 /*---------------------------------------------------------------------*/
2513 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2515 retcode
= SQLFetch(hstmt
);
2516 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2518 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2519 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2520 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2522 for (i
=0; i
<noCols
; i
++) // Find the Column name
2524 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2526 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2527 wxStrncpy(colInf
[i
].FkTableName
, szFkTable
, DB_MAX_TABLE_NAME_LEN
); // Name of the Table where this Foriegn is the Primary Key
2528 colInf
[i
].FkTableName
[DB_MAX_TABLE_NAME_LEN
] = 0; // Prevent buffer overrun
2533 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2537 } // wxDb::GetKeyFields()
2541 /********** wxDb::GetColumns() **********/
2542 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2544 * 1) The last array element of the tableName[] argument must be zero (null).
2545 * This is how the end of the array is detected.
2546 * 2) This function returns an array of wxDbColInf structures. If no columns
2547 * were found, or an error occurred, this pointer will be zero (null). THE
2548 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2549 * IS FINISHED WITH IT. i.e.
2551 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2554 * // Use the column inf
2556 * // Destroy the memory
2560 * userID is evaluated in the following manner:
2561 * userID == NULL ... UserID is ignored
2562 * userID == "" ... UserID set equal to 'this->uid'
2563 * userID != "" ... UserID set equal to 'userID'
2565 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2566 * by this function. This function should use its own wxDb instance
2567 * to avoid undesired unbinding of columns.
2572 wxDbColInf
*colInf
= 0;
2580 convertUserID(userID
,UserID
);
2582 // Pass 1 - Determine how many columns there are.
2583 // Pass 2 - Allocate the wxDbColInf array and fill in
2584 // the array with the column information.
2586 for (pass
= 1; pass
<= 2; pass
++)
2590 if (noCols
== 0) // Probably a bogus table name(s)
2592 // Allocate n wxDbColInf objects to hold the column information
2593 colInf
= new wxDbColInf
[noCols
+1];
2596 // Mark the end of the array
2597 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2598 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2599 colInf
[noCols
].sqlDataType
= 0;
2601 // Loop through each table name
2603 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2605 TableName
= tableName
[tbl
];
2606 // Oracle and Interbase table names are uppercase only, so force
2607 // the name to uppercase just in case programmer forgot to do this
2608 if ((Dbms() == dbmsORACLE
) ||
2609 (Dbms() == dbmsFIREBIRD
) ||
2610 (Dbms() == dbmsINTERBASE
))
2611 TableName
= TableName
.Upper();
2613 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2615 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2616 // use the call below that leaves out the user name
2617 if (!UserID
.empty() &&
2618 Dbms() != dbmsMY_SQL
&&
2619 Dbms() != dbmsACCESS
&&
2620 Dbms() != dbmsMS_SQL_SERVER
)
2622 retcode
= SQLColumns(hstmt
,
2623 NULL
, 0, // All qualifiers
2624 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2625 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2626 NULL
, 0); // All columns
2630 retcode
= SQLColumns(hstmt
,
2631 NULL
, 0, // All qualifiers
2633 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2634 NULL
, 0); // All columns
2636 if (retcode
!= SQL_SUCCESS
)
2637 { // Error occurred, abort
2638 DispAllErrors(henv
, hdbc
, hstmt
);
2641 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2645 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2647 if (pass
== 1) // First pass, just add up the number of columns
2649 else // Pass 2; Fill in the array of structures
2651 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2653 // NOTE: Only the ODBC 1.x fields are retrieved
2654 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2655 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2656 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2657 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2658 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2659 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2660 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2661 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2662 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2663 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2664 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2665 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2667 // Determine the wxDb data type that is used to represent the native data type of this data source
2668 colInf
[colNo
].dbDataType
= 0;
2669 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2672 // IODBC does not return a correct columnLength, so we set
2673 // columnLength = bufferSize if no column length was returned
2674 // IODBC returns the columnLength in bufferSize. (bug)
2675 if (colInf
[colNo
].columnLength
< 1)
2677 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2680 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2682 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2683 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2684 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2685 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2686 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2687 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2688 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2689 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2694 if (retcode
!= SQL_NO_DATA_FOUND
)
2695 { // Error occurred, abort
2696 DispAllErrors(henv
, hdbc
, hstmt
);
2699 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2705 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2708 } // wxDb::GetColumns()
2711 /********** wxDb::GetColumns() **********/
2713 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2715 // Same as the above GetColumns() function except this one gets columns
2716 // only for a single table, and if 'numCols' is not NULL, the number of
2717 // columns stored in the returned wxDbColInf is set in '*numCols'
2719 // userID is evaluated in the following manner:
2720 // userID == NULL ... UserID is ignored
2721 // userID == "" ... UserID set equal to 'this->uid'
2722 // userID != "" ... UserID set equal to 'userID'
2724 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2725 // by this function. This function should use its own wxDb instance
2726 // to avoid undesired unbinding of columns.
2731 wxDbColInf
*colInf
= 0;
2739 convertUserID(userID
,UserID
);
2741 // Pass 1 - Determine how many columns there are.
2742 // Pass 2 - Allocate the wxDbColInf array and fill in
2743 // the array with the column information.
2745 for (pass
= 1; pass
<= 2; pass
++)
2749 if (noCols
== 0) // Probably a bogus table name(s)
2751 // Allocate n wxDbColInf objects to hold the column information
2752 colInf
= new wxDbColInf
[noCols
+1];
2755 // Mark the end of the array
2756 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2757 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2758 colInf
[noCols
].sqlDataType
= 0;
2761 TableName
= tableName
;
2762 // Oracle and Interbase table names are uppercase only, so force
2763 // the name to uppercase just in case programmer forgot to do this
2764 if ((Dbms() == dbmsORACLE
) ||
2765 (Dbms() == dbmsFIREBIRD
) ||
2766 (Dbms() == dbmsINTERBASE
))
2767 TableName
= TableName
.Upper();
2769 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2771 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2772 // use the call below that leaves out the user name
2773 if (!UserID
.empty() &&
2774 Dbms() != dbmsMY_SQL
&&
2775 Dbms() != dbmsACCESS
&&
2776 Dbms() != dbmsMS_SQL_SERVER
)
2778 retcode
= SQLColumns(hstmt
,
2779 NULL
, 0, // All qualifiers
2780 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2781 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2782 NULL
, 0); // All columns
2786 retcode
= SQLColumns(hstmt
,
2787 NULL
, 0, // All qualifiers
2789 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2790 NULL
, 0); // All columns
2792 if (retcode
!= SQL_SUCCESS
)
2793 { // Error occurred, abort
2794 DispAllErrors(henv
, hdbc
, hstmt
);
2797 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2803 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2805 if (pass
== 1) // First pass, just add up the number of columns
2807 else // Pass 2; Fill in the array of structures
2809 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2811 // NOTE: Only the ODBC 1.x fields are retrieved
2812 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2813 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2814 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2815 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2816 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2817 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2818 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2819 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2820 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2821 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2822 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2823 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2824 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2825 // Start Values for Primary/Foriegn Key (=No)
2826 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2827 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2828 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2829 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2831 // BJO 20000428 : Virtuoso returns type names with upper cases!
2832 if (Dbms() == dbmsVIRTUOSO
)
2834 wxString s
= colInf
[colNo
].typeName
;
2836 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2839 // Determine the wxDb data type that is used to represent the native data type of this data source
2840 colInf
[colNo
].dbDataType
= 0;
2841 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2844 // IODBC does not return a correct columnLength, so we set
2845 // columnLength = bufferSize if no column length was returned
2846 // IODBC returns the columnLength in bufferSize. (bug)
2847 if (colInf
[colNo
].columnLength
< 1)
2849 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2853 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2855 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2856 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2857 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2858 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2859 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2860 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2861 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2862 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2868 if (retcode
!= SQL_NO_DATA_FOUND
)
2869 { // Error occurred, abort
2870 DispAllErrors(henv
, hdbc
, hstmt
);
2873 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2880 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2882 // Store Primary and Foriegn Keys
2883 GetKeyFields(tableName
,colInf
,noCols
);
2889 } // wxDb::GetColumns()
2892 #else // New GetColumns
2897 These are tentative new GetColumns members which should be more database
2898 independent and which always returns the columns in the order they were
2901 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2902 wxChar* userID)) calls the second implementation for each separate table
2903 before merging the results. This makes the code easier to maintain as
2904 only one member (the second) makes the real work
2905 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2906 wxChar *userID) is a little bit improved
2907 - It doesn't anymore rely on the type-name to find out which database-type
2909 - It ends by sorting the columns, so that they are returned in the same
2910 order they were created
2920 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2923 // The last array element of the tableName[] argument must be zero (null).
2924 // This is how the end of the array is detected.
2928 // How many tables ?
2930 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2932 // Create a table to maintain the columns for each separate table
2933 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2936 for (i
= 0 ; i
< tbl
; i
++)
2939 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2940 if (TableColumns
[i
].colInf
== NULL
)
2942 noCols
+= TableColumns
[i
].noCols
;
2945 // Now merge all the separate table infos
2946 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2948 // Mark the end of the array
2949 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2950 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2951 colInf
[noCols
].sqlDataType
= 0;
2956 for (i
= 0 ; i
< tbl
; i
++)
2958 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2960 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2964 delete [] TableColumns
;
2967 } // wxDb::GetColumns() -- NEW
2970 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2972 // Same as the above GetColumns() function except this one gets columns
2973 // only for a single table, and if 'numCols' is not NULL, the number of
2974 // columns stored in the returned wxDbColInf is set in '*numCols'
2976 // userID is evaluated in the following manner:
2977 // userID == NULL ... UserID is ignored
2978 // userID == "" ... UserID set equal to 'this->uid'
2979 // userID != "" ... UserID set equal to 'userID'
2981 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2982 // by this function. This function should use its own wxDb instance
2983 // to avoid undesired unbinding of columns.
2987 wxDbColInf
*colInf
= 0;
2995 convertUserID(userID
,UserID
);
2997 // Pass 1 - Determine how many columns there are.
2998 // Pass 2 - Allocate the wxDbColInf array and fill in
2999 // the array with the column information.
3001 for (pass
= 1; pass
<= 2; pass
++)
3005 if (noCols
== 0) // Probably a bogus table name(s)
3007 // Allocate n wxDbColInf objects to hold the column information
3008 colInf
= new wxDbColInf
[noCols
+1];
3011 // Mark the end of the array
3012 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
3013 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
3014 colInf
[noCols
].sqlDataType
= 0;
3017 TableName
= tableName
;
3018 // Oracle and Interbase table names are uppercase only, so force
3019 // the name to uppercase just in case programmer forgot to do this
3020 if ((Dbms() == dbmsORACLE
) ||
3021 (Dbms() == dbmsFIREBIRD
) ||
3022 (Dbms() == dbmsINTERBASE
))
3023 TableName
= TableName
.Upper();
3025 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3027 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3028 // use the call below that leaves out the user name
3029 if (!UserID
.empty() &&
3030 Dbms() != dbmsMY_SQL
&&
3031 Dbms() != dbmsACCESS
&&
3032 Dbms() != dbmsMS_SQL_SERVER
)
3034 retcode
= SQLColumns(hstmt
,
3035 NULL
, 0, // All qualifiers
3036 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3037 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3038 NULL
, 0); // All columns
3042 retcode
= SQLColumns(hstmt
,
3043 NULL
, 0, // All qualifiers
3045 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
3046 NULL
, 0); // All columns
3048 if (retcode
!= SQL_SUCCESS
)
3049 { // Error occurred, abort
3050 DispAllErrors(henv
, hdbc
, hstmt
);
3053 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3059 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3061 if (pass
== 1) // First pass, just add up the number of columns
3063 else // Pass 2; Fill in the array of structures
3065 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
3067 // NOTE: Only the ODBC 1.x fields are retrieved
3068 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
3069 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
3070 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3071 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
3072 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
3073 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
3074 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
3075 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
3076 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
3077 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
3078 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
3079 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
3080 // Start Values for Primary/Foriegn Key (=No)
3081 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3082 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3083 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3084 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
3087 // IODBC does not return a correct columnLength, so we set
3088 // columnLength = bufferSize if no column length was returned
3089 // IODBC returns the columnLength in bufferSize. (bug)
3090 if (colInf
[colNo
].columnLength
< 1)
3092 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
3096 // Determine the wxDb data type that is used to represent the native data type of this data source
3097 colInf
[colNo
].dbDataType
= 0;
3098 // Get the intern datatype
3099 switch (colInf
[colNo
].sqlDataType
)
3102 #if defined(SQL_WCHAR)
3105 #if defined(SQL_WVARCHAR)
3111 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
3113 case SQL_LONGVARCHAR
:
3114 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_MEMO
;
3120 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3127 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3131 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3134 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3139 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3140 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3147 if (retcode
!= SQL_NO_DATA_FOUND
)
3148 { // Error occurred, abort
3149 DispAllErrors(henv
, hdbc
, hstmt
);
3152 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3159 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3161 // Store Primary and Foreign Keys
3162 GetKeyFields(tableName
,colInf
,noCols
);
3164 ///////////////////////////////////////////////////////////////////////////
3165 // Now sort the the columns in order to make them appear in the right order
3166 ///////////////////////////////////////////////////////////////////////////
3168 // Build a generic SELECT statement which returns 0 rows
3171 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3174 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3176 DispAllErrors(henv
, hdbc
, hstmt
);
3180 // Get the number of result columns
3181 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3183 DispAllErrors(henv
, hdbc
, hstmt
);
3187 if (noCols
== 0) // Probably a bogus table name
3196 for (colNum
= 0; colNum
< noCols
; colNum
++)
3198 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3200 &Sword
, &Sdword
) != SQL_SUCCESS
)
3202 DispAllErrors(henv
, hdbc
, hstmt
);
3206 wxString Name1
= name
;
3207 Name1
= Name1
.Upper();
3209 // Where is this name in the array ?
3210 for (i
= colNum
; i
< noCols
; i
++)
3212 wxString Name2
= colInf
[i
].colName
;
3213 Name2
= Name2
.Upper();
3216 if (colNum
!= i
) // swap to sort
3218 wxDbColInf tmpColInf
= colInf
[colNum
];
3219 colInf
[colNum
] = colInf
[i
];
3220 colInf
[i
] = tmpColInf
;
3226 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3228 ///////////////////////////////////////////////////////////////////////////
3230 ///////////////////////////////////////////////////////////////////////////
3236 } // wxDb::GetColumns()
3239 #endif // #else OLD_GETCOLUMNS
3242 /********** wxDb::GetColumnCount() **********/
3243 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3245 * Returns a count of how many columns are in a table.
3246 * If an error occurs in computing the number of columns
3247 * this function will return a -1 for the count
3249 * userID is evaluated in the following manner:
3250 * userID == NULL ... UserID is ignored
3251 * userID == "" ... UserID set equal to 'this->uid'
3252 * userID != "" ... UserID set equal to 'userID'
3254 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3255 * by this function. This function should use its own wxDb instance
3256 * to avoid undesired unbinding of columns.
3266 convertUserID(userID
,UserID
);
3268 TableName
= tableName
;
3269 // Oracle and Interbase table names are uppercase only, so force
3270 // the name to uppercase just in case programmer forgot to do this
3271 if ((Dbms() == dbmsORACLE
) ||
3272 (Dbms() == dbmsFIREBIRD
) ||
3273 (Dbms() == dbmsINTERBASE
))
3274 TableName
= TableName
.Upper();
3276 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3278 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3279 // use the call below that leaves out the user name
3280 if (!UserID
.empty() &&
3281 Dbms() != dbmsMY_SQL
&&
3282 Dbms() != dbmsACCESS
&&
3283 Dbms() != dbmsMS_SQL_SERVER
)
3285 retcode
= SQLColumns(hstmt
,
3286 NULL
, 0, // All qualifiers
3287 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3288 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3289 NULL
, 0); // All columns
3293 retcode
= SQLColumns(hstmt
,
3294 NULL
, 0, // All qualifiers
3296 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3297 NULL
, 0); // All columns
3299 if (retcode
!= SQL_SUCCESS
)
3300 { // Error occurred, abort
3301 DispAllErrors(henv
, hdbc
, hstmt
);
3302 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3306 // Count the columns
3307 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3310 if (retcode
!= SQL_NO_DATA_FOUND
)
3311 { // Error occurred, abort
3312 DispAllErrors(henv
, hdbc
, hstmt
);
3313 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3317 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3320 } // wxDb::GetColumnCount()
3323 /********** wxDb::GetCatalog() *******/
3324 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3326 * ---------------------------------------------------------------------
3327 * -- 19991203 : mj10777 : Create ------
3328 * -- : Creates a wxDbInf with Tables / Cols Array ------
3329 * -- : uses SQLTables and fills pTableInf; ------
3330 * -- : pColInf is set to NULL and numCols to 0; ------
3331 * -- : returns pDbInf (wxDbInf) ------
3332 * -- - if unsuccessful (pDbInf == NULL) ------
3333 * -- : pColInf can be filled with GetColumns(..); ------
3334 * -- : numCols can be filled with GetColumnCount(..); ------
3335 * ---------------------------------------------------------------------
3337 * userID is evaluated in the following manner:
3338 * userID == NULL ... UserID is ignored
3339 * userID == "" ... UserID set equal to 'this->uid'
3340 * userID != "" ... UserID set equal to 'userID'
3342 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3343 * by this function. This function should use its own wxDb instance
3344 * to avoid undesired unbinding of columns.
3347 int noTab
= 0; // Counter while filling table entries
3351 wxString tblNameSave
;
3354 convertUserID(userID
,UserID
);
3356 //-------------------------------------------------------------
3357 // Create the Database Array of catalog entries
3359 wxDbInf
*pDbInf
= new wxDbInf
;
3361 //-------------------------------------------------------------
3362 // Table Information
3363 // Pass 1 - Determine how many Tables there are.
3364 // Pass 2 - Create the Table array and fill it
3365 // - Create the Cols array = NULL
3366 //-------------------------------------------------------------
3368 for (pass
= 1; pass
<= 2; pass
++)
3370 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3371 tblNameSave
.Empty();
3373 if (!UserID
.empty() &&
3374 Dbms() != dbmsMY_SQL
&&
3375 Dbms() != dbmsACCESS
&&
3376 Dbms() != dbmsMS_SQL_SERVER
)
3378 retcode
= SQLTables(hstmt
,
3379 NULL
, 0, // All qualifiers
3380 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3381 NULL
, 0, // All tables
3382 NULL
, 0); // All columns
3386 retcode
= SQLTables(hstmt
,
3387 NULL
, 0, // All qualifiers
3388 NULL
, 0, // User specified
3389 NULL
, 0, // All tables
3390 NULL
, 0); // All columns
3393 if (retcode
!= SQL_SUCCESS
)
3395 DispAllErrors(henv
, hdbc
, hstmt
);
3397 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3401 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3403 if (pass
== 1) // First pass, just count the Tables
3405 if (pDbInf
->numTables
== 0)
3407 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3408 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3410 pDbInf
->numTables
++; // Counter for Tables
3412 if (pass
== 2) // Create and fill the Table entries
3414 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3415 { // no, then create the Array
3416 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3418 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3420 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3421 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3422 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3428 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3430 // Query how many columns are in each table
3431 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3433 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3438 } // wxDb::GetCatalog()
3441 /********** wxDb::Catalog() **********/
3442 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3444 * Creates the text file specified in 'filename' which will contain
3445 * a minimal data dictionary of all tables accessible by the user specified
3448 * userID is evaluated in the following manner:
3449 * userID == NULL ... UserID is ignored
3450 * userID == "" ... UserID set equal to 'this->uid'
3451 * userID != "" ... UserID set equal to 'userID'
3453 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3454 * by this function. This function should use its own wxDb instance
3455 * to avoid undesired unbinding of columns.
3458 wxASSERT(fileName
.length());
3462 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3463 wxString tblNameSave
;
3464 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3466 wxChar typeName
[30+1];
3467 SDWORD precision
, length
;
3469 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3473 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3476 convertUserID(userID
,UserID
);
3478 if (!UserID
.empty() &&
3479 Dbms() != dbmsMY_SQL
&&
3480 Dbms() != dbmsACCESS
&&
3481 Dbms() != dbmsFIREBIRD
&&
3482 Dbms() != dbmsINTERBASE
&&
3483 Dbms() != dbmsMS_SQL_SERVER
)
3485 retcode
= SQLColumns(hstmt
,
3486 NULL
, 0, // All qualifiers
3487 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3488 NULL
, 0, // All tables
3489 NULL
, 0); // All columns
3493 retcode
= SQLColumns(hstmt
,
3494 NULL
, 0, // All qualifiers
3495 NULL
, 0, // User specified
3496 NULL
, 0, // All tables
3497 NULL
, 0); // All columns
3499 if (retcode
!= SQL_SUCCESS
)
3501 DispAllErrors(henv
, hdbc
, hstmt
);
3507 tblNameSave
.Empty();
3512 retcode
= SQLFetch(hstmt
);
3513 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3516 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3517 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3518 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3519 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3520 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3521 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3523 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3526 wxFputs(wxT("\n"), fp
);
3527 wxFputs(wxT("================================ "), fp
);
3528 wxFputs(wxT("================================ "), fp
);
3529 wxFputs(wxT("===================== "), fp
);
3530 wxFputs(wxT("========= "), fp
);
3531 wxFputs(wxT("=========\n"), fp
);
3532 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3533 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3534 wxFputs(outStr
.c_str(), fp
);
3535 wxFputs(wxT("================================ "), fp
);
3536 wxFputs(wxT("================================ "), fp
);
3537 wxFputs(wxT("===================== "), fp
);
3538 wxFputs(wxT("========= "), fp
);
3539 wxFputs(wxT("=========\n"), fp
);
3540 tblNameSave
= tblName
;
3543 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3544 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3545 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3547 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3554 if (retcode
!= SQL_NO_DATA_FOUND
)
3555 DispAllErrors(henv
, hdbc
, hstmt
);
3557 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3560 return(retcode
== SQL_NO_DATA_FOUND
);
3562 } // wxDb::Catalog()
3565 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3567 * Table name can refer to a table, view, alias or synonym. Returns true
3568 * if the object exists in the database. This function does not indicate
3569 * whether or not the user has privleges to query or perform other functions
3572 * userID is evaluated in the following manner:
3573 * userID == NULL ... UserID is ignored
3574 * userID == "" ... UserID set equal to 'this->uid'
3575 * userID != "" ... UserID set equal to 'userID'
3578 wxASSERT(tableName
.length());
3582 if (Dbms() == dbmsDBASE
)
3585 if (tablePath
.length())
3586 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3588 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3591 exists
= wxFileExists(dbName
);
3596 convertUserID(userID
,UserID
);
3598 TableName
= tableName
;
3599 // Oracle and Interbase table names are uppercase only, so force
3600 // the name to uppercase just in case programmer forgot to do this
3601 if ((Dbms() == dbmsORACLE
) ||
3602 (Dbms() == dbmsFIREBIRD
) ||
3603 (Dbms() == dbmsINTERBASE
))
3604 TableName
= TableName
.Upper();
3606 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3609 // Some databases cannot accept a user name when looking up table names,
3610 // so we use the call below that leaves out the user name
3611 if (!UserID
.empty() &&
3612 Dbms() != dbmsMY_SQL
&&
3613 Dbms() != dbmsACCESS
&&
3614 Dbms() != dbmsMS_SQL_SERVER
&&
3615 Dbms() != dbmsDB2
&&
3616 Dbms() != dbmsFIREBIRD
&&
3617 Dbms() != dbmsINTERBASE
&&
3618 Dbms() != dbmsPERVASIVE_SQL
)
3620 retcode
= SQLTables(hstmt
,
3621 NULL
, 0, // All qualifiers
3622 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3623 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3624 NULL
, 0); // All table types
3628 retcode
= SQLTables(hstmt
,
3629 NULL
, 0, // All qualifiers
3630 NULL
, 0, // All owners
3631 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3632 NULL
, 0); // All table types
3634 if (retcode
!= SQL_SUCCESS
)
3635 return(DispAllErrors(henv
, hdbc
, hstmt
));
3637 retcode
= SQLFetch(hstmt
);
3638 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3640 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3641 return(DispAllErrors(henv
, hdbc
, hstmt
));
3644 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3648 } // wxDb::TableExists()
3651 /********** wxDb::TablePrivileges() **********/
3652 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3653 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3655 wxASSERT(tableName
.length());
3657 wxDbTablePrivilegeInfo result
;
3661 // We probably need to be able to dynamically set this based on
3662 // the driver type, and state.
3663 wxChar curRole
[]=wxT("public");
3667 wxString UserID
,Schema
;
3668 convertUserID(userID
,UserID
);
3669 convertUserID(schema
,Schema
);
3671 TableName
= tableName
;
3672 // Oracle and Interbase table names are uppercase only, so force
3673 // the name to uppercase just in case programmer forgot to do this
3674 if ((Dbms() == dbmsORACLE
) ||
3675 (Dbms() == dbmsFIREBIRD
) ||
3676 (Dbms() == dbmsINTERBASE
))
3677 TableName
= TableName
.Upper();
3679 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3681 // Some databases cannot accept a user name when looking up table names,
3682 // so we use the call below that leaves out the user name
3683 if (!Schema
.empty() &&
3684 Dbms() != dbmsMY_SQL
&&
3685 Dbms() != dbmsACCESS
&&
3686 Dbms() != dbmsMS_SQL_SERVER
)
3688 retcode
= SQLTablePrivileges(hstmt
,
3690 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3691 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3695 retcode
= SQLTablePrivileges(hstmt
,
3698 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3701 #ifdef DBDEBUG_CONSOLE
3702 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3705 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3706 return (DispAllErrors(henv
, hdbc
, hstmt
));
3708 bool failed
= false;
3709 retcode
= SQLFetch(hstmt
);
3710 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3712 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3715 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3718 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3721 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3724 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3727 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3730 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3735 return(DispAllErrors(henv
, hdbc
, hstmt
));
3737 #ifdef DBDEBUG_CONSOLE
3738 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3739 result
.privilege
,result
.tableOwner
,result
.tableName
,
3740 result
.grantor
, result
.grantee
);
3743 if (UserID
.IsSameAs(result
.tableOwner
,false))
3745 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3749 if (UserID
.IsSameAs(result
.grantee
,false) &&
3750 !wxStrcmp(result
.privilege
,priv
))
3752 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3756 if (!wxStrcmp(result
.grantee
,curRole
) &&
3757 !wxStrcmp(result
.privilege
,priv
))
3759 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3763 retcode
= SQLFetch(hstmt
);
3766 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3769 } // wxDb::TablePrivileges
3772 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3776 if (Dbms() == dbmsACCESS
)
3777 TableName
= _T("\"");
3778 TableName
+= tableName
;
3779 if (Dbms() == dbmsACCESS
)
3780 TableName
+= _T("\"");
3783 } // wxDb::SQLTableName()
3786 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3790 if (Dbms() == dbmsACCESS
)
3793 if (Dbms() == dbmsACCESS
)
3794 ColName
+= _T("\"");
3797 } // wxDb::SQLColumnName()
3800 /********** wxDb::SetSqlLogging() **********/
3801 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3803 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3804 wxASSERT(state
== sqlLogOFF
|| filename
.length());
3806 if (state
== sqlLogON
)
3810 fpSqlLog
= wxFopen(filename
.c_str(), (append
? wxT("at") : wxT("wt")));
3811 if (fpSqlLog
== NULL
)
3819 if (fclose(fpSqlLog
))
3825 sqlLogState
= state
;
3828 } // wxDb::SetSqlLogging()
3831 /********** wxDb::WriteSqlLog() **********/
3832 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3834 wxASSERT(logMsg
.length());
3836 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3839 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3841 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3843 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3848 } // wxDb::WriteSqlLog()
3851 /********** wxDb::Dbms() **********/
3852 wxDBMS
wxDb::Dbms(void)
3854 * Be aware that not all database engines use the exact same syntax, and not
3855 * every ODBC compliant database is compliant to the same level of compliancy.
3856 * Some manufacturers support the minimum Level 1 compliancy, and others up
3857 * through Level 3. Others support subsets of features for levels above 1.
3859 * If you find an inconsistency between the wxDb class and a specific database
3860 * engine, and an identifier to this section, and special handle the database in
3861 * the area where behavior is non-conforming with the other databases.
3864 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3865 * ---------------------------------------------------
3868 * - Currently the only database supported by the class to support VIEWS
3871 * - Does not support the SQL_TIMESTAMP structure
3872 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3873 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3874 * is true. The user must create ALL indexes from their program.
3875 * - Table names can only be 8 characters long
3876 * - Column names can only be 10 characters long
3879 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3880 * after every table name involved in the query/join if that tables matching record(s)
3882 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3884 * SYBASE (Enterprise)
3885 * - If a column is part of the Primary Key, the column cannot be NULL
3886 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3889 * - If a column is part of the Primary Key, the column cannot be NULL
3890 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3891 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3892 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3893 * column definition if it is not defined correctly, but it is experimental
3894 * - Does not support sub-queries in SQL statements
3897 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3898 * - Does not support sub-queries in SQL statements
3901 * - Primary keys must be declared as NOT NULL
3902 * - Table and index names must not be longer than 13 characters in length (technically
3903 * table names can be up to 18 characters, but the primary index is created using the
3904 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3909 * - Columns that are part of primary keys must be defined as being NOT NULL
3910 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3911 * column definition if it is not defined correctly, but it is experimental
3914 // Should only need to do this once for each new database connection
3915 // so return the value we already determined it to be to save time
3916 // and lots of string comparisons
3917 if (dbmsType
!= dbmsUNIDENTIFIED
)
3920 #ifdef DBDEBUG_CONSOLE
3921 // When run in console mode, use standard out to display errors.
3922 cout
<< "Database connecting to: " << dbInf
.dbmsName
<< endl
;
3923 #endif // DBDEBUG_CONSOLE
3925 wxLogDebug(wxT("Database connecting to: "));
3926 wxLogDebug(dbInf
.dbmsName
);
3928 wxChar baseName
[25+1];
3929 wxStrncpy(baseName
, dbInf
.dbmsName
, 25);
3932 // RGG 20001025 : add support for Interbase
3933 // GT : Integrated to base classes on 20001121
3934 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3935 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3937 // BJO 20000428 : add support for Virtuoso
3938 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3939 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3941 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3942 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3944 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3945 // connected through an OpenLink driver.
3946 // Is it also returned by Sybase Adapatitve server?
3947 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3948 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3950 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3951 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3952 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3954 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3957 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3958 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3961 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3962 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3965 if (!wxStricmp(baseName
,wxT("Pervasive")))
3966 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3969 if (!wxStricmp(baseName
,wxT("Informix")))
3970 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3972 if (!wxStricmp(baseName
,wxT("Firebird")))
3973 return((wxDBMS
)(dbmsType
= dbmsFIREBIRD
));
3976 if (!wxStricmp(baseName
,wxT("Oracle")))
3977 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3978 if (!wxStricmp(baseName
,wxT("ACCESS")))
3979 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3980 if (!wxStricmp(baseName
,wxT("Sybase")))
3981 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3984 if (!wxStricmp(baseName
,wxT("DBASE")))
3985 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3986 if (!wxStricmp(baseName
,wxT("xBase")))
3987 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3988 if (!wxStricmp(baseName
,wxT("MySQL")))
3989 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3990 if (!wxStricmp(baseName
,wxT("MaxDB")))
3991 return((wxDBMS
)(dbmsType
= dbmsMAXDB
));
3994 if (!wxStricmp(baseName
,wxT("DB2")))
3995 return((wxDBMS
)(dbmsType
= dbmsDB2
));
3997 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
4002 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
4003 int dataType
, ULONG columnLength
,
4004 const wxString
&optionalParam
)
4006 wxASSERT(tableName
.length());
4007 wxASSERT(columnName
.length());
4008 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
4009 dataType
!= DB_DATA_TYPE_VARCHAR
);
4011 // Must specify a columnLength if modifying a VARCHAR type column
4012 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
4015 wxString dataTypeName
;
4017 wxString alterSlashModify
;
4021 case DB_DATA_TYPE_VARCHAR
:
4022 dataTypeName
= typeInfVarchar
.TypeName
;
4024 case DB_DATA_TYPE_INTEGER
:
4025 dataTypeName
= typeInfInteger
.TypeName
;
4027 case DB_DATA_TYPE_FLOAT
:
4028 dataTypeName
= typeInfFloat
.TypeName
;
4030 case DB_DATA_TYPE_DATE
:
4031 dataTypeName
= typeInfDate
.TypeName
;
4033 case DB_DATA_TYPE_BLOB
:
4034 dataTypeName
= typeInfBlob
.TypeName
;
4040 // Set the modify or alter syntax depending on the type of database connected to
4044 alterSlashModify
= _T("MODIFY");
4046 case dbmsMS_SQL_SERVER
:
4047 alterSlashModify
= _T("ALTER COLUMN");
4049 case dbmsUNIDENTIFIED
:
4051 case dbmsSYBASE_ASA
:
4052 case dbmsSYBASE_ASE
:
4057 case dbmsXBASE_SEQUITER
:
4059 alterSlashModify
= _T("MODIFY");
4063 // create the SQL statement
4064 if ( Dbms() == dbmsMY_SQL
)
4066 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4067 columnName
.c_str(), dataTypeName
.c_str());
4071 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
4072 columnName
.c_str(), dataTypeName
.c_str());
4075 // For varchars only, append the size of the column
4076 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
4077 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
4080 s
.Printf(wxT("(%lu)"), columnLength
);
4084 // for passing things like "NOT NULL"
4085 if (optionalParam
.length())
4087 sqlStmt
+= wxT(" ");
4088 sqlStmt
+= optionalParam
;
4091 return ExecSql(sqlStmt
);
4093 } // wxDb::ModifyColumn()
4095 /********** wxDb::EscapeSqlChars() **********/
4096 wxString
wxDb::EscapeSqlChars(const wxString
& valueOrig
)
4098 wxString
value(valueOrig
);
4102 // Access doesn't seem to care about backslashes, so only escape single quotes.
4103 value
.Replace(wxT("'"), wxT("''"));
4107 // All the others are supposed to be the same for now, add special
4108 // handling for them if necessary
4109 value
.Replace(wxT("\\"), wxT("\\\\"));
4110 value
.Replace(wxT("'"), wxT("\\'"));
4115 } // wxDb::EscapeSqlChars()
4118 /********** wxDbGetConnection() **********/
4119 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
4123 // Used to keep a pointer to a DB connection that matches the requested
4124 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4125 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4126 // rather than having to re-query the datasource to get all the values
4127 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4128 wxDb
*matchingDbConnection
= NULL
;
4130 // Scan the linked list searching for an available database connection
4131 // that's already been opened but is currently not in use.
4132 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4134 // The database connection must be for the same datasource
4135 // name and must currently not be in use.
4137 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
4139 if (pDbConfig
->UseConnectionStr())
4141 if (pList
->PtrDb
->OpenedWithConnectionString() &&
4142 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
4144 // Found a free connection
4145 pList
->Free
= false;
4146 return(pList
->PtrDb
);
4151 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
4152 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
4154 // Found a free connection
4155 pList
->Free
= false;
4156 return(pList
->PtrDb
);
4161 if (pDbConfig
->UseConnectionStr())
4163 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
4164 matchingDbConnection
= pList
->PtrDb
;
4168 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4169 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4170 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4171 matchingDbConnection
= pList
->PtrDb
;
4175 // No available connections. A new connection must be made and
4176 // appended to the end of the linked list.
4179 // Find the end of the list
4180 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4181 // Append a new list item
4182 pList
->PtrNext
= new wxDbList
;
4183 pList
->PtrNext
->PtrPrev
= pList
;
4184 pList
= pList
->PtrNext
;
4188 // Create the first node on the list
4189 pList
= PtrBegDbList
= new wxDbList
;
4193 // Initialize new node in the linked list
4195 pList
->Free
= false;
4196 pList
->Dsn
= pDbConfig
->GetDsn();
4197 pList
->Uid
= pDbConfig
->GetUserID();
4198 pList
->AuthStr
= pDbConfig
->GetPassword();
4199 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4201 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4205 if (!matchingDbConnection
)
4207 if (pDbConfig
->UseConnectionStr())
4209 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4213 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4217 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4219 // Connect to the datasource
4222 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4223 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4224 return(pList
->PtrDb
);
4226 else // Unable to connect, destroy list item
4229 pList
->PtrPrev
->PtrNext
= 0;
4231 PtrBegDbList
= 0; // Empty list again
4233 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4234 pList
->PtrDb
->Close(); // Close the wxDb object
4235 delete pList
->PtrDb
; // Deletes the wxDb object
4236 delete pList
; // Deletes the linked list object
4240 } // wxDbGetConnection()
4243 /********** wxDbFreeConnection() **********/
4244 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4248 // Scan the linked list searching for the database connection
4249 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4251 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4252 return (pList
->Free
= true);
4255 // Never found the database object, return failure
4258 } // wxDbFreeConnection()
4261 /********** wxDbCloseConnections() **********/
4262 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4264 wxDbList
*pList
, *pNext
;
4266 // Traverse the linked list closing database connections and freeing memory as I go.
4267 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4269 pNext
= pList
->PtrNext
; // Save the pointer to next
4270 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4271 pList
->PtrDb
->Close(); // Close the wxDb object
4272 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4273 delete pList
->PtrDb
; // Deletes the wxDb object
4274 delete pList
; // Deletes the linked list object
4277 // Mark the list as empty
4280 } // wxDbCloseConnections()
4283 /********** wxDbConnectionsInUse() **********/
4284 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4289 // Scan the linked list counting db connections that are currently in use
4290 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4292 if (pList
->Free
== false)
4298 } // wxDbConnectionsInUse()
4302 /********** wxDbLogExtendedErrorMsg() **********/
4303 // DEBUG ONLY function
4304 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4306 const wxChar
*ErrFile
,
4309 static wxString msg
;
4314 if (ErrFile
|| ErrLine
)
4316 msg
+= wxT("File: ");
4318 msg
+= wxT(" Line: ");
4319 tStr
.Printf(wxT("%d"),ErrLine
);
4320 msg
+= tStr
.c_str();
4324 msg
.Append (wxT("\nODBC errors:\n"));
4327 // Display errors for this connection
4329 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4331 if (pDb
->errorList
[i
])
4333 msg
.Append(pDb
->errorList
[i
]);
4334 if (wxStrcmp(pDb
->errorList
[i
], wxEmptyString
) != 0)
4335 msg
.Append(wxT("\n"));
4336 // Clear the errmsg buffer so the next error will not
4337 // end up showing the previous error that have occurred
4338 wxStrcpy(pDb
->errorList
[i
], wxEmptyString
);
4343 wxLogDebug(msg
.c_str());
4346 } // wxDbLogExtendedErrorMsg()
4349 /********** wxDbSqlLog() **********/
4350 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4352 bool append
= false;
4355 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4357 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4362 SQLLOGstate
= state
;
4363 SQLLOGfn
= filename
;
4371 /********** wxDbCreateDataSource() **********/
4372 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4373 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4375 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4376 * Very rudimentary creation of an ODBC data source.
4378 * ODBC driver must be ODBC 3.0 compliant to use this function
4383 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4389 dsnLocation
= ODBC_ADD_SYS_DSN
;
4391 dsnLocation
= ODBC_ADD_DSN
;
4393 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4394 // so that is why I used it, as wxString does not deal well with
4395 // embedded nulls in strings
4396 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4398 // Replace the separator from above with the '\0' separator needed
4399 // by the SQLConfigDataSource() function
4403 k
= setupStr
.Find((wxChar
)2,true);
4404 if (k
!= wxNOT_FOUND
)
4405 setupStr
[(UINT
)k
] = wxT('\0');
4407 while (k
!= wxNOT_FOUND
);
4409 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4410 driverName
, setupStr
.c_str());
4412 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4414 // check for errors caused by ConfigDSN based functions
4417 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4418 errMsg
[0] = wxT('\0');
4420 // This function is only supported in ODBC drivers v3.0 compliant and above
4421 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4424 #ifdef DBDEBUG_CONSOLE
4425 // When run in console mode, use standard out to display errors.
4426 cout
<< errMsg
<< endl
;
4427 cout
<< wxT("Press any key to continue...") << endl
;
4429 #endif // DBDEBUG_CONSOLE
4432 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4433 #endif // __WXDEBUG__
4439 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4440 // necessary to use this function, so this function is not supported
4442 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4445 #endif // __VISUALC__
4449 } // wxDbCreateDataSource()
4453 /********** wxDbGetDataSource() **********/
4454 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4455 SWORD DsDescMaxLength
, UWORD direction
)
4457 * Dsn and DsDesc will contain the data source name and data source
4458 * description upon return
4462 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4463 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4465 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4466 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4471 } // wxDbGetDataSource()
4474 // Change this to 0 to remove use of all deprecated functions
4475 #if wxODBC_BACKWARD_COMPATABILITY
4476 /********************************************************************
4477 ********************************************************************
4479 * The following functions are all DEPRECATED and are included for
4480 * backward compatibility reasons only
4482 ********************************************************************
4483 ********************************************************************/
4484 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4486 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4488 /***** DEPRECATED: use wxGetDataSource() *****/
4489 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4492 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4494 /***** DEPRECATED: use wxDbGetConnection() *****/
4495 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4497 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4499 /***** DEPRECATED: use wxDbFreeConnection() *****/
4500 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4502 return wxDbFreeConnection(pDb
);
4504 /***** DEPRECATED: use wxDbCloseConnections() *****/
4505 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4507 wxDbCloseConnections();
4509 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4510 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4512 return wxDbConnectionsInUse();