1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
28 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
29 #pragma implementation "db.h"
32 #include "wx/wxprec.h"
38 #ifdef DBDEBUG_CONSOLE
39 #include "wx/ioswrap.h"
43 #include "wx/string.h"
44 #include "wx/object.h"
49 #include "wx/filefn.h"
50 #include "wx/wxchar.h"
62 // DLL options compatibility check:
64 WX_CHECK_BUILD_OPTIONS("wxODBC")
66 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
68 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
69 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
72 extern wxList TablesInUse
;
75 // SQL Log defaults to be used by GetDbConnection
76 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
78 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
80 // The wxDb::errorList is copied to this variable when the wxDb object
81 // is closed. This way, the error list is still available after the
82 // database object is closed. This is necessary if the database
83 // connection fails so the calling application can show the operator
84 // why the connection failed. Note: as each wxDb object is closed, it
85 // will overwrite the errors of the previously destroyed wxDb object in
86 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
88 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
91 // This type defines the return row-struct form
92 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
95 wxChar tableQual
[128+1];
96 wxChar tableOwner
[128+1];
97 wxChar tableName
[128+1];
98 wxChar grantor
[128+1];
99 wxChar grantee
[128+1];
100 wxChar privilege
[128+1];
101 wxChar grantable
[3+1];
102 } wxDbTablePrivilegeInfo
;
105 /********** wxDbConnectInf Constructor - form 1 **********/
106 wxDbConnectInf::wxDbConnectInf()
109 freeHenvOnDestroy
= false;
115 /********** wxDbConnectInf Constructor - form 2 **********/
116 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
117 const wxString
&password
, const wxString
&defaultDir
,
118 const wxString
&fileType
, const wxString
&description
)
121 freeHenvOnDestroy
= false;
132 SetPassword(password
);
133 SetDescription(description
);
134 SetFileType(fileType
);
135 SetDefaultDir(defaultDir
);
136 } // wxDbConnectInf Constructor
139 wxDbConnectInf::~wxDbConnectInf()
141 if (freeHenvOnDestroy
)
145 } // wxDbConnectInf Destructor
149 /********** wxDbConnectInf::Initialize() **********/
150 bool wxDbConnectInf::Initialize()
152 freeHenvOnDestroy
= false;
154 if (freeHenvOnDestroy
&& Henv
)
161 ConnectionStr
[0] = 0;
166 useConnectionStr
= false;
169 } // wxDbConnectInf::Initialize()
172 /********** wxDbConnectInf::AllocHenv() **********/
173 bool wxDbConnectInf::AllocHenv()
175 // This is here to help trap if you are getting a new henv
176 // without releasing an existing henv
179 // Initialize the ODBC Environment for Database Operations
180 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
182 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
186 freeHenvOnDestroy
= true;
189 } // wxDbConnectInf::AllocHenv()
192 void wxDbConnectInf::FreeHenv()
200 freeHenvOnDestroy
= false;
202 } // wxDbConnectInf::FreeHenv()
205 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
207 wxASSERT(dsn
.Length() < sizeof(Dsn
));
210 } // wxDbConnectInf::SetDsn()
213 void wxDbConnectInf::SetUserID(const wxString
&uid
)
215 wxASSERT(uid
.Length() < sizeof(Uid
));
217 } // wxDbConnectInf::SetUserID()
220 void wxDbConnectInf::SetPassword(const wxString
&password
)
222 wxASSERT(password
.Length() < sizeof(AuthStr
));
224 wxStrcpy(AuthStr
, password
);
225 } // wxDbConnectInf::SetPassword()
227 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
229 wxASSERT(connectStr
.Length() < sizeof(ConnectionStr
));
231 useConnectionStr
= wxStrlen(connectStr
) > 0;
233 wxStrcpy(ConnectionStr
, connectStr
);
234 } // wxDbConnectInf::SetConnectionStr()
237 /********** wxDbColFor Constructor **********/
238 wxDbColFor::wxDbColFor()
241 } // wxDbColFor::wxDbColFor()
244 /********** wxDbColFor::Initialize() **********/
245 void wxDbColFor::Initialize()
255 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
258 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
259 } // wxDbColFor::Initialize()
262 /********** wxDbColFor::Format() **********/
263 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
264 short columnLength
, short decimalDigits
)
266 // ----------------------------------------------------------------------------------------
267 // -- 19991224 : mj10777 : Create
268 // There is still a lot of work to do here, but it is a start
269 // It handles all the basic data-types that I have run into up to now
270 // The main work will have be with Dates and float Formatting
271 // (US 1,000.00 ; EU 1.000,00)
272 // There are wxWindow plans for locale support and the new wxDateTime. If
273 // they define some constants (wxEUROPEAN) that can be gloably used,
274 // they should be used here.
275 // ----------------------------------------------------------------------------------------
276 // There should also be a function to scan in a string to fill the variable
277 // ----------------------------------------------------------------------------------------
279 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
280 i_dbDataType
= dbDataType
;
281 i_sqlDataType
= sqlDataType
;
282 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
284 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
286 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
287 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
288 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
289 i_dbDataType
= DB_DATA_TYPE_DATE
;
290 if (i_sqlDataType
== SQL_C_BIT
)
291 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
292 if (i_sqlDataType
== SQL_NUMERIC
)
293 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
294 if (i_sqlDataType
== SQL_REAL
)
295 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
296 if (i_sqlDataType
== SQL_C_BINARY
)
297 i_dbDataType
= DB_DATA_TYPE_BLOB
;
300 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
302 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
305 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
307 case DB_DATA_TYPE_VARCHAR
:
310 case DB_DATA_TYPE_INTEGER
:
313 case DB_DATA_TYPE_FLOAT
:
314 if (decimalDigits
== 0)
317 tempStr
.Printf(wxT("%s%d.%d"), tempStr
.c_str(),columnLength
, decimalDigits
);
318 s_Field
.Printf(wxT("%sf"), tempStr
.c_str());
320 case DB_DATA_TYPE_DATE
:
321 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
323 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
325 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
327 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
329 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
331 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
333 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
335 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
337 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
339 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
342 case DB_DATA_TYPE_BLOB
:
343 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
346 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType
,sqlDataType
); //
350 } // wxDbColFor::Format()
353 /********** wxDbColInf Constructor **********/
354 wxDbColInf::wxDbColInf()
357 } // wxDbColInf::wxDbColInf()
360 /********** wxDbColInf Destructor ********/
361 wxDbColInf::~wxDbColInf()
366 } // wxDbColInf::~wxDbColInf()
369 bool wxDbColInf::Initialize()
391 } // wxDbColInf::Initialize()
394 /********** wxDbTableInf Constructor ********/
395 wxDbTableInf::wxDbTableInf()
398 } // wxDbTableInf::wxDbTableInf()
401 /********** wxDbTableInf Constructor ********/
402 wxDbTableInf::~wxDbTableInf()
407 } // wxDbTableInf::~wxDbTableInf()
410 bool wxDbTableInf::Initialize()
419 } // wxDbTableInf::Initialize()
422 /********** wxDbInf Constructor *************/
426 } // wxDbInf::wxDbInf()
429 /********** wxDbInf Destructor *************/
435 } // wxDbInf::~wxDbInf()
438 /********** wxDbInf::Initialize() *************/
439 bool wxDbInf::Initialize()
447 } // wxDbInf::Initialize()
450 /********** wxDb Constructor **********/
451 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
453 // Copy the HENV into the db class
455 fwdOnlyCursors
= FwdOnlyCursors
;
461 /********** wxDb Destructor **********/
464 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
474 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
475 /********** wxDb::initialize() **********/
476 void wxDb::initialize()
478 * Private member function that sets all wxDb member variables to
479 * known values at creation of the wxDb
484 fpSqlLog
= 0; // Sql Log file pointer
485 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
487 dbmsType
= dbmsUNIDENTIFIED
;
489 wxStrcpy(sqlState
,wxEmptyString
);
490 wxStrcpy(errorMsg
,wxEmptyString
);
491 nativeError
= cbErrorMsg
= 0;
492 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
493 wxStrcpy(errorList
[i
], wxEmptyString
);
495 // Init typeInf structures
496 typeInfVarchar
.TypeName
.Empty();
497 typeInfVarchar
.FsqlType
= 0;
498 typeInfVarchar
.Precision
= 0;
499 typeInfVarchar
.CaseSensitive
= 0;
500 typeInfVarchar
.MaximumScale
= 0;
502 typeInfInteger
.TypeName
.Empty();
503 typeInfInteger
.FsqlType
= 0;
504 typeInfInteger
.Precision
= 0;
505 typeInfInteger
.CaseSensitive
= 0;
506 typeInfInteger
.MaximumScale
= 0;
508 typeInfFloat
.TypeName
.Empty();
509 typeInfFloat
.FsqlType
= 0;
510 typeInfFloat
.Precision
= 0;
511 typeInfFloat
.CaseSensitive
= 0;
512 typeInfFloat
.MaximumScale
= 0;
514 typeInfDate
.TypeName
.Empty();
515 typeInfDate
.FsqlType
= 0;
516 typeInfDate
.Precision
= 0;
517 typeInfDate
.CaseSensitive
= 0;
518 typeInfDate
.MaximumScale
= 0;
520 typeInfBlob
.TypeName
.Empty();
521 typeInfBlob
.FsqlType
= 0;
522 typeInfBlob
.Precision
= 0;
523 typeInfBlob
.CaseSensitive
= 0;
524 typeInfBlob
.MaximumScale
= 0;
526 // Error reporting is turned OFF by default
529 // Allocate a data source connection handle
530 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
533 // Initialize the db status flag
536 // Mark database as not open as of yet
539 dbOpenedWithConnectionString
= false;
540 } // wxDb::initialize()
543 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
545 // NOTE: Return value from this function MUST be copied
546 // immediately, as the value is not good after
547 // this function has left scope.
549 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
553 if (!wxStrlen(userID
))
561 // dBase does not use user names, and some drivers fail if you try to pass one
562 if ( Dbms() == dbmsDBASE
563 || Dbms() == dbmsXBASE_SEQUITER
)
566 // Oracle user names may only be in uppercase, so force
567 // the name to uppercase
568 if (Dbms() == dbmsORACLE
)
569 UserID
= UserID
.Upper();
571 return UserID
.c_str();
572 } // wxDb::convertUserID()
575 bool wxDb::open(bool failOnDataTypeUnsupported
)
578 If using Intersolv branded ODBC drivers, this is the place where you would substitute
579 your branded driver license information
581 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
582 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
585 // Mark database as open
588 // Allocate a statement handle for the database connection
589 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
590 return(DispAllErrors(henv
, hdbc
));
592 // Set Connection Options
593 if (!setConnectionOptions())
596 // Query the data source for inf. about itself
597 if (!getDbInfo(failOnDataTypeUnsupported
))
600 // Query the data source regarding data type information
603 // The way it was determined which SQL data types to use was by calling SQLGetInfo
604 // for all of the possible SQL data types to see which ones were supported. If
605 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
606 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
607 // types I've selected below will not alway's be what we want. These are just
608 // what happened to work against an Oracle 7/Intersolv combination. The following is
609 // a complete list of the results I got back against the Oracle 7 database:
611 // SQL_BIGINT SQL_NO_DATA_FOUND
612 // SQL_BINARY SQL_NO_DATA_FOUND
613 // SQL_BIT SQL_NO_DATA_FOUND
614 // SQL_CHAR type name = 'CHAR', Precision = 255
615 // SQL_DATE SQL_NO_DATA_FOUND
616 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
617 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
618 // SQL_FLOAT SQL_NO_DATA_FOUND
619 // SQL_INTEGER SQL_NO_DATA_FOUND
620 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
621 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
622 // SQL_NUMERIC SQL_NO_DATA_FOUND
623 // SQL_REAL SQL_NO_DATA_FOUND
624 // SQL_SMALLINT SQL_NO_DATA_FOUND
625 // SQL_TIME SQL_NO_DATA_FOUND
626 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
627 // SQL_VARBINARY type name = 'RAW', Precision = 255
628 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
629 // =====================================================================
630 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
632 // SQL_VARCHAR type name = 'TEXT', Precision = 255
633 // SQL_TIMESTAMP type name = 'DATETIME'
634 // SQL_DECIMAL SQL_NO_DATA_FOUND
635 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
636 // SQL_FLOAT SQL_NO_DATA_FOUND
637 // SQL_REAL type name = 'SINGLE', Precision = 7
638 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
639 // SQL_INTEGER type name = 'LONG', Precision = 10
641 // VARCHAR = Variable length character string
642 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
643 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
646 typeInfVarchar
.FsqlType
= SQL_CHAR
;
648 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
651 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
652 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
653 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
654 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
655 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
657 if (failOnDataTypeUnsupported
)
661 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
663 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
665 typeInfFloat
.FsqlType
= SQL_FLOAT
;
667 typeInfFloat
.FsqlType
= SQL_REAL
;
669 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
672 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
674 // If SQL_INTEGER is not supported, use the floating point
675 // data type to store integers as well as floats
676 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
678 if (failOnDataTypeUnsupported
)
682 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
685 typeInfInteger
.FsqlType
= SQL_INTEGER
;
688 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
690 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
693 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
695 typeInfDate
.FsqlType
= SQL_TIME
;
698 #endif // SQL_DATETIME defined
700 if (failOnDataTypeUnsupported
)
705 typeInfDate
.FsqlType
= SQL_DATE
;
708 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
711 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
713 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
715 if (failOnDataTypeUnsupported
)
719 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
722 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
725 #ifdef DBDEBUG_CONSOLE
726 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
727 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
728 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
729 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
730 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
734 // Completed Successfully
738 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
740 wxASSERT(inConnectStr
.Length());
747 if (!FwdOnlyCursors())
749 // Specify that the ODBC cursor library be used, if needed. This must be
750 // specified before the connection is made.
751 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
753 #ifdef DBDEBUG_CONSOLE
754 if (retcode
== SQL_SUCCESS
)
755 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
757 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
759 wxUnusedVar(retcode
);
763 // Connect to the data source
764 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
765 short outConnectBufferLen
;
767 inConnectionStr
= inConnectStr
;
769 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
770 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
771 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
773 if ((retcode
!= SQL_SUCCESS
) &&
774 (retcode
!= SQL_SUCCESS_WITH_INFO
))
775 return(DispAllErrors(henv
, hdbc
));
777 outConnectBuffer
[outConnectBufferLen
] = 0;
778 outConnectionStr
= outConnectBuffer
;
779 dbOpenedWithConnectionString
= true;
781 return open(failOnDataTypeUnsupported
);
784 /********** wxDb::Open() **********/
785 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
787 wxASSERT(Dsn
.Length());
792 inConnectionStr
= wxT("");
793 outConnectionStr
= wxT("");
797 if (!FwdOnlyCursors())
799 // Specify that the ODBC cursor library be used, if needed. This must be
800 // specified before the connection is made.
801 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
803 #ifdef DBDEBUG_CONSOLE
804 if (retcode
== SQL_SUCCESS
)
805 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
807 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
809 wxUnusedVar( retcode
);
813 // Connect to the data source
814 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
815 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
816 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
818 if ((retcode
!= SQL_SUCCESS
) &&
819 (retcode
!= SQL_SUCCESS_WITH_INFO
))
820 return(DispAllErrors(henv
, hdbc
));
822 return open(failOnDataTypeUnsupported
);
827 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
829 wxASSERT(dbConnectInf
);
831 // Use the connection string if one is present
832 if (dbConnectInf
->UseConnectionStr())
833 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
835 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
836 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
840 bool wxDb::Open(wxDb
*copyDb
)
842 dsn
= copyDb
->GetDatasourceName();
843 uid
= copyDb
->GetUsername();
844 authStr
= copyDb
->GetPassword();
845 inConnectionStr
= copyDb
->GetConnectionInStr();
846 outConnectionStr
= copyDb
->GetConnectionOutStr();
850 if (!FwdOnlyCursors())
852 // Specify that the ODBC cursor library be used, if needed. This must be
853 // specified before the connection is made.
854 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
856 #ifdef DBDEBUG_CONSOLE
857 if (retcode
== SQL_SUCCESS
)
858 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
860 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
862 wxUnusedVar( retcode
);
866 if (copyDb
->OpenedWithConnectionString())
868 // Connect to the data source
869 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
870 short outConnectBufferLen
;
872 inConnectionStr
= copyDb
->GetConnectionInStr();
874 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
875 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
876 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
878 if ((retcode
!= SQL_SUCCESS
) &&
879 (retcode
!= SQL_SUCCESS_WITH_INFO
))
880 return(DispAllErrors(henv
, hdbc
));
882 outConnectBuffer
[outConnectBufferLen
] = 0;
883 outConnectionStr
= outConnectBuffer
;
884 dbOpenedWithConnectionString
= true;
888 // Connect to the data source
889 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
890 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
891 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
894 if ((retcode
!= SQL_SUCCESS
) &&
895 (retcode
!= SQL_SUCCESS_WITH_INFO
))
896 return(DispAllErrors(henv
, hdbc
));
899 If using Intersolv branded ODBC drivers, this is the place where you would substitute
900 your branded driver license information
902 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
903 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
906 // Mark database as open
909 // Allocate a statement handle for the database connection
910 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
911 return(DispAllErrors(henv
, hdbc
));
913 // Set Connection Options
914 if (!setConnectionOptions())
917 // Instead of Querying the data source for info about itself, it can just be copied
918 // from the wxDb instance that was passed in (copyDb).
919 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
920 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
921 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
922 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
923 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
924 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
925 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
926 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
927 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
928 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
929 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
930 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
931 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
932 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
933 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
934 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
935 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
936 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
937 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
938 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
939 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
940 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
941 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
942 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
943 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
944 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
945 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
946 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
947 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
948 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
949 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
951 // VARCHAR = Variable length character string
952 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
953 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
954 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
955 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
956 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
959 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
960 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
961 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
962 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
963 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
966 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
967 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
968 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
969 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
970 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
973 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
974 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
975 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
976 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
977 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
980 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
981 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
982 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
983 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
984 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
986 #ifdef DBDEBUG_CONSOLE
987 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
988 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
989 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
990 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
991 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
995 // Completed Successfully
1000 /********** wxDb::setConnectionOptions() **********/
1001 bool wxDb::setConnectionOptions(void)
1003 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1008 // I need to get the DBMS name here, because some of the connection options
1009 // are database specific and need to call the Dbms() function.
1012 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1013 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1014 return(DispAllErrors(henv
, hdbc
));
1016 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1017 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1018 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1020 // By default, MS Sql Server closes cursors on commit and rollback. The following
1021 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1022 // after a transaction. This is a driver specific option and is not part of the
1023 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1024 // The database settings don't have any effect one way or the other.
1025 if (Dbms() == dbmsMS_SQL_SERVER
)
1027 const long SQL_PRESERVE_CURSORS
= 1204L;
1028 const long SQL_PC_ON
= 1L;
1029 /* retcode = */ SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1032 // Display the connection options to verify them
1033 #ifdef DBDEBUG_CONSOLE
1035 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1037 retcode
= SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
);
1038 if (retcode
!= SQL_SUCCESS
)
1039 return(DispAllErrors(henv
, hdbc
));
1040 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1042 retcode
= SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
);
1043 if (retcode
!= SQL_SUCCESS
)
1044 return(DispAllErrors(henv
, hdbc
));
1045 cout
<< wxT("ODBC CURSORS: ");
1048 case(SQL_CUR_USE_IF_NEEDED
):
1049 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1051 case(SQL_CUR_USE_ODBC
):
1052 cout
<< wxT("SQL_CUR_USE_ODBC");
1054 case(SQL_CUR_USE_DRIVER
):
1055 cout
<< wxT("SQL_CUR_USE_DRIVER");
1060 retcode
= SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
)
1061 if (retcode
!= SQL_SUCCESS
)
1062 return(DispAllErrors(henv
, hdbc
));
1063 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1068 // Completed Successfully
1071 } // wxDb::setConnectionOptions()
1074 /********** wxDb::getDbInfo() **********/
1075 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1080 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, sizeof(dbInf
.serverName
), &cb
);
1081 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1083 DispAllErrors(henv
, hdbc
);
1084 if (failOnDataTypeUnsupported
)
1088 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, sizeof(dbInf
.databaseName
), &cb
);
1089 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1091 DispAllErrors(henv
, hdbc
);
1092 if (failOnDataTypeUnsupported
)
1096 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, sizeof(dbInf
.dbmsName
), &cb
);
1097 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1099 DispAllErrors(henv
, hdbc
);
1100 if (failOnDataTypeUnsupported
)
1105 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1106 // causing database connectivity to fail in some cases.
1107 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, sizeof(dbInf
.dbmsVer
), &cb
);
1108 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1110 DispAllErrors(henv
, hdbc
);
1111 if (failOnDataTypeUnsupported
)
1115 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1116 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1118 DispAllErrors(henv
, hdbc
);
1119 if (failOnDataTypeUnsupported
)
1123 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1124 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1126 DispAllErrors(henv
, hdbc
);
1127 if (failOnDataTypeUnsupported
)
1131 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, sizeof(dbInf
.driverName
), &cb
);
1132 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1134 DispAllErrors(henv
, hdbc
);
1135 if (failOnDataTypeUnsupported
)
1139 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, sizeof(dbInf
.odbcVer
), &cb
);
1140 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1142 DispAllErrors(henv
, hdbc
);
1143 if (failOnDataTypeUnsupported
)
1147 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, sizeof(dbInf
.drvMgrOdbcVer
), &cb
);
1148 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1150 DispAllErrors(henv
, hdbc
);
1151 if (failOnDataTypeUnsupported
)
1155 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, sizeof(dbInf
.driverVer
), &cb
);
1156 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1158 DispAllErrors(henv
, hdbc
);
1159 if (failOnDataTypeUnsupported
)
1163 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1164 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1166 DispAllErrors(henv
, hdbc
);
1167 if (failOnDataTypeUnsupported
)
1171 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1172 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1174 // Not all drivers support this call - Nick Gorham(unixODBC)
1175 dbInf
.cliConfLvl
= 0;
1176 DispAllErrors(henv
, hdbc
);
1177 if (failOnDataTypeUnsupported
)
1181 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1182 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1184 DispAllErrors(henv
, hdbc
);
1185 if (failOnDataTypeUnsupported
)
1189 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, sizeof(dbInf
.outerJoins
), &cb
);
1190 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1192 DispAllErrors(henv
, hdbc
);
1193 if (failOnDataTypeUnsupported
)
1197 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, sizeof(dbInf
.procedureSupport
), &cb
);
1198 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1200 DispAllErrors(henv
, hdbc
);
1201 if (failOnDataTypeUnsupported
)
1205 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, sizeof(dbInf
.accessibleTables
), &cb
);
1206 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1208 DispAllErrors(henv
, hdbc
);
1209 if (failOnDataTypeUnsupported
)
1213 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1214 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1216 DispAllErrors(henv
, hdbc
);
1217 if (failOnDataTypeUnsupported
)
1221 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1222 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1224 DispAllErrors(henv
, hdbc
);
1225 if (failOnDataTypeUnsupported
)
1229 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1230 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1232 DispAllErrors(henv
, hdbc
);
1233 if (failOnDataTypeUnsupported
)
1237 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, sizeof(dbInf
.supportIEF
), &cb
);
1238 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1240 DispAllErrors(henv
, hdbc
);
1241 if (failOnDataTypeUnsupported
)
1245 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1246 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1248 DispAllErrors(henv
, hdbc
);
1249 if (failOnDataTypeUnsupported
)
1253 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1254 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1256 DispAllErrors(henv
, hdbc
);
1257 if (failOnDataTypeUnsupported
)
1261 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1262 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1264 DispAllErrors(henv
, hdbc
);
1265 if (failOnDataTypeUnsupported
)
1269 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1270 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1272 DispAllErrors(henv
, hdbc
);
1273 if (failOnDataTypeUnsupported
)
1277 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1278 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1280 DispAllErrors(henv
, hdbc
);
1281 if (failOnDataTypeUnsupported
)
1285 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1286 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1288 DispAllErrors(henv
, hdbc
);
1289 if (failOnDataTypeUnsupported
)
1293 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1294 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1296 DispAllErrors(henv
, hdbc
);
1297 if (failOnDataTypeUnsupported
)
1301 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1302 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1304 DispAllErrors(henv
, hdbc
);
1305 if (failOnDataTypeUnsupported
)
1309 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1310 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1312 DispAllErrors(henv
, hdbc
);
1313 if (failOnDataTypeUnsupported
)
1317 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1318 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1320 DispAllErrors(henv
, hdbc
);
1321 if (failOnDataTypeUnsupported
)
1325 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1326 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1328 DispAllErrors(henv
, hdbc
);
1329 if (failOnDataTypeUnsupported
)
1333 #ifdef DBDEBUG_CONSOLE
1334 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1335 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1336 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1337 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1339 cout
<< wxT("API Conf. Level: ");
1340 switch(dbInf
.apiConfLvl
)
1342 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1343 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1344 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1348 cout
<< wxT("SAG CLI Conf. Level: ");
1349 switch(dbInf
.cliConfLvl
)
1351 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1352 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1356 cout
<< wxT("SQL Conf. Level: ");
1357 switch(dbInf
.sqlConfLvl
)
1359 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1360 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1361 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1365 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1366 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1367 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1368 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1369 cout
<< wxT("Cursor COMMIT Behavior: ");
1370 switch(dbInf
.cursorCommitBehavior
)
1372 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1373 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1374 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1378 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1379 switch(dbInf
.cursorRollbackBehavior
)
1381 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1382 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1383 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1387 cout
<< wxT("Support NOT NULL clause: ");
1388 switch(dbInf
.supportNotNullClause
)
1390 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1391 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1395 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1396 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1398 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1401 cout
<< wxT("Default Transaction Isolation: ";
1402 switch(dbInf
.txnIsolation
)
1404 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1405 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1406 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1407 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1409 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1414 cout
<< wxT("Transaction Isolation Options: ");
1415 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1416 cout
<< wxT("Read Uncommitted, ");
1417 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1418 cout
<< wxT("Read Committed, ");
1419 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1420 cout
<< wxT("Repeatable Read, ");
1421 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1422 cout
<< wxT("Serializable, ");
1424 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1425 cout
<< wxT("Versioning");
1429 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1430 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1431 cout
<< wxT("Next, ");
1432 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1433 cout
<< wxT("Prev, ");
1434 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1435 cout
<< wxT("First, ");
1436 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1437 cout
<< wxT("Last, ");
1438 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1439 cout
<< wxT("Absolute, ");
1440 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1441 cout
<< wxT("Relative, ");
1443 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1444 cout
<< wxT("Resume, ");
1446 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1447 cout
<< wxT("Bookmark");
1450 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1451 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1452 cout
<< wxT("No Change, ");
1453 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1454 cout
<< wxT("Exclusive, ");
1455 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1456 cout
<< wxT("UnLock");
1459 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1460 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1461 cout
<< wxT("Position, ");
1462 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1463 cout
<< wxT("Refresh, ");
1464 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1465 cout
<< wxT("Upd, "));
1466 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1467 cout
<< wxT("Del, ");
1468 if (dbInf
.posOperations
& SQL_POS_ADD
)
1472 cout
<< wxT("Positioned Statements Supported: ");
1473 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1474 cout
<< wxT("Pos delete, ");
1475 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1476 cout
<< wxT("Pos update, ");
1477 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1478 cout
<< wxT("Select for update");
1481 cout
<< wxT("Scroll Concurrency: ");
1482 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1483 cout
<< wxT("Read Only, ");
1484 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1485 cout
<< wxT("Lock, ");
1486 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1487 cout
<< wxT("Opt. Rowver, ");
1488 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1489 cout
<< wxT("Opt. Values");
1492 cout
<< wxT("Scroll Options: ");
1493 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1494 cout
<< wxT("Fwd Only, ");
1495 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1496 cout
<< wxT("Static, ");
1497 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1498 cout
<< wxT("Keyset Driven, ");
1499 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1500 cout
<< wxT("Dynamic, ");
1501 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1502 cout
<< wxT("Mixed");
1505 cout
<< wxT("Static Sensitivity: ");
1506 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1507 cout
<< wxT("Additions, ");
1508 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1509 cout
<< wxT("Deletions, ");
1510 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1511 cout
<< wxT("Updates");
1514 cout
<< wxT("Transaction Capable?: ");
1515 switch(dbInf
.txnCapable
)
1517 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1518 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1519 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1520 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1521 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1528 // Completed Successfully
1531 } // wxDb::getDbInfo()
1534 /********** wxDb::getDataTypeInfo() **********/
1535 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1538 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1539 * the data type inf. is gathered for.
1541 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1546 // Get information about the data type specified
1547 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1548 return(DispAllErrors(henv
, hdbc
, hstmt
));
1551 retcode
= SQLFetch(hstmt
);
1552 if (retcode
!= SQL_SUCCESS
)
1554 #ifdef DBDEBUG_CONSOLE
1555 if (retcode
== SQL_NO_DATA_FOUND
)
1556 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1558 DispAllErrors(henv
, hdbc
, hstmt
);
1559 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1563 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1565 // Obtain columns from the record
1566 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, typeName
, sizeof(typeName
), &cbRet
) != SQL_SUCCESS
)
1567 return(DispAllErrors(henv
, hdbc
, hstmt
));
1569 structSQLTypeInfo
.TypeName
= typeName
;
1571 // BJO 20000503: no more needed with new GetColumns...
1574 if (Dbms() == dbmsMY_SQL
)
1576 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1577 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1578 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1579 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1580 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1581 structSQLTypeInfo
.TypeName
= wxT("int");
1582 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1583 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1584 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1585 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1586 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1587 structSQLTypeInfo
.TypeName
= wxT("char");
1590 // BJO 20000427 : OpenLink driver
1591 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1592 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1594 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1595 structSQLTypeInfo
.TypeName
= wxT("real");
1599 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1600 return(DispAllErrors(henv
, hdbc
, hstmt
));
1601 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1602 return(DispAllErrors(henv
, hdbc
, hstmt
));
1603 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1604 // return(DispAllErrors(henv, hdbc, hstmt));
1606 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1607 return(DispAllErrors(henv
, hdbc
, hstmt
));
1609 if (structSQLTypeInfo
.MaximumScale
< 0)
1610 structSQLTypeInfo
.MaximumScale
= 0;
1612 // Close the statement handle which closes open cursors
1613 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1614 return(DispAllErrors(henv
, hdbc
, hstmt
));
1616 // Completed Successfully
1619 } // wxDb::getDataTypeInfo()
1622 /********** wxDb::Close() **********/
1623 void wxDb::Close(void)
1625 // Close the Sql Log file
1632 // Free statement handle
1635 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1636 DispAllErrors(henv
, hdbc
);
1639 // Disconnect from the datasource
1640 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1641 DispAllErrors(henv
, hdbc
);
1643 // Free the connection to the datasource
1644 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1645 DispAllErrors(henv
, hdbc
);
1647 // There should be zero Ctable objects still connected to this db object
1648 wxASSERT(nTables
== 0);
1652 wxList::compatibility_iterator pNode
;
1653 pNode
= TablesInUse
.GetFirst();
1657 tiu
= (wxTablesInUse
*)pNode
->GetData();
1658 if (tiu
->pDb
== this)
1660 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1661 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1662 wxLogDebug(s
.c_str(),s2
.c_str());
1664 pNode
= pNode
->GetNext();
1668 // Copy the error messages to a global variable
1670 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1671 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1673 dbmsType
= dbmsUNIDENTIFIED
;
1679 /********** wxDb::CommitTrans() **********/
1680 bool wxDb::CommitTrans(void)
1684 // Commit the transaction
1685 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1686 return(DispAllErrors(henv
, hdbc
));
1689 // Completed successfully
1692 } // wxDb::CommitTrans()
1695 /********** wxDb::RollbackTrans() **********/
1696 bool wxDb::RollbackTrans(void)
1698 // Rollback the transaction
1699 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1700 return(DispAllErrors(henv
, hdbc
));
1702 // Completed successfully
1705 } // wxDb::RollbackTrans()
1708 /********** wxDb::DispAllErrors() **********/
1709 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1711 * This function is called internally whenever an error condition prevents the user's
1712 * request from being executed. This function will query the datasource as to the
1713 * actual error(s) that just occured on the previous request of the datasource.
1715 * The function will retrieve each error condition from the datasource and
1716 * Printf the codes/text values into a string which it then logs via logError().
1717 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1718 * window and program execution will be paused until the user presses a key.
1720 * This function always returns a false, so that functions which call this function
1721 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1722 * of the users request, so that the calling code can then process the error msg log
1725 wxString odbcErrMsg
;
1728 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1730 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1733 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1734 logError(odbcErrMsg
, sqlState
);
1737 #ifdef DBDEBUG_CONSOLE
1738 // When run in console mode, use standard out to display errors.
1739 cout
<< odbcErrMsg
.c_str() << endl
;
1740 cout
<< wxT("Press any key to continue...") << endl
;
1745 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1750 return false; // This function always returns false.
1752 } // wxDb::DispAllErrors()
1755 /********** wxDb::GetNextError() **********/
1756 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1759 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (SQLINTEGER
*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1761 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1767 } // wxDb::GetNextError()
1770 /********** wxDb::DispNextError() **********/
1771 void wxDb::DispNextError(void)
1773 wxString odbcErrMsg
;
1775 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1776 logError(odbcErrMsg
, sqlState
);
1781 #ifdef DBDEBUG_CONSOLE
1782 // When run in console mode, use standard out to display errors.
1783 cout
<< odbcErrMsg
.c_str() << endl
;
1784 cout
<< wxT("Press any key to continue...") << endl
;
1789 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1790 #endif // __WXDEBUG__
1792 } // wxDb::DispNextError()
1795 /********** wxDb::logError() **********/
1796 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1798 wxASSERT(errMsg
.Length());
1800 static int pLast
= -1;
1803 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1806 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1807 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1811 wxStrcpy(errorList
[pLast
], errMsg
);
1813 if (SQLState
.Length())
1814 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1815 DB_STATUS
= dbStatus
;
1817 // Add the errmsg to the sql log
1818 WriteSqlLog(errMsg
);
1820 } // wxDb::logError()
1823 /**********wxDb::TranslateSqlState() **********/
1824 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1826 if (!wxStrcmp(SQLState
, wxT("01000")))
1827 return(DB_ERR_GENERAL_WARNING
);
1828 if (!wxStrcmp(SQLState
, wxT("01002")))
1829 return(DB_ERR_DISCONNECT_ERROR
);
1830 if (!wxStrcmp(SQLState
, wxT("01004")))
1831 return(DB_ERR_DATA_TRUNCATED
);
1832 if (!wxStrcmp(SQLState
, wxT("01006")))
1833 return(DB_ERR_PRIV_NOT_REVOKED
);
1834 if (!wxStrcmp(SQLState
, wxT("01S00")))
1835 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1836 if (!wxStrcmp(SQLState
, wxT("01S01")))
1837 return(DB_ERR_ERROR_IN_ROW
);
1838 if (!wxStrcmp(SQLState
, wxT("01S02")))
1839 return(DB_ERR_OPTION_VALUE_CHANGED
);
1840 if (!wxStrcmp(SQLState
, wxT("01S03")))
1841 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1842 if (!wxStrcmp(SQLState
, wxT("01S04")))
1843 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1844 if (!wxStrcmp(SQLState
, wxT("07001")))
1845 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1846 if (!wxStrcmp(SQLState
, wxT("07006")))
1847 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1848 if (!wxStrcmp(SQLState
, wxT("08001")))
1849 return(DB_ERR_UNABLE_TO_CONNECT
);
1850 if (!wxStrcmp(SQLState
, wxT("08002")))
1851 return(DB_ERR_CONNECTION_IN_USE
);
1852 if (!wxStrcmp(SQLState
, wxT("08003")))
1853 return(DB_ERR_CONNECTION_NOT_OPEN
);
1854 if (!wxStrcmp(SQLState
, wxT("08004")))
1855 return(DB_ERR_REJECTED_CONNECTION
);
1856 if (!wxStrcmp(SQLState
, wxT("08007")))
1857 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1858 if (!wxStrcmp(SQLState
, wxT("08S01")))
1859 return(DB_ERR_COMM_LINK_FAILURE
);
1860 if (!wxStrcmp(SQLState
, wxT("21S01")))
1861 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1862 if (!wxStrcmp(SQLState
, wxT("21S02")))
1863 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1864 if (!wxStrcmp(SQLState
, wxT("22001")))
1865 return(DB_ERR_STRING_RIGHT_TRUNC
);
1866 if (!wxStrcmp(SQLState
, wxT("22003")))
1867 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1868 if (!wxStrcmp(SQLState
, wxT("22005")))
1869 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1870 if (!wxStrcmp(SQLState
, wxT("22008")))
1871 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1872 if (!wxStrcmp(SQLState
, wxT("22012")))
1873 return(DB_ERR_DIVIDE_BY_ZERO
);
1874 if (!wxStrcmp(SQLState
, wxT("22026")))
1875 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1876 if (!wxStrcmp(SQLState
, wxT("23000")))
1877 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1878 if (!wxStrcmp(SQLState
, wxT("24000")))
1879 return(DB_ERR_INVALID_CURSOR_STATE
);
1880 if (!wxStrcmp(SQLState
, wxT("25000")))
1881 return(DB_ERR_INVALID_TRANS_STATE
);
1882 if (!wxStrcmp(SQLState
, wxT("28000")))
1883 return(DB_ERR_INVALID_AUTH_SPEC
);
1884 if (!wxStrcmp(SQLState
, wxT("34000")))
1885 return(DB_ERR_INVALID_CURSOR_NAME
);
1886 if (!wxStrcmp(SQLState
, wxT("37000")))
1887 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1888 if (!wxStrcmp(SQLState
, wxT("3C000")))
1889 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1890 if (!wxStrcmp(SQLState
, wxT("40001")))
1891 return(DB_ERR_SERIALIZATION_FAILURE
);
1892 if (!wxStrcmp(SQLState
, wxT("42000")))
1893 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1894 if (!wxStrcmp(SQLState
, wxT("70100")))
1895 return(DB_ERR_OPERATION_ABORTED
);
1896 if (!wxStrcmp(SQLState
, wxT("IM001")))
1897 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1898 if (!wxStrcmp(SQLState
, wxT("IM002")))
1899 return(DB_ERR_NO_DATA_SOURCE
);
1900 if (!wxStrcmp(SQLState
, wxT("IM003")))
1901 return(DB_ERR_DRIVER_LOAD_ERROR
);
1902 if (!wxStrcmp(SQLState
, wxT("IM004")))
1903 return(DB_ERR_SQLALLOCENV_FAILED
);
1904 if (!wxStrcmp(SQLState
, wxT("IM005")))
1905 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1906 if (!wxStrcmp(SQLState
, wxT("IM006")))
1907 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1908 if (!wxStrcmp(SQLState
, wxT("IM007")))
1909 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1910 if (!wxStrcmp(SQLState
, wxT("IM008")))
1911 return(DB_ERR_DIALOG_FAILED
);
1912 if (!wxStrcmp(SQLState
, wxT("IM009")))
1913 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1914 if (!wxStrcmp(SQLState
, wxT("IM010")))
1915 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1916 if (!wxStrcmp(SQLState
, wxT("IM011")))
1917 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1918 if (!wxStrcmp(SQLState
, wxT("IM012")))
1919 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1920 if (!wxStrcmp(SQLState
, wxT("IM013")))
1921 return(DB_ERR_TRACE_FILE_ERROR
);
1922 if (!wxStrcmp(SQLState
, wxT("S0001")))
1923 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1924 if (!wxStrcmp(SQLState
, wxT("S0002")))
1925 return(DB_ERR_TABLE_NOT_FOUND
);
1926 if (!wxStrcmp(SQLState
, wxT("S0011")))
1927 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1928 if (!wxStrcmp(SQLState
, wxT("S0012")))
1929 return(DB_ERR_INDEX_NOT_FOUND
);
1930 if (!wxStrcmp(SQLState
, wxT("S0021")))
1931 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1932 if (!wxStrcmp(SQLState
, wxT("S0022")))
1933 return(DB_ERR_COLUMN_NOT_FOUND
);
1934 if (!wxStrcmp(SQLState
, wxT("S0023")))
1935 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1936 if (!wxStrcmp(SQLState
, wxT("S1000")))
1937 return(DB_ERR_GENERAL_ERROR
);
1938 if (!wxStrcmp(SQLState
, wxT("S1001")))
1939 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1940 if (!wxStrcmp(SQLState
, wxT("S1002")))
1941 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1942 if (!wxStrcmp(SQLState
, wxT("S1003")))
1943 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1944 if (!wxStrcmp(SQLState
, wxT("S1004")))
1945 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1946 if (!wxStrcmp(SQLState
, wxT("S1008")))
1947 return(DB_ERR_OPERATION_CANCELLED
);
1948 if (!wxStrcmp(SQLState
, wxT("S1009")))
1949 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1950 if (!wxStrcmp(SQLState
, wxT("S1010")))
1951 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1952 if (!wxStrcmp(SQLState
, wxT("S1011")))
1953 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1954 if (!wxStrcmp(SQLState
, wxT("S1012")))
1955 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1956 if (!wxStrcmp(SQLState
, wxT("S1015")))
1957 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1958 if (!wxStrcmp(SQLState
, wxT("S1090")))
1959 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1960 if (!wxStrcmp(SQLState
, wxT("S1091")))
1961 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1962 if (!wxStrcmp(SQLState
, wxT("S1092")))
1963 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1964 if (!wxStrcmp(SQLState
, wxT("S1093")))
1965 return(DB_ERR_INVALID_PARAM_NO
);
1966 if (!wxStrcmp(SQLState
, wxT("S1094")))
1967 return(DB_ERR_INVALID_SCALE_VALUE
);
1968 if (!wxStrcmp(SQLState
, wxT("S1095")))
1969 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1970 if (!wxStrcmp(SQLState
, wxT("S1096")))
1971 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1972 if (!wxStrcmp(SQLState
, wxT("S1097")))
1973 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1974 if (!wxStrcmp(SQLState
, wxT("S1098")))
1975 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1976 if (!wxStrcmp(SQLState
, wxT("S1099")))
1977 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1978 if (!wxStrcmp(SQLState
, wxT("S1100")))
1979 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1980 if (!wxStrcmp(SQLState
, wxT("S1101")))
1981 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1982 if (!wxStrcmp(SQLState
, wxT("S1103")))
1983 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1984 if (!wxStrcmp(SQLState
, wxT("S1104")))
1985 return(DB_ERR_INVALID_PRECISION_VALUE
);
1986 if (!wxStrcmp(SQLState
, wxT("S1105")))
1987 return(DB_ERR_INVALID_PARAM_TYPE
);
1988 if (!wxStrcmp(SQLState
, wxT("S1106")))
1989 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1990 if (!wxStrcmp(SQLState
, wxT("S1107")))
1991 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1992 if (!wxStrcmp(SQLState
, wxT("S1108")))
1993 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1994 if (!wxStrcmp(SQLState
, wxT("S1109")))
1995 return(DB_ERR_INVALID_CURSOR_POSITION
);
1996 if (!wxStrcmp(SQLState
, wxT("S1110")))
1997 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1998 if (!wxStrcmp(SQLState
, wxT("S1111")))
1999 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2000 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2001 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2002 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2003 return(DB_ERR_TIMEOUT_EXPIRED
);
2008 } // wxDb::TranslateSqlState()
2011 /********** wxDb::Grant() **********/
2012 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2016 // Build the grant statement
2017 sqlStmt
= wxT("GRANT ");
2018 if (privileges
== DB_GRANT_ALL
)
2019 sqlStmt
+= wxT("ALL");
2023 if (privileges
& DB_GRANT_SELECT
)
2025 sqlStmt
+= wxT("SELECT");
2028 if (privileges
& DB_GRANT_INSERT
)
2031 sqlStmt
+= wxT(", ");
2032 sqlStmt
+= wxT("INSERT");
2034 if (privileges
& DB_GRANT_UPDATE
)
2037 sqlStmt
+= wxT(", ");
2038 sqlStmt
+= wxT("UPDATE");
2040 if (privileges
& DB_GRANT_DELETE
)
2043 sqlStmt
+= wxT(", ");
2044 sqlStmt
+= wxT("DELETE");
2048 sqlStmt
+= wxT(" ON ");
2049 sqlStmt
+= SQLTableName(tableName
);
2050 sqlStmt
+= wxT(" TO ");
2051 sqlStmt
+= userList
;
2053 #ifdef DBDEBUG_CONSOLE
2054 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2057 WriteSqlLog(sqlStmt
);
2059 return(ExecSql(sqlStmt
));
2064 /********** wxDb::CreateView() **********/
2065 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2066 const wxString
&pSqlStmt
, bool attemptDrop
)
2070 // Drop the view first
2071 if (attemptDrop
&& !DropView(viewName
))
2074 // Build the create view statement
2075 sqlStmt
= wxT("CREATE VIEW ");
2076 sqlStmt
+= viewName
;
2078 if (colList
.Length())
2080 sqlStmt
+= wxT(" (");
2082 sqlStmt
+= wxT(")");
2085 sqlStmt
+= wxT(" AS ");
2086 sqlStmt
+= pSqlStmt
;
2088 WriteSqlLog(sqlStmt
);
2090 #ifdef DBDEBUG_CONSOLE
2091 cout
<< sqlStmt
.c_str() << endl
;
2094 return(ExecSql(sqlStmt
));
2096 } // wxDb::CreateView()
2099 /********** wxDb::DropView() **********/
2100 bool wxDb::DropView(const wxString
&viewName
)
2103 * NOTE: This function returns true if the View does not exist, but
2104 * only for identified databases. Code will need to be added
2105 * below for any other databases when those databases are defined
2106 * to handle this situation consistently
2110 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2112 WriteSqlLog(sqlStmt
);
2114 #ifdef DBDEBUG_CONSOLE
2115 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2118 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2120 // Check for "Base table not found" error and ignore
2121 GetNextError(henv
, hdbc
, hstmt
);
2122 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2124 // Check for product specific error codes
2125 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2128 DispAllErrors(henv
, hdbc
, hstmt
);
2135 // Commit the transaction
2141 } // wxDb::DropView()
2144 /********** wxDb::ExecSql() **********/
2145 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2149 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2151 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2152 if (retcode
== SQL_SUCCESS
||
2153 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2159 DispAllErrors(henv
, hdbc
, hstmt
);
2163 } // wxDb::ExecSql()
2166 /********** wxDb::ExecSql() with column info **********/
2167 bool wxDb::ExecSql(const wxString
&pSqlStmt
, wxDbColInf
** columns
, short& numcols
)
2169 //execute the statement first
2170 if (!ExecSql(pSqlStmt
))
2174 if (SQLNumResultCols(hstmt
, &noCols
) != SQL_SUCCESS
)
2176 DispAllErrors(henv
, hdbc
, hstmt
);
2185 // Get column information
2187 wxChar name
[DB_MAX_COLUMN_NAME_LEN
+1];
2190 wxDbColInf
* pColInf
= new wxDbColInf
[noCols
];
2192 //fill in column information (name, datatype)
2193 for (colNum
= 0; colNum
< noCols
; colNum
++)
2195 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_NAME
,
2197 &Sword
, &Sdword
) != SQL_SUCCESS
)
2199 DispAllErrors(henv
, hdbc
, hstmt
);
2204 wxStrncpy(pColInf
[colNum
].colName
, name
, DB_MAX_COLUMN_NAME_LEN
);
2206 if (SQLColAttributes(hstmt
, (UWORD
)(colNum
+1), SQL_COLUMN_TYPE
,
2207 NULL
, 0, &Sword
, &Sdword
) != SQL_SUCCESS
)
2209 DispAllErrors(henv
, hdbc
, hstmt
);
2218 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2224 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2231 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2235 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_DATE
;
2238 pColInf
[colNum
].dbDataType
= DB_DATA_TYPE_BLOB
;
2243 errMsg
.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sdword
);
2244 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2251 } // wxDb::ExecSql()
2253 /********** wxDb::GetNext() **********/
2254 bool wxDb::GetNext(void)
2256 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2260 DispAllErrors(henv
, hdbc
, hstmt
);
2264 } // wxDb::GetNext()
2267 /********** wxDb::GetData() **********/
2268 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2271 wxASSERT(cbReturned
);
2273 long bufferSize
= maxLen
;
2275 if (cType
== SQL_C_WXCHAR
)
2276 bufferSize
= maxLen
* sizeof(wxChar
);
2278 if (SQLGetData(hstmt
, colNo
, cType
, pData
, bufferSize
, cbReturned
) == SQL_SUCCESS
)
2282 DispAllErrors(henv
, hdbc
, hstmt
);
2286 } // wxDb::GetData()
2289 /********** wxDb::GetKeyFields() **********/
2290 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2292 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2293 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2295 // SQLSMALLINT iKeySeq;
2296 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2297 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2303 * -----------------------------------------------------------------------
2304 * -- 19991224 : mj10777 : Create ------
2305 * -- : Three things are done and stored here : ------
2306 * -- : 1) which Column(s) is/are Primary Key(s) ------
2307 * -- : 2) which tables use this Key as a Foreign Key ------
2308 * -- : 3) which columns are Foreign Key and the name ------
2309 * -- : of the Table where the Key is the Primary Key -----
2310 * -- : Called from GetColumns(const wxString &tableName, ------
2311 * -- int *numCols,const wxChar *userID ) ------
2312 * -----------------------------------------------------------------------
2315 /*---------------------------------------------------------------------*/
2316 /* Get the names of the columns in the primary key. */
2317 /*---------------------------------------------------------------------*/
2318 retcode
= SQLPrimaryKeys(hstmt
,
2319 NULL
, 0, /* Catalog name */
2320 NULL
, 0, /* Schema name */
2321 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2323 /*---------------------------------------------------------------------*/
2324 /* Fetch and display the result set. This will be a list of the */
2325 /* columns in the primary key of the tableName table. */
2326 /*---------------------------------------------------------------------*/
2327 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2329 retcode
= SQLFetch(hstmt
);
2330 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2332 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2333 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2335 for (i
=0;i
<noCols
;i
++) // Find the Column name
2336 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2337 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2340 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2342 /*---------------------------------------------------------------------*/
2343 /* Get all the foreign keys that refer to tableName primary key. */
2344 /*---------------------------------------------------------------------*/
2345 retcode
= SQLForeignKeys(hstmt
,
2346 NULL
, 0, /* Primary catalog */
2347 NULL
, 0, /* Primary schema */
2348 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2349 NULL
, 0, /* Foreign catalog */
2350 NULL
, 0, /* Foreign schema */
2351 NULL
, 0); /* Foreign table */
2353 /*---------------------------------------------------------------------*/
2354 /* Fetch and display the result set. This will be all of the foreign */
2355 /* keys in other tables that refer to the tableName primary key. */
2356 /*---------------------------------------------------------------------*/
2359 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2361 retcode
= SQLFetch(hstmt
);
2362 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2364 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2365 GetData( 4, SQL_C_WXCHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2366 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2367 GetData( 7, SQL_C_WXCHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2368 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2369 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2373 tempStr
.Trim(); // Get rid of any unneeded blanks
2374 if (!tempStr
.empty())
2376 for (i
=0; i
<noCols
; i
++)
2377 { // Find the Column name
2378 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2379 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2383 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2385 /*---------------------------------------------------------------------*/
2386 /* Get all the foreign keys in the tablename table. */
2387 /*---------------------------------------------------------------------*/
2388 retcode
= SQLForeignKeys(hstmt
,
2389 NULL
, 0, /* Primary catalog */
2390 NULL
, 0, /* Primary schema */
2391 NULL
, 0, /* Primary table */
2392 NULL
, 0, /* Foreign catalog */
2393 NULL
, 0, /* Foreign schema */
2394 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2396 /*---------------------------------------------------------------------*/
2397 /* Fetch and display the result set. This will be all of the */
2398 /* primary keys in other tables that are referred to by foreign */
2399 /* keys in the tableName table. */
2400 /*---------------------------------------------------------------------*/
2401 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2403 retcode
= SQLFetch(hstmt
);
2404 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2406 GetData( 3, SQL_C_WXCHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2407 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2408 GetData( 8, SQL_C_WXCHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2410 for (i
=0; i
<noCols
; i
++) // Find the Column name
2412 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2414 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2415 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2420 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2424 } // wxDb::GetKeyFields()
2428 /********** wxDb::GetColumns() **********/
2429 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2431 * 1) The last array element of the tableName[] argument must be zero (null).
2432 * This is how the end of the array is detected.
2433 * 2) This function returns an array of wxDbColInf structures. If no columns
2434 * were found, or an error occured, this pointer will be zero (null). THE
2435 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2436 * IS FINISHED WITH IT. i.e.
2438 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2441 * // Use the column inf
2443 * // Destroy the memory
2447 * userID is evaluated in the following manner:
2448 * userID == NULL ... UserID is ignored
2449 * userID == "" ... UserID set equal to 'this->uid'
2450 * userID != "" ... UserID set equal to 'userID'
2452 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2453 * by this function. This function should use its own wxDb instance
2454 * to avoid undesired unbinding of columns.
2459 wxDbColInf
*colInf
= 0;
2467 convertUserID(userID
,UserID
);
2469 // Pass 1 - Determine how many columns there are.
2470 // Pass 2 - Allocate the wxDbColInf array and fill in
2471 // the array with the column information.
2473 for (pass
= 1; pass
<= 2; pass
++)
2477 if (noCols
== 0) // Probably a bogus table name(s)
2479 // Allocate n wxDbColInf objects to hold the column information
2480 colInf
= new wxDbColInf
[noCols
+1];
2483 // Mark the end of the array
2484 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2485 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2486 colInf
[noCols
].sqlDataType
= 0;
2488 // Loop through each table name
2490 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2492 TableName
= tableName
[tbl
];
2493 // Oracle and Interbase table names are uppercase only, so force
2494 // the name to uppercase just in case programmer forgot to do this
2495 if ((Dbms() == dbmsORACLE
) ||
2496 (Dbms() == dbmsINTERBASE
))
2497 TableName
= TableName
.Upper();
2499 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2501 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2502 // use the call below that leaves out the user name
2503 if (!UserID
.empty() &&
2504 Dbms() != dbmsMY_SQL
&&
2505 Dbms() != dbmsACCESS
&&
2506 Dbms() != dbmsMS_SQL_SERVER
)
2508 retcode
= SQLColumns(hstmt
,
2509 NULL
, 0, // All qualifiers
2510 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2511 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2512 NULL
, 0); // All columns
2516 retcode
= SQLColumns(hstmt
,
2517 NULL
, 0, // All qualifiers
2519 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2520 NULL
, 0); // All columns
2522 if (retcode
!= SQL_SUCCESS
)
2523 { // Error occured, abort
2524 DispAllErrors(henv
, hdbc
, hstmt
);
2527 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2531 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2533 if (pass
== 1) // First pass, just add up the number of columns
2535 else // Pass 2; Fill in the array of structures
2537 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2539 // NOTE: Only the ODBC 1.x fields are retrieved
2540 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2541 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2542 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2543 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2544 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2545 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2546 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2547 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2548 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2549 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2550 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2551 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2553 // Determine the wxDb data type that is used to represent the native data type of this data source
2554 colInf
[colNo
].dbDataType
= 0;
2555 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2558 // IODBC does not return a correct columnLength, so we set
2559 // columnLength = bufferSize if no column length was returned
2560 // IODBC returns the columnLength in bufferSize. (bug)
2561 if (colInf
[colNo
].columnLength
< 1)
2563 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2566 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2568 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2569 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2570 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2571 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2572 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2573 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2574 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2575 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2580 if (retcode
!= SQL_NO_DATA_FOUND
)
2581 { // Error occured, abort
2582 DispAllErrors(henv
, hdbc
, hstmt
);
2585 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2591 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2594 } // wxDb::GetColumns()
2597 /********** wxDb::GetColumns() **********/
2599 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2601 // Same as the above GetColumns() function except this one gets columns
2602 // only for a single table, and if 'numCols' is not NULL, the number of
2603 // columns stored in the returned wxDbColInf is set in '*numCols'
2605 // userID is evaluated in the following manner:
2606 // userID == NULL ... UserID is ignored
2607 // userID == "" ... UserID set equal to 'this->uid'
2608 // userID != "" ... UserID set equal to 'userID'
2610 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2611 // by this function. This function should use its own wxDb instance
2612 // to avoid undesired unbinding of columns.
2617 wxDbColInf
*colInf
= 0;
2625 convertUserID(userID
,UserID
);
2627 // Pass 1 - Determine how many columns there are.
2628 // Pass 2 - Allocate the wxDbColInf array and fill in
2629 // the array with the column information.
2631 for (pass
= 1; pass
<= 2; pass
++)
2635 if (noCols
== 0) // Probably a bogus table name(s)
2637 // Allocate n wxDbColInf objects to hold the column information
2638 colInf
= new wxDbColInf
[noCols
+1];
2641 // Mark the end of the array
2642 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2643 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2644 colInf
[noCols
].sqlDataType
= 0;
2647 TableName
= tableName
;
2648 // Oracle and Interbase table names are uppercase only, so force
2649 // the name to uppercase just in case programmer forgot to do this
2650 if ((Dbms() == dbmsORACLE
) ||
2651 (Dbms() == dbmsINTERBASE
))
2652 TableName
= TableName
.Upper();
2654 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2656 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2657 // use the call below that leaves out the user name
2658 if (!UserID
.empty() &&
2659 Dbms() != dbmsMY_SQL
&&
2660 Dbms() != dbmsACCESS
&&
2661 Dbms() != dbmsMS_SQL_SERVER
)
2663 retcode
= SQLColumns(hstmt
,
2664 NULL
, 0, // All qualifiers
2665 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2666 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2667 NULL
, 0); // All columns
2671 retcode
= SQLColumns(hstmt
,
2672 NULL
, 0, // All qualifiers
2674 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2675 NULL
, 0); // All columns
2677 if (retcode
!= SQL_SUCCESS
)
2678 { // Error occured, abort
2679 DispAllErrors(henv
, hdbc
, hstmt
);
2682 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2688 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2690 if (pass
== 1) // First pass, just add up the number of columns
2692 else // Pass 2; Fill in the array of structures
2694 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2696 // NOTE: Only the ODBC 1.x fields are retrieved
2697 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2698 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2699 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2700 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2701 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2702 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2703 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2704 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2705 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2706 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2707 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2708 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2709 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2710 // Start Values for Primary/Foriegn Key (=No)
2711 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2712 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2713 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2714 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2716 // BJO 20000428 : Virtuoso returns type names with upper cases!
2717 if (Dbms() == dbmsVIRTUOSO
)
2719 wxString s
= colInf
[colNo
].typeName
;
2721 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2724 // Determine the wxDb data type that is used to represent the native data type of this data source
2725 colInf
[colNo
].dbDataType
= 0;
2726 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2729 // IODBC does not return a correct columnLength, so we set
2730 // columnLength = bufferSize if no column length was returned
2731 // IODBC returns the columnLength in bufferSize. (bug)
2732 if (colInf
[colNo
].columnLength
< 1)
2734 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2738 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2740 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2741 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2742 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2743 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2744 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2745 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2746 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2747 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2753 if (retcode
!= SQL_NO_DATA_FOUND
)
2754 { // Error occured, abort
2755 DispAllErrors(henv
, hdbc
, hstmt
);
2758 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2765 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2767 // Store Primary and Foriegn Keys
2768 GetKeyFields(tableName
,colInf
,noCols
);
2774 } // wxDb::GetColumns()
2777 #else // New GetColumns
2782 These are tentative new GetColumns members which should be more database
2783 independant and which always returns the columns in the order they were
2786 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2787 wxChar* userID)) calls the second implementation for each separate table
2788 before merging the results. This makes the code easier to maintain as
2789 only one member (the second) makes the real work
2790 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2791 wxChar *userID) is a little bit improved
2792 - It doesn't anymore rely on the type-name to find out which database-type
2794 - It ends by sorting the columns, so that they are returned in the same
2795 order they were created
2805 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2808 // The last array element of the tableName[] argument must be zero (null).
2809 // This is how the end of the array is detected.
2813 // How many tables ?
2815 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2817 // Create a table to maintain the columns for each separate table
2818 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2821 for (i
= 0 ; i
< tbl
; i
++)
2824 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2825 if (TableColumns
[i
].colInf
== NULL
)
2827 noCols
+= TableColumns
[i
].noCols
;
2830 // Now merge all the separate table infos
2831 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2833 // Mark the end of the array
2834 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2835 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2836 colInf
[noCols
].sqlDataType
= 0;
2841 for (i
= 0 ; i
< tbl
; i
++)
2843 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2845 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2849 delete [] TableColumns
;
2852 } // wxDb::GetColumns() -- NEW
2855 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2857 // Same as the above GetColumns() function except this one gets columns
2858 // only for a single table, and if 'numCols' is not NULL, the number of
2859 // columns stored in the returned wxDbColInf is set in '*numCols'
2861 // userID is evaluated in the following manner:
2862 // userID == NULL ... UserID is ignored
2863 // userID == "" ... UserID set equal to 'this->uid'
2864 // userID != "" ... UserID set equal to 'userID'
2866 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2867 // by this function. This function should use its own wxDb instance
2868 // to avoid undesired unbinding of columns.
2872 wxDbColInf
*colInf
= 0;
2880 convertUserID(userID
,UserID
);
2882 // Pass 1 - Determine how many columns there are.
2883 // Pass 2 - Allocate the wxDbColInf array and fill in
2884 // the array with the column information.
2886 for (pass
= 1; pass
<= 2; pass
++)
2890 if (noCols
== 0) // Probably a bogus table name(s)
2892 // Allocate n wxDbColInf objects to hold the column information
2893 colInf
= new wxDbColInf
[noCols
+1];
2896 // Mark the end of the array
2897 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2898 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2899 colInf
[noCols
].sqlDataType
= 0;
2902 TableName
= tableName
;
2903 // Oracle and Interbase table names are uppercase only, so force
2904 // the name to uppercase just in case programmer forgot to do this
2905 if ((Dbms() == dbmsORACLE
) ||
2906 (Dbms() == dbmsINTERBASE
))
2907 TableName
= TableName
.Upper();
2909 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2911 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2912 // use the call below that leaves out the user name
2913 if (!UserID
.empty() &&
2914 Dbms() != dbmsMY_SQL
&&
2915 Dbms() != dbmsACCESS
&&
2916 Dbms() != dbmsMS_SQL_SERVER
)
2918 retcode
= SQLColumns(hstmt
,
2919 NULL
, 0, // All qualifiers
2920 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2921 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2922 NULL
, 0); // All columns
2926 retcode
= SQLColumns(hstmt
,
2927 NULL
, 0, // All qualifiers
2929 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2930 NULL
, 0); // All columns
2932 if (retcode
!= SQL_SUCCESS
)
2933 { // Error occured, abort
2934 DispAllErrors(henv
, hdbc
, hstmt
);
2937 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2943 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2945 if (pass
== 1) // First pass, just add up the number of columns
2947 else // Pass 2; Fill in the array of structures
2949 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2951 // NOTE: Only the ODBC 1.x fields are retrieved
2952 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2953 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2954 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2955 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2956 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2957 GetData( 6, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2958 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnLength
, 0, &cb
);
2959 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferSize
, 0, &cb
);
2960 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2961 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2962 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2963 GetData(12, SQL_C_WXCHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2964 // Start Values for Primary/Foriegn Key (=No)
2965 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2966 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2967 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2968 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2971 // IODBC does not return a correct columnLength, so we set
2972 // columnLength = bufferSize if no column length was returned
2973 // IODBC returns the columnLength in bufferSize. (bug)
2974 if (colInf
[colNo
].columnLength
< 1)
2976 colInf
[colNo
].columnLength
= colInf
[colNo
].bufferSize
;
2980 // Determine the wxDb data type that is used to represent the native data type of this data source
2981 colInf
[colNo
].dbDataType
= 0;
2982 // Get the intern datatype
2983 switch (colInf
[colNo
].sqlDataType
)
2987 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2993 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
3000 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
3004 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
3007 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
3012 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
3013 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3020 if (retcode
!= SQL_NO_DATA_FOUND
)
3021 { // Error occured, abort
3022 DispAllErrors(henv
, hdbc
, hstmt
);
3025 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3032 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3034 // Store Primary and Foreign Keys
3035 GetKeyFields(tableName
,colInf
,noCols
);
3037 ///////////////////////////////////////////////////////////////////////////
3038 // Now sort the the columns in order to make them appear in the right order
3039 ///////////////////////////////////////////////////////////////////////////
3041 // Build a generic SELECT statement which returns 0 rows
3044 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
3047 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
3049 DispAllErrors(henv
, hdbc
, hstmt
);
3053 // Get the number of result columns
3054 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
3056 DispAllErrors(henv
, hdbc
, hstmt
);
3060 if (noCols
== 0) // Probably a bogus table name
3069 for (colNum
= 0; colNum
< noCols
; colNum
++)
3071 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
3073 &Sword
, &Sdword
) != SQL_SUCCESS
)
3075 DispAllErrors(henv
, hdbc
, hstmt
);
3079 wxString Name1
= name
;
3080 Name1
= Name1
.Upper();
3082 // Where is this name in the array ?
3083 for (i
= colNum
; i
< noCols
; i
++)
3085 wxString Name2
= colInf
[i
].colName
;
3086 Name2
= Name2
.Upper();
3089 if (colNum
!= i
) // swap to sort
3091 wxDbColInf tmpColInf
= colInf
[colNum
];
3092 colInf
[colNum
] = colInf
[i
];
3093 colInf
[i
] = tmpColInf
;
3099 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3101 ///////////////////////////////////////////////////////////////////////////
3103 ///////////////////////////////////////////////////////////////////////////
3109 } // wxDb::GetColumns()
3112 #endif // #else OLD_GETCOLUMNS
3115 /********** wxDb::GetColumnCount() **********/
3116 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3118 * Returns a count of how many columns are in a table.
3119 * If an error occurs in computing the number of columns
3120 * this function will return a -1 for the count
3122 * userID is evaluated in the following manner:
3123 * userID == NULL ... UserID is ignored
3124 * userID == "" ... UserID set equal to 'this->uid'
3125 * userID != "" ... UserID set equal to 'userID'
3127 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3128 * by this function. This function should use its own wxDb instance
3129 * to avoid undesired unbinding of columns.
3139 convertUserID(userID
,UserID
);
3141 TableName
= tableName
;
3142 // Oracle and Interbase table names are uppercase only, so force
3143 // the name to uppercase just in case programmer forgot to do this
3144 if ((Dbms() == dbmsORACLE
) ||
3145 (Dbms() == dbmsINTERBASE
))
3146 TableName
= TableName
.Upper();
3148 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3150 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3151 // use the call below that leaves out the user name
3152 if (!UserID
.empty() &&
3153 Dbms() != dbmsMY_SQL
&&
3154 Dbms() != dbmsACCESS
&&
3155 Dbms() != dbmsMS_SQL_SERVER
)
3157 retcode
= SQLColumns(hstmt
,
3158 NULL
, 0, // All qualifiers
3159 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3160 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3161 NULL
, 0); // All columns
3165 retcode
= SQLColumns(hstmt
,
3166 NULL
, 0, // All qualifiers
3168 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3169 NULL
, 0); // All columns
3171 if (retcode
!= SQL_SUCCESS
)
3172 { // Error occured, abort
3173 DispAllErrors(henv
, hdbc
, hstmt
);
3174 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3178 // Count the columns
3179 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3182 if (retcode
!= SQL_NO_DATA_FOUND
)
3183 { // Error occured, abort
3184 DispAllErrors(henv
, hdbc
, hstmt
);
3185 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3189 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3192 } // wxDb::GetColumnCount()
3195 /********** wxDb::GetCatalog() *******/
3196 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3198 * ---------------------------------------------------------------------
3199 * -- 19991203 : mj10777 : Create ------
3200 * -- : Creates a wxDbInf with Tables / Cols Array ------
3201 * -- : uses SQLTables and fills pTableInf; ------
3202 * -- : pColInf is set to NULL and numCols to 0; ------
3203 * -- : returns pDbInf (wxDbInf) ------
3204 * -- - if unsuccesfull (pDbInf == NULL) ------
3205 * -- : pColInf can be filled with GetColumns(..); ------
3206 * -- : numCols can be filled with GetColumnCount(..); ------
3207 * ---------------------------------------------------------------------
3209 * userID is evaluated in the following manner:
3210 * userID == NULL ... UserID is ignored
3211 * userID == "" ... UserID set equal to 'this->uid'
3212 * userID != "" ... UserID set equal to 'userID'
3214 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3215 * by this function. This function should use its own wxDb instance
3216 * to avoid undesired unbinding of columns.
3219 int noTab
= 0; // Counter while filling table entries
3223 wxString tblNameSave
;
3226 convertUserID(userID
,UserID
);
3228 //-------------------------------------------------------------
3229 // Create the Database Array of catalog entries
3231 wxDbInf
*pDbInf
= new wxDbInf
;
3233 //-------------------------------------------------------------
3234 // Table Information
3235 // Pass 1 - Determine how many Tables there are.
3236 // Pass 2 - Create the Table array and fill it
3237 // - Create the Cols array = NULL
3238 //-------------------------------------------------------------
3240 for (pass
= 1; pass
<= 2; pass
++)
3242 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3243 tblNameSave
.Empty();
3245 if (!UserID
.empty() &&
3246 Dbms() != dbmsMY_SQL
&&
3247 Dbms() != dbmsACCESS
&&
3248 Dbms() != dbmsMS_SQL_SERVER
)
3250 retcode
= SQLTables(hstmt
,
3251 NULL
, 0, // All qualifiers
3252 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3253 NULL
, 0, // All tables
3254 NULL
, 0); // All columns
3258 retcode
= SQLTables(hstmt
,
3259 NULL
, 0, // All qualifiers
3260 NULL
, 0, // User specified
3261 NULL
, 0, // All tables
3262 NULL
, 0); // All columns
3265 if (retcode
!= SQL_SUCCESS
)
3267 DispAllErrors(henv
, hdbc
, hstmt
);
3269 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3273 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3275 if (pass
== 1) // First pass, just count the Tables
3277 if (pDbInf
->numTables
== 0)
3279 GetData( 1, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3280 GetData( 2, SQL_C_WXCHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3282 pDbInf
->numTables
++; // Counter for Tables
3284 if (pass
== 2) // Create and fill the Table entries
3286 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3287 { // no, then create the Array
3288 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3290 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3292 GetData( 3, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3293 GetData( 4, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3294 GetData( 5, SQL_C_WXCHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3300 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3302 // Query how many columns are in each table
3303 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3305 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3310 } // wxDb::GetCatalog()
3313 /********** wxDb::Catalog() **********/
3314 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3316 * Creates the text file specified in 'filename' which will contain
3317 * a minimal data dictionary of all tables accessible by the user specified
3320 * userID is evaluated in the following manner:
3321 * userID == NULL ... UserID is ignored
3322 * userID == "" ... UserID set equal to 'this->uid'
3323 * userID != "" ... UserID set equal to 'userID'
3325 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3326 * by this function. This function should use its own wxDb instance
3327 * to avoid undesired unbinding of columns.
3330 wxASSERT(fileName
.Length());
3334 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3335 wxString tblNameSave
;
3336 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3338 wxChar typeName
[30+1];
3339 SDWORD precision
, length
;
3341 FILE *fp
= wxFopen(fileName
.fn_str(),wxT("wt"));
3345 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3348 convertUserID(userID
,UserID
);
3350 if (!UserID
.empty() &&
3351 Dbms() != dbmsMY_SQL
&&
3352 Dbms() != dbmsACCESS
&&
3353 Dbms() != dbmsINTERBASE
&&
3354 Dbms() != dbmsMS_SQL_SERVER
)
3356 retcode
= SQLColumns(hstmt
,
3357 NULL
, 0, // All qualifiers
3358 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3359 NULL
, 0, // All tables
3360 NULL
, 0); // All columns
3364 retcode
= SQLColumns(hstmt
,
3365 NULL
, 0, // All qualifiers
3366 NULL
, 0, // User specified
3367 NULL
, 0, // All tables
3368 NULL
, 0); // All columns
3370 if (retcode
!= SQL_SUCCESS
)
3372 DispAllErrors(henv
, hdbc
, hstmt
);
3378 tblNameSave
.Empty();
3383 retcode
= SQLFetch(hstmt
);
3384 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3387 GetData(3,SQL_C_WXCHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3388 GetData(4,SQL_C_WXCHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3389 GetData(5,SQL_C_SSHORT
, (UCHAR
*)&sqlDataType
, 0, &cb
);
3390 GetData(6,SQL_C_WXCHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3391 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3392 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3394 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3397 wxFputs(wxT("\n"), fp
);
3398 wxFputs(wxT("================================ "), fp
);
3399 wxFputs(wxT("================================ "), fp
);
3400 wxFputs(wxT("===================== "), fp
);
3401 wxFputs(wxT("========= "), fp
);
3402 wxFputs(wxT("=========\n"), fp
);
3403 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3404 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3405 wxFputs(outStr
.c_str(), fp
);
3406 wxFputs(wxT("================================ "), fp
);
3407 wxFputs(wxT("================================ "), fp
);
3408 wxFputs(wxT("===================== "), fp
);
3409 wxFputs(wxT("========= "), fp
);
3410 wxFputs(wxT("=========\n"), fp
);
3411 tblNameSave
= tblName
;
3414 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3415 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3416 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3418 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3425 if (retcode
!= SQL_NO_DATA_FOUND
)
3426 DispAllErrors(henv
, hdbc
, hstmt
);
3428 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3431 return(retcode
== SQL_NO_DATA_FOUND
);
3433 } // wxDb::Catalog()
3436 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3438 * Table name can refer to a table, view, alias or synonym. Returns true
3439 * if the object exists in the database. This function does not indicate
3440 * whether or not the user has privleges to query or perform other functions
3443 * userID is evaluated in the following manner:
3444 * userID == NULL ... UserID is ignored
3445 * userID == "" ... UserID set equal to 'this->uid'
3446 * userID != "" ... UserID set equal to 'userID'
3449 wxASSERT(tableName
.Length());
3453 if (Dbms() == dbmsDBASE
)
3456 if (tablePath
.Length())
3457 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3459 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3462 exists
= wxFileExists(dbName
);
3467 convertUserID(userID
,UserID
);
3469 TableName
= tableName
;
3470 // Oracle and Interbase table names are uppercase only, so force
3471 // the name to uppercase just in case programmer forgot to do this
3472 if ((Dbms() == dbmsORACLE
) ||
3473 (Dbms() == dbmsINTERBASE
))
3474 TableName
= TableName
.Upper();
3476 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3479 // Some databases cannot accept a user name when looking up table names,
3480 // so we use the call below that leaves out the user name
3481 if (!UserID
.empty() &&
3482 Dbms() != dbmsMY_SQL
&&
3483 Dbms() != dbmsACCESS
&&
3484 Dbms() != dbmsMS_SQL_SERVER
&&
3485 Dbms() != dbmsDB2
&&
3486 Dbms() != dbmsINTERBASE
&&
3487 Dbms() != dbmsPERVASIVE_SQL
)
3489 retcode
= SQLTables(hstmt
,
3490 NULL
, 0, // All qualifiers
3491 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3492 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3493 NULL
, 0); // All table types
3497 retcode
= SQLTables(hstmt
,
3498 NULL
, 0, // All qualifiers
3499 NULL
, 0, // All owners
3500 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3501 NULL
, 0); // All table types
3503 if (retcode
!= SQL_SUCCESS
)
3504 return(DispAllErrors(henv
, hdbc
, hstmt
));
3506 retcode
= SQLFetch(hstmt
);
3507 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3509 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3510 return(DispAllErrors(henv
, hdbc
, hstmt
));
3513 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3517 } // wxDb::TableExists()
3520 /********** wxDb::TablePrivileges() **********/
3521 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3522 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3524 wxASSERT(tableName
.Length());
3526 wxDbTablePrivilegeInfo result
;
3530 // We probably need to be able to dynamically set this based on
3531 // the driver type, and state.
3532 wxChar curRole
[]=wxT("public");
3536 wxString UserID
,Schema
;
3537 convertUserID(userID
,UserID
);
3538 convertUserID(schema
,Schema
);
3540 TableName
= tableName
;
3541 // Oracle and Interbase table names are uppercase only, so force
3542 // the name to uppercase just in case programmer forgot to do this
3543 if ((Dbms() == dbmsORACLE
) ||
3544 (Dbms() == dbmsINTERBASE
))
3545 TableName
= TableName
.Upper();
3547 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3549 // Some databases cannot accept a user name when looking up table names,
3550 // so we use the call below that leaves out the user name
3551 if (!Schema
.empty() &&
3552 Dbms() != dbmsMY_SQL
&&
3553 Dbms() != dbmsACCESS
&&
3554 Dbms() != dbmsMS_SQL_SERVER
)
3556 retcode
= SQLTablePrivileges(hstmt
,
3558 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3559 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3563 retcode
= SQLTablePrivileges(hstmt
,
3566 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3569 #ifdef DBDEBUG_CONSOLE
3570 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3573 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3574 return (DispAllErrors(henv
, hdbc
, hstmt
));
3576 bool failed
= false;
3577 retcode
= SQLFetch(hstmt
);
3578 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3580 if (SQLGetData(hstmt
, 1, SQL_C_WXCHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3583 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_WXCHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3586 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_WXCHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3589 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_WXCHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3592 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_WXCHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3595 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_WXCHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3598 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_WXCHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3603 return(DispAllErrors(henv
, hdbc
, hstmt
));
3605 #ifdef DBDEBUG_CONSOLE
3606 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3607 result
.privilege
,result
.tableOwner
,result
.tableName
,
3608 result
.grantor
, result
.grantee
);
3611 if (UserID
.IsSameAs(result
.tableOwner
,false))
3613 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3617 if (UserID
.IsSameAs(result
.grantee
,false) &&
3618 !wxStrcmp(result
.privilege
,priv
))
3620 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3624 if (!wxStrcmp(result
.grantee
,curRole
) &&
3625 !wxStrcmp(result
.privilege
,priv
))
3627 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3631 retcode
= SQLFetch(hstmt
);
3634 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3637 } // wxDb::TablePrivileges
3640 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3644 if (Dbms() == dbmsACCESS
)
3645 TableName
= _T("\"");
3646 TableName
+= tableName
;
3647 if (Dbms() == dbmsACCESS
)
3648 TableName
+= _T("\"");
3651 } // wxDb::SQLTableName()
3654 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3658 if (Dbms() == dbmsACCESS
)
3661 if (Dbms() == dbmsACCESS
)
3662 ColName
+= _T("\"");
3665 } // wxDb::SQLColumnName()
3668 /********** wxDb::SetSqlLogging() **********/
3669 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3671 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3672 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3674 if (state
== sqlLogON
)
3678 fpSqlLog
= wxFopen(filename
.fn_str(), (append
? wxT("at") : wxT("wt")));
3679 if (fpSqlLog
== NULL
)
3687 if (fclose(fpSqlLog
))
3693 sqlLogState
= state
;
3696 } // wxDb::SetSqlLogging()
3699 /********** wxDb::WriteSqlLog() **********/
3700 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3702 wxASSERT(logMsg
.Length());
3704 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3707 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3709 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3711 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3716 } // wxDb::WriteSqlLog()
3719 /********** wxDb::Dbms() **********/
3720 wxDBMS
wxDb::Dbms(void)
3722 * Be aware that not all database engines use the exact same syntax, and not
3723 * every ODBC compliant database is compliant to the same level of compliancy.
3724 * Some manufacturers support the minimum Level 1 compliancy, and others up
3725 * through Level 3. Others support subsets of features for levels above 1.
3727 * If you find an inconsistency between the wxDb class and a specific database
3728 * engine, and an identifier to this section, and special handle the database in
3729 * the area where behavior is non-conforming with the other databases.
3732 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3733 * ---------------------------------------------------
3736 * - Currently the only database supported by the class to support VIEWS
3739 * - Does not support the SQL_TIMESTAMP structure
3740 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3741 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3742 * is true. The user must create ALL indexes from their program.
3743 * - Table names can only be 8 characters long
3744 * - Column names can only be 10 characters long
3747 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3748 * after every table name involved in the query/join if that tables matching record(s)
3750 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3752 * SYBASE (Enterprise)
3753 * - If a column is part of the Primary Key, the column cannot be NULL
3754 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3757 * - If a column is part of the Primary Key, the column cannot be NULL
3758 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3759 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3760 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3761 * column definition if it is not defined correctly, but it is experimental
3762 * - Does not support sub-queries in SQL statements
3765 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3766 * - Does not support sub-queries in SQL statements
3769 * - Primary keys must be declared as NOT NULL
3770 * - Table and index names must not be longer than 13 characters in length (technically
3771 * table names can be up to 18 characters, but the primary index is created using the
3772 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3777 * - Columns that are part of primary keys must be defined as being NOT NULL
3778 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3779 * column definition if it is not defined correctly, but it is experimental
3782 // Should only need to do this once for each new database connection
3783 // so return the value we already determined it to be to save time
3784 // and lots of string comparisons
3785 if (dbmsType
!= dbmsUNIDENTIFIED
)
3788 wxChar baseName
[25+1];
3789 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3792 // RGG 20001025 : add support for Interbase
3793 // GT : Integrated to base classes on 20001121
3794 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3795 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3797 // BJO 20000428 : add support for Virtuoso
3798 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3799 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3801 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3802 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3804 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3805 // connected through an OpenLink driver.
3806 // Is it also returned by Sybase Adapatitve server?
3807 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3808 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3810 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3811 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3812 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3814 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3817 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3818 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3821 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3822 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3825 if (!wxStricmp(baseName
,wxT("Pervasive")))
3826 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3829 if (!wxStricmp(baseName
,wxT("Informix")))
3830 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3833 if (!wxStricmp(baseName
,wxT("Oracle")))
3834 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3835 if (!wxStricmp(baseName
,wxT("ACCESS")))
3836 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3837 if (!wxStricmp(baseName
,wxT("Sybase")))
3838 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3841 if (!wxStricmp(baseName
,wxT("DBASE")))
3842 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3843 if (!wxStricmp(baseName
,wxT("xBase")))
3844 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3845 if (!wxStricmp(baseName
,wxT("MySQL")))
3846 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3849 if (!wxStricmp(baseName
,wxT("DB2")))
3850 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3852 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3857 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3858 int dataType
, ULONG columnLength
,
3859 const wxString
&optionalParam
)
3861 wxASSERT(tableName
.Length());
3862 wxASSERT(columnName
.Length());
3863 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3864 dataType
!= DB_DATA_TYPE_VARCHAR
);
3866 // Must specify a columnLength if modifying a VARCHAR type column
3867 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3870 wxString dataTypeName
;
3872 wxString alterSlashModify
;
3876 case DB_DATA_TYPE_VARCHAR
:
3877 dataTypeName
= typeInfVarchar
.TypeName
;
3879 case DB_DATA_TYPE_INTEGER
:
3880 dataTypeName
= typeInfInteger
.TypeName
;
3882 case DB_DATA_TYPE_FLOAT
:
3883 dataTypeName
= typeInfFloat
.TypeName
;
3885 case DB_DATA_TYPE_DATE
:
3886 dataTypeName
= typeInfDate
.TypeName
;
3888 case DB_DATA_TYPE_BLOB
:
3889 dataTypeName
= typeInfBlob
.TypeName
;
3895 // Set the modify or alter syntax depending on the type of database connected to
3899 alterSlashModify
= _T("MODIFY");
3901 case dbmsMS_SQL_SERVER
:
3902 alterSlashModify
= _T("ALTER COLUMN");
3904 case dbmsUNIDENTIFIED
:
3906 case dbmsSYBASE_ASA
:
3907 case dbmsSYBASE_ASE
:
3912 case dbmsXBASE_SEQUITER
:
3914 alterSlashModify
= _T("MODIFY");
3918 // create the SQL statement
3919 if ( Dbms() == dbmsMY_SQL
)
3921 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3922 columnName
.c_str(), dataTypeName
.c_str());
3926 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3927 columnName
.c_str(), dataTypeName
.c_str());
3930 // For varchars only, append the size of the column
3931 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3932 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3935 s
.Printf(wxT("(%lu)"), columnLength
);
3939 // for passing things like "NOT NULL"
3940 if (optionalParam
.Length())
3942 sqlStmt
+= wxT(" ");
3943 sqlStmt
+= optionalParam
;
3946 return ExecSql(sqlStmt
);
3948 } // wxDb::ModifyColumn()
3951 /********** wxDbGetConnection() **********/
3952 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3956 // Used to keep a pointer to a DB connection that matches the requested
3957 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3958 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3959 // rather than having to re-query the datasource to get all the values
3960 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3961 wxDb
*matchingDbConnection
= NULL
;
3963 // Scan the linked list searching for an available database connection
3964 // that's already been opened but is currently not in use.
3965 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3967 // The database connection must be for the same datasource
3968 // name and must currently not be in use.
3970 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
3972 if (pDbConfig
->UseConnectionStr())
3974 if (pList
->PtrDb
->OpenedWithConnectionString() &&
3975 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
3977 // Found a free connection
3978 pList
->Free
= false;
3979 return(pList
->PtrDb
);
3984 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
3985 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
3987 // Found a free connection
3988 pList
->Free
= false;
3989 return(pList
->PtrDb
);
3994 if (pDbConfig
->UseConnectionStr())
3996 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
3997 matchingDbConnection
= pList
->PtrDb
;
4001 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
4002 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
4003 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
4004 matchingDbConnection
= pList
->PtrDb
;
4008 // No available connections. A new connection must be made and
4009 // appended to the end of the linked list.
4012 // Find the end of the list
4013 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
4014 // Append a new list item
4015 pList
->PtrNext
= new wxDbList
;
4016 pList
->PtrNext
->PtrPrev
= pList
;
4017 pList
= pList
->PtrNext
;
4021 // Create the first node on the list
4022 pList
= PtrBegDbList
= new wxDbList
;
4026 // Initialize new node in the linked list
4028 pList
->Free
= false;
4029 pList
->Dsn
= pDbConfig
->GetDsn();
4030 pList
->Uid
= pDbConfig
->GetUserID();
4031 pList
->AuthStr
= pDbConfig
->GetPassword();
4032 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
4034 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
4038 if (!matchingDbConnection
)
4040 if (pDbConfig
->UseConnectionStr())
4042 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
4046 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
4050 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
4052 // Connect to the datasource
4055 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
4056 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
4057 return(pList
->PtrDb
);
4059 else // Unable to connect, destroy list item
4062 pList
->PtrPrev
->PtrNext
= 0;
4064 PtrBegDbList
= 0; // Empty list again
4066 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4067 pList
->PtrDb
->Close(); // Close the wxDb object
4068 delete pList
->PtrDb
; // Deletes the wxDb object
4069 delete pList
; // Deletes the linked list object
4073 } // wxDbGetConnection()
4076 /********** wxDbFreeConnection() **********/
4077 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
4081 // Scan the linked list searching for the database connection
4082 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4084 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
4085 return (pList
->Free
= true);
4088 // Never found the database object, return failure
4091 } // wxDbFreeConnection()
4094 /********** wxDbCloseConnections() **********/
4095 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4097 wxDbList
*pList
, *pNext
;
4099 // Traverse the linked list closing database connections and freeing memory as I go.
4100 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4102 pNext
= pList
->PtrNext
; // Save the pointer to next
4103 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4104 pList
->PtrDb
->Close(); // Close the wxDb object
4105 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4106 delete pList
->PtrDb
; // Deletes the wxDb object
4107 delete pList
; // Deletes the linked list object
4110 // Mark the list as empty
4113 } // wxDbCloseConnections()
4116 /********** wxDbConnectionsInUse() **********/
4117 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4122 // Scan the linked list counting db connections that are currently in use
4123 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4125 if (pList
->Free
== false)
4131 } // wxDbConnectionsInUse()
4135 /********** wxDbLogExtendedErrorMsg() **********/
4136 // DEBUG ONLY function
4137 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4139 const wxChar
*ErrFile
,
4142 static wxString msg
;
4147 if (ErrFile
|| ErrLine
)
4149 msg
+= wxT("File: ");
4151 msg
+= wxT(" Line: ");
4152 tStr
.Printf(wxT("%d"),ErrLine
);
4153 msg
+= tStr
.c_str();
4157 msg
.Append (wxT("\nODBC errors:\n"));
4160 // Display errors for this connection
4162 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4164 if (pDb
->errorList
[i
])
4166 msg
.Append(pDb
->errorList
[i
]);
4167 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
4168 msg
.Append(wxT("\n"));
4169 // Clear the errmsg buffer so the next error will not
4170 // end up showing the previous error that have occurred
4171 wxStrcpy(pDb
->errorList
[i
],wxT(""));
4176 wxLogDebug(msg
.c_str());
4179 } // wxDbLogExtendedErrorMsg()
4182 /********** wxDbSqlLog() **********/
4183 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4185 bool append
= false;
4188 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4190 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4195 SQLLOGstate
= state
;
4196 SQLLOGfn
= filename
;
4204 /********** wxDbCreateDataSource() **********/
4205 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4206 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4208 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4209 * Very rudimentary creation of an ODBC data source.
4211 * ODBC driver must be ODBC 3.0 compliant to use this function
4216 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4222 dsnLocation
= ODBC_ADD_SYS_DSN
;
4224 dsnLocation
= ODBC_ADD_DSN
;
4226 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4227 // so that is why I used it, as wxString does not deal well with
4228 // embedded nulls in strings
4229 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4231 // Replace the separator from above with the '\0' seperator needed
4232 // by the SQLConfigDataSource() function
4236 k
= setupStr
.Find((wxChar
)2,true);
4237 if (k
!= wxNOT_FOUND
)
4238 setupStr
[(UINT
)k
] = wxT('\0');
4240 while (k
!= wxNOT_FOUND
);
4242 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4243 driverName
, setupStr
.c_str());
4245 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4247 // check for errors caused by ConfigDSN based functions
4250 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4251 errMsg
[0] = wxT('\0');
4253 // This function is only supported in ODBC drivers v3.0 compliant and above
4254 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4257 #ifdef DBDEBUG_CONSOLE
4258 // When run in console mode, use standard out to display errors.
4259 cout
<< errMsg
<< endl
;
4260 cout
<< wxT("Press any key to continue...") << endl
;
4262 #endif // DBDEBUG_CONSOLE
4265 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4266 #endif // __WXDEBUG__
4272 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4273 // necessary to use this function, so this function is not supported
4275 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4278 #endif // __VISUALC__
4282 } // wxDbCreateDataSource()
4286 /********** wxDbGetDataSource() **********/
4287 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMaxLength
, wxChar
*DsDesc
,
4288 SWORD DsDescMaxLength
, UWORD direction
)
4290 * Dsn and DsDesc will contain the data source name and data source
4291 * description upon return
4295 SWORD lengthDsn
= (SWORD
)(DsnMaxLength
*sizeof(wxChar
));
4296 SWORD lengthDsDesc
= (SWORD
)(DsDescMaxLength
*sizeof(wxChar
));
4298 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, lengthDsn
, &cb1
,
4299 (SQLTCHAR FAR
*) DsDesc
, lengthDsDesc
, &cb2
) == SQL_SUCCESS
)
4304 } // wxDbGetDataSource()
4307 // Change this to 0 to remove use of all deprecated functions
4308 #if wxODBC_BACKWARD_COMPATABILITY
4309 /********************************************************************
4310 ********************************************************************
4312 * The following functions are all DEPRECATED and are included for
4313 * backward compatability reasons only
4315 ********************************************************************
4316 ********************************************************************/
4317 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4319 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4321 /***** DEPRECATED: use wxGetDataSource() *****/
4322 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4325 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4327 /***** DEPRECATED: use wxDbGetConnection() *****/
4328 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4330 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4332 /***** DEPRECATED: use wxDbFreeConnection() *****/
4333 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4335 return wxDbFreeConnection(pDb
);
4337 /***** DEPRECATED: use wxDbCloseConnections() *****/
4338 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4340 wxDbCloseConnections();
4342 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4343 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4345 return wxDbConnectionsInUse();