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 columnSize
, 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(),columnSize
,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()
354 /********** wxDbColInf Constructor **********/
355 wxDbColInf::wxDbColInf()
358 } // wxDbColInf::wxDbColInf()
361 /********** wxDbColInf Destructor ********/
362 wxDbColInf::~wxDbColInf()
367 } // wxDbColInf::~wxDbColInf()
370 bool wxDbColInf::Initialize()
392 } // wxDbColInf::Initialize()
395 /********** wxDbTableInf Constructor ********/
396 wxDbTableInf::wxDbTableInf()
399 } // wxDbTableInf::wxDbTableInf()
402 /********** wxDbTableInf Constructor ********/
403 wxDbTableInf::~wxDbTableInf()
408 } // wxDbTableInf::~wxDbTableInf()
411 bool wxDbTableInf::Initialize()
420 } // wxDbTableInf::Initialize()
423 /********** wxDbInf Constructor *************/
427 } // wxDbInf::wxDbInf()
430 /********** wxDbInf Destructor *************/
436 } // wxDbInf::~wxDbInf()
439 /********** wxDbInf::Initialize() *************/
440 bool wxDbInf::Initialize()
448 } // wxDbInf::Initialize()
451 /********** wxDb Constructor **********/
452 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
454 // Copy the HENV into the db class
456 fwdOnlyCursors
= FwdOnlyCursors
;
462 /********** wxDb Destructor **********/
465 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
475 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
476 /********** wxDb::initialize() **********/
477 void wxDb::initialize()
479 * Private member function that sets all wxDb member variables to
480 * known values at creation of the wxDb
485 fpSqlLog
= 0; // Sql Log file pointer
486 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
488 dbmsType
= dbmsUNIDENTIFIED
;
490 wxStrcpy(sqlState
,wxEmptyString
);
491 wxStrcpy(errorMsg
,wxEmptyString
);
492 nativeError
= cbErrorMsg
= 0;
493 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
494 wxStrcpy(errorList
[i
], wxEmptyString
);
496 // Init typeInf structures
497 typeInfVarchar
.TypeName
.Empty();
498 typeInfVarchar
.FsqlType
= 0;
499 typeInfVarchar
.Precision
= 0;
500 typeInfVarchar
.CaseSensitive
= 0;
501 typeInfVarchar
.MaximumScale
= 0;
503 typeInfInteger
.TypeName
.Empty();
504 typeInfInteger
.FsqlType
= 0;
505 typeInfInteger
.Precision
= 0;
506 typeInfInteger
.CaseSensitive
= 0;
507 typeInfInteger
.MaximumScale
= 0;
509 typeInfFloat
.TypeName
.Empty();
510 typeInfFloat
.FsqlType
= 0;
511 typeInfFloat
.Precision
= 0;
512 typeInfFloat
.CaseSensitive
= 0;
513 typeInfFloat
.MaximumScale
= 0;
515 typeInfDate
.TypeName
.Empty();
516 typeInfDate
.FsqlType
= 0;
517 typeInfDate
.Precision
= 0;
518 typeInfDate
.CaseSensitive
= 0;
519 typeInfDate
.MaximumScale
= 0;
521 typeInfBlob
.TypeName
.Empty();
522 typeInfBlob
.FsqlType
= 0;
523 typeInfBlob
.Precision
= 0;
524 typeInfBlob
.CaseSensitive
= 0;
525 typeInfBlob
.MaximumScale
= 0;
527 // Error reporting is turned OFF by default
530 // Allocate a data source connection handle
531 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
534 // Initialize the db status flag
537 // Mark database as not open as of yet
540 dbOpenedWithConnectionString
= false;
541 } // wxDb::initialize()
544 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
546 // NOTE: Return value from this function MUST be copied
547 // immediately, as the value is not good after
548 // this function has left scope.
550 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
554 if (!wxStrlen(userID
))
562 // dBase does not use user names, and some drivers fail if you try to pass one
563 if ( Dbms() == dbmsDBASE
564 || Dbms() == dbmsXBASE_SEQUITER
)
567 // Oracle user names may only be in uppercase, so force
568 // the name to uppercase
569 if (Dbms() == dbmsORACLE
)
570 UserID
= UserID
.Upper();
572 return UserID
.c_str();
573 } // wxDb::convertUserID()
576 bool wxDb::open(bool failOnDataTypeUnsupported
)
579 If using Intersolv branded ODBC drivers, this is the place where you would substitute
580 your branded driver license information
582 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
583 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
586 // Mark database as open
589 // Allocate a statement handle for the database connection
590 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
591 return(DispAllErrors(henv
, hdbc
));
593 // Set Connection Options
594 if (!setConnectionOptions())
597 // Query the data source for inf. about itself
598 if (!getDbInfo(failOnDataTypeUnsupported
))
601 // Query the data source regarding data type information
604 // The way it was determined which SQL data types to use was by calling SQLGetInfo
605 // for all of the possible SQL data types to see which ones were supported. If
606 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
607 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
608 // types I've selected below will not alway's be what we want. These are just
609 // what happened to work against an Oracle 7/Intersolv combination. The following is
610 // a complete list of the results I got back against the Oracle 7 database:
612 // SQL_BIGINT SQL_NO_DATA_FOUND
613 // SQL_BINARY SQL_NO_DATA_FOUND
614 // SQL_BIT SQL_NO_DATA_FOUND
615 // SQL_CHAR type name = 'CHAR', Precision = 255
616 // SQL_DATE SQL_NO_DATA_FOUND
617 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
618 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
619 // SQL_FLOAT SQL_NO_DATA_FOUND
620 // SQL_INTEGER SQL_NO_DATA_FOUND
621 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
622 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
623 // SQL_NUMERIC SQL_NO_DATA_FOUND
624 // SQL_REAL SQL_NO_DATA_FOUND
625 // SQL_SMALLINT SQL_NO_DATA_FOUND
626 // SQL_TIME SQL_NO_DATA_FOUND
627 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
628 // SQL_VARBINARY type name = 'RAW', Precision = 255
629 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
630 // =====================================================================
631 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
633 // SQL_VARCHAR type name = 'TEXT', Precision = 255
634 // SQL_TIMESTAMP type name = 'DATETIME'
635 // SQL_DECIMAL SQL_NO_DATA_FOUND
636 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
637 // SQL_FLOAT SQL_NO_DATA_FOUND
638 // SQL_REAL type name = 'SINGLE', Precision = 7
639 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
640 // SQL_INTEGER type name = 'LONG', Precision = 10
642 // VARCHAR = Variable length character string
643 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
644 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
647 typeInfVarchar
.FsqlType
= SQL_CHAR
;
649 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
652 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
653 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
654 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
655 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
656 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
658 if (failOnDataTypeUnsupported
)
662 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
664 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
666 typeInfFloat
.FsqlType
= SQL_FLOAT
;
668 typeInfFloat
.FsqlType
= SQL_REAL
;
670 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
673 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
675 // If SQL_INTEGER is not supported, use the floating point
676 // data type to store integers as well as floats
677 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
679 if (failOnDataTypeUnsupported
)
683 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
686 typeInfInteger
.FsqlType
= SQL_INTEGER
;
689 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
691 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
694 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
696 typeInfDate
.FsqlType
= SQL_TIME
;
699 #endif // SQL_DATETIME defined
701 if (failOnDataTypeUnsupported
)
706 typeInfDate
.FsqlType
= SQL_DATE
;
709 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
712 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
714 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
716 if (failOnDataTypeUnsupported
)
720 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
723 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
726 #ifdef DBDEBUG_CONSOLE
727 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
728 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
729 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
730 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
731 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
735 // Completed Successfully
739 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
741 wxASSERT(inConnectStr
.Length());
748 if (!FwdOnlyCursors())
750 // Specify that the ODBC cursor library be used, if needed. This must be
751 // specified before the connection is made.
752 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
754 #ifdef DBDEBUG_CONSOLE
755 if (retcode
== SQL_SUCCESS
)
756 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
758 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
760 wxUnusedVar(retcode
);
764 // Connect to the data source
765 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
766 short outConnectBufferLen
;
768 inConnectionStr
= inConnectStr
;
770 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
771 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
772 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
774 if ((retcode
!= SQL_SUCCESS
) &&
775 (retcode
!= SQL_SUCCESS_WITH_INFO
))
776 return(DispAllErrors(henv
, hdbc
));
778 outConnectBuffer
[outConnectBufferLen
] = 0;
779 outConnectionStr
= outConnectBuffer
;
780 dbOpenedWithConnectionString
= true;
782 return open(failOnDataTypeUnsupported
);
785 /********** wxDb::Open() **********/
786 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
788 wxASSERT(Dsn
.Length());
793 inConnectionStr
= wxT("");
794 outConnectionStr
= wxT("");
798 if (!FwdOnlyCursors())
800 // Specify that the ODBC cursor library be used, if needed. This must be
801 // specified before the connection is made.
802 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
804 #ifdef DBDEBUG_CONSOLE
805 if (retcode
== SQL_SUCCESS
)
806 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
808 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
810 wxUnusedVar( retcode
);
814 // Connect to the data source
815 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
816 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
817 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
819 if ((retcode
!= SQL_SUCCESS
) &&
820 (retcode
!= SQL_SUCCESS_WITH_INFO
))
821 return(DispAllErrors(henv
, hdbc
));
823 return open(failOnDataTypeUnsupported
);
828 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
830 wxASSERT(dbConnectInf
);
832 // Use the connection string if one is present
833 if (dbConnectInf
->UseConnectionStr())
834 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
836 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
837 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
841 bool wxDb::Open(wxDb
*copyDb
)
843 dsn
= copyDb
->GetDatasourceName();
844 uid
= copyDb
->GetUsername();
845 authStr
= copyDb
->GetPassword();
846 inConnectionStr
= copyDb
->GetConnectionInStr();
847 outConnectionStr
= copyDb
->GetConnectionOutStr();
851 if (!FwdOnlyCursors())
853 // Specify that the ODBC cursor library be used, if needed. This must be
854 // specified before the connection is made.
855 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
857 #ifdef DBDEBUG_CONSOLE
858 if (retcode
== SQL_SUCCESS
)
859 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
861 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
863 wxUnusedVar( retcode
);
867 if (copyDb
->OpenedWithConnectionString())
869 // Connect to the data source
870 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
871 short outConnectBufferLen
;
873 inConnectionStr
= copyDb
->GetConnectionInStr();
875 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
876 (SWORD
)inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
877 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
879 if ((retcode
!= SQL_SUCCESS
) &&
880 (retcode
!= SQL_SUCCESS_WITH_INFO
))
881 return(DispAllErrors(henv
, hdbc
));
883 outConnectBuffer
[outConnectBufferLen
] = 0;
884 outConnectionStr
= outConnectBuffer
;
885 dbOpenedWithConnectionString
= true;
889 // Connect to the data source
890 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
891 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
892 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
895 if ((retcode
!= SQL_SUCCESS
) &&
896 (retcode
!= SQL_SUCCESS_WITH_INFO
))
897 return(DispAllErrors(henv
, hdbc
));
900 If using Intersolv branded ODBC drivers, this is the place where you would substitute
901 your branded driver license information
903 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
904 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
907 // Mark database as open
910 // Allocate a statement handle for the database connection
911 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
912 return(DispAllErrors(henv
, hdbc
));
914 // Set Connection Options
915 if (!setConnectionOptions())
918 // Instead of Querying the data source for info about itself, it can just be copied
919 // from the wxDb instance that was passed in (copyDb).
920 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
921 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
922 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
923 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
924 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
925 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
926 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
927 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
928 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
929 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
930 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
931 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
932 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
933 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
934 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
935 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
936 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
937 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
938 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
939 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
940 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
941 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
942 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
943 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
944 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
945 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
946 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
947 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
948 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
949 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
950 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
952 // VARCHAR = Variable length character string
953 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
954 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
955 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
956 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
957 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
960 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
961 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
962 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
963 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
964 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
967 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
968 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
969 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
970 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
971 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
974 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
975 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
976 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
977 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
978 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
981 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
982 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
983 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
984 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
985 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
987 #ifdef DBDEBUG_CONSOLE
988 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
989 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
990 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
991 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
992 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
996 // Completed Successfully
1001 /********** wxDb::setConnectionOptions() **********/
1002 bool wxDb::setConnectionOptions(void)
1004 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1009 // I need to get the DBMS name here, because some of the connection options
1010 // are database specific and need to call the Dbms() function.
1011 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1012 return(DispAllErrors(henv
, hdbc
));
1014 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1015 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1016 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1018 // By default, MS Sql Server closes cursors on commit and rollback. The following
1019 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1020 // after a transaction. This is a driver specific option and is not part of the
1021 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1022 // The database settings don't have any effect one way or the other.
1023 if (Dbms() == dbmsMS_SQL_SERVER
)
1025 const long SQL_PRESERVE_CURSORS
= 1204L;
1026 const long SQL_PC_ON
= 1L;
1027 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1030 // Display the connection options to verify them
1031 #ifdef DBDEBUG_CONSOLE
1033 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1035 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
1036 return(DispAllErrors(henv
, hdbc
));
1037 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1039 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
1040 return(DispAllErrors(henv
, hdbc
));
1041 cout
<< wxT("ODBC CURSORS: ");
1044 case(SQL_CUR_USE_IF_NEEDED
):
1045 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1047 case(SQL_CUR_USE_ODBC
):
1048 cout
<< wxT("SQL_CUR_USE_ODBC");
1050 case(SQL_CUR_USE_DRIVER
):
1051 cout
<< wxT("SQL_CUR_USE_DRIVER");
1056 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
1057 return(DispAllErrors(henv
, hdbc
));
1058 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1063 // Completed Successfully
1066 } // wxDb::setConnectionOptions()
1069 /********** wxDb::getDbInfo() **********/
1070 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1075 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
);
1076 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1078 DispAllErrors(henv
, hdbc
);
1079 if (failOnDataTypeUnsupported
)
1083 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
);
1084 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1086 DispAllErrors(henv
, hdbc
);
1087 if (failOnDataTypeUnsupported
)
1091 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
);
1092 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1094 DispAllErrors(henv
, hdbc
);
1095 if (failOnDataTypeUnsupported
)
1100 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1101 // causing database connectivity to fail in some cases.
1102 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1103 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1105 DispAllErrors(henv
, hdbc
);
1106 if (failOnDataTypeUnsupported
)
1110 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1111 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1113 DispAllErrors(henv
, hdbc
);
1114 if (failOnDataTypeUnsupported
)
1118 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1119 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1121 DispAllErrors(henv
, hdbc
);
1122 if (failOnDataTypeUnsupported
)
1126 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
);
1127 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1129 DispAllErrors(henv
, hdbc
);
1130 if (failOnDataTypeUnsupported
)
1134 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
);
1135 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1137 DispAllErrors(henv
, hdbc
);
1138 if (failOnDataTypeUnsupported
)
1142 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1143 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1145 DispAllErrors(henv
, hdbc
);
1146 if (failOnDataTypeUnsupported
)
1150 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
);
1151 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1153 DispAllErrors(henv
, hdbc
);
1154 if (failOnDataTypeUnsupported
)
1158 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1159 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1161 DispAllErrors(henv
, hdbc
);
1162 if (failOnDataTypeUnsupported
)
1166 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1167 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1169 // Not all drivers support this call - Nick Gorham(unixODBC)
1170 dbInf
.cliConfLvl
= 0;
1171 DispAllErrors(henv
, hdbc
);
1172 if (failOnDataTypeUnsupported
)
1176 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1177 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1179 DispAllErrors(henv
, hdbc
);
1180 if (failOnDataTypeUnsupported
)
1184 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
);
1185 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1187 DispAllErrors(henv
, hdbc
);
1188 if (failOnDataTypeUnsupported
)
1192 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
);
1193 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1195 DispAllErrors(henv
, hdbc
);
1196 if (failOnDataTypeUnsupported
)
1200 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
);
1201 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1203 DispAllErrors(henv
, hdbc
);
1204 if (failOnDataTypeUnsupported
)
1208 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1209 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1211 DispAllErrors(henv
, hdbc
);
1212 if (failOnDataTypeUnsupported
)
1216 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1217 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1219 DispAllErrors(henv
, hdbc
);
1220 if (failOnDataTypeUnsupported
)
1224 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1225 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1227 DispAllErrors(henv
, hdbc
);
1228 if (failOnDataTypeUnsupported
)
1232 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
);
1233 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1235 DispAllErrors(henv
, hdbc
);
1236 if (failOnDataTypeUnsupported
)
1240 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1241 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1243 DispAllErrors(henv
, hdbc
);
1244 if (failOnDataTypeUnsupported
)
1248 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1249 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1251 DispAllErrors(henv
, hdbc
);
1252 if (failOnDataTypeUnsupported
)
1256 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1257 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1259 DispAllErrors(henv
, hdbc
);
1260 if (failOnDataTypeUnsupported
)
1264 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1265 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1267 DispAllErrors(henv
, hdbc
);
1268 if (failOnDataTypeUnsupported
)
1272 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1273 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1275 DispAllErrors(henv
, hdbc
);
1276 if (failOnDataTypeUnsupported
)
1280 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1281 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1283 DispAllErrors(henv
, hdbc
);
1284 if (failOnDataTypeUnsupported
)
1288 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1289 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1291 DispAllErrors(henv
, hdbc
);
1292 if (failOnDataTypeUnsupported
)
1296 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1297 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1299 DispAllErrors(henv
, hdbc
);
1300 if (failOnDataTypeUnsupported
)
1304 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1305 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1307 DispAllErrors(henv
, hdbc
);
1308 if (failOnDataTypeUnsupported
)
1312 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1313 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1315 DispAllErrors(henv
, hdbc
);
1316 if (failOnDataTypeUnsupported
)
1320 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1321 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1323 DispAllErrors(henv
, hdbc
);
1324 if (failOnDataTypeUnsupported
)
1328 #ifdef DBDEBUG_CONSOLE
1329 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1330 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1331 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1332 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1334 cout
<< wxT("API Conf. Level: ");
1335 switch(dbInf
.apiConfLvl
)
1337 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1338 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1339 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1343 cout
<< wxT("SAG CLI Conf. Level: ");
1344 switch(dbInf
.cliConfLvl
)
1346 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1347 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1351 cout
<< wxT("SQL Conf. Level: ");
1352 switch(dbInf
.sqlConfLvl
)
1354 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1355 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1356 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1360 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1361 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1362 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1363 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1364 cout
<< wxT("Cursor COMMIT Behavior: ");
1365 switch(dbInf
.cursorCommitBehavior
)
1367 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1368 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1369 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1373 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1374 switch(dbInf
.cursorRollbackBehavior
)
1376 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1377 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1378 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1382 cout
<< wxT("Support NOT NULL clause: ");
1383 switch(dbInf
.supportNotNullClause
)
1385 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1386 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1390 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1391 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1393 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1396 cout
<< wxT("Default Transaction Isolation: ";
1397 switch(dbInf
.txnIsolation
)
1399 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1400 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1401 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1402 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1404 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1409 cout
<< wxT("Transaction Isolation Options: ");
1410 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1411 cout
<< wxT("Read Uncommitted, ");
1412 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1413 cout
<< wxT("Read Committed, ");
1414 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1415 cout
<< wxT("Repeatable Read, ");
1416 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1417 cout
<< wxT("Serializable, ");
1419 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1420 cout
<< wxT("Versioning");
1424 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1425 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1426 cout
<< wxT("Next, ");
1427 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1428 cout
<< wxT("Prev, ");
1429 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1430 cout
<< wxT("First, ");
1431 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1432 cout
<< wxT("Last, ");
1433 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1434 cout
<< wxT("Absolute, ");
1435 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1436 cout
<< wxT("Relative, ");
1438 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1439 cout
<< wxT("Resume, ");
1441 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1442 cout
<< wxT("Bookmark");
1445 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1446 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1447 cout
<< wxT("No Change, ");
1448 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1449 cout
<< wxT("Exclusive, ");
1450 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1451 cout
<< wxT("UnLock");
1454 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1455 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1456 cout
<< wxT("Position, ");
1457 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1458 cout
<< wxT("Refresh, ");
1459 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1460 cout
<< wxT("Upd, "));
1461 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1462 cout
<< wxT("Del, ");
1463 if (dbInf
.posOperations
& SQL_POS_ADD
)
1467 cout
<< wxT("Positioned Statements Supported: ");
1468 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1469 cout
<< wxT("Pos delete, ");
1470 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1471 cout
<< wxT("Pos update, ");
1472 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1473 cout
<< wxT("Select for update");
1476 cout
<< wxT("Scroll Concurrency: ");
1477 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1478 cout
<< wxT("Read Only, ");
1479 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1480 cout
<< wxT("Lock, ");
1481 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1482 cout
<< wxT("Opt. Rowver, ");
1483 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1484 cout
<< wxT("Opt. Values");
1487 cout
<< wxT("Scroll Options: ");
1488 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1489 cout
<< wxT("Fwd Only, ");
1490 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1491 cout
<< wxT("Static, ");
1492 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1493 cout
<< wxT("Keyset Driven, ");
1494 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1495 cout
<< wxT("Dynamic, ");
1496 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1497 cout
<< wxT("Mixed");
1500 cout
<< wxT("Static Sensitivity: ");
1501 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1502 cout
<< wxT("Additions, ");
1503 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1504 cout
<< wxT("Deletions, ");
1505 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1506 cout
<< wxT("Updates");
1509 cout
<< wxT("Transaction Capable?: ");
1510 switch(dbInf
.txnCapable
)
1512 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1513 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1514 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1515 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1516 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1523 // Completed Successfully
1526 } // wxDb::getDbInfo()
1529 /********** wxDb::getDataTypeInfo() **********/
1530 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1533 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1534 * the data type inf. is gathered for.
1536 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1541 // Get information about the data type specified
1542 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1543 return(DispAllErrors(henv
, hdbc
, hstmt
));
1546 retcode
= SQLFetch(hstmt
);
1547 if (retcode
!= SQL_SUCCESS
)
1549 #ifdef DBDEBUG_CONSOLE
1550 if (retcode
== SQL_NO_DATA_FOUND
)
1551 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1553 DispAllErrors(henv
, hdbc
, hstmt
);
1554 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1558 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1560 // Obtain columns from the record
1561 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1562 return(DispAllErrors(henv
, hdbc
, hstmt
));
1564 structSQLTypeInfo
.TypeName
= typeName
;
1566 // BJO 20000503: no more needed with new GetColumns...
1569 if (Dbms() == dbmsMY_SQL
)
1571 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1572 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1573 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1574 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1575 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1576 structSQLTypeInfo
.TypeName
= wxT("int");
1577 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1578 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1579 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1580 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1581 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1582 structSQLTypeInfo
.TypeName
= wxT("char");
1585 // BJO 20000427 : OpenLink driver
1586 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1587 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1589 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1590 structSQLTypeInfo
.TypeName
= wxT("real");
1594 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1595 return(DispAllErrors(henv
, hdbc
, hstmt
));
1596 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1597 return(DispAllErrors(henv
, hdbc
, hstmt
));
1598 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1599 // return(DispAllErrors(henv, hdbc, hstmt));
1601 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1602 return(DispAllErrors(henv
, hdbc
, hstmt
));
1604 if (structSQLTypeInfo
.MaximumScale
< 0)
1605 structSQLTypeInfo
.MaximumScale
= 0;
1607 // Close the statement handle which closes open cursors
1608 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1609 return(DispAllErrors(henv
, hdbc
, hstmt
));
1611 // Completed Successfully
1614 } // wxDb::getDataTypeInfo()
1617 /********** wxDb::Close() **********/
1618 void wxDb::Close(void)
1620 // Close the Sql Log file
1627 // Free statement handle
1630 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1631 DispAllErrors(henv
, hdbc
);
1634 // Disconnect from the datasource
1635 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1636 DispAllErrors(henv
, hdbc
);
1638 // Free the connection to the datasource
1639 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1640 DispAllErrors(henv
, hdbc
);
1642 // There should be zero Ctable objects still connected to this db object
1643 wxASSERT(nTables
== 0);
1647 wxList::compatibility_iterator pNode
;
1648 pNode
= TablesInUse
.GetFirst();
1652 tiu
= (wxTablesInUse
*)pNode
->GetData();
1653 if (tiu
->pDb
== this)
1655 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1656 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1657 wxLogDebug(s
.c_str(),s2
.c_str());
1659 pNode
= pNode
->GetNext();
1663 // Copy the error messages to a global variable
1665 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1666 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1668 dbmsType
= dbmsUNIDENTIFIED
;
1674 /********** wxDb::CommitTrans() **********/
1675 bool wxDb::CommitTrans(void)
1679 // Commit the transaction
1680 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1681 return(DispAllErrors(henv
, hdbc
));
1684 // Completed successfully
1687 } // wxDb::CommitTrans()
1690 /********** wxDb::RollbackTrans() **********/
1691 bool wxDb::RollbackTrans(void)
1693 // Rollback the transaction
1694 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1695 return(DispAllErrors(henv
, hdbc
));
1697 // Completed successfully
1700 } // wxDb::RollbackTrans()
1703 /********** wxDb::DispAllErrors() **********/
1704 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1706 * This function is called internally whenever an error condition prevents the user's
1707 * request from being executed. This function will query the datasource as to the
1708 * actual error(s) that just occured on the previous request of the datasource.
1710 * The function will retrieve each error condition from the datasource and
1711 * Printf the codes/text values into a string which it then logs via logError().
1712 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1713 * window and program execution will be paused until the user presses a key.
1715 * This function always returns a false, so that functions which call this function
1716 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1717 * of the users request, so that the calling code can then process the error msg log
1720 wxString odbcErrMsg
;
1722 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1724 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1725 logError(odbcErrMsg
, sqlState
);
1728 #ifdef DBDEBUG_CONSOLE
1729 // When run in console mode, use standard out to display errors.
1730 cout
<< odbcErrMsg
.c_str() << endl
;
1731 cout
<< wxT("Press any key to continue...") << endl
;
1736 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1741 return false; // This function always returns false.
1743 } // wxDb::DispAllErrors()
1746 /********** wxDb::GetNextError() **********/
1747 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1749 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, (long*) &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1754 } // wxDb::GetNextError()
1757 /********** wxDb::DispNextError() **********/
1758 void wxDb::DispNextError(void)
1760 wxString odbcErrMsg
;
1762 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1763 logError(odbcErrMsg
, sqlState
);
1768 #ifdef DBDEBUG_CONSOLE
1769 // When run in console mode, use standard out to display errors.
1770 cout
<< odbcErrMsg
.c_str() << endl
;
1771 cout
<< wxT("Press any key to continue...") << endl
;
1776 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1777 #endif // __WXDEBUG__
1779 } // wxDb::DispNextError()
1782 /********** wxDb::logError() **********/
1783 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1785 wxASSERT(errMsg
.Length());
1787 static int pLast
= -1;
1790 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1793 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1794 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1798 wxStrcpy(errorList
[pLast
], errMsg
);
1800 if (SQLState
.Length())
1801 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1802 DB_STATUS
= dbStatus
;
1804 // Add the errmsg to the sql log
1805 WriteSqlLog(errMsg
);
1807 } // wxDb::logError()
1810 /**********wxDb::TranslateSqlState() **********/
1811 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1813 if (!wxStrcmp(SQLState
, wxT("01000")))
1814 return(DB_ERR_GENERAL_WARNING
);
1815 if (!wxStrcmp(SQLState
, wxT("01002")))
1816 return(DB_ERR_DISCONNECT_ERROR
);
1817 if (!wxStrcmp(SQLState
, wxT("01004")))
1818 return(DB_ERR_DATA_TRUNCATED
);
1819 if (!wxStrcmp(SQLState
, wxT("01006")))
1820 return(DB_ERR_PRIV_NOT_REVOKED
);
1821 if (!wxStrcmp(SQLState
, wxT("01S00")))
1822 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1823 if (!wxStrcmp(SQLState
, wxT("01S01")))
1824 return(DB_ERR_ERROR_IN_ROW
);
1825 if (!wxStrcmp(SQLState
, wxT("01S02")))
1826 return(DB_ERR_OPTION_VALUE_CHANGED
);
1827 if (!wxStrcmp(SQLState
, wxT("01S03")))
1828 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1829 if (!wxStrcmp(SQLState
, wxT("01S04")))
1830 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1831 if (!wxStrcmp(SQLState
, wxT("07001")))
1832 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1833 if (!wxStrcmp(SQLState
, wxT("07006")))
1834 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1835 if (!wxStrcmp(SQLState
, wxT("08001")))
1836 return(DB_ERR_UNABLE_TO_CONNECT
);
1837 if (!wxStrcmp(SQLState
, wxT("08002")))
1838 return(DB_ERR_CONNECTION_IN_USE
);
1839 if (!wxStrcmp(SQLState
, wxT("08003")))
1840 return(DB_ERR_CONNECTION_NOT_OPEN
);
1841 if (!wxStrcmp(SQLState
, wxT("08004")))
1842 return(DB_ERR_REJECTED_CONNECTION
);
1843 if (!wxStrcmp(SQLState
, wxT("08007")))
1844 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1845 if (!wxStrcmp(SQLState
, wxT("08S01")))
1846 return(DB_ERR_COMM_LINK_FAILURE
);
1847 if (!wxStrcmp(SQLState
, wxT("21S01")))
1848 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1849 if (!wxStrcmp(SQLState
, wxT("21S02")))
1850 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1851 if (!wxStrcmp(SQLState
, wxT("22001")))
1852 return(DB_ERR_STRING_RIGHT_TRUNC
);
1853 if (!wxStrcmp(SQLState
, wxT("22003")))
1854 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1855 if (!wxStrcmp(SQLState
, wxT("22005")))
1856 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1857 if (!wxStrcmp(SQLState
, wxT("22008")))
1858 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1859 if (!wxStrcmp(SQLState
, wxT("22012")))
1860 return(DB_ERR_DIVIDE_BY_ZERO
);
1861 if (!wxStrcmp(SQLState
, wxT("22026")))
1862 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1863 if (!wxStrcmp(SQLState
, wxT("23000")))
1864 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1865 if (!wxStrcmp(SQLState
, wxT("24000")))
1866 return(DB_ERR_INVALID_CURSOR_STATE
);
1867 if (!wxStrcmp(SQLState
, wxT("25000")))
1868 return(DB_ERR_INVALID_TRANS_STATE
);
1869 if (!wxStrcmp(SQLState
, wxT("28000")))
1870 return(DB_ERR_INVALID_AUTH_SPEC
);
1871 if (!wxStrcmp(SQLState
, wxT("34000")))
1872 return(DB_ERR_INVALID_CURSOR_NAME
);
1873 if (!wxStrcmp(SQLState
, wxT("37000")))
1874 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1875 if (!wxStrcmp(SQLState
, wxT("3C000")))
1876 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1877 if (!wxStrcmp(SQLState
, wxT("40001")))
1878 return(DB_ERR_SERIALIZATION_FAILURE
);
1879 if (!wxStrcmp(SQLState
, wxT("42000")))
1880 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1881 if (!wxStrcmp(SQLState
, wxT("70100")))
1882 return(DB_ERR_OPERATION_ABORTED
);
1883 if (!wxStrcmp(SQLState
, wxT("IM001")))
1884 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1885 if (!wxStrcmp(SQLState
, wxT("IM002")))
1886 return(DB_ERR_NO_DATA_SOURCE
);
1887 if (!wxStrcmp(SQLState
, wxT("IM003")))
1888 return(DB_ERR_DRIVER_LOAD_ERROR
);
1889 if (!wxStrcmp(SQLState
, wxT("IM004")))
1890 return(DB_ERR_SQLALLOCENV_FAILED
);
1891 if (!wxStrcmp(SQLState
, wxT("IM005")))
1892 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1893 if (!wxStrcmp(SQLState
, wxT("IM006")))
1894 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1895 if (!wxStrcmp(SQLState
, wxT("IM007")))
1896 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1897 if (!wxStrcmp(SQLState
, wxT("IM008")))
1898 return(DB_ERR_DIALOG_FAILED
);
1899 if (!wxStrcmp(SQLState
, wxT("IM009")))
1900 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1901 if (!wxStrcmp(SQLState
, wxT("IM010")))
1902 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1903 if (!wxStrcmp(SQLState
, wxT("IM011")))
1904 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1905 if (!wxStrcmp(SQLState
, wxT("IM012")))
1906 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1907 if (!wxStrcmp(SQLState
, wxT("IM013")))
1908 return(DB_ERR_TRACE_FILE_ERROR
);
1909 if (!wxStrcmp(SQLState
, wxT("S0001")))
1910 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1911 if (!wxStrcmp(SQLState
, wxT("S0002")))
1912 return(DB_ERR_TABLE_NOT_FOUND
);
1913 if (!wxStrcmp(SQLState
, wxT("S0011")))
1914 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1915 if (!wxStrcmp(SQLState
, wxT("S0012")))
1916 return(DB_ERR_INDEX_NOT_FOUND
);
1917 if (!wxStrcmp(SQLState
, wxT("S0021")))
1918 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1919 if (!wxStrcmp(SQLState
, wxT("S0022")))
1920 return(DB_ERR_COLUMN_NOT_FOUND
);
1921 if (!wxStrcmp(SQLState
, wxT("S0023")))
1922 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1923 if (!wxStrcmp(SQLState
, wxT("S1000")))
1924 return(DB_ERR_GENERAL_ERROR
);
1925 if (!wxStrcmp(SQLState
, wxT("S1001")))
1926 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1927 if (!wxStrcmp(SQLState
, wxT("S1002")))
1928 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1929 if (!wxStrcmp(SQLState
, wxT("S1003")))
1930 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1931 if (!wxStrcmp(SQLState
, wxT("S1004")))
1932 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1933 if (!wxStrcmp(SQLState
, wxT("S1008")))
1934 return(DB_ERR_OPERATION_CANCELLED
);
1935 if (!wxStrcmp(SQLState
, wxT("S1009")))
1936 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1937 if (!wxStrcmp(SQLState
, wxT("S1010")))
1938 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1939 if (!wxStrcmp(SQLState
, wxT("S1011")))
1940 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1941 if (!wxStrcmp(SQLState
, wxT("S1012")))
1942 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1943 if (!wxStrcmp(SQLState
, wxT("S1015")))
1944 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1945 if (!wxStrcmp(SQLState
, wxT("S1090")))
1946 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1947 if (!wxStrcmp(SQLState
, wxT("S1091")))
1948 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1949 if (!wxStrcmp(SQLState
, wxT("S1092")))
1950 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1951 if (!wxStrcmp(SQLState
, wxT("S1093")))
1952 return(DB_ERR_INVALID_PARAM_NO
);
1953 if (!wxStrcmp(SQLState
, wxT("S1094")))
1954 return(DB_ERR_INVALID_SCALE_VALUE
);
1955 if (!wxStrcmp(SQLState
, wxT("S1095")))
1956 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1957 if (!wxStrcmp(SQLState
, wxT("S1096")))
1958 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1959 if (!wxStrcmp(SQLState
, wxT("S1097")))
1960 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1961 if (!wxStrcmp(SQLState
, wxT("S1098")))
1962 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1963 if (!wxStrcmp(SQLState
, wxT("S1099")))
1964 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1965 if (!wxStrcmp(SQLState
, wxT("S1100")))
1966 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1967 if (!wxStrcmp(SQLState
, wxT("S1101")))
1968 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1969 if (!wxStrcmp(SQLState
, wxT("S1103")))
1970 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1971 if (!wxStrcmp(SQLState
, wxT("S1104")))
1972 return(DB_ERR_INVALID_PRECISION_VALUE
);
1973 if (!wxStrcmp(SQLState
, wxT("S1105")))
1974 return(DB_ERR_INVALID_PARAM_TYPE
);
1975 if (!wxStrcmp(SQLState
, wxT("S1106")))
1976 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1977 if (!wxStrcmp(SQLState
, wxT("S1107")))
1978 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1979 if (!wxStrcmp(SQLState
, wxT("S1108")))
1980 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1981 if (!wxStrcmp(SQLState
, wxT("S1109")))
1982 return(DB_ERR_INVALID_CURSOR_POSITION
);
1983 if (!wxStrcmp(SQLState
, wxT("S1110")))
1984 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1985 if (!wxStrcmp(SQLState
, wxT("S1111")))
1986 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1987 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1988 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1989 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1990 return(DB_ERR_TIMEOUT_EXPIRED
);
1995 } // wxDb::TranslateSqlState()
1998 /********** wxDb::Grant() **********/
1999 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2003 // Build the grant statement
2004 sqlStmt
= wxT("GRANT ");
2005 if (privileges
== DB_GRANT_ALL
)
2006 sqlStmt
+= wxT("ALL");
2010 if (privileges
& DB_GRANT_SELECT
)
2012 sqlStmt
+= wxT("SELECT");
2015 if (privileges
& DB_GRANT_INSERT
)
2018 sqlStmt
+= wxT(", ");
2019 sqlStmt
+= wxT("INSERT");
2021 if (privileges
& DB_GRANT_UPDATE
)
2024 sqlStmt
+= wxT(", ");
2025 sqlStmt
+= wxT("UPDATE");
2027 if (privileges
& DB_GRANT_DELETE
)
2030 sqlStmt
+= wxT(", ");
2031 sqlStmt
+= wxT("DELETE");
2035 sqlStmt
+= wxT(" ON ");
2036 sqlStmt
+= SQLTableName(tableName
);
2037 sqlStmt
+= wxT(" TO ");
2038 sqlStmt
+= userList
;
2040 #ifdef DBDEBUG_CONSOLE
2041 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2044 WriteSqlLog(sqlStmt
);
2046 return(ExecSql(sqlStmt
));
2051 /********** wxDb::CreateView() **********/
2052 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2053 const wxString
&pSqlStmt
, bool attemptDrop
)
2057 // Drop the view first
2058 if (attemptDrop
&& !DropView(viewName
))
2061 // Build the create view statement
2062 sqlStmt
= wxT("CREATE VIEW ");
2063 sqlStmt
+= viewName
;
2065 if (colList
.Length())
2067 sqlStmt
+= wxT(" (");
2069 sqlStmt
+= wxT(")");
2072 sqlStmt
+= wxT(" AS ");
2073 sqlStmt
+= pSqlStmt
;
2075 WriteSqlLog(sqlStmt
);
2077 #ifdef DBDEBUG_CONSOLE
2078 cout
<< sqlStmt
.c_str() << endl
;
2081 return(ExecSql(sqlStmt
));
2083 } // wxDb::CreateView()
2086 /********** wxDb::DropView() **********/
2087 bool wxDb::DropView(const wxString
&viewName
)
2090 * NOTE: This function returns true if the View does not exist, but
2091 * only for identified databases. Code will need to be added
2092 * below for any other databases when those databases are defined
2093 * to handle this situation consistently
2097 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2099 WriteSqlLog(sqlStmt
);
2101 #ifdef DBDEBUG_CONSOLE
2102 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2105 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2107 // Check for "Base table not found" error and ignore
2108 GetNextError(henv
, hdbc
, hstmt
);
2109 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2111 // Check for product specific error codes
2112 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2115 DispAllErrors(henv
, hdbc
, hstmt
);
2122 // Commit the transaction
2128 } // wxDb::DropView()
2131 /********** wxDb::ExecSql() **********/
2132 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2136 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2138 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2139 if (retcode
== SQL_SUCCESS
||
2140 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2146 DispAllErrors(henv
, hdbc
, hstmt
);
2150 } // wxDb::ExecSql()
2153 /********** wxDb::GetNext() **********/
2154 bool wxDb::GetNext(void)
2156 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2160 DispAllErrors(henv
, hdbc
, hstmt
);
2164 } // wxDb::GetNext()
2167 /********** wxDb::GetData() **********/
2168 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2171 wxASSERT(cbReturned
);
2173 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2177 DispAllErrors(henv
, hdbc
, hstmt
);
2181 } // wxDb::GetData()
2184 /********** wxDb::GetKeyFields() **********/
2185 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2187 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2188 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2190 // SQLSMALLINT iKeySeq;
2191 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2192 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2198 * -----------------------------------------------------------------------
2199 * -- 19991224 : mj10777 : Create ------
2200 * -- : Three things are done and stored here : ------
2201 * -- : 1) which Column(s) is/are Primary Key(s) ------
2202 * -- : 2) which tables use this Key as a Foreign Key ------
2203 * -- : 3) which columns are Foreign Key and the name ------
2204 * -- : of the Table where the Key is the Primary Key -----
2205 * -- : Called from GetColumns(const wxString &tableName, ------
2206 * -- int *numCols,const wxChar *userID ) ------
2207 * -----------------------------------------------------------------------
2210 /*---------------------------------------------------------------------*/
2211 /* Get the names of the columns in the primary key. */
2212 /*---------------------------------------------------------------------*/
2213 retcode
= SQLPrimaryKeys(hstmt
,
2214 NULL
, 0, /* Catalog name */
2215 NULL
, 0, /* Schema name */
2216 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2218 /*---------------------------------------------------------------------*/
2219 /* Fetch and display the result set. This will be a list of the */
2220 /* columns in the primary key of the tableName table. */
2221 /*---------------------------------------------------------------------*/
2222 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2224 retcode
= SQLFetch(hstmt
);
2225 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2227 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2228 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2230 for (i
=0;i
<noCols
;i
++) // Find the Column name
2231 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2232 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2235 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2237 /*---------------------------------------------------------------------*/
2238 /* Get all the foreign keys that refer to tableName primary key. */
2239 /*---------------------------------------------------------------------*/
2240 retcode
= SQLForeignKeys(hstmt
,
2241 NULL
, 0, /* Primary catalog */
2242 NULL
, 0, /* Primary schema */
2243 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2244 NULL
, 0, /* Foreign catalog */
2245 NULL
, 0, /* Foreign schema */
2246 NULL
, 0); /* Foreign table */
2248 /*---------------------------------------------------------------------*/
2249 /* Fetch and display the result set. This will be all of the foreign */
2250 /* keys in other tables that refer to the tableName primary key. */
2251 /*---------------------------------------------------------------------*/
2254 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2256 retcode
= SQLFetch(hstmt
);
2257 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2259 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2260 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2261 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2262 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2263 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2264 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2268 tempStr
.Trim(); // Get rid of any unneeded blanks
2269 if (!tempStr
.IsEmpty())
2271 for (i
=0; i
<noCols
; i
++)
2272 { // Find the Column name
2273 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2274 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2278 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2280 /*---------------------------------------------------------------------*/
2281 /* Get all the foreign keys in the tablename table. */
2282 /*---------------------------------------------------------------------*/
2283 retcode
= SQLForeignKeys(hstmt
,
2284 NULL
, 0, /* Primary catalog */
2285 NULL
, 0, /* Primary schema */
2286 NULL
, 0, /* Primary table */
2287 NULL
, 0, /* Foreign catalog */
2288 NULL
, 0, /* Foreign schema */
2289 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2291 /*---------------------------------------------------------------------*/
2292 /* Fetch and display the result set. This will be all of the */
2293 /* primary keys in other tables that are referred to by foreign */
2294 /* keys in the tableName table. */
2295 /*---------------------------------------------------------------------*/
2296 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2298 retcode
= SQLFetch(hstmt
);
2299 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2301 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2302 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2303 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2305 for (i
=0; i
<noCols
; i
++) // Find the Column name
2307 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2309 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2310 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2315 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2319 } // wxDb::GetKeyFields()
2323 /********** wxDb::GetColumns() **********/
2324 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2326 * 1) The last array element of the tableName[] argument must be zero (null).
2327 * This is how the end of the array is detected.
2328 * 2) This function returns an array of wxDbColInf structures. If no columns
2329 * were found, or an error occured, this pointer will be zero (null). THE
2330 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2331 * IS FINISHED WITH IT. i.e.
2333 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2336 * // Use the column inf
2338 * // Destroy the memory
2342 * userID is evaluated in the following manner:
2343 * userID == NULL ... UserID is ignored
2344 * userID == "" ... UserID set equal to 'this->uid'
2345 * userID != "" ... UserID set equal to 'userID'
2347 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2348 * by this function. This function should use its own wxDb instance
2349 * to avoid undesired unbinding of columns.
2354 wxDbColInf
*colInf
= 0;
2362 convertUserID(userID
,UserID
);
2364 // Pass 1 - Determine how many columns there are.
2365 // Pass 2 - Allocate the wxDbColInf array and fill in
2366 // the array with the column information.
2368 for (pass
= 1; pass
<= 2; pass
++)
2372 if (noCols
== 0) // Probably a bogus table name(s)
2374 // Allocate n wxDbColInf objects to hold the column information
2375 colInf
= new wxDbColInf
[noCols
+1];
2378 // Mark the end of the array
2379 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2380 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2381 colInf
[noCols
].sqlDataType
= 0;
2383 // Loop through each table name
2385 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2387 TableName
= tableName
[tbl
];
2388 // Oracle and Interbase table names are uppercase only, so force
2389 // the name to uppercase just in case programmer forgot to do this
2390 if ((Dbms() == dbmsORACLE
) ||
2391 (Dbms() == dbmsINTERBASE
))
2392 TableName
= TableName
.Upper();
2394 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2396 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2397 // use the call below that leaves out the user name
2398 if (!UserID
.IsEmpty() &&
2399 Dbms() != dbmsMY_SQL
&&
2400 Dbms() != dbmsACCESS
&&
2401 Dbms() != dbmsMS_SQL_SERVER
)
2403 retcode
= SQLColumns(hstmt
,
2404 NULL
, 0, // All qualifiers
2405 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2406 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2407 NULL
, 0); // All columns
2411 retcode
= SQLColumns(hstmt
,
2412 NULL
, 0, // All qualifiers
2414 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2415 NULL
, 0); // All columns
2417 if (retcode
!= SQL_SUCCESS
)
2418 { // Error occured, abort
2419 DispAllErrors(henv
, hdbc
, hstmt
);
2422 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2426 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2428 if (pass
== 1) // First pass, just add up the number of columns
2430 else // Pass 2; Fill in the array of structures
2432 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2434 // NOTE: Only the ODBC 1.x fields are retrieved
2435 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2436 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2437 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2438 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2439 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2440 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2441 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2442 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2443 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2444 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2445 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2446 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2448 // Determine the wxDb data type that is used to represent the native data type of this data source
2449 colInf
[colNo
].dbDataType
= 0;
2450 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2453 // IODBC does not return a correct columnSize, so we set
2454 // columnSize = bufferLength if no column size was returned
2455 // IODBC returns the columnSize in bufferLength.. (bug)
2456 if (colInf
[colNo
].columnSize
< 1)
2458 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2461 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2463 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2464 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2465 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2466 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2467 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2468 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2469 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2470 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2475 if (retcode
!= SQL_NO_DATA_FOUND
)
2476 { // Error occured, abort
2477 DispAllErrors(henv
, hdbc
, hstmt
);
2480 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2486 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2489 } // wxDb::GetColumns()
2492 /********** wxDb::GetColumns() **********/
2494 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2496 // Same as the above GetColumns() function except this one gets columns
2497 // only for a single table, and if 'numCols' is not NULL, the number of
2498 // columns stored in the returned wxDbColInf is set in '*numCols'
2500 // userID is evaluated in the following manner:
2501 // userID == NULL ... UserID is ignored
2502 // userID == "" ... UserID set equal to 'this->uid'
2503 // userID != "" ... UserID set equal to 'userID'
2505 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2506 // by this function. This function should use its own wxDb instance
2507 // to avoid undesired unbinding of columns.
2512 wxDbColInf
*colInf
= 0;
2520 convertUserID(userID
,UserID
);
2522 // Pass 1 - Determine how many columns there are.
2523 // Pass 2 - Allocate the wxDbColInf array and fill in
2524 // the array with the column information.
2526 for (pass
= 1; pass
<= 2; pass
++)
2530 if (noCols
== 0) // Probably a bogus table name(s)
2532 // Allocate n wxDbColInf objects to hold the column information
2533 colInf
= new wxDbColInf
[noCols
+1];
2536 // Mark the end of the array
2537 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2538 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2539 colInf
[noCols
].sqlDataType
= 0;
2542 TableName
= tableName
;
2543 // Oracle and Interbase table names are uppercase only, so force
2544 // the name to uppercase just in case programmer forgot to do this
2545 if ((Dbms() == dbmsORACLE
) ||
2546 (Dbms() == dbmsINTERBASE
))
2547 TableName
= TableName
.Upper();
2549 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2551 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2552 // use the call below that leaves out the user name
2553 if (!UserID
.IsEmpty() &&
2554 Dbms() != dbmsMY_SQL
&&
2555 Dbms() != dbmsACCESS
&&
2556 Dbms() != dbmsMS_SQL_SERVER
)
2558 retcode
= SQLColumns(hstmt
,
2559 NULL
, 0, // All qualifiers
2560 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2561 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2562 NULL
, 0); // All columns
2566 retcode
= SQLColumns(hstmt
,
2567 NULL
, 0, // All qualifiers
2569 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2570 NULL
, 0); // All columns
2572 if (retcode
!= SQL_SUCCESS
)
2573 { // Error occured, abort
2574 DispAllErrors(henv
, hdbc
, hstmt
);
2577 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2583 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2585 if (pass
== 1) // First pass, just add up the number of columns
2587 else // Pass 2; Fill in the array of structures
2589 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2591 // NOTE: Only the ODBC 1.x fields are retrieved
2592 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2593 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2594 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2595 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2596 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2597 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2598 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2599 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2600 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2601 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2602 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2603 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2604 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2605 // Start Values for Primary/Foriegn Key (=No)
2606 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2607 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2608 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2609 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2611 // BJO 20000428 : Virtuoso returns type names with upper cases!
2612 if (Dbms() == dbmsVIRTUOSO
)
2614 wxString s
= colInf
[colNo
].typeName
;
2616 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2619 // Determine the wxDb data type that is used to represent the native data type of this data source
2620 colInf
[colNo
].dbDataType
= 0;
2621 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2624 // IODBC does not return a correct columnSize, so we set
2625 // columnSize = bufferLength if no column size was returned
2626 // IODBC returns the columnSize in bufferLength.. (bug)
2627 if (colInf
[colNo
].columnSize
< 1)
2629 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2633 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2635 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2636 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2637 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2638 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2639 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2640 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2641 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2642 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2648 if (retcode
!= SQL_NO_DATA_FOUND
)
2649 { // Error occured, abort
2650 DispAllErrors(henv
, hdbc
, hstmt
);
2653 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2660 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2662 // Store Primary and Foriegn Keys
2663 GetKeyFields(tableName
,colInf
,noCols
);
2669 } // wxDb::GetColumns()
2672 #else // New GetColumns
2677 These are tentative new GetColumns members which should be more database
2678 independant and which always returns the columns in the order they were
2681 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2682 wxChar* userID)) calls the second implementation for each separate table
2683 before merging the results. This makes the code easier to maintain as
2684 only one member (the second) makes the real work
2685 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2686 wxChar *userID) is a little bit improved
2687 - It doesn't anymore rely on the type-name to find out which database-type
2689 - It ends by sorting the columns, so that they are returned in the same
2690 order they were created
2700 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2703 // The last array element of the tableName[] argument must be zero (null).
2704 // This is how the end of the array is detected.
2708 // How many tables ?
2710 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2712 // Create a table to maintain the columns for each separate table
2713 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2716 for (i
= 0 ; i
< tbl
; i
++)
2719 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2720 if (TableColumns
[i
].colInf
== NULL
)
2722 noCols
+= TableColumns
[i
].noCols
;
2725 // Now merge all the separate table infos
2726 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2728 // Mark the end of the array
2729 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2730 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2731 colInf
[noCols
].sqlDataType
= 0;
2736 for (i
= 0 ; i
< tbl
; i
++)
2738 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2740 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2744 delete [] TableColumns
;
2747 } // wxDb::GetColumns() -- NEW
2750 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2752 // Same as the above GetColumns() function except this one gets columns
2753 // only for a single table, and if 'numCols' is not NULL, the number of
2754 // columns stored in the returned wxDbColInf is set in '*numCols'
2756 // userID is evaluated in the following manner:
2757 // userID == NULL ... UserID is ignored
2758 // userID == "" ... UserID set equal to 'this->uid'
2759 // userID != "" ... UserID set equal to 'userID'
2761 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2762 // by this function. This function should use its own wxDb instance
2763 // to avoid undesired unbinding of columns.
2767 wxDbColInf
*colInf
= 0;
2775 convertUserID(userID
,UserID
);
2777 // Pass 1 - Determine how many columns there are.
2778 // Pass 2 - Allocate the wxDbColInf array and fill in
2779 // the array with the column information.
2781 for (pass
= 1; pass
<= 2; pass
++)
2785 if (noCols
== 0) // Probably a bogus table name(s)
2787 // Allocate n wxDbColInf objects to hold the column information
2788 colInf
= new wxDbColInf
[noCols
+1];
2791 // Mark the end of the array
2792 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2793 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2794 colInf
[noCols
].sqlDataType
= 0;
2797 TableName
= tableName
;
2798 // Oracle and Interbase table names are uppercase only, so force
2799 // the name to uppercase just in case programmer forgot to do this
2800 if ((Dbms() == dbmsORACLE
) ||
2801 (Dbms() == dbmsINTERBASE
))
2802 TableName
= TableName
.Upper();
2804 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2806 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2807 // use the call below that leaves out the user name
2808 if (!UserID
.IsEmpty() &&
2809 Dbms() != dbmsMY_SQL
&&
2810 Dbms() != dbmsACCESS
&&
2811 Dbms() != dbmsMS_SQL_SERVER
)
2813 retcode
= SQLColumns(hstmt
,
2814 NULL
, 0, // All qualifiers
2815 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2816 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2817 NULL
, 0); // All columns
2821 retcode
= SQLColumns(hstmt
,
2822 NULL
, 0, // All qualifiers
2824 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2825 NULL
, 0); // All columns
2827 if (retcode
!= SQL_SUCCESS
)
2828 { // Error occured, abort
2829 DispAllErrors(henv
, hdbc
, hstmt
);
2832 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2838 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2840 if (pass
== 1) // First pass, just add up the number of columns
2842 else // Pass 2; Fill in the array of structures
2844 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2846 // NOTE: Only the ODBC 1.x fields are retrieved
2847 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2848 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2849 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2850 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2851 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2852 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2853 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2854 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2855 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2856 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2857 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2858 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2859 // Start Values for Primary/Foriegn Key (=No)
2860 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2861 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2862 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2863 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2866 // IODBC does not return a correct columnSize, so we set
2867 // columnSize = bufferLength if no column size was returned
2868 // IODBC returns the columnSize in bufferLength.. (bug)
2869 if (colInf
[colNo
].columnSize
< 1)
2871 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2875 // Determine the wxDb data type that is used to represent the native data type of this data source
2876 colInf
[colNo
].dbDataType
= 0;
2877 // Get the intern datatype
2878 switch (colInf
[colNo
].sqlDataType
)
2882 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2888 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2895 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2898 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2901 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2906 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
2907 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2914 if (retcode
!= SQL_NO_DATA_FOUND
)
2915 { // Error occured, abort
2916 DispAllErrors(henv
, hdbc
, hstmt
);
2919 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2926 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2928 // Store Primary and Foreign Keys
2929 GetKeyFields(tableName
,colInf
,noCols
);
2931 ///////////////////////////////////////////////////////////////////////////
2932 // Now sort the the columns in order to make them appear in the right order
2933 ///////////////////////////////////////////////////////////////////////////
2935 // Build a generic SELECT statement which returns 0 rows
2938 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2941 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2943 DispAllErrors(henv
, hdbc
, hstmt
);
2947 // Get the number of result columns
2948 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2950 DispAllErrors(henv
, hdbc
, hstmt
);
2954 if (noCols
== 0) // Probably a bogus table name
2963 for (colNum
= 0; colNum
< noCols
; colNum
++)
2965 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2967 &Sword
, &Sdword
) != SQL_SUCCESS
)
2969 DispAllErrors(henv
, hdbc
, hstmt
);
2973 wxString Name1
= name
;
2974 Name1
= Name1
.Upper();
2976 // Where is this name in the array ?
2977 for (i
= colNum
; i
< noCols
; i
++)
2979 wxString Name2
= colInf
[i
].colName
;
2980 Name2
= Name2
.Upper();
2983 if (colNum
!= i
) // swap to sort
2985 wxDbColInf tmpColInf
= colInf
[colNum
];
2986 colInf
[colNum
] = colInf
[i
];
2987 colInf
[i
] = tmpColInf
;
2993 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2995 ///////////////////////////////////////////////////////////////////////////
2997 ///////////////////////////////////////////////////////////////////////////
3003 } // wxDb::GetColumns()
3006 #endif // #else OLD_GETCOLUMNS
3009 /********** wxDb::GetColumnCount() **********/
3010 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3012 * Returns a count of how many columns are in a table.
3013 * If an error occurs in computing the number of columns
3014 * this function will return a -1 for the count
3016 * userID is evaluated in the following manner:
3017 * userID == NULL ... UserID is ignored
3018 * userID == "" ... UserID set equal to 'this->uid'
3019 * userID != "" ... UserID set equal to 'userID'
3021 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3022 * by this function. This function should use its own wxDb instance
3023 * to avoid undesired unbinding of columns.
3033 convertUserID(userID
,UserID
);
3035 TableName
= tableName
;
3036 // Oracle and Interbase table names are uppercase only, so force
3037 // the name to uppercase just in case programmer forgot to do this
3038 if ((Dbms() == dbmsORACLE
) ||
3039 (Dbms() == dbmsINTERBASE
))
3040 TableName
= TableName
.Upper();
3042 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3044 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3045 // use the call below that leaves out the user name
3046 if (!UserID
.IsEmpty() &&
3047 Dbms() != dbmsMY_SQL
&&
3048 Dbms() != dbmsACCESS
&&
3049 Dbms() != dbmsMS_SQL_SERVER
)
3051 retcode
= SQLColumns(hstmt
,
3052 NULL
, 0, // All qualifiers
3053 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3054 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3055 NULL
, 0); // All columns
3059 retcode
= SQLColumns(hstmt
,
3060 NULL
, 0, // All qualifiers
3062 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3063 NULL
, 0); // All columns
3065 if (retcode
!= SQL_SUCCESS
)
3066 { // Error occured, abort
3067 DispAllErrors(henv
, hdbc
, hstmt
);
3068 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3072 // Count the columns
3073 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3076 if (retcode
!= SQL_NO_DATA_FOUND
)
3077 { // Error occured, abort
3078 DispAllErrors(henv
, hdbc
, hstmt
);
3079 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3083 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3086 } // wxDb::GetColumnCount()
3089 /********** wxDb::GetCatalog() *******/
3090 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3092 * ---------------------------------------------------------------------
3093 * -- 19991203 : mj10777 : Create ------
3094 * -- : Creates a wxDbInf with Tables / Cols Array ------
3095 * -- : uses SQLTables and fills pTableInf; ------
3096 * -- : pColInf is set to NULL and numCols to 0; ------
3097 * -- : returns pDbInf (wxDbInf) ------
3098 * -- - if unsuccesfull (pDbInf == NULL) ------
3099 * -- : pColInf can be filled with GetColumns(..); ------
3100 * -- : numCols can be filled with GetColumnCount(..); ------
3101 * ---------------------------------------------------------------------
3103 * userID is evaluated in the following manner:
3104 * userID == NULL ... UserID is ignored
3105 * userID == "" ... UserID set equal to 'this->uid'
3106 * userID != "" ... UserID set equal to 'userID'
3108 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3109 * by this function. This function should use its own wxDb instance
3110 * to avoid undesired unbinding of columns.
3113 int noTab
= 0; // Counter while filling table entries
3117 wxString tblNameSave
;
3120 convertUserID(userID
,UserID
);
3122 //-------------------------------------------------------------
3123 // Create the Database Array of catalog entries
3125 wxDbInf
*pDbInf
= new wxDbInf
;
3127 //-------------------------------------------------------------
3128 // Table Information
3129 // Pass 1 - Determine how many Tables there are.
3130 // Pass 2 - Create the Table array and fill it
3131 // - Create the Cols array = NULL
3132 //-------------------------------------------------------------
3134 for (pass
= 1; pass
<= 2; pass
++)
3136 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3137 tblNameSave
.Empty();
3139 if (!UserID
.IsEmpty() &&
3140 Dbms() != dbmsMY_SQL
&&
3141 Dbms() != dbmsACCESS
&&
3142 Dbms() != dbmsMS_SQL_SERVER
)
3144 retcode
= SQLTables(hstmt
,
3145 NULL
, 0, // All qualifiers
3146 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3147 NULL
, 0, // All tables
3148 NULL
, 0); // All columns
3152 retcode
= SQLTables(hstmt
,
3153 NULL
, 0, // All qualifiers
3154 NULL
, 0, // User specified
3155 NULL
, 0, // All tables
3156 NULL
, 0); // All columns
3159 if (retcode
!= SQL_SUCCESS
)
3161 DispAllErrors(henv
, hdbc
, hstmt
);
3163 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3167 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3169 if (pass
== 1) // First pass, just count the Tables
3171 if (pDbInf
->numTables
== 0)
3173 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3174 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3176 pDbInf
->numTables
++; // Counter for Tables
3178 if (pass
== 2) // Create and fill the Table entries
3180 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3181 { // no, then create the Array
3182 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3184 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3186 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3187 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3188 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3194 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3196 // Query how many columns are in each table
3197 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3199 (pDbInf
->pTableInf
+noTab
)->numCols
= (UWORD
)GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3204 } // wxDb::GetCatalog()
3207 /********** wxDb::Catalog() **********/
3208 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3210 * Creates the text file specified in 'filename' which will contain
3211 * a minimal data dictionary of all tables accessible by the user specified
3214 * userID is evaluated in the following manner:
3215 * userID == NULL ... UserID is ignored
3216 * userID == "" ... UserID set equal to 'this->uid'
3217 * userID != "" ... UserID set equal to 'userID'
3219 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3220 * by this function. This function should use its own wxDb instance
3221 * to avoid undesired unbinding of columns.
3224 wxASSERT(fileName
.Length());
3228 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3229 wxString tblNameSave
;
3230 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3232 wxChar typeName
[30+1];
3233 SDWORD precision
, length
;
3235 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3239 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3242 convertUserID(userID
,UserID
);
3244 if (!UserID
.IsEmpty() &&
3245 Dbms() != dbmsMY_SQL
&&
3246 Dbms() != dbmsACCESS
&&
3247 Dbms() != dbmsINTERBASE
&&
3248 Dbms() != dbmsMS_SQL_SERVER
)
3250 retcode
= SQLColumns(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
= SQLColumns(hstmt
,
3259 NULL
, 0, // All qualifiers
3260 NULL
, 0, // User specified
3261 NULL
, 0, // All tables
3262 NULL
, 0); // All columns
3264 if (retcode
!= SQL_SUCCESS
)
3266 DispAllErrors(henv
, hdbc
, hstmt
);
3272 tblNameSave
.Empty();
3277 retcode
= SQLFetch(hstmt
);
3278 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3281 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3282 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3283 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3284 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3285 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3286 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3288 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3291 wxFputs(wxT("\n"), fp
);
3292 wxFputs(wxT("================================ "), fp
);
3293 wxFputs(wxT("================================ "), fp
);
3294 wxFputs(wxT("===================== "), fp
);
3295 wxFputs(wxT("========= "), fp
);
3296 wxFputs(wxT("=========\n"), fp
);
3297 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3298 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3299 wxFputs(outStr
.c_str(), fp
);
3300 wxFputs(wxT("================================ "), fp
);
3301 wxFputs(wxT("================================ "), fp
);
3302 wxFputs(wxT("===================== "), fp
);
3303 wxFputs(wxT("========= "), fp
);
3304 wxFputs(wxT("=========\n"), fp
);
3305 tblNameSave
= tblName
;
3308 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3309 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3310 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3312 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3319 if (retcode
!= SQL_NO_DATA_FOUND
)
3320 DispAllErrors(henv
, hdbc
, hstmt
);
3322 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3325 return(retcode
== SQL_NO_DATA_FOUND
);
3327 } // wxDb::Catalog()
3330 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3332 * Table name can refer to a table, view, alias or synonym. Returns true
3333 * if the object exists in the database. This function does not indicate
3334 * whether or not the user has privleges to query or perform other functions
3337 * userID is evaluated in the following manner:
3338 * userID == NULL ... UserID is ignored
3339 * userID == "" ... UserID set equal to 'this->uid'
3340 * userID != "" ... UserID set equal to 'userID'
3343 wxASSERT(tableName
.Length());
3347 if (Dbms() == dbmsDBASE
)
3350 if (tablePath
.Length())
3351 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3353 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3356 exists
= wxFileExists(dbName
);
3361 convertUserID(userID
,UserID
);
3363 TableName
= tableName
;
3364 // Oracle and Interbase table names are uppercase only, so force
3365 // the name to uppercase just in case programmer forgot to do this
3366 if ((Dbms() == dbmsORACLE
) ||
3367 (Dbms() == dbmsINTERBASE
))
3368 TableName
= TableName
.Upper();
3370 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3373 // Some databases cannot accept a user name when looking up table names,
3374 // so we use the call below that leaves out the user name
3375 if (!UserID
.IsEmpty() &&
3376 Dbms() != dbmsMY_SQL
&&
3377 Dbms() != dbmsACCESS
&&
3378 Dbms() != dbmsMS_SQL_SERVER
&&
3379 Dbms() != dbmsDB2
&&
3380 Dbms() != dbmsINTERBASE
&&
3381 Dbms() != dbmsPERVASIVE_SQL
)
3383 retcode
= SQLTables(hstmt
,
3384 NULL
, 0, // All qualifiers
3385 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3386 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3387 NULL
, 0); // All table types
3391 retcode
= SQLTables(hstmt
,
3392 NULL
, 0, // All qualifiers
3393 NULL
, 0, // All owners
3394 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3395 NULL
, 0); // All table types
3397 if (retcode
!= SQL_SUCCESS
)
3398 return(DispAllErrors(henv
, hdbc
, hstmt
));
3400 retcode
= SQLFetch(hstmt
);
3401 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3403 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3404 return(DispAllErrors(henv
, hdbc
, hstmt
));
3407 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3411 } // wxDb::TableExists()
3414 /********** wxDb::TablePrivileges() **********/
3415 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3416 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3418 wxASSERT(tableName
.Length());
3420 wxDbTablePrivilegeInfo result
;
3424 // We probably need to be able to dynamically set this based on
3425 // the driver type, and state.
3426 wxChar curRole
[]=wxT("public");
3430 wxString UserID
,Schema
;
3431 convertUserID(userID
,UserID
);
3432 convertUserID(schema
,Schema
);
3434 TableName
= tableName
;
3435 // Oracle and Interbase table names are uppercase only, so force
3436 // the name to uppercase just in case programmer forgot to do this
3437 if ((Dbms() == dbmsORACLE
) ||
3438 (Dbms() == dbmsINTERBASE
))
3439 TableName
= TableName
.Upper();
3441 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3443 // Some databases cannot accept a user name when looking up table names,
3444 // so we use the call below that leaves out the user name
3445 if (!Schema
.IsEmpty() &&
3446 Dbms() != dbmsMY_SQL
&&
3447 Dbms() != dbmsACCESS
&&
3448 Dbms() != dbmsMS_SQL_SERVER
)
3450 retcode
= SQLTablePrivileges(hstmt
,
3452 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3453 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3457 retcode
= SQLTablePrivileges(hstmt
,
3460 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3463 #ifdef DBDEBUG_CONSOLE
3464 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3467 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3468 return(DispAllErrors(henv
, hdbc
, hstmt
));
3470 bool failed
= false;
3471 retcode
= SQLFetch(hstmt
);
3472 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3474 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3477 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3480 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3483 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3486 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3489 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3492 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3497 return(DispAllErrors(henv
, hdbc
, hstmt
));
3499 #ifdef DBDEBUG_CONSOLE
3500 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3501 result
.privilege
,result
.tableOwner
,result
.tableName
,
3502 result
.grantor
, result
.grantee
);
3505 if (UserID
.IsSameAs(result
.tableOwner
,false))
3507 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3511 if (UserID
.IsSameAs(result
.grantee
,false) &&
3512 !wxStrcmp(result
.privilege
,priv
))
3514 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3518 if (!wxStrcmp(result
.grantee
,curRole
) &&
3519 !wxStrcmp(result
.privilege
,priv
))
3521 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3525 retcode
= SQLFetch(hstmt
);
3528 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3531 } // wxDb::TablePrivileges
3534 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3538 if (Dbms() == dbmsACCESS
)
3539 TableName
= _T("\"");
3540 TableName
+= tableName
;
3541 if (Dbms() == dbmsACCESS
)
3542 TableName
+= _T("\"");
3545 } // wxDb::SQLTableName()
3548 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3552 if (Dbms() == dbmsACCESS
)
3555 if (Dbms() == dbmsACCESS
)
3556 ColName
+= _T("\"");
3559 } // wxDb::SQLColumnName()
3562 /********** wxDb::SetSqlLogging() **********/
3563 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3565 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3566 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3568 if (state
== sqlLogON
)
3572 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3573 if (fpSqlLog
== NULL
)
3581 if (fclose(fpSqlLog
))
3587 sqlLogState
= state
;
3590 } // wxDb::SetSqlLogging()
3593 /********** wxDb::WriteSqlLog() **********/
3594 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3596 wxASSERT(logMsg
.Length());
3598 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3601 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3603 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3605 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3610 } // wxDb::WriteSqlLog()
3613 /********** wxDb::Dbms() **********/
3614 wxDBMS
wxDb::Dbms(void)
3616 * Be aware that not all database engines use the exact same syntax, and not
3617 * every ODBC compliant database is compliant to the same level of compliancy.
3618 * Some manufacturers support the minimum Level 1 compliancy, and others up
3619 * through Level 3. Others support subsets of features for levels above 1.
3621 * If you find an inconsistency between the wxDb class and a specific database
3622 * engine, and an identifier to this section, and special handle the database in
3623 * the area where behavior is non-conforming with the other databases.
3626 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3627 * ---------------------------------------------------
3630 * - Currently the only database supported by the class to support VIEWS
3633 * - Does not support the SQL_TIMESTAMP structure
3634 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3635 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3636 * is true. The user must create ALL indexes from their program.
3637 * - Table names can only be 8 characters long
3638 * - Column names can only be 10 characters long
3641 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3642 * after every table name involved in the query/join if that tables matching record(s)
3644 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3646 * SYBASE (Enterprise)
3647 * - If a column is part of the Primary Key, the column cannot be NULL
3648 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3651 * - If a column is part of the Primary Key, the column cannot be NULL
3652 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3653 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3654 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3655 * column definition if it is not defined correctly, but it is experimental
3656 * - Does not support sub-queries in SQL statements
3659 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3660 * - Does not support sub-queries in SQL statements
3663 * - Primary keys must be declared as NOT NULL
3664 * - Table and index names must not be longer than 13 characters in length (technically
3665 * table names can be up to 18 characters, but the primary index is created using the
3666 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3671 * - Columns that are part of primary keys must be defined as being NOT NULL
3672 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3673 * column definition if it is not defined correctly, but it is experimental
3676 // Should only need to do this once for each new database connection
3677 // so return the value we already determined it to be to save time
3678 // and lots of string comparisons
3679 if (dbmsType
!= dbmsUNIDENTIFIED
)
3682 wxChar baseName
[25+1];
3683 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3686 // RGG 20001025 : add support for Interbase
3687 // GT : Integrated to base classes on 20001121
3688 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3689 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3691 // BJO 20000428 : add support for Virtuoso
3692 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3693 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3695 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3696 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3698 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3699 // connected through an OpenLink driver.
3700 // Is it also returned by Sybase Adapatitve server?
3701 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3702 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3704 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3705 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3706 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3708 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3711 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3712 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3715 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3716 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3719 if (!wxStricmp(baseName
,wxT("Pervasive")))
3720 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3723 if (!wxStricmp(baseName
,wxT("Informix")))
3724 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3727 if (!wxStricmp(baseName
,wxT("Oracle")))
3728 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3729 if (!wxStricmp(baseName
,wxT("ACCESS")))
3730 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3731 if (!wxStricmp(baseName
,wxT("Sybase")))
3732 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3735 if (!wxStricmp(baseName
,wxT("DBASE")))
3736 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3737 if (!wxStricmp(baseName
,wxT("xBase")))
3738 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3739 if (!wxStricmp(baseName
,wxT("MySQL")))
3740 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3743 if (!wxStricmp(baseName
,wxT("DB2")))
3744 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3746 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3751 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3752 int dataType
, ULONG columnLength
,
3753 const wxString
&optionalParam
)
3755 wxASSERT(tableName
.Length());
3756 wxASSERT(columnName
.Length());
3757 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3758 dataType
!= DB_DATA_TYPE_VARCHAR
);
3760 // Must specify a columnLength if modifying a VARCHAR type column
3761 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3764 wxString dataTypeName
;
3766 wxString alterSlashModify
;
3770 case DB_DATA_TYPE_VARCHAR
:
3771 dataTypeName
= typeInfVarchar
.TypeName
;
3773 case DB_DATA_TYPE_INTEGER
:
3774 dataTypeName
= typeInfInteger
.TypeName
;
3776 case DB_DATA_TYPE_FLOAT
:
3777 dataTypeName
= typeInfFloat
.TypeName
;
3779 case DB_DATA_TYPE_DATE
:
3780 dataTypeName
= typeInfDate
.TypeName
;
3782 case DB_DATA_TYPE_BLOB
:
3783 dataTypeName
= typeInfBlob
.TypeName
;
3789 // Set the modify or alter syntax depending on the type of database connected to
3793 alterSlashModify
= _T("MODIFY");
3795 case dbmsMS_SQL_SERVER
:
3796 alterSlashModify
= _T("ALTER COLUMN");
3798 case dbmsUNIDENTIFIED
:
3800 case dbmsSYBASE_ASA
:
3801 case dbmsSYBASE_ASE
:
3806 case dbmsXBASE_SEQUITER
:
3808 alterSlashModify
= _T("MODIFY");
3812 // create the SQL statement
3813 if ( Dbms() == dbmsMY_SQL
)
3815 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3816 columnName
.c_str(), dataTypeName
.c_str());
3820 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3821 columnName
.c_str(), dataTypeName
.c_str());
3824 // For varchars only, append the size of the column
3825 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3826 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3829 s
.Printf(wxT("(%lu)"), columnLength
);
3833 // for passing things like "NOT NULL"
3834 if (optionalParam
.Length())
3836 sqlStmt
+= wxT(" ");
3837 sqlStmt
+= optionalParam
;
3840 return ExecSql(sqlStmt
);
3842 } // wxDb::ModifyColumn()
3845 /********** wxDbGetConnection() **********/
3846 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3850 // Used to keep a pointer to a DB connection that matches the requested
3851 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3852 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3853 // rather than having to re-query the datasource to get all the values
3854 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3855 wxDb
*matchingDbConnection
= NULL
;
3857 // Scan the linked list searching for an available database connection
3858 // that's already been opened but is currently not in use.
3859 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3861 // The database connection must be for the same datasource
3862 // name and must currently not be in use.
3864 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
3866 if (pDbConfig
->UseConnectionStr())
3868 if (pList
->PtrDb
->OpenedWithConnectionString() &&
3869 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
3871 // Found a free connection
3872 pList
->Free
= false;
3873 return(pList
->PtrDb
);
3878 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
3879 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
3881 // Found a free connection
3882 pList
->Free
= false;
3883 return(pList
->PtrDb
);
3888 if (pDbConfig
->UseConnectionStr())
3890 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
3891 matchingDbConnection
= pList
->PtrDb
;
3895 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3896 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3897 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3898 matchingDbConnection
= pList
->PtrDb
;
3902 // No available connections. A new connection must be made and
3903 // appended to the end of the linked list.
3906 // Find the end of the list
3907 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3908 // Append a new list item
3909 pList
->PtrNext
= new wxDbList
;
3910 pList
->PtrNext
->PtrPrev
= pList
;
3911 pList
= pList
->PtrNext
;
3915 // Create the first node on the list
3916 pList
= PtrBegDbList
= new wxDbList
;
3920 // Initialize new node in the linked list
3922 pList
->Free
= false;
3923 pList
->Dsn
= pDbConfig
->GetDsn();
3924 pList
->Uid
= pDbConfig
->GetUserID();
3925 pList
->AuthStr
= pDbConfig
->GetPassword();
3926 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
3928 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3932 if (!matchingDbConnection
)
3934 if (pDbConfig
->UseConnectionStr())
3936 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
3940 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3944 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3946 // Connect to the datasource
3949 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
3950 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
3951 return(pList
->PtrDb
);
3953 else // Unable to connect, destroy list item
3956 pList
->PtrPrev
->PtrNext
= 0;
3958 PtrBegDbList
= 0; // Empty list again
3960 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3961 pList
->PtrDb
->Close(); // Close the wxDb object
3962 delete pList
->PtrDb
; // Deletes the wxDb object
3963 delete pList
; // Deletes the linked list object
3967 } // wxDbGetConnection()
3970 /********** wxDbFreeConnection() **********/
3971 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3975 // Scan the linked list searching for the database connection
3976 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3978 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3979 return (pList
->Free
= true);
3982 // Never found the database object, return failure
3985 } // wxDbFreeConnection()
3988 /********** wxDbCloseConnections() **********/
3989 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3991 wxDbList
*pList
, *pNext
;
3993 // Traverse the linked list closing database connections and freeing memory as I go.
3994 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3996 pNext
= pList
->PtrNext
; // Save the pointer to next
3997 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3998 pList
->PtrDb
->Close(); // Close the wxDb object
3999 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4000 delete pList
->PtrDb
; // Deletes the wxDb object
4001 delete pList
; // Deletes the linked list object
4004 // Mark the list as empty
4007 } // wxDbCloseConnections()
4010 /********** wxDbConnectionsInUse() **********/
4011 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4016 // Scan the linked list counting db connections that are currently in use
4017 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4019 if (pList
->Free
== false)
4025 } // wxDbConnectionsInUse()
4029 /********** wxDbLogExtendedErrorMsg() **********/
4030 // DEBUG ONLY function
4031 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4033 const wxChar
*ErrFile
,
4036 static wxString msg
;
4041 if (ErrFile
|| ErrLine
)
4043 msg
+= wxT("File: ");
4045 msg
+= wxT(" Line: ");
4046 tStr
.Printf(wxT("%d"),ErrLine
);
4047 msg
+= tStr
.c_str();
4051 msg
.Append (wxT("\nODBC errors:\n"));
4054 // Display errors for this connection
4056 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4058 if (pDb
->errorList
[i
])
4060 msg
.Append(pDb
->errorList
[i
]);
4061 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
4062 msg
.Append(wxT("\n"));
4063 // Clear the errmsg buffer so the next error will not
4064 // end up showing the previous error that have occurred
4065 wxStrcpy(pDb
->errorList
[i
],wxT(""));
4070 wxLogDebug(msg
.c_str());
4073 } // wxDbLogExtendedErrorMsg()
4076 /********** wxDbSqlLog() **********/
4077 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4079 bool append
= false;
4082 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4084 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4089 SQLLOGstate
= state
;
4090 SQLLOGfn
= filename
;
4098 /********** wxDbCreateDataSource() **********/
4099 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4100 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4102 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4103 * Very rudimentary creation of an ODBC data source.
4105 * ODBC driver must be ODBC 3.0 compliant to use this function
4110 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4116 dsnLocation
= ODBC_ADD_SYS_DSN
;
4118 dsnLocation
= ODBC_ADD_DSN
;
4120 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4121 // so that is why I used it, as wxString does not deal well with
4122 // embedded nulls in strings
4123 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4125 // Replace the separator from above with the '\0' seperator needed
4126 // by the SQLConfigDataSource() function
4130 k
= setupStr
.Find((wxChar
)2,true);
4131 if (k
!= wxNOT_FOUND
)
4132 setupStr
[(UINT
)k
] = wxT('\0');
4134 while (k
!= wxNOT_FOUND
);
4136 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4137 driverName
, setupStr
.c_str());
4139 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4141 // check for errors caused by ConfigDSN based functions
4144 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4145 errMsg
[0] = wxT('\0');
4147 // This function is only supported in ODBC drivers v3.0 compliant and above
4148 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4151 #ifdef DBDEBUG_CONSOLE
4152 // When run in console mode, use standard out to display errors.
4153 cout
<< errMsg
<< endl
;
4154 cout
<< wxT("Press any key to continue...") << endl
;
4156 #endif // DBDEBUG_CONSOLE
4159 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4160 #endif // __WXDEBUG__
4166 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4167 // necessary to use this function, so this function is not supported
4169 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4172 #endif // __VISUALC__
4176 } // wxDbCreateDataSource()
4180 /********** wxDbGetDataSource() **********/
4181 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4182 SWORD DsDescMax
, UWORD direction
)
4184 * Dsn and DsDesc will contain the data source name and data source
4185 * description upon return
4190 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4191 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4196 } // wxDbGetDataSource()
4199 // Change this to 0 to remove use of all deprecated functions
4200 #if wxODBC_BACKWARD_COMPATABILITY
4201 /********************************************************************
4202 ********************************************************************
4204 * The following functions are all DEPRECATED and are included for
4205 * backward compatability reasons only
4207 ********************************************************************
4208 ********************************************************************/
4209 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4211 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4213 /***** DEPRECATED: use wxGetDataSource() *****/
4214 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4217 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4219 /***** DEPRECATED: use wxDbGetConnection() *****/
4220 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4222 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4224 /***** DEPRECATED: use wxDbFreeConnection() *****/
4225 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4227 return wxDbFreeConnection(pDb
);
4229 /***** DEPRECATED: use wxDbCloseConnections() *****/
4230 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4232 wxDbCloseConnections();
4234 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4235 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4237 return wxDbConnectionsInUse();