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::~wxDbColFor()
246 } // wxDbColFor::~wxDbColFor()
249 /********** wxDbColFor::Initialize() **********/
250 void wxDbColFor::Initialize()
260 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
263 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
264 } // wxDbColFor::Initialize()
267 /********** wxDbColFor::Format() **********/
268 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
269 short columnSize
, short decimalDigits
)
271 // ----------------------------------------------------------------------------------------
272 // -- 19991224 : mj10777 : Create
273 // There is still a lot of work to do here, but it is a start
274 // It handles all the basic data-types that I have run into up to now
275 // The main work will have be with Dates and float Formatting
276 // (US 1,000.00 ; EU 1.000,00)
277 // There are wxWindow plans for locale support and the new wxDateTime. If
278 // they define some constants (wxEUROPEAN) that can be gloably used,
279 // they should be used here.
280 // ----------------------------------------------------------------------------------------
281 // There should also be a function to scan in a string to fill the variable
282 // ----------------------------------------------------------------------------------------
284 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
285 i_dbDataType
= dbDataType
;
286 i_sqlDataType
= sqlDataType
;
287 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
289 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
291 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
292 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
293 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
294 i_dbDataType
= DB_DATA_TYPE_DATE
;
295 if (i_sqlDataType
== SQL_C_BIT
)
296 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
297 if (i_sqlDataType
== SQL_NUMERIC
)
298 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
299 if (i_sqlDataType
== SQL_REAL
)
300 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
301 if (i_sqlDataType
== SQL_C_BINARY
)
302 i_dbDataType
= DB_DATA_TYPE_BLOB
;
305 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
307 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
310 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
312 case DB_DATA_TYPE_VARCHAR
:
315 case DB_DATA_TYPE_INTEGER
:
318 case DB_DATA_TYPE_FLOAT
:
319 if (decimalDigits
== 0)
322 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
323 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
325 case DB_DATA_TYPE_DATE
:
326 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
328 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
330 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
332 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
334 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
336 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
338 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
340 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
342 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
344 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
347 case DB_DATA_TYPE_BLOB
:
348 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
351 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
355 } // wxDbColFor::Format()
359 /********** wxDbColInf Constructor **********/
360 wxDbColInf::wxDbColInf()
363 } // wxDbColInf::wxDbColInf()
366 /********** wxDbColInf Destructor ********/
367 wxDbColInf::~wxDbColInf()
372 } // wxDbColInf::~wxDbColInf()
375 bool wxDbColInf::Initialize()
397 } // wxDbColInf::Initialize()
400 /********** wxDbTableInf Constructor ********/
401 wxDbTableInf::wxDbTableInf()
404 } // wxDbTableInf::wxDbTableInf()
407 /********** wxDbTableInf Constructor ********/
408 wxDbTableInf::~wxDbTableInf()
413 } // wxDbTableInf::~wxDbTableInf()
416 bool wxDbTableInf::Initialize()
425 } // wxDbTableInf::Initialize()
428 /********** wxDbInf Constructor *************/
432 } // wxDbInf::wxDbInf()
435 /********** wxDbInf Destructor *************/
441 } // wxDbInf::~wxDbInf()
444 /********** wxDbInf::Initialize() *************/
445 bool wxDbInf::Initialize()
453 } // wxDbInf::Initialize()
456 /********** wxDb Constructor **********/
457 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
459 // Copy the HENV into the db class
461 fwdOnlyCursors
= FwdOnlyCursors
;
467 /********** wxDb Destructor **********/
470 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
480 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
481 /********** wxDb::initialize() **********/
482 void wxDb::initialize()
484 * Private member function that sets all wxDb member variables to
485 * known values at creation of the wxDb
490 fpSqlLog
= 0; // Sql Log file pointer
491 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
493 dbmsType
= dbmsUNIDENTIFIED
;
495 wxStrcpy(sqlState
,wxEmptyString
);
496 wxStrcpy(errorMsg
,wxEmptyString
);
497 nativeError
= cbErrorMsg
= 0;
498 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
499 wxStrcpy(errorList
[i
], wxEmptyString
);
501 // Init typeInf structures
502 typeInfVarchar
.TypeName
.Empty();
503 typeInfVarchar
.FsqlType
= 0;
504 typeInfVarchar
.Precision
= 0;
505 typeInfVarchar
.CaseSensitive
= 0;
506 typeInfVarchar
.MaximumScale
= 0;
508 typeInfInteger
.TypeName
.Empty();
509 typeInfInteger
.FsqlType
= 0;
510 typeInfInteger
.Precision
= 0;
511 typeInfInteger
.CaseSensitive
= 0;
512 typeInfInteger
.MaximumScale
= 0;
514 typeInfFloat
.TypeName
.Empty();
515 typeInfFloat
.FsqlType
= 0;
516 typeInfFloat
.Precision
= 0;
517 typeInfFloat
.CaseSensitive
= 0;
518 typeInfFloat
.MaximumScale
= 0;
520 typeInfDate
.TypeName
.Empty();
521 typeInfDate
.FsqlType
= 0;
522 typeInfDate
.Precision
= 0;
523 typeInfDate
.CaseSensitive
= 0;
524 typeInfDate
.MaximumScale
= 0;
526 typeInfBlob
.TypeName
.Empty();
527 typeInfBlob
.FsqlType
= 0;
528 typeInfBlob
.Precision
= 0;
529 typeInfBlob
.CaseSensitive
= 0;
530 typeInfBlob
.MaximumScale
= 0;
532 // Error reporting is turned OFF by default
535 // Allocate a data source connection handle
536 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
539 // Initialize the db status flag
542 // Mark database as not open as of yet
545 dbOpenedWithConnectionString
= false;
546 } // wxDb::initialize()
549 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
551 // NOTE: Return value from this function MUST be copied
552 // immediately, as the value is not good after
553 // this function has left scope.
555 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
559 if (!wxStrlen(userID
))
567 // dBase does not use user names, and some drivers fail if you try to pass one
568 if ( Dbms() == dbmsDBASE
569 || Dbms() == dbmsXBASE_SEQUITER
)
572 // Oracle user names may only be in uppercase, so force
573 // the name to uppercase
574 if (Dbms() == dbmsORACLE
)
575 UserID
= UserID
.Upper();
577 return UserID
.c_str();
578 } // wxDb::convertUserID()
581 bool wxDb::open(bool failOnDataTypeUnsupported
)
584 If using Intersolv branded ODBC drivers, this is the place where you would substitute
585 your branded driver license information
587 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
588 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
591 // Mark database as open
594 // Allocate a statement handle for the database connection
595 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
596 return(DispAllErrors(henv
, hdbc
));
598 // Set Connection Options
599 if (!setConnectionOptions())
602 // Query the data source for inf. about itself
603 if (!getDbInfo(failOnDataTypeUnsupported
))
606 // Query the data source regarding data type information
609 // The way it was determined which SQL data types to use was by calling SQLGetInfo
610 // for all of the possible SQL data types to see which ones were supported. If
611 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
612 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
613 // types I've selected below will not alway's be what we want. These are just
614 // what happened to work against an Oracle 7/Intersolv combination. The following is
615 // a complete list of the results I got back against the Oracle 7 database:
617 // SQL_BIGINT SQL_NO_DATA_FOUND
618 // SQL_BINARY SQL_NO_DATA_FOUND
619 // SQL_BIT SQL_NO_DATA_FOUND
620 // SQL_CHAR type name = 'CHAR', Precision = 255
621 // SQL_DATE SQL_NO_DATA_FOUND
622 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
623 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
624 // SQL_FLOAT SQL_NO_DATA_FOUND
625 // SQL_INTEGER SQL_NO_DATA_FOUND
626 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
627 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
628 // SQL_NUMERIC SQL_NO_DATA_FOUND
629 // SQL_REAL SQL_NO_DATA_FOUND
630 // SQL_SMALLINT SQL_NO_DATA_FOUND
631 // SQL_TIME SQL_NO_DATA_FOUND
632 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
633 // SQL_VARBINARY type name = 'RAW', Precision = 255
634 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
635 // =====================================================================
636 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
638 // SQL_VARCHAR type name = 'TEXT', Precision = 255
639 // SQL_TIMESTAMP type name = 'DATETIME'
640 // SQL_DECIMAL SQL_NO_DATA_FOUND
641 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
642 // SQL_FLOAT SQL_NO_DATA_FOUND
643 // SQL_REAL type name = 'SINGLE', Precision = 7
644 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
645 // SQL_INTEGER type name = 'LONG', Precision = 10
647 // VARCHAR = Variable length character string
648 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
649 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
652 typeInfVarchar
.FsqlType
= SQL_CHAR
;
654 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
657 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
658 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
659 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
660 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
661 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
663 if (failOnDataTypeUnsupported
)
667 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
669 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
671 typeInfFloat
.FsqlType
= SQL_FLOAT
;
673 typeInfFloat
.FsqlType
= SQL_REAL
;
675 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
678 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
680 // If SQL_INTEGER is not supported, use the floating point
681 // data type to store integers as well as floats
682 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
684 if (failOnDataTypeUnsupported
)
688 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
691 typeInfInteger
.FsqlType
= SQL_INTEGER
;
694 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
696 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
699 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
701 typeInfDate
.FsqlType
= SQL_TIME
;
704 #endif // SQL_DATETIME defined
706 if (failOnDataTypeUnsupported
)
711 typeInfDate
.FsqlType
= SQL_DATE
;
714 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
717 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
719 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
721 if (failOnDataTypeUnsupported
)
725 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
728 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
731 #ifdef DBDEBUG_CONSOLE
732 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
733 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
734 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
735 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
736 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
740 // Completed Successfully
744 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
746 wxASSERT(inConnectStr
.Length());
753 if (!FwdOnlyCursors())
755 // Specify that the ODBC cursor library be used, if needed. This must be
756 // specified before the connection is made.
757 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
759 #ifdef DBDEBUG_CONSOLE
760 if (retcode
== SQL_SUCCESS
)
761 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
763 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
765 wxUnusedVar(retcode
);
769 // Connect to the data source
770 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
771 short outConnectBufferLen
;
773 inConnectionStr
= inConnectStr
;
775 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
776 inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
777 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
779 if ((retcode
!= SQL_SUCCESS
) &&
780 (retcode
!= SQL_SUCCESS_WITH_INFO
))
781 return(DispAllErrors(henv
, hdbc
));
783 outConnectBuffer
[outConnectBufferLen
] = 0;
784 outConnectionStr
= outConnectBuffer
;
785 dbOpenedWithConnectionString
= true;
787 return open(failOnDataTypeUnsupported
);
790 /********** wxDb::Open() **********/
791 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
793 wxASSERT(Dsn
.Length());
798 inConnectionStr
= wxT("");
799 outConnectionStr
= wxT("");
803 if (!FwdOnlyCursors())
805 // Specify that the ODBC cursor library be used, if needed. This must be
806 // specified before the connection is made.
807 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
809 #ifdef DBDEBUG_CONSOLE
810 if (retcode
== SQL_SUCCESS
)
811 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
813 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
815 wxUnusedVar( retcode
);
819 // Connect to the data source
820 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
821 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
822 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
824 if ((retcode
!= SQL_SUCCESS
) &&
825 (retcode
!= SQL_SUCCESS_WITH_INFO
))
826 return(DispAllErrors(henv
, hdbc
));
828 return open(failOnDataTypeUnsupported
);
833 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
835 wxASSERT(dbConnectInf
);
837 // Use the connection string if one is present
838 if (dbConnectInf
->UseConnectionStr())
839 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
841 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
842 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
846 bool wxDb::Open(wxDb
*copyDb
)
848 dsn
= copyDb
->GetDatasourceName();
849 uid
= copyDb
->GetUsername();
850 authStr
= copyDb
->GetPassword();
851 inConnectionStr
= copyDb
->GetConnectionInStr();
852 outConnectionStr
= copyDb
->GetConnectionOutStr();
856 if (!FwdOnlyCursors())
858 // Specify that the ODBC cursor library be used, if needed. This must be
859 // specified before the connection is made.
860 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
862 #ifdef DBDEBUG_CONSOLE
863 if (retcode
== SQL_SUCCESS
)
864 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
866 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
868 wxUnusedVar( retcode
);
872 if (copyDb
->OpenedWithConnectionString())
874 // Connect to the data source
875 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
876 short outConnectBufferLen
;
878 inConnectionStr
= copyDb
->GetConnectionInStr();
880 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
881 inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
882 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
884 if ((retcode
!= SQL_SUCCESS
) &&
885 (retcode
!= SQL_SUCCESS_WITH_INFO
))
886 return(DispAllErrors(henv
, hdbc
));
888 outConnectBuffer
[outConnectBufferLen
] = 0;
889 outConnectionStr
= outConnectBuffer
;
890 dbOpenedWithConnectionString
= true;
894 // Connect to the data source
895 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
896 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
897 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
900 if ((retcode
!= SQL_SUCCESS
) &&
901 (retcode
!= SQL_SUCCESS_WITH_INFO
))
902 return(DispAllErrors(henv
, hdbc
));
905 If using Intersolv branded ODBC drivers, this is the place where you would substitute
906 your branded driver license information
908 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
909 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
912 // Mark database as open
915 // Allocate a statement handle for the database connection
916 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
917 return(DispAllErrors(henv
, hdbc
));
919 // Set Connection Options
920 if (!setConnectionOptions())
923 // Instead of Querying the data source for info about itself, it can just be copied
924 // from the wxDb instance that was passed in (copyDb).
925 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
926 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
927 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
928 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
929 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
930 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
931 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
932 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
933 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
934 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
935 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
936 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
937 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
938 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
939 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
940 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
941 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
942 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
943 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
944 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
945 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
946 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
947 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
948 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
949 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
950 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
951 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
952 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
953 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
954 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
955 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
957 // VARCHAR = Variable length character string
958 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
959 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
960 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
961 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
962 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
965 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
966 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
967 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
968 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
969 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
972 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
973 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
974 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
975 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
976 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
979 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
980 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
981 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
982 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
983 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
986 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
987 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
988 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
989 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
990 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
992 #ifdef DBDEBUG_CONSOLE
993 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
994 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
995 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
996 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
997 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1001 // Completed Successfully
1006 /********** wxDb::setConnectionOptions() **********/
1007 bool wxDb::setConnectionOptions(void)
1009 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1014 // I need to get the DBMS name here, because some of the connection options
1015 // are database specific and need to call the Dbms() function.
1016 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1017 return(DispAllErrors(henv
, hdbc
));
1019 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1020 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1021 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1023 // By default, MS Sql Server closes cursors on commit and rollback. The following
1024 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1025 // after a transaction. This is a driver specific option and is not part of the
1026 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1027 // The database settings don't have any effect one way or the other.
1028 if (Dbms() == dbmsMS_SQL_SERVER
)
1030 const long SQL_PRESERVE_CURSORS
= 1204L;
1031 const long SQL_PC_ON
= 1L;
1032 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1035 // Display the connection options to verify them
1036 #ifdef DBDEBUG_CONSOLE
1038 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1040 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
1041 return(DispAllErrors(henv
, hdbc
));
1042 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1044 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
1045 return(DispAllErrors(henv
, hdbc
));
1046 cout
<< wxT("ODBC CURSORS: ");
1049 case(SQL_CUR_USE_IF_NEEDED
):
1050 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1052 case(SQL_CUR_USE_ODBC
):
1053 cout
<< wxT("SQL_CUR_USE_ODBC");
1055 case(SQL_CUR_USE_DRIVER
):
1056 cout
<< wxT("SQL_CUR_USE_DRIVER");
1061 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
1062 return(DispAllErrors(henv
, hdbc
));
1063 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1068 // Completed Successfully
1071 } // wxDb::setConnectionOptions()
1074 /********** wxDb::getDbInfo() **********/
1075 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1080 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
);
1081 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1083 DispAllErrors(henv
, hdbc
);
1084 if (failOnDataTypeUnsupported
)
1088 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
);
1089 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1091 DispAllErrors(henv
, hdbc
);
1092 if (failOnDataTypeUnsupported
)
1096 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
);
1097 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1099 DispAllErrors(henv
, hdbc
);
1100 if (failOnDataTypeUnsupported
)
1105 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1106 // causing database connectivity to fail in some cases.
1107 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1108 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1110 DispAllErrors(henv
, hdbc
);
1111 if (failOnDataTypeUnsupported
)
1115 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1116 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1118 DispAllErrors(henv
, hdbc
);
1119 if (failOnDataTypeUnsupported
)
1123 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1124 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1126 DispAllErrors(henv
, hdbc
);
1127 if (failOnDataTypeUnsupported
)
1131 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
);
1132 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1134 DispAllErrors(henv
, hdbc
);
1135 if (failOnDataTypeUnsupported
)
1139 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
);
1140 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1142 DispAllErrors(henv
, hdbc
);
1143 if (failOnDataTypeUnsupported
)
1147 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1148 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1150 DispAllErrors(henv
, hdbc
);
1151 if (failOnDataTypeUnsupported
)
1155 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
);
1156 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1158 DispAllErrors(henv
, hdbc
);
1159 if (failOnDataTypeUnsupported
)
1163 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1164 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1166 DispAllErrors(henv
, hdbc
);
1167 if (failOnDataTypeUnsupported
)
1171 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1172 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1174 // Not all drivers support this call - Nick Gorham(unixODBC)
1175 dbInf
.cliConfLvl
= 0;
1176 DispAllErrors(henv
, hdbc
);
1177 if (failOnDataTypeUnsupported
)
1181 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1182 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1184 DispAllErrors(henv
, hdbc
);
1185 if (failOnDataTypeUnsupported
)
1189 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
);
1190 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1192 DispAllErrors(henv
, hdbc
);
1193 if (failOnDataTypeUnsupported
)
1197 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
);
1198 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1200 DispAllErrors(henv
, hdbc
);
1201 if (failOnDataTypeUnsupported
)
1205 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
);
1206 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1208 DispAllErrors(henv
, hdbc
);
1209 if (failOnDataTypeUnsupported
)
1213 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1214 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1216 DispAllErrors(henv
, hdbc
);
1217 if (failOnDataTypeUnsupported
)
1221 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1222 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1224 DispAllErrors(henv
, hdbc
);
1225 if (failOnDataTypeUnsupported
)
1229 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1230 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1232 DispAllErrors(henv
, hdbc
);
1233 if (failOnDataTypeUnsupported
)
1237 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
);
1238 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1240 DispAllErrors(henv
, hdbc
);
1241 if (failOnDataTypeUnsupported
)
1245 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1246 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1248 DispAllErrors(henv
, hdbc
);
1249 if (failOnDataTypeUnsupported
)
1253 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1254 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1256 DispAllErrors(henv
, hdbc
);
1257 if (failOnDataTypeUnsupported
)
1261 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1262 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1264 DispAllErrors(henv
, hdbc
);
1265 if (failOnDataTypeUnsupported
)
1269 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1270 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1272 DispAllErrors(henv
, hdbc
);
1273 if (failOnDataTypeUnsupported
)
1277 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1278 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1280 DispAllErrors(henv
, hdbc
);
1281 if (failOnDataTypeUnsupported
)
1285 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1286 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1288 DispAllErrors(henv
, hdbc
);
1289 if (failOnDataTypeUnsupported
)
1293 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1294 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1296 DispAllErrors(henv
, hdbc
);
1297 if (failOnDataTypeUnsupported
)
1301 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1302 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1304 DispAllErrors(henv
, hdbc
);
1305 if (failOnDataTypeUnsupported
)
1309 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1310 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1312 DispAllErrors(henv
, hdbc
);
1313 if (failOnDataTypeUnsupported
)
1317 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1318 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1320 DispAllErrors(henv
, hdbc
);
1321 if (failOnDataTypeUnsupported
)
1325 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1326 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1328 DispAllErrors(henv
, hdbc
);
1329 if (failOnDataTypeUnsupported
)
1333 #ifdef DBDEBUG_CONSOLE
1334 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1335 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1336 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1337 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1339 cout
<< wxT("API Conf. Level: ");
1340 switch(dbInf
.apiConfLvl
)
1342 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1343 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1344 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1348 cout
<< wxT("SAG CLI Conf. Level: ");
1349 switch(dbInf
.cliConfLvl
)
1351 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1352 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1356 cout
<< wxT("SQL Conf. Level: ");
1357 switch(dbInf
.sqlConfLvl
)
1359 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1360 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1361 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1365 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1366 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1367 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1368 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1369 cout
<< wxT("Cursor COMMIT Behavior: ");
1370 switch(dbInf
.cursorCommitBehavior
)
1372 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1373 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1374 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1378 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1379 switch(dbInf
.cursorRollbackBehavior
)
1381 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1382 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1383 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1387 cout
<< wxT("Support NOT NULL clause: ");
1388 switch(dbInf
.supportNotNullClause
)
1390 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1391 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1395 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1396 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1398 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1401 cout
<< wxT("Default Transaction Isolation: ";
1402 switch(dbInf
.txnIsolation
)
1404 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1405 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1406 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1407 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1409 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1414 cout
<< wxT("Transaction Isolation Options: ");
1415 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1416 cout
<< wxT("Read Uncommitted, ");
1417 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1418 cout
<< wxT("Read Committed, ");
1419 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1420 cout
<< wxT("Repeatable Read, ");
1421 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1422 cout
<< wxT("Serializable, ");
1424 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1425 cout
<< wxT("Versioning");
1429 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1430 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1431 cout
<< wxT("Next, ");
1432 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1433 cout
<< wxT("Prev, ");
1434 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1435 cout
<< wxT("First, ");
1436 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1437 cout
<< wxT("Last, ");
1438 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1439 cout
<< wxT("Absolute, ");
1440 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1441 cout
<< wxT("Relative, ");
1443 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1444 cout
<< wxT("Resume, ");
1446 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1447 cout
<< wxT("Bookmark");
1450 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1451 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1452 cout
<< wxT("No Change, ");
1453 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1454 cout
<< wxT("Exclusive, ");
1455 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1456 cout
<< wxT("UnLock");
1459 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1460 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1461 cout
<< wxT("Position, ");
1462 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1463 cout
<< wxT("Refresh, ");
1464 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1465 cout
<< wxT("Upd, "));
1466 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1467 cout
<< wxT("Del, ");
1468 if (dbInf
.posOperations
& SQL_POS_ADD
)
1472 cout
<< wxT("Positioned Statements Supported: ");
1473 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1474 cout
<< wxT("Pos delete, ");
1475 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1476 cout
<< wxT("Pos update, ");
1477 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1478 cout
<< wxT("Select for update");
1481 cout
<< wxT("Scroll Concurrency: ");
1482 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1483 cout
<< wxT("Read Only, ");
1484 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1485 cout
<< wxT("Lock, ");
1486 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1487 cout
<< wxT("Opt. Rowver, ");
1488 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1489 cout
<< wxT("Opt. Values");
1492 cout
<< wxT("Scroll Options: ");
1493 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1494 cout
<< wxT("Fwd Only, ");
1495 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1496 cout
<< wxT("Static, ");
1497 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1498 cout
<< wxT("Keyset Driven, ");
1499 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1500 cout
<< wxT("Dynamic, ");
1501 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1502 cout
<< wxT("Mixed");
1505 cout
<< wxT("Static Sensitivity: ");
1506 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1507 cout
<< wxT("Additions, ");
1508 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1509 cout
<< wxT("Deletions, ");
1510 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1511 cout
<< wxT("Updates");
1514 cout
<< wxT("Transaction Capable?: ");
1515 switch(dbInf
.txnCapable
)
1517 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1518 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1519 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1520 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1521 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1528 // Completed Successfully
1531 } // wxDb::getDbInfo()
1534 /********** wxDb::getDataTypeInfo() **********/
1535 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1538 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1539 * the data type inf. is gathered for.
1541 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1546 // Get information about the data type specified
1547 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1548 return(DispAllErrors(henv
, hdbc
, hstmt
));
1551 retcode
= SQLFetch(hstmt
);
1552 if (retcode
!= SQL_SUCCESS
)
1554 #ifdef DBDEBUG_CONSOLE
1555 if (retcode
== SQL_NO_DATA_FOUND
)
1556 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1558 DispAllErrors(henv
, hdbc
, hstmt
);
1559 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1563 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1565 // Obtain columns from the record
1566 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1567 return(DispAllErrors(henv
, hdbc
, hstmt
));
1569 structSQLTypeInfo
.TypeName
= typeName
;
1571 // BJO 20000503: no more needed with new GetColumns...
1574 if (Dbms() == dbmsMY_SQL
)
1576 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1577 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1578 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1579 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1580 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1581 structSQLTypeInfo
.TypeName
= wxT("int");
1582 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1583 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1584 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1585 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1586 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1587 structSQLTypeInfo
.TypeName
= wxT("char");
1590 // BJO 20000427 : OpenLink driver
1591 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1592 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1594 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1595 structSQLTypeInfo
.TypeName
= wxT("real");
1599 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1600 return(DispAllErrors(henv
, hdbc
, hstmt
));
1601 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1602 return(DispAllErrors(henv
, hdbc
, hstmt
));
1603 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1604 // return(DispAllErrors(henv, hdbc, hstmt));
1606 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1607 return(DispAllErrors(henv
, hdbc
, hstmt
));
1609 if (structSQLTypeInfo
.MaximumScale
< 0)
1610 structSQLTypeInfo
.MaximumScale
= 0;
1612 // Close the statement handle which closes open cursors
1613 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1614 return(DispAllErrors(henv
, hdbc
, hstmt
));
1616 // Completed Successfully
1619 } // wxDb::getDataTypeInfo()
1622 /********** wxDb::Close() **********/
1623 void wxDb::Close(void)
1625 // Close the Sql Log file
1632 // Free statement handle
1635 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1636 DispAllErrors(henv
, hdbc
);
1639 // Disconnect from the datasource
1640 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1641 DispAllErrors(henv
, hdbc
);
1643 // Free the connection to the datasource
1644 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1645 DispAllErrors(henv
, hdbc
);
1647 // There should be zero Ctable objects still connected to this db object
1648 wxASSERT(nTables
== 0);
1652 wxList::compatibility_iterator pNode
;
1653 pNode
= TablesInUse
.GetFirst();
1657 tiu
= (wxTablesInUse
*)pNode
->GetData();
1658 if (tiu
->pDb
== this)
1660 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1661 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1662 wxLogDebug(s
.c_str(),s2
.c_str());
1664 pNode
= pNode
->GetNext();
1668 // Copy the error messages to a global variable
1670 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1671 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1673 dbmsType
= dbmsUNIDENTIFIED
;
1679 /********** wxDb::CommitTrans() **********/
1680 bool wxDb::CommitTrans(void)
1684 // Commit the transaction
1685 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1686 return(DispAllErrors(henv
, hdbc
));
1689 // Completed successfully
1692 } // wxDb::CommitTrans()
1695 /********** wxDb::RollbackTrans() **********/
1696 bool wxDb::RollbackTrans(void)
1698 // Rollback the transaction
1699 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1700 return(DispAllErrors(henv
, hdbc
));
1702 // Completed successfully
1705 } // wxDb::RollbackTrans()
1708 /********** wxDb::DispAllErrors() **********/
1709 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1711 * This function is called internally whenever an error condition prevents the user's
1712 * request from being executed. This function will query the datasource as to the
1713 * actual error(s) that just occured on the previous request of the datasource.
1715 * The function will retrieve each error condition from the datasource and
1716 * Printf the codes/text values into a string which it then logs via logError().
1717 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1718 * window and program execution will be paused until the user presses a key.
1720 * This function always returns a false, so that functions which call this function
1721 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1722 * of the users request, so that the calling code can then process the error msg log
1725 wxString odbcErrMsg
;
1727 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1729 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1730 logError(odbcErrMsg
, sqlState
);
1733 #ifdef DBDEBUG_CONSOLE
1734 // When run in console mode, use standard out to display errors.
1735 cout
<< odbcErrMsg
.c_str() << endl
;
1736 cout
<< wxT("Press any key to continue...") << endl
;
1741 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1746 return false; // This function always returns false.
1748 } // wxDb::DispAllErrors()
1751 /********** wxDb::GetNextError() **********/
1752 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1754 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1759 } // wxDb::GetNextError()
1762 /********** wxDb::DispNextError() **********/
1763 void wxDb::DispNextError(void)
1765 wxString odbcErrMsg
;
1767 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1768 logError(odbcErrMsg
, sqlState
);
1773 #ifdef DBDEBUG_CONSOLE
1774 // When run in console mode, use standard out to display errors.
1775 cout
<< odbcErrMsg
.c_str() << endl
;
1776 cout
<< wxT("Press any key to continue...") << endl
;
1781 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1782 #endif // __WXDEBUG__
1784 } // wxDb::DispNextError()
1787 /********** wxDb::logError() **********/
1788 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1790 wxASSERT(errMsg
.Length());
1792 static int pLast
= -1;
1795 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1798 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1799 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1803 wxStrcpy(errorList
[pLast
], errMsg
);
1805 if (SQLState
.Length())
1806 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1807 DB_STATUS
= dbStatus
;
1809 // Add the errmsg to the sql log
1810 WriteSqlLog(errMsg
);
1812 } // wxDb::logError()
1815 /**********wxDb::TranslateSqlState() **********/
1816 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1818 if (!wxStrcmp(SQLState
, wxT("01000")))
1819 return(DB_ERR_GENERAL_WARNING
);
1820 if (!wxStrcmp(SQLState
, wxT("01002")))
1821 return(DB_ERR_DISCONNECT_ERROR
);
1822 if (!wxStrcmp(SQLState
, wxT("01004")))
1823 return(DB_ERR_DATA_TRUNCATED
);
1824 if (!wxStrcmp(SQLState
, wxT("01006")))
1825 return(DB_ERR_PRIV_NOT_REVOKED
);
1826 if (!wxStrcmp(SQLState
, wxT("01S00")))
1827 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1828 if (!wxStrcmp(SQLState
, wxT("01S01")))
1829 return(DB_ERR_ERROR_IN_ROW
);
1830 if (!wxStrcmp(SQLState
, wxT("01S02")))
1831 return(DB_ERR_OPTION_VALUE_CHANGED
);
1832 if (!wxStrcmp(SQLState
, wxT("01S03")))
1833 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1834 if (!wxStrcmp(SQLState
, wxT("01S04")))
1835 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1836 if (!wxStrcmp(SQLState
, wxT("07001")))
1837 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1838 if (!wxStrcmp(SQLState
, wxT("07006")))
1839 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1840 if (!wxStrcmp(SQLState
, wxT("08001")))
1841 return(DB_ERR_UNABLE_TO_CONNECT
);
1842 if (!wxStrcmp(SQLState
, wxT("08002")))
1843 return(DB_ERR_CONNECTION_IN_USE
);
1844 if (!wxStrcmp(SQLState
, wxT("08003")))
1845 return(DB_ERR_CONNECTION_NOT_OPEN
);
1846 if (!wxStrcmp(SQLState
, wxT("08004")))
1847 return(DB_ERR_REJECTED_CONNECTION
);
1848 if (!wxStrcmp(SQLState
, wxT("08007")))
1849 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1850 if (!wxStrcmp(SQLState
, wxT("08S01")))
1851 return(DB_ERR_COMM_LINK_FAILURE
);
1852 if (!wxStrcmp(SQLState
, wxT("21S01")))
1853 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1854 if (!wxStrcmp(SQLState
, wxT("21S02")))
1855 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1856 if (!wxStrcmp(SQLState
, wxT("22001")))
1857 return(DB_ERR_STRING_RIGHT_TRUNC
);
1858 if (!wxStrcmp(SQLState
, wxT("22003")))
1859 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1860 if (!wxStrcmp(SQLState
, wxT("22005")))
1861 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1862 if (!wxStrcmp(SQLState
, wxT("22008")))
1863 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1864 if (!wxStrcmp(SQLState
, wxT("22012")))
1865 return(DB_ERR_DIVIDE_BY_ZERO
);
1866 if (!wxStrcmp(SQLState
, wxT("22026")))
1867 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1868 if (!wxStrcmp(SQLState
, wxT("23000")))
1869 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1870 if (!wxStrcmp(SQLState
, wxT("24000")))
1871 return(DB_ERR_INVALID_CURSOR_STATE
);
1872 if (!wxStrcmp(SQLState
, wxT("25000")))
1873 return(DB_ERR_INVALID_TRANS_STATE
);
1874 if (!wxStrcmp(SQLState
, wxT("28000")))
1875 return(DB_ERR_INVALID_AUTH_SPEC
);
1876 if (!wxStrcmp(SQLState
, wxT("34000")))
1877 return(DB_ERR_INVALID_CURSOR_NAME
);
1878 if (!wxStrcmp(SQLState
, wxT("37000")))
1879 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1880 if (!wxStrcmp(SQLState
, wxT("3C000")))
1881 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1882 if (!wxStrcmp(SQLState
, wxT("40001")))
1883 return(DB_ERR_SERIALIZATION_FAILURE
);
1884 if (!wxStrcmp(SQLState
, wxT("42000")))
1885 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1886 if (!wxStrcmp(SQLState
, wxT("70100")))
1887 return(DB_ERR_OPERATION_ABORTED
);
1888 if (!wxStrcmp(SQLState
, wxT("IM001")))
1889 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1890 if (!wxStrcmp(SQLState
, wxT("IM002")))
1891 return(DB_ERR_NO_DATA_SOURCE
);
1892 if (!wxStrcmp(SQLState
, wxT("IM003")))
1893 return(DB_ERR_DRIVER_LOAD_ERROR
);
1894 if (!wxStrcmp(SQLState
, wxT("IM004")))
1895 return(DB_ERR_SQLALLOCENV_FAILED
);
1896 if (!wxStrcmp(SQLState
, wxT("IM005")))
1897 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1898 if (!wxStrcmp(SQLState
, wxT("IM006")))
1899 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1900 if (!wxStrcmp(SQLState
, wxT("IM007")))
1901 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1902 if (!wxStrcmp(SQLState
, wxT("IM008")))
1903 return(DB_ERR_DIALOG_FAILED
);
1904 if (!wxStrcmp(SQLState
, wxT("IM009")))
1905 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1906 if (!wxStrcmp(SQLState
, wxT("IM010")))
1907 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1908 if (!wxStrcmp(SQLState
, wxT("IM011")))
1909 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1910 if (!wxStrcmp(SQLState
, wxT("IM012")))
1911 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1912 if (!wxStrcmp(SQLState
, wxT("IM013")))
1913 return(DB_ERR_TRACE_FILE_ERROR
);
1914 if (!wxStrcmp(SQLState
, wxT("S0001")))
1915 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1916 if (!wxStrcmp(SQLState
, wxT("S0002")))
1917 return(DB_ERR_TABLE_NOT_FOUND
);
1918 if (!wxStrcmp(SQLState
, wxT("S0011")))
1919 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1920 if (!wxStrcmp(SQLState
, wxT("S0012")))
1921 return(DB_ERR_INDEX_NOT_FOUND
);
1922 if (!wxStrcmp(SQLState
, wxT("S0021")))
1923 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1924 if (!wxStrcmp(SQLState
, wxT("S0022")))
1925 return(DB_ERR_COLUMN_NOT_FOUND
);
1926 if (!wxStrcmp(SQLState
, wxT("S0023")))
1927 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1928 if (!wxStrcmp(SQLState
, wxT("S1000")))
1929 return(DB_ERR_GENERAL_ERROR
);
1930 if (!wxStrcmp(SQLState
, wxT("S1001")))
1931 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1932 if (!wxStrcmp(SQLState
, wxT("S1002")))
1933 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1934 if (!wxStrcmp(SQLState
, wxT("S1003")))
1935 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1936 if (!wxStrcmp(SQLState
, wxT("S1004")))
1937 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1938 if (!wxStrcmp(SQLState
, wxT("S1008")))
1939 return(DB_ERR_OPERATION_CANCELLED
);
1940 if (!wxStrcmp(SQLState
, wxT("S1009")))
1941 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1942 if (!wxStrcmp(SQLState
, wxT("S1010")))
1943 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1944 if (!wxStrcmp(SQLState
, wxT("S1011")))
1945 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1946 if (!wxStrcmp(SQLState
, wxT("S1012")))
1947 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1948 if (!wxStrcmp(SQLState
, wxT("S1015")))
1949 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1950 if (!wxStrcmp(SQLState
, wxT("S1090")))
1951 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1952 if (!wxStrcmp(SQLState
, wxT("S1091")))
1953 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1954 if (!wxStrcmp(SQLState
, wxT("S1092")))
1955 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1956 if (!wxStrcmp(SQLState
, wxT("S1093")))
1957 return(DB_ERR_INVALID_PARAM_NO
);
1958 if (!wxStrcmp(SQLState
, wxT("S1094")))
1959 return(DB_ERR_INVALID_SCALE_VALUE
);
1960 if (!wxStrcmp(SQLState
, wxT("S1095")))
1961 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1962 if (!wxStrcmp(SQLState
, wxT("S1096")))
1963 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1964 if (!wxStrcmp(SQLState
, wxT("S1097")))
1965 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1966 if (!wxStrcmp(SQLState
, wxT("S1098")))
1967 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1968 if (!wxStrcmp(SQLState
, wxT("S1099")))
1969 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1970 if (!wxStrcmp(SQLState
, wxT("S1100")))
1971 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1972 if (!wxStrcmp(SQLState
, wxT("S1101")))
1973 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1974 if (!wxStrcmp(SQLState
, wxT("S1103")))
1975 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1976 if (!wxStrcmp(SQLState
, wxT("S1104")))
1977 return(DB_ERR_INVALID_PRECISION_VALUE
);
1978 if (!wxStrcmp(SQLState
, wxT("S1105")))
1979 return(DB_ERR_INVALID_PARAM_TYPE
);
1980 if (!wxStrcmp(SQLState
, wxT("S1106")))
1981 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1982 if (!wxStrcmp(SQLState
, wxT("S1107")))
1983 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1984 if (!wxStrcmp(SQLState
, wxT("S1108")))
1985 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1986 if (!wxStrcmp(SQLState
, wxT("S1109")))
1987 return(DB_ERR_INVALID_CURSOR_POSITION
);
1988 if (!wxStrcmp(SQLState
, wxT("S1110")))
1989 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1990 if (!wxStrcmp(SQLState
, wxT("S1111")))
1991 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1992 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1993 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1994 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1995 return(DB_ERR_TIMEOUT_EXPIRED
);
2000 } // wxDb::TranslateSqlState()
2003 /********** wxDb::Grant() **********/
2004 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2008 // Build the grant statement
2009 sqlStmt
= wxT("GRANT ");
2010 if (privileges
== DB_GRANT_ALL
)
2011 sqlStmt
+= wxT("ALL");
2015 if (privileges
& DB_GRANT_SELECT
)
2017 sqlStmt
+= wxT("SELECT");
2020 if (privileges
& DB_GRANT_INSERT
)
2023 sqlStmt
+= wxT(", ");
2024 sqlStmt
+= wxT("INSERT");
2026 if (privileges
& DB_GRANT_UPDATE
)
2029 sqlStmt
+= wxT(", ");
2030 sqlStmt
+= wxT("UPDATE");
2032 if (privileges
& DB_GRANT_DELETE
)
2035 sqlStmt
+= wxT(", ");
2036 sqlStmt
+= wxT("DELETE");
2040 sqlStmt
+= wxT(" ON ");
2041 sqlStmt
+= SQLTableName(tableName
);
2042 sqlStmt
+= wxT(" TO ");
2043 sqlStmt
+= userList
;
2045 #ifdef DBDEBUG_CONSOLE
2046 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2049 WriteSqlLog(sqlStmt
);
2051 return(ExecSql(sqlStmt
));
2056 /********** wxDb::CreateView() **********/
2057 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2058 const wxString
&pSqlStmt
, bool attemptDrop
)
2062 // Drop the view first
2063 if (attemptDrop
&& !DropView(viewName
))
2066 // Build the create view statement
2067 sqlStmt
= wxT("CREATE VIEW ");
2068 sqlStmt
+= viewName
;
2070 if (colList
.Length())
2072 sqlStmt
+= wxT(" (");
2074 sqlStmt
+= wxT(")");
2077 sqlStmt
+= wxT(" AS ");
2078 sqlStmt
+= pSqlStmt
;
2080 WriteSqlLog(sqlStmt
);
2082 #ifdef DBDEBUG_CONSOLE
2083 cout
<< sqlStmt
.c_str() << endl
;
2086 return(ExecSql(sqlStmt
));
2088 } // wxDb::CreateView()
2091 /********** wxDb::DropView() **********/
2092 bool wxDb::DropView(const wxString
&viewName
)
2095 * NOTE: This function returns true if the View does not exist, but
2096 * only for identified databases. Code will need to be added
2097 * below for any other databases when those databases are defined
2098 * to handle this situation consistently
2102 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2104 WriteSqlLog(sqlStmt
);
2106 #ifdef DBDEBUG_CONSOLE
2107 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2110 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2112 // Check for "Base table not found" error and ignore
2113 GetNextError(henv
, hdbc
, hstmt
);
2114 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2116 // Check for product specific error codes
2117 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2120 DispAllErrors(henv
, hdbc
, hstmt
);
2127 // Commit the transaction
2133 } // wxDb::DropView()
2136 /********** wxDb::ExecSql() **********/
2137 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2141 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2143 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2144 if (retcode
== SQL_SUCCESS
||
2145 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2151 DispAllErrors(henv
, hdbc
, hstmt
);
2155 } // wxDb::ExecSql()
2158 /********** wxDb::GetNext() **********/
2159 bool wxDb::GetNext(void)
2161 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2165 DispAllErrors(henv
, hdbc
, hstmt
);
2169 } // wxDb::GetNext()
2172 /********** wxDb::GetData() **********/
2173 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2176 wxASSERT(cbReturned
);
2178 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2182 DispAllErrors(henv
, hdbc
, hstmt
);
2186 } // wxDb::GetData()
2189 /********** wxDb::GetKeyFields() **********/
2190 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2192 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2193 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2195 // SQLSMALLINT iKeySeq;
2196 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2197 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2203 * -----------------------------------------------------------------------
2204 * -- 19991224 : mj10777 : Create ------
2205 * -- : Three things are done and stored here : ------
2206 * -- : 1) which Column(s) is/are Primary Key(s) ------
2207 * -- : 2) which tables use this Key as a Foreign Key ------
2208 * -- : 3) which columns are Foreign Key and the name ------
2209 * -- : of the Table where the Key is the Primary Key -----
2210 * -- : Called from GetColumns(const wxString &tableName, ------
2211 * -- int *numCols,const wxChar *userID ) ------
2212 * -----------------------------------------------------------------------
2215 /*---------------------------------------------------------------------*/
2216 /* Get the names of the columns in the primary key. */
2217 /*---------------------------------------------------------------------*/
2218 retcode
= SQLPrimaryKeys(hstmt
,
2219 NULL
, 0, /* Catalog name */
2220 NULL
, 0, /* Schema name */
2221 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2223 /*---------------------------------------------------------------------*/
2224 /* Fetch and display the result set. This will be a list of the */
2225 /* columns in the primary key of the tableName table. */
2226 /*---------------------------------------------------------------------*/
2227 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2229 retcode
= SQLFetch(hstmt
);
2230 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2232 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2233 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2235 for (i
=0;i
<noCols
;i
++) // Find the Column name
2236 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2237 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2240 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2242 /*---------------------------------------------------------------------*/
2243 /* Get all the foreign keys that refer to tableName primary key. */
2244 /*---------------------------------------------------------------------*/
2245 retcode
= SQLForeignKeys(hstmt
,
2246 NULL
, 0, /* Primary catalog */
2247 NULL
, 0, /* Primary schema */
2248 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2249 NULL
, 0, /* Foreign catalog */
2250 NULL
, 0, /* Foreign schema */
2251 NULL
, 0); /* Foreign table */
2253 /*---------------------------------------------------------------------*/
2254 /* Fetch and display the result set. This will be all of the foreign */
2255 /* keys in other tables that refer to the tableName primary key. */
2256 /*---------------------------------------------------------------------*/
2259 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2261 retcode
= SQLFetch(hstmt
);
2262 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2264 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2265 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2266 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2267 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2268 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2269 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2273 tempStr
.Trim(); // Get rid of any unneeded blanks
2274 if (!tempStr
.IsEmpty())
2276 for (i
=0; i
<noCols
; i
++)
2277 { // Find the Column name
2278 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2279 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2283 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2285 /*---------------------------------------------------------------------*/
2286 /* Get all the foreign keys in the tablename table. */
2287 /*---------------------------------------------------------------------*/
2288 retcode
= SQLForeignKeys(hstmt
,
2289 NULL
, 0, /* Primary catalog */
2290 NULL
, 0, /* Primary schema */
2291 NULL
, 0, /* Primary table */
2292 NULL
, 0, /* Foreign catalog */
2293 NULL
, 0, /* Foreign schema */
2294 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2296 /*---------------------------------------------------------------------*/
2297 /* Fetch and display the result set. This will be all of the */
2298 /* primary keys in other tables that are referred to by foreign */
2299 /* keys in the tableName table. */
2300 /*---------------------------------------------------------------------*/
2301 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2303 retcode
= SQLFetch(hstmt
);
2304 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2306 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2307 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2308 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2310 for (i
=0; i
<noCols
; i
++) // Find the Column name
2312 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2314 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2315 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2320 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2324 } // wxDb::GetKeyFields()
2328 /********** wxDb::GetColumns() **********/
2329 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2331 * 1) The last array element of the tableName[] argument must be zero (null).
2332 * This is how the end of the array is detected.
2333 * 2) This function returns an array of wxDbColInf structures. If no columns
2334 * were found, or an error occured, this pointer will be zero (null). THE
2335 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2336 * IS FINISHED WITH IT. i.e.
2338 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2341 * // Use the column inf
2343 * // Destroy the memory
2347 * userID is evaluated in the following manner:
2348 * userID == NULL ... UserID is ignored
2349 * userID == "" ... UserID set equal to 'this->uid'
2350 * userID != "" ... UserID set equal to 'userID'
2352 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2353 * by this function. This function should use its own wxDb instance
2354 * to avoid undesired unbinding of columns.
2359 wxDbColInf
*colInf
= 0;
2367 convertUserID(userID
,UserID
);
2369 // Pass 1 - Determine how many columns there are.
2370 // Pass 2 - Allocate the wxDbColInf array and fill in
2371 // the array with the column information.
2373 for (pass
= 1; pass
<= 2; pass
++)
2377 if (noCols
== 0) // Probably a bogus table name(s)
2379 // Allocate n wxDbColInf objects to hold the column information
2380 colInf
= new wxDbColInf
[noCols
+1];
2383 // Mark the end of the array
2384 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2385 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2386 colInf
[noCols
].sqlDataType
= 0;
2388 // Loop through each table name
2390 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2392 TableName
= tableName
[tbl
];
2393 // Oracle and Interbase table names are uppercase only, so force
2394 // the name to uppercase just in case programmer forgot to do this
2395 if ((Dbms() == dbmsORACLE
) ||
2396 (Dbms() == dbmsINTERBASE
))
2397 TableName
= TableName
.Upper();
2399 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2401 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2402 // use the call below that leaves out the user name
2403 if (!UserID
.IsEmpty() &&
2404 Dbms() != dbmsMY_SQL
&&
2405 Dbms() != dbmsACCESS
&&
2406 Dbms() != dbmsMS_SQL_SERVER
)
2408 retcode
= SQLColumns(hstmt
,
2409 NULL
, 0, // All qualifiers
2410 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2411 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2412 NULL
, 0); // All columns
2416 retcode
= SQLColumns(hstmt
,
2417 NULL
, 0, // All qualifiers
2419 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2420 NULL
, 0); // All columns
2422 if (retcode
!= SQL_SUCCESS
)
2423 { // Error occured, abort
2424 DispAllErrors(henv
, hdbc
, hstmt
);
2427 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2431 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2433 if (pass
== 1) // First pass, just add up the number of columns
2435 else // Pass 2; Fill in the array of structures
2437 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2439 // NOTE: Only the ODBC 1.x fields are retrieved
2440 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2441 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2442 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2443 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2444 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2445 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2446 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2447 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2448 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2449 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2450 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2451 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2453 // Determine the wxDb data type that is used to represent the native data type of this data source
2454 colInf
[colNo
].dbDataType
= 0;
2455 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2458 // IODBC does not return a correct columnSize, so we set
2459 // columnSize = bufferLength if no column size was returned
2460 // IODBC returns the columnSize in bufferLength.. (bug)
2461 if (colInf
[colNo
].columnSize
< 1)
2463 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2466 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2468 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2469 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2470 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2471 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2472 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2473 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2474 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2475 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2480 if (retcode
!= SQL_NO_DATA_FOUND
)
2481 { // Error occured, abort
2482 DispAllErrors(henv
, hdbc
, hstmt
);
2485 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2491 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2494 } // wxDb::GetColumns()
2497 /********** wxDb::GetColumns() **********/
2499 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2501 // Same as the above GetColumns() function except this one gets columns
2502 // only for a single table, and if 'numCols' is not NULL, the number of
2503 // columns stored in the returned wxDbColInf is set in '*numCols'
2505 // userID is evaluated in the following manner:
2506 // userID == NULL ... UserID is ignored
2507 // userID == "" ... UserID set equal to 'this->uid'
2508 // userID != "" ... UserID set equal to 'userID'
2510 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2511 // by this function. This function should use its own wxDb instance
2512 // to avoid undesired unbinding of columns.
2517 wxDbColInf
*colInf
= 0;
2525 convertUserID(userID
,UserID
);
2527 // Pass 1 - Determine how many columns there are.
2528 // Pass 2 - Allocate the wxDbColInf array and fill in
2529 // the array with the column information.
2531 for (pass
= 1; pass
<= 2; pass
++)
2535 if (noCols
== 0) // Probably a bogus table name(s)
2537 // Allocate n wxDbColInf objects to hold the column information
2538 colInf
= new wxDbColInf
[noCols
+1];
2541 // Mark the end of the array
2542 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2543 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2544 colInf
[noCols
].sqlDataType
= 0;
2547 TableName
= tableName
;
2548 // Oracle and Interbase table names are uppercase only, so force
2549 // the name to uppercase just in case programmer forgot to do this
2550 if ((Dbms() == dbmsORACLE
) ||
2551 (Dbms() == dbmsINTERBASE
))
2552 TableName
= TableName
.Upper();
2554 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2556 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2557 // use the call below that leaves out the user name
2558 if (!UserID
.IsEmpty() &&
2559 Dbms() != dbmsMY_SQL
&&
2560 Dbms() != dbmsACCESS
&&
2561 Dbms() != dbmsMS_SQL_SERVER
)
2563 retcode
= SQLColumns(hstmt
,
2564 NULL
, 0, // All qualifiers
2565 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2566 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2567 NULL
, 0); // All columns
2571 retcode
= SQLColumns(hstmt
,
2572 NULL
, 0, // All qualifiers
2574 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2575 NULL
, 0); // All columns
2577 if (retcode
!= SQL_SUCCESS
)
2578 { // Error occured, abort
2579 DispAllErrors(henv
, hdbc
, hstmt
);
2582 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2588 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2590 if (pass
== 1) // First pass, just add up the number of columns
2592 else // Pass 2; Fill in the array of structures
2594 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2596 // NOTE: Only the ODBC 1.x fields are retrieved
2597 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2598 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2599 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2600 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2601 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2602 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2603 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2604 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2605 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2606 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2607 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2608 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2609 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2610 // Start Values for Primary/Foriegn Key (=No)
2611 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2612 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2613 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2614 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2616 // BJO 20000428 : Virtuoso returns type names with upper cases!
2617 if (Dbms() == dbmsVIRTUOSO
)
2619 wxString s
= colInf
[colNo
].typeName
;
2621 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2624 // Determine the wxDb data type that is used to represent the native data type of this data source
2625 colInf
[colNo
].dbDataType
= 0;
2626 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2629 // IODBC does not return a correct columnSize, so we set
2630 // columnSize = bufferLength if no column size was returned
2631 // IODBC returns the columnSize in bufferLength.. (bug)
2632 if (colInf
[colNo
].columnSize
< 1)
2634 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2638 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2640 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2641 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2642 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2643 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2644 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2646 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2647 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2653 if (retcode
!= SQL_NO_DATA_FOUND
)
2654 { // Error occured, abort
2655 DispAllErrors(henv
, hdbc
, hstmt
);
2658 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2665 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2667 // Store Primary and Foriegn Keys
2668 GetKeyFields(tableName
,colInf
,noCols
);
2674 } // wxDb::GetColumns()
2677 #else // New GetColumns
2682 These are tentative new GetColumns members which should be more database
2683 independant and which always returns the columns in the order they were
2686 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2687 wxChar* userID)) calls the second implementation for each separate table
2688 before merging the results. This makes the code easier to maintain as
2689 only one member (the second) makes the real work
2690 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2691 wxChar *userID) is a little bit improved
2692 - It doesn't anymore rely on the type-name to find out which database-type
2694 - It ends by sorting the columns, so that they are returned in the same
2695 order they were created
2705 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2708 // The last array element of the tableName[] argument must be zero (null).
2709 // This is how the end of the array is detected.
2713 // How many tables ?
2715 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2717 // Create a table to maintain the columns for each separate table
2718 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2721 for (i
= 0 ; i
< tbl
; i
++)
2724 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2725 if (TableColumns
[i
].colInf
== NULL
)
2727 noCols
+= TableColumns
[i
].noCols
;
2730 // Now merge all the separate table infos
2731 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2733 // Mark the end of the array
2734 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2735 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2736 colInf
[noCols
].sqlDataType
= 0;
2741 for (i
= 0 ; i
< tbl
; i
++)
2743 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2745 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2749 delete [] TableColumns
;
2752 } // wxDb::GetColumns() -- NEW
2755 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2757 // Same as the above GetColumns() function except this one gets columns
2758 // only for a single table, and if 'numCols' is not NULL, the number of
2759 // columns stored in the returned wxDbColInf is set in '*numCols'
2761 // userID is evaluated in the following manner:
2762 // userID == NULL ... UserID is ignored
2763 // userID == "" ... UserID set equal to 'this->uid'
2764 // userID != "" ... UserID set equal to 'userID'
2766 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2767 // by this function. This function should use its own wxDb instance
2768 // to avoid undesired unbinding of columns.
2772 wxDbColInf
*colInf
= 0;
2780 convertUserID(userID
,UserID
);
2782 // Pass 1 - Determine how many columns there are.
2783 // Pass 2 - Allocate the wxDbColInf array and fill in
2784 // the array with the column information.
2786 for (pass
= 1; pass
<= 2; pass
++)
2790 if (noCols
== 0) // Probably a bogus table name(s)
2792 // Allocate n wxDbColInf objects to hold the column information
2793 colInf
= new wxDbColInf
[noCols
+1];
2796 // Mark the end of the array
2797 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2798 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2799 colInf
[noCols
].sqlDataType
= 0;
2802 TableName
= tableName
;
2803 // Oracle and Interbase table names are uppercase only, so force
2804 // the name to uppercase just in case programmer forgot to do this
2805 if ((Dbms() == dbmsORACLE
) ||
2806 (Dbms() == dbmsINTERBASE
))
2807 TableName
= TableName
.Upper();
2809 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2811 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2812 // use the call below that leaves out the user name
2813 if (!UserID
.IsEmpty() &&
2814 Dbms() != dbmsMY_SQL
&&
2815 Dbms() != dbmsACCESS
&&
2816 Dbms() != dbmsMS_SQL_SERVER
)
2818 retcode
= SQLColumns(hstmt
,
2819 NULL
, 0, // All qualifiers
2820 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2821 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2822 NULL
, 0); // All columns
2826 retcode
= SQLColumns(hstmt
,
2827 NULL
, 0, // All qualifiers
2829 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2830 NULL
, 0); // All columns
2832 if (retcode
!= SQL_SUCCESS
)
2833 { // Error occured, abort
2834 DispAllErrors(henv
, hdbc
, hstmt
);
2837 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2843 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2845 if (pass
== 1) // First pass, just add up the number of columns
2847 else // Pass 2; Fill in the array of structures
2849 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2851 // NOTE: Only the ODBC 1.x fields are retrieved
2852 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2853 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2854 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2855 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2856 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2857 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2858 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2859 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2860 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2861 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2862 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2863 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2864 // Start Values for Primary/Foriegn Key (=No)
2865 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2866 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2867 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2868 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2871 // IODBC does not return a correct columnSize, so we set
2872 // columnSize = bufferLength if no column size was returned
2873 // IODBC returns the columnSize in bufferLength.. (bug)
2874 if (colInf
[colNo
].columnSize
< 1)
2876 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2880 // Determine the wxDb data type that is used to represent the native data type of this data source
2881 colInf
[colNo
].dbDataType
= 0;
2882 // Get the intern datatype
2883 switch (colInf
[colNo
].sqlDataType
)
2887 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2893 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2900 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2903 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2906 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2911 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf
[colNo
].sqlDataType
);
2912 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2919 if (retcode
!= SQL_NO_DATA_FOUND
)
2920 { // Error occured, abort
2921 DispAllErrors(henv
, hdbc
, hstmt
);
2924 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2931 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2933 // Store Primary and Foreign Keys
2934 GetKeyFields(tableName
,colInf
,noCols
);
2936 ///////////////////////////////////////////////////////////////////////////
2937 // Now sort the the columns in order to make them appear in the right order
2938 ///////////////////////////////////////////////////////////////////////////
2940 // Build a generic SELECT statement which returns 0 rows
2943 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2946 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2948 DispAllErrors(henv
, hdbc
, hstmt
);
2952 // Get the number of result columns
2953 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2955 DispAllErrors(henv
, hdbc
, hstmt
);
2959 if (noCols
== 0) // Probably a bogus table name
2968 for (colNum
= 0; colNum
< noCols
; colNum
++)
2970 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2972 &Sword
, &Sdword
) != SQL_SUCCESS
)
2974 DispAllErrors(henv
, hdbc
, hstmt
);
2978 wxString Name1
= name
;
2979 Name1
= Name1
.Upper();
2981 // Where is this name in the array ?
2982 for (i
= colNum
; i
< noCols
; i
++)
2984 wxString Name2
= colInf
[i
].colName
;
2985 Name2
= Name2
.Upper();
2988 if (colNum
!= i
) // swap to sort
2990 wxDbColInf tmpColInf
= colInf
[colNum
];
2991 colInf
[colNum
] = colInf
[i
];
2992 colInf
[i
] = tmpColInf
;
2998 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3000 ///////////////////////////////////////////////////////////////////////////
3002 ///////////////////////////////////////////////////////////////////////////
3008 } // wxDb::GetColumns()
3011 #endif // #else OLD_GETCOLUMNS
3014 /********** wxDb::GetColumnCount() **********/
3015 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3017 * Returns a count of how many columns are in a table.
3018 * If an error occurs in computing the number of columns
3019 * this function will return a -1 for the count
3021 * userID is evaluated in the following manner:
3022 * userID == NULL ... UserID is ignored
3023 * userID == "" ... UserID set equal to 'this->uid'
3024 * userID != "" ... UserID set equal to 'userID'
3026 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3027 * by this function. This function should use its own wxDb instance
3028 * to avoid undesired unbinding of columns.
3038 convertUserID(userID
,UserID
);
3040 TableName
= tableName
;
3041 // Oracle and Interbase table names are uppercase only, so force
3042 // the name to uppercase just in case programmer forgot to do this
3043 if ((Dbms() == dbmsORACLE
) ||
3044 (Dbms() == dbmsINTERBASE
))
3045 TableName
= TableName
.Upper();
3047 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3049 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3050 // use the call below that leaves out the user name
3051 if (!UserID
.IsEmpty() &&
3052 Dbms() != dbmsMY_SQL
&&
3053 Dbms() != dbmsACCESS
&&
3054 Dbms() != dbmsMS_SQL_SERVER
)
3056 retcode
= SQLColumns(hstmt
,
3057 NULL
, 0, // All qualifiers
3058 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3059 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3060 NULL
, 0); // All columns
3064 retcode
= SQLColumns(hstmt
,
3065 NULL
, 0, // All qualifiers
3067 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3068 NULL
, 0); // All columns
3070 if (retcode
!= SQL_SUCCESS
)
3071 { // Error occured, abort
3072 DispAllErrors(henv
, hdbc
, hstmt
);
3073 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3077 // Count the columns
3078 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3081 if (retcode
!= SQL_NO_DATA_FOUND
)
3082 { // Error occured, abort
3083 DispAllErrors(henv
, hdbc
, hstmt
);
3084 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3088 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3091 } // wxDb::GetColumnCount()
3094 /********** wxDb::GetCatalog() *******/
3095 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3097 * ---------------------------------------------------------------------
3098 * -- 19991203 : mj10777 : Create ------
3099 * -- : Creates a wxDbInf with Tables / Cols Array ------
3100 * -- : uses SQLTables and fills pTableInf; ------
3101 * -- : pColInf is set to NULL and numCols to 0; ------
3102 * -- : returns pDbInf (wxDbInf) ------
3103 * -- - if unsuccesfull (pDbInf == NULL) ------
3104 * -- : pColInf can be filled with GetColumns(..); ------
3105 * -- : numCols can be filled with GetColumnCount(..); ------
3106 * ---------------------------------------------------------------------
3108 * userID is evaluated in the following manner:
3109 * userID == NULL ... UserID is ignored
3110 * userID == "" ... UserID set equal to 'this->uid'
3111 * userID != "" ... UserID set equal to 'userID'
3113 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3114 * by this function. This function should use its own wxDb instance
3115 * to avoid undesired unbinding of columns.
3118 int noTab
= 0; // Counter while filling table entries
3122 wxString tblNameSave
;
3125 convertUserID(userID
,UserID
);
3127 //-------------------------------------------------------------
3128 // Create the Database Array of catalog entries
3130 wxDbInf
*pDbInf
= new wxDbInf
;
3132 //-------------------------------------------------------------
3133 // Table Information
3134 // Pass 1 - Determine how many Tables there are.
3135 // Pass 2 - Create the Table array and fill it
3136 // - Create the Cols array = NULL
3137 //-------------------------------------------------------------
3139 for (pass
= 1; pass
<= 2; pass
++)
3141 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3142 tblNameSave
.Empty();
3144 if (!UserID
.IsEmpty() &&
3145 Dbms() != dbmsMY_SQL
&&
3146 Dbms() != dbmsACCESS
&&
3147 Dbms() != dbmsMS_SQL_SERVER
)
3149 retcode
= SQLTables(hstmt
,
3150 NULL
, 0, // All qualifiers
3151 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3152 NULL
, 0, // All tables
3153 NULL
, 0); // All columns
3157 retcode
= SQLTables(hstmt
,
3158 NULL
, 0, // All qualifiers
3159 NULL
, 0, // User specified
3160 NULL
, 0, // All tables
3161 NULL
, 0); // All columns
3164 if (retcode
!= SQL_SUCCESS
)
3166 DispAllErrors(henv
, hdbc
, hstmt
);
3168 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3172 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3174 if (pass
== 1) // First pass, just count the Tables
3176 if (pDbInf
->numTables
== 0)
3178 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3179 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3181 pDbInf
->numTables
++; // Counter for Tables
3183 if (pass
== 2) // Create and fill the Table entries
3185 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3186 { // no, then create the Array
3187 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3189 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3191 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3192 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3193 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3199 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3201 // Query how many columns are in each table
3202 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3204 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3209 } // wxDb::GetCatalog()
3212 /********** wxDb::Catalog() **********/
3213 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3215 * Creates the text file specified in 'filename' which will contain
3216 * a minimal data dictionary of all tables accessible by the user specified
3219 * userID is evaluated in the following manner:
3220 * userID == NULL ... UserID is ignored
3221 * userID == "" ... UserID set equal to 'this->uid'
3222 * userID != "" ... UserID set equal to 'userID'
3224 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3225 * by this function. This function should use its own wxDb instance
3226 * to avoid undesired unbinding of columns.
3229 wxASSERT(fileName
.Length());
3233 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3234 wxString tblNameSave
;
3235 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3237 wxChar typeName
[30+1];
3238 SDWORD precision
, length
;
3240 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3244 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3247 convertUserID(userID
,UserID
);
3249 if (!UserID
.IsEmpty() &&
3250 Dbms() != dbmsMY_SQL
&&
3251 Dbms() != dbmsACCESS
&&
3252 Dbms() != dbmsINTERBASE
&&
3253 Dbms() != dbmsMS_SQL_SERVER
)
3255 retcode
= SQLColumns(hstmt
,
3256 NULL
, 0, // All qualifiers
3257 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3258 NULL
, 0, // All tables
3259 NULL
, 0); // All columns
3263 retcode
= SQLColumns(hstmt
,
3264 NULL
, 0, // All qualifiers
3265 NULL
, 0, // User specified
3266 NULL
, 0, // All tables
3267 NULL
, 0); // All columns
3269 if (retcode
!= SQL_SUCCESS
)
3271 DispAllErrors(henv
, hdbc
, hstmt
);
3277 tblNameSave
.Empty();
3282 retcode
= SQLFetch(hstmt
);
3283 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3286 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3287 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3288 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3289 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3290 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3291 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3293 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3296 wxFputs(wxT("\n"), fp
);
3297 wxFputs(wxT("================================ "), fp
);
3298 wxFputs(wxT("================================ "), fp
);
3299 wxFputs(wxT("===================== "), fp
);
3300 wxFputs(wxT("========= "), fp
);
3301 wxFputs(wxT("=========\n"), fp
);
3302 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3303 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3304 wxFputs(outStr
.c_str(), fp
);
3305 wxFputs(wxT("================================ "), fp
);
3306 wxFputs(wxT("================================ "), fp
);
3307 wxFputs(wxT("===================== "), fp
);
3308 wxFputs(wxT("========= "), fp
);
3309 wxFputs(wxT("=========\n"), fp
);
3310 tblNameSave
= tblName
;
3313 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3314 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3315 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3317 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3324 if (retcode
!= SQL_NO_DATA_FOUND
)
3325 DispAllErrors(henv
, hdbc
, hstmt
);
3327 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3330 return(retcode
== SQL_NO_DATA_FOUND
);
3332 } // wxDb::Catalog()
3335 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3337 * Table name can refer to a table, view, alias or synonym. Returns true
3338 * if the object exists in the database. This function does not indicate
3339 * whether or not the user has privleges to query or perform other functions
3342 * userID is evaluated in the following manner:
3343 * userID == NULL ... UserID is ignored
3344 * userID == "" ... UserID set equal to 'this->uid'
3345 * userID != "" ... UserID set equal to 'userID'
3348 wxASSERT(tableName
.Length());
3352 if (Dbms() == dbmsDBASE
)
3355 if (tablePath
.Length())
3356 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3358 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3361 exists
= wxFileExists(dbName
);
3366 convertUserID(userID
,UserID
);
3368 TableName
= tableName
;
3369 // Oracle and Interbase table names are uppercase only, so force
3370 // the name to uppercase just in case programmer forgot to do this
3371 if ((Dbms() == dbmsORACLE
) ||
3372 (Dbms() == dbmsINTERBASE
))
3373 TableName
= TableName
.Upper();
3375 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3378 // Some databases cannot accept a user name when looking up table names,
3379 // so we use the call below that leaves out the user name
3380 if (!UserID
.IsEmpty() &&
3381 Dbms() != dbmsMY_SQL
&&
3382 Dbms() != dbmsACCESS
&&
3383 Dbms() != dbmsMS_SQL_SERVER
&&
3384 Dbms() != dbmsDB2
&&
3385 Dbms() != dbmsINTERBASE
&&
3386 Dbms() != dbmsPERVASIVE_SQL
)
3388 retcode
= SQLTables(hstmt
,
3389 NULL
, 0, // All qualifiers
3390 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3391 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3392 NULL
, 0); // All table types
3396 retcode
= SQLTables(hstmt
,
3397 NULL
, 0, // All qualifiers
3398 NULL
, 0, // All owners
3399 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3400 NULL
, 0); // All table types
3402 if (retcode
!= SQL_SUCCESS
)
3403 return(DispAllErrors(henv
, hdbc
, hstmt
));
3405 retcode
= SQLFetch(hstmt
);
3406 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3408 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3409 return(DispAllErrors(henv
, hdbc
, hstmt
));
3412 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3416 } // wxDb::TableExists()
3419 /********** wxDb::TablePrivileges() **********/
3420 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3421 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3423 wxASSERT(tableName
.Length());
3425 wxDbTablePrivilegeInfo result
;
3429 // We probably need to be able to dynamically set this based on
3430 // the driver type, and state.
3431 wxChar curRole
[]=wxT("public");
3435 wxString UserID
,Schema
;
3436 convertUserID(userID
,UserID
);
3437 convertUserID(schema
,Schema
);
3439 TableName
= tableName
;
3440 // Oracle and Interbase table names are uppercase only, so force
3441 // the name to uppercase just in case programmer forgot to do this
3442 if ((Dbms() == dbmsORACLE
) ||
3443 (Dbms() == dbmsINTERBASE
))
3444 TableName
= TableName
.Upper();
3446 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3448 // Some databases cannot accept a user name when looking up table names,
3449 // so we use the call below that leaves out the user name
3450 if (!Schema
.IsEmpty() &&
3451 Dbms() != dbmsMY_SQL
&&
3452 Dbms() != dbmsACCESS
&&
3453 Dbms() != dbmsMS_SQL_SERVER
)
3455 retcode
= SQLTablePrivileges(hstmt
,
3457 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3458 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3462 retcode
= SQLTablePrivileges(hstmt
,
3465 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3468 #ifdef DBDEBUG_CONSOLE
3469 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3472 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3473 return(DispAllErrors(henv
, hdbc
, hstmt
));
3475 bool failed
= false;
3476 retcode
= SQLFetch(hstmt
);
3477 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3479 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3482 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3485 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3488 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3491 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3494 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3497 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3502 return(DispAllErrors(henv
, hdbc
, hstmt
));
3504 #ifdef DBDEBUG_CONSOLE
3505 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3506 result
.privilege
,result
.tableOwner
,result
.tableName
,
3507 result
.grantor
, result
.grantee
);
3510 if (UserID
.IsSameAs(result
.tableOwner
,false))
3512 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3516 if (UserID
.IsSameAs(result
.grantee
,false) &&
3517 !wxStrcmp(result
.privilege
,priv
))
3519 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3523 if (!wxStrcmp(result
.grantee
,curRole
) &&
3524 !wxStrcmp(result
.privilege
,priv
))
3526 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3530 retcode
= SQLFetch(hstmt
);
3533 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3536 } // wxDb::TablePrivileges
3539 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3543 if (Dbms() == dbmsACCESS
)
3544 TableName
= _T("\"");
3545 TableName
+= tableName
;
3546 if (Dbms() == dbmsACCESS
)
3547 TableName
+= _T("\"");
3550 } // wxDb::SQLTableName()
3553 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3557 if (Dbms() == dbmsACCESS
)
3560 if (Dbms() == dbmsACCESS
)
3561 ColName
+= _T("\"");
3564 } // wxDb::SQLColumnName()
3567 /********** wxDb::SetSqlLogging() **********/
3568 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3570 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3571 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3573 if (state
== sqlLogON
)
3577 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3578 if (fpSqlLog
== NULL
)
3586 if (fclose(fpSqlLog
))
3592 sqlLogState
= state
;
3595 } // wxDb::SetSqlLogging()
3598 /********** wxDb::WriteSqlLog() **********/
3599 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3601 wxASSERT(logMsg
.Length());
3603 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3606 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3608 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3610 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3615 } // wxDb::WriteSqlLog()
3618 /********** wxDb::Dbms() **********/
3619 wxDBMS
wxDb::Dbms(void)
3621 * Be aware that not all database engines use the exact same syntax, and not
3622 * every ODBC compliant database is compliant to the same level of compliancy.
3623 * Some manufacturers support the minimum Level 1 compliancy, and others up
3624 * through Level 3. Others support subsets of features for levels above 1.
3626 * If you find an inconsistency between the wxDb class and a specific database
3627 * engine, and an identifier to this section, and special handle the database in
3628 * the area where behavior is non-conforming with the other databases.
3631 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3632 * ---------------------------------------------------
3635 * - Currently the only database supported by the class to support VIEWS
3638 * - Does not support the SQL_TIMESTAMP structure
3639 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3640 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3641 * is true. The user must create ALL indexes from their program.
3642 * - Table names can only be 8 characters long
3643 * - Column names can only be 10 characters long
3646 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3647 * after every table name involved in the query/join if that tables matching record(s)
3649 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3651 * SYBASE (Enterprise)
3652 * - If a column is part of the Primary Key, the column cannot be NULL
3653 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3656 * - If a column is part of the Primary Key, the column cannot be NULL
3657 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3658 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3659 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3660 * column definition if it is not defined correctly, but it is experimental
3661 * - Does not support sub-queries in SQL statements
3664 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3665 * - Does not support sub-queries in SQL statements
3668 * - Primary keys must be declared as NOT NULL
3669 * - Table and index names must not be longer than 13 characters in length (technically
3670 * table names can be up to 18 characters, but the primary index is created using the
3671 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3676 * - Columns that are part of primary keys must be defined as being NOT NULL
3677 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3678 * column definition if it is not defined correctly, but it is experimental
3681 // Should only need to do this once for each new database connection
3682 // so return the value we already determined it to be to save time
3683 // and lots of string comparisons
3684 if (dbmsType
!= dbmsUNIDENTIFIED
)
3687 wxChar baseName
[25+1];
3688 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3691 // RGG 20001025 : add support for Interbase
3692 // GT : Integrated to base classes on 20001121
3693 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3694 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3696 // BJO 20000428 : add support for Virtuoso
3697 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3698 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3700 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3701 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3703 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3704 // connected through an OpenLink driver.
3705 // Is it also returned by Sybase Adapatitve server?
3706 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3707 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3709 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3710 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3711 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3713 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3716 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3717 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3720 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3721 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3724 if (!wxStricmp(baseName
,wxT("Pervasive")))
3725 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3728 if (!wxStricmp(baseName
,wxT("Informix")))
3729 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3732 if (!wxStricmp(baseName
,wxT("Oracle")))
3733 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3734 if (!wxStricmp(baseName
,wxT("ACCESS")))
3735 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3736 if (!wxStricmp(baseName
,wxT("Sybase")))
3737 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3740 if (!wxStricmp(baseName
,wxT("DBASE")))
3741 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3742 if (!wxStricmp(baseName
,wxT("xBase")))
3743 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3744 if (!wxStricmp(baseName
,wxT("MySQL")))
3745 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3748 if (!wxStricmp(baseName
,wxT("DB2")))
3749 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3751 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3756 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3757 int dataType
, ULONG columnLength
,
3758 const wxString
&optionalParam
)
3760 wxASSERT(tableName
.Length());
3761 wxASSERT(columnName
.Length());
3762 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3763 dataType
!= DB_DATA_TYPE_VARCHAR
);
3765 // Must specify a columnLength if modifying a VARCHAR type column
3766 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3769 wxString dataTypeName
;
3771 wxString alterSlashModify
;
3775 case DB_DATA_TYPE_VARCHAR
:
3776 dataTypeName
= typeInfVarchar
.TypeName
;
3778 case DB_DATA_TYPE_INTEGER
:
3779 dataTypeName
= typeInfInteger
.TypeName
;
3781 case DB_DATA_TYPE_FLOAT
:
3782 dataTypeName
= typeInfFloat
.TypeName
;
3784 case DB_DATA_TYPE_DATE
:
3785 dataTypeName
= typeInfDate
.TypeName
;
3787 case DB_DATA_TYPE_BLOB
:
3788 dataTypeName
= typeInfBlob
.TypeName
;
3794 // Set the modify or alter syntax depending on the type of database connected to
3798 alterSlashModify
= _T("MODIFY");
3800 case dbmsMS_SQL_SERVER
:
3801 alterSlashModify
= _T("ALTER COLUMN");
3803 case dbmsUNIDENTIFIED
:
3805 case dbmsSYBASE_ASA
:
3806 case dbmsSYBASE_ASE
:
3811 case dbmsXBASE_SEQUITER
:
3813 alterSlashModify
= _T("MODIFY");
3817 // create the SQL statement
3818 if ( Dbms() == dbmsMY_SQL
)
3820 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3821 columnName
.c_str(), dataTypeName
.c_str());
3825 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3826 columnName
.c_str(), dataTypeName
.c_str());
3829 // For varchars only, append the size of the column
3830 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3831 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3834 s
.Printf(wxT("(%lu)"), columnLength
);
3838 // for passing things like "NOT NULL"
3839 if (optionalParam
.Length())
3841 sqlStmt
+= wxT(" ");
3842 sqlStmt
+= optionalParam
;
3845 return ExecSql(sqlStmt
);
3847 } // wxDb::ModifyColumn()
3850 /********** wxDbGetConnection() **********/
3851 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3855 // Used to keep a pointer to a DB connection that matches the requested
3856 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3857 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3858 // rather than having to re-query the datasource to get all the values
3859 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3860 wxDb
*matchingDbConnection
= NULL
;
3862 // Scan the linked list searching for an available database connection
3863 // that's already been opened but is currently not in use.
3864 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3866 // The database connection must be for the same datasource
3867 // name and must currently not be in use.
3869 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
3871 if (pDbConfig
->UseConnectionStr())
3873 if (pList
->PtrDb
->OpenedWithConnectionString() &&
3874 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
3876 // Found a free connection
3877 pList
->Free
= false;
3878 return(pList
->PtrDb
);
3883 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
3884 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
3886 // Found a free connection
3887 pList
->Free
= false;
3888 return(pList
->PtrDb
);
3893 if (pDbConfig
->UseConnectionStr())
3895 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
3896 matchingDbConnection
= pList
->PtrDb
;
3900 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3901 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3902 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3903 matchingDbConnection
= pList
->PtrDb
;
3907 // No available connections. A new connection must be made and
3908 // appended to the end of the linked list.
3911 // Find the end of the list
3912 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3913 // Append a new list item
3914 pList
->PtrNext
= new wxDbList
;
3915 pList
->PtrNext
->PtrPrev
= pList
;
3916 pList
= pList
->PtrNext
;
3920 // Create the first node on the list
3921 pList
= PtrBegDbList
= new wxDbList
;
3925 // Initialize new node in the linked list
3927 pList
->Free
= false;
3928 pList
->Dsn
= pDbConfig
->GetDsn();
3929 pList
->Uid
= pDbConfig
->GetUserID();
3930 pList
->AuthStr
= pDbConfig
->GetPassword();
3931 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
3933 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3937 if (!matchingDbConnection
)
3939 if (pDbConfig
->UseConnectionStr())
3941 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
3945 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3949 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3951 // Connect to the datasource
3954 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
3955 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, true);
3956 return(pList
->PtrDb
);
3958 else // Unable to connect, destroy list item
3961 pList
->PtrPrev
->PtrNext
= 0;
3963 PtrBegDbList
= 0; // Empty list again
3965 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3966 pList
->PtrDb
->Close(); // Close the wxDb object
3967 delete pList
->PtrDb
; // Deletes the wxDb object
3968 delete pList
; // Deletes the linked list object
3972 } // wxDbGetConnection()
3975 /********** wxDbFreeConnection() **********/
3976 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3980 // Scan the linked list searching for the database connection
3981 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3983 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3984 return (pList
->Free
= true);
3987 // Never found the database object, return failure
3990 } // wxDbFreeConnection()
3993 /********** wxDbCloseConnections() **********/
3994 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3996 wxDbList
*pList
, *pNext
;
3998 // Traverse the linked list closing database connections and freeing memory as I go.
3999 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4001 pNext
= pList
->PtrNext
; // Save the pointer to next
4002 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4003 pList
->PtrDb
->Close(); // Close the wxDb object
4004 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
4005 delete pList
->PtrDb
; // Deletes the wxDb object
4006 delete pList
; // Deletes the linked list object
4009 // Mark the list as empty
4012 } // wxDbCloseConnections()
4015 /********** wxDbConnectionsInUse() **********/
4016 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4021 // Scan the linked list counting db connections that are currently in use
4022 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4024 if (pList
->Free
== false)
4030 } // wxDbConnectionsInUse()
4034 /********** wxDbLogExtendedErrorMsg() **********/
4035 // DEBUG ONLY function
4036 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4038 const wxChar
*ErrFile
,
4041 static wxString msg
;
4046 if (ErrFile
|| ErrLine
)
4048 msg
+= wxT("File: ");
4050 msg
+= wxT(" Line: ");
4051 tStr
.Printf(wxT("%d"),ErrLine
);
4052 msg
+= tStr
.c_str();
4056 msg
.Append (wxT("\nODBC errors:\n"));
4059 // Display errors for this connection
4061 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4063 if (pDb
->errorList
[i
])
4065 msg
.Append(pDb
->errorList
[i
]);
4066 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
4067 msg
.Append(wxT("\n"));
4068 // Clear the errmsg buffer so the next error will not
4069 // end up showing the previous error that have occurred
4070 wxStrcpy(pDb
->errorList
[i
],wxT(""));
4075 wxLogDebug(msg
.c_str());
4078 } // wxDbLogExtendedErrorMsg()
4081 /********** wxDbSqlLog() **********/
4082 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4084 bool append
= false;
4087 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4089 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4094 SQLLOGstate
= state
;
4095 SQLLOGfn
= filename
;
4103 /********** wxDbCreateDataSource() **********/
4104 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4105 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4107 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4108 * Very rudimentary creation of an ODBC data source.
4110 * ODBC driver must be ODBC 3.0 compliant to use this function
4115 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4121 dsnLocation
= ODBC_ADD_SYS_DSN
;
4123 dsnLocation
= ODBC_ADD_DSN
;
4125 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4126 // so that is why I used it, as wxString does not deal well with
4127 // embedded nulls in strings
4128 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4130 // Replace the separator from above with the '\0' seperator needed
4131 // by the SQLConfigDataSource() function
4135 k
= setupStr
.Find((wxChar
)2,true);
4136 if (k
!= wxNOT_FOUND
)
4137 setupStr
[(UINT
)k
] = wxT('\0');
4139 while (k
!= wxNOT_FOUND
);
4141 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4142 driverName
, setupStr
.c_str());
4144 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4146 // check for errors caused by ConfigDSN based functions
4149 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4150 errMsg
[0] = wxT('\0');
4152 // This function is only supported in ODBC drivers v3.0 compliant and above
4153 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4156 #ifdef DBDEBUG_CONSOLE
4157 // When run in console mode, use standard out to display errors.
4158 cout
<< errMsg
<< endl
;
4159 cout
<< wxT("Press any key to continue...") << endl
;
4161 #endif // DBDEBUG_CONSOLE
4164 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4165 #endif // __WXDEBUG__
4171 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4172 // necessary to use this function, so this function is not supported
4174 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4177 #endif // __VISUALC__
4181 } // wxDbCreateDataSource()
4185 /********** wxDbGetDataSource() **********/
4186 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4187 SWORD DsDescMax
, UWORD direction
)
4189 * Dsn and DsDesc will contain the data source name and data source
4190 * description upon return
4195 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4196 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4201 } // wxDbGetDataSource()
4204 // Change this to 0 to remove use of all deprecated functions
4205 #if wxODBC_BACKWARD_COMPATABILITY
4206 /********************************************************************
4207 ********************************************************************
4209 * The following functions are all DEPRECATED and are included for
4210 * backward compatability reasons only
4212 ********************************************************************
4213 ********************************************************************/
4214 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4216 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4218 /***** DEPRECATED: use wxGetDataSource() *****/
4219 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4222 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4224 /***** DEPRECATED: use wxDbGetConnection() *****/
4225 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4227 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4229 /***** DEPRECATED: use wxDbFreeConnection() *****/
4230 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4232 return wxDbFreeConnection(pDb
);
4234 /***** DEPRECATED: use wxDbCloseConnections() *****/
4235 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4237 wxDbCloseConnections();
4239 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4240 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4242 return wxDbConnectionsInUse();