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, plus:
22 // Notice: This class library and its intellectual design are free of charge for use,
23 // modification, enhancement, debugging under the following conditions:
24 // 1) These classes may only be used as part of the implementation of a
25 // wxWindows-based application
26 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
27 // user groups free of all charges for use with the wxWindows library.
28 // 3) These classes may not be distributed as part of any other class library,
29 // DLL, text (written or electronic), other than a complete distribution of
30 // the wxWindows GUI development toolkit.
31 ///////////////////////////////////////////////////////////////////////////////
38 #pragma implementation "db.h"
41 #include "wx/wxprec.h"
47 #ifdef DBDEBUG_CONSOLE
48 #include "wx/ioswrap.h"
52 #include "wx/string.h"
53 #include "wx/object.h"
56 #include "wx/msgdlg.h"
59 #include "wx/filefn.h"
60 #include "wx/wxchar.h"
72 WXDLLEXPORT_DATA(wxDbList
*) PtrBegDbList
= 0;
75 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
76 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
79 extern wxList TablesInUse
;
82 // SQL Log defaults to be used by GetDbConnection
83 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
85 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
87 // The wxDb::errorList is copied to this variable when the wxDb object
88 // is closed. This way, the error list is still available after the
89 // database object is closed. This is necessary if the database
90 // connection fails so the calling application can show the operator
91 // why the connection failed. Note: as each wxDb object is closed, it
92 // will overwrite the errors of the previously destroyed wxDb object in
93 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
95 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
98 // This type defines the return row-struct form
99 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
102 wxChar tableQual
[128+1];
103 wxChar tableOwner
[128+1];
104 wxChar tableName
[128+1];
105 wxChar grantor
[128+1];
106 wxChar grantee
[128+1];
107 wxChar privilege
[128+1];
108 wxChar grantable
[3+1];
109 } wxDbTablePrivilegeInfo
;
112 /********** wxDbConnectInf Constructor - form 1 **********/
113 wxDbConnectInf::wxDbConnectInf()
116 freeHenvOnDestroy
= false;
122 /********** wxDbConnectInf Constructor - form 2 **********/
123 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
124 const wxString
&password
, const wxString
&defaultDir
,
125 const wxString
&fileType
, const wxString
&description
)
128 freeHenvOnDestroy
= false;
139 SetPassword(password
);
140 SetDescription(description
);
141 SetFileType(fileType
);
142 SetDefaultDir(defaultDir
);
143 } // wxDbConnectInf Constructor
146 wxDbConnectInf::~wxDbConnectInf()
148 if (freeHenvOnDestroy
)
152 } // wxDbConnectInf Destructor
156 /********** wxDbConnectInf::Initialize() **********/
157 bool wxDbConnectInf::Initialize()
159 freeHenvOnDestroy
= false;
161 if (freeHenvOnDestroy
&& Henv
)
173 } // wxDbConnectInf::Initialize()
176 /********** wxDbConnectInf::AllocHenv() **********/
177 bool wxDbConnectInf::AllocHenv()
179 // This is here to help trap if you are getting a new henv
180 // without releasing an existing henv
183 // Initialize the ODBC Environment for Database Operations
184 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
186 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
190 freeHenvOnDestroy
= true;
193 } // wxDbConnectInf::AllocHenv()
196 void wxDbConnectInf::FreeHenv()
204 freeHenvOnDestroy
= false;
206 } // wxDbConnectInf::FreeHenv()
209 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
211 wxASSERT(dsn
.Length() < sizeof(Dsn
));
214 } // wxDbConnectInf::SetDsn()
217 void wxDbConnectInf::SetUserID(const wxString
&uid
)
219 wxASSERT(uid
.Length() < sizeof(Uid
));
221 } // wxDbConnectInf::SetUserID()
224 void wxDbConnectInf::SetPassword(const wxString
&password
)
226 wxASSERT(password
.Length() < sizeof(AuthStr
));
228 wxStrcpy(AuthStr
,password
);
229 } // wxDbConnectInf::SetPassword()
233 /********** wxDbColFor Constructor **********/
234 wxDbColFor::wxDbColFor()
237 } // wxDbColFor::wxDbColFor()
240 wxDbColFor::~wxDbColFor()
242 } // wxDbColFor::~wxDbColFor()
245 /********** wxDbColFor::Initialize() **********/
246 void wxDbColFor::Initialize()
256 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
259 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
260 } // wxDbColFor::Initialize()
263 /********** wxDbColFor::Format() **********/
264 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
265 short columnSize
, short decimalDigits
)
267 // ----------------------------------------------------------------------------------------
268 // -- 19991224 : mj10777 : Create
269 // There is still a lot of work to do here, but it is a start
270 // It handles all the basic data-types that I have run into up to now
271 // The main work will have be with Dates and float Formatting
272 // (US 1,000.00 ; EU 1.000,00)
273 // There are wxWindow plans for locale support and the new wxDateTime. If
274 // they define some constants (wxEUROPEAN) that can be gloably used,
275 // they should be used here.
276 // ----------------------------------------------------------------------------------------
277 // There should also be a function to scan in a string to fill the variable
278 // ----------------------------------------------------------------------------------------
280 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
281 i_dbDataType
= dbDataType
;
282 i_sqlDataType
= sqlDataType
;
283 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
285 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
287 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
288 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
289 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
290 i_dbDataType
= DB_DATA_TYPE_DATE
;
291 if (i_sqlDataType
== SQL_C_BIT
)
292 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
293 if (i_sqlDataType
== SQL_NUMERIC
)
294 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
295 if (i_sqlDataType
== SQL_REAL
)
296 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
297 if (i_sqlDataType
== SQL_C_BINARY
)
298 i_dbDataType
= DB_DATA_TYPE_BLOB
;
301 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
303 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
306 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
308 case DB_DATA_TYPE_VARCHAR
:
311 case DB_DATA_TYPE_INTEGER
:
314 case DB_DATA_TYPE_FLOAT
:
315 if (decimalDigits
== 0)
318 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
319 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
321 case DB_DATA_TYPE_DATE
:
322 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
324 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
326 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
328 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
330 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
332 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
334 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
336 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
338 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
340 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
343 case DB_DATA_TYPE_BLOB
:
344 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
347 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
351 } // wxDbColFor::Format()
355 /********** wxDbColInf Constructor **********/
356 wxDbColInf::wxDbColInf()
359 } // wxDbColInf::wxDbColInf()
362 /********** wxDbColInf Destructor ********/
363 wxDbColInf::~wxDbColInf()
368 } // wxDbColInf::~wxDbColInf()
371 bool wxDbColInf::Initialize()
393 } // wxDbColInf::Initialize()
396 /********** wxDbTableInf Constructor ********/
397 wxDbTableInf::wxDbTableInf()
400 } // wxDbTableInf::wxDbTableInf()
403 /********** wxDbTableInf Constructor ********/
404 wxDbTableInf::~wxDbTableInf()
409 } // wxDbTableInf::~wxDbTableInf()
412 bool wxDbTableInf::Initialize()
421 } // wxDbTableInf::Initialize()
424 /********** wxDbInf Constructor *************/
428 } // wxDbInf::wxDbInf()
431 /********** wxDbInf Destructor *************/
437 } // wxDbInf::~wxDbInf()
440 /********** wxDbInf::Initialize() *************/
441 bool wxDbInf::Initialize()
449 } // wxDbInf::Initialize()
452 /********** wxDb Constructor **********/
453 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
455 // Copy the HENV into the db class
457 fwdOnlyCursors
= FwdOnlyCursors
;
463 /********** wxDb Destructor **********/
466 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
476 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
477 /********** wxDb::initialize() **********/
478 void wxDb::initialize()
480 * Private member function that sets all wxDb member variables to
481 * known values at creation of the wxDb
486 fpSqlLog
= 0; // Sql Log file pointer
487 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
489 dbmsType
= dbmsUNIDENTIFIED
;
491 wxStrcpy(sqlState
,wxEmptyString
);
492 wxStrcpy(errorMsg
,wxEmptyString
);
493 nativeError
= cbErrorMsg
= 0;
494 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
495 wxStrcpy(errorList
[i
], wxEmptyString
);
497 // Init typeInf structures
498 typeInfVarchar
.TypeName
.Empty();
499 typeInfVarchar
.FsqlType
= 0;
500 typeInfVarchar
.Precision
= 0;
501 typeInfVarchar
.CaseSensitive
= 0;
502 typeInfVarchar
.MaximumScale
= 0;
504 typeInfInteger
.TypeName
.Empty();
505 typeInfInteger
.FsqlType
= 0;
506 typeInfInteger
.Precision
= 0;
507 typeInfInteger
.CaseSensitive
= 0;
508 typeInfInteger
.MaximumScale
= 0;
510 typeInfFloat
.TypeName
.Empty();
511 typeInfFloat
.FsqlType
= 0;
512 typeInfFloat
.Precision
= 0;
513 typeInfFloat
.CaseSensitive
= 0;
514 typeInfFloat
.MaximumScale
= 0;
516 typeInfDate
.TypeName
.Empty();
517 typeInfDate
.FsqlType
= 0;
518 typeInfDate
.Precision
= 0;
519 typeInfDate
.CaseSensitive
= 0;
520 typeInfDate
.MaximumScale
= 0;
522 typeInfBlob
.TypeName
.Empty();
523 typeInfBlob
.FsqlType
= 0;
524 typeInfBlob
.Precision
= 0;
525 typeInfBlob
.CaseSensitive
= 0;
526 typeInfBlob
.MaximumScale
= 0;
528 // Error reporting is turned OFF by default
531 // Allocate a data source connection handle
532 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
535 // Initialize the db status flag
538 // Mark database as not open as of yet
541 } // wxDb::initialize()
544 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
546 // NOTE: Return value from this function MUST be copied
547 // immediately, as the value is not good after
548 // this function has left scope.
550 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
554 if (!wxStrlen(userID
))
562 // dBase does not use user names, and some drivers fail if you try to pass one
563 if (Dbms() == dbmsDBASE
)
566 // Oracle user names may only be in uppercase, so force
567 // the name to uppercase
568 if (Dbms() == dbmsORACLE
)
569 UserID
= UserID
.Upper();
571 return UserID
.c_str();
572 } // wxDb::convertUserID()
575 /********** wxDb::Open() **********/
576 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
)
578 wxASSERT(Dsn
.Length());
585 if (!FwdOnlyCursors())
587 // Specify that the ODBC cursor library be used, if needed. This must be
588 // specified before the connection is made.
589 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
591 #ifdef DBDEBUG_CONSOLE
592 if (retcode
== SQL_SUCCESS
)
593 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
595 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
599 // Connect to the data source
600 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
601 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
602 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
605 if (retcode == SQL_SUCCESS_WITH_INFO)
606 DispAllErrors(henv, hdbc);
607 else if (retcode != SQL_SUCCESS)
608 return(DispAllErrors(henv, hdbc));
610 if (retcode == SQL_ERROR)
611 return(DispAllErrors(henv, hdbc));
613 if ((retcode
!= SQL_SUCCESS
) &&
614 (retcode
!= SQL_SUCCESS_WITH_INFO
))
615 return(DispAllErrors(henv
, hdbc
));
618 If using Intersolv branded ODBC drivers, this is the place where you would substitute
619 your branded driver license information
621 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
622 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
625 // Mark database as open
628 // Allocate a statement handle for the database connection
629 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
630 return(DispAllErrors(henv
, hdbc
));
632 // Set Connection Options
633 if (!setConnectionOptions())
636 // Query the data source for inf. about itself
640 // Query the data source regarding data type information
643 // The way it was determined which SQL data types to use was by calling SQLGetInfo
644 // for all of the possible SQL data types to see which ones were supported. If
645 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
646 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
647 // types I've selected below will not alway's be what we want. These are just
648 // what happened to work against an Oracle 7/Intersolv combination. The following is
649 // a complete list of the results I got back against the Oracle 7 database:
651 // SQL_BIGINT SQL_NO_DATA_FOUND
652 // SQL_BINARY SQL_NO_DATA_FOUND
653 // SQL_BIT SQL_NO_DATA_FOUND
654 // SQL_CHAR type name = 'CHAR', Precision = 255
655 // SQL_DATE SQL_NO_DATA_FOUND
656 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
657 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
658 // SQL_FLOAT SQL_NO_DATA_FOUND
659 // SQL_INTEGER SQL_NO_DATA_FOUND
660 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
661 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
662 // SQL_NUMERIC SQL_NO_DATA_FOUND
663 // SQL_REAL SQL_NO_DATA_FOUND
664 // SQL_SMALLINT SQL_NO_DATA_FOUND
665 // SQL_TIME SQL_NO_DATA_FOUND
666 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
667 // SQL_VARBINARY type name = 'RAW', Precision = 255
668 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
669 // =====================================================================
670 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
672 // SQL_VARCHAR type name = 'TEXT', Precision = 255
673 // SQL_TIMESTAMP type name = 'DATETIME'
674 // SQL_DECIMAL SQL_NO_DATA_FOUND
675 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
676 // SQL_FLOAT SQL_NO_DATA_FOUND
677 // SQL_REAL type name = 'SINGLE', Precision = 7
678 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
679 // SQL_INTEGER type name = 'LONG', Precision = 10
681 // VARCHAR = Variable length character string
682 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
683 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
686 typeInfVarchar
.FsqlType
= SQL_CHAR
;
688 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
691 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
692 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
693 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
694 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
695 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
698 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
700 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
702 typeInfFloat
.FsqlType
= SQL_FLOAT
;
704 typeInfFloat
.FsqlType
= SQL_REAL
;
706 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
709 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
711 // If SQL_INTEGER is not supported, use the floating point
712 // data type to store integers as well as floats
713 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
716 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
719 typeInfInteger
.FsqlType
= SQL_INTEGER
;
722 if (Dbms() != dbmsDBASE
)
724 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
727 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
731 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
734 typeInfDate
.FsqlType
= SQL_DATE
;
737 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
739 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
742 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
745 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
747 //typeInfBlob.TypeName = "BLOB";
749 #ifdef DBDEBUG_CONSOLE
750 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
751 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
752 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
753 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
754 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
758 // Completed Successfully
764 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
766 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
767 dbConnectInf
->GetPassword());
771 bool wxDb::Open(wxDb
*copyDb
)
773 dsn
= copyDb
->GetDatasourceName();
774 uid
= copyDb
->GetUsername();
775 authStr
= copyDb
->GetPassword();
779 if (!FwdOnlyCursors())
781 // Specify that the ODBC cursor library be used, if needed. This must be
782 // specified before the connection is made.
783 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
785 #ifdef DBDEBUG_CONSOLE
786 if (retcode
== SQL_SUCCESS
)
787 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
789 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
793 // Connect to the data source
794 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
795 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
796 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
798 if (retcode
== SQL_ERROR
)
799 return(DispAllErrors(henv
, hdbc
));
802 If using Intersolv branded ODBC drivers, this is the place where you would substitute
803 your branded driver license information
805 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
806 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
809 // Mark database as open
812 // Allocate a statement handle for the database connection
813 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
814 return(DispAllErrors(henv
, hdbc
));
816 // Set Connection Options
817 if (!setConnectionOptions())
820 // Instead of Querying the data source for info about itself, it can just be copied
821 // from the wxDb instance that was passed in (copyDb).
822 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
823 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
824 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
825 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
826 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
827 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
828 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
829 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
830 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
831 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
832 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
833 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
834 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
835 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
836 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
837 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
838 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
839 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
840 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
841 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
842 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
843 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
844 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
845 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
846 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
847 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
848 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
849 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
850 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
851 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
852 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
854 // VARCHAR = Variable length character string
855 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
856 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
857 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
858 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
859 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
862 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
863 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
864 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
865 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
866 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
869 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
870 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
871 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
872 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
873 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
876 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
877 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
878 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
879 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
880 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
883 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
884 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
885 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
886 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
887 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
889 #ifdef DBDEBUG_CONSOLE
890 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
891 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
892 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
893 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
894 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
898 // Completed Successfully
903 /********** wxDb::setConnectionOptions() **********/
904 bool wxDb::setConnectionOptions(void)
906 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
911 // I need to get the DBMS name here, because some of the connection options
912 // are database specific and need to call the Dbms() function.
913 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
914 return(DispAllErrors(henv
, hdbc
));
916 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
917 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
918 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
920 // By default, MS Sql Server closes cursors on commit and rollback. The following
921 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
922 // after a transaction. This is a driver specific option and is not part of the
923 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
924 // The database settings don't have any effect one way or the other.
925 if (Dbms() == dbmsMS_SQL_SERVER
)
927 const long SQL_PRESERVE_CURSORS
= 1204L;
928 const long SQL_PC_ON
= 1L;
929 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
932 // Display the connection options to verify them
933 #ifdef DBDEBUG_CONSOLE
935 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
937 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
938 return(DispAllErrors(henv
, hdbc
));
939 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
941 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
942 return(DispAllErrors(henv
, hdbc
));
943 cout
<< wxT("ODBC CURSORS: ");
946 case(SQL_CUR_USE_IF_NEEDED
):
947 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
949 case(SQL_CUR_USE_ODBC
):
950 cout
<< wxT("SQL_CUR_USE_ODBC");
952 case(SQL_CUR_USE_DRIVER
):
953 cout
<< wxT("SQL_CUR_USE_DRIVER");
958 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
959 return(DispAllErrors(henv
, hdbc
));
960 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
965 // Completed Successfully
968 } // wxDb::setConnectionOptions()
971 /********** wxDb::getDbInfo() **********/
972 bool wxDb::getDbInfo(void)
977 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
978 return(DispAllErrors(henv
, hdbc
));
980 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
981 return(DispAllErrors(henv
, hdbc
));
983 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
984 return(DispAllErrors(henv
, hdbc
));
987 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
988 // causing database connectivity to fail in some cases.
989 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
991 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
992 return(DispAllErrors(henv
, hdbc
));
994 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
995 return(DispAllErrors(henv
, hdbc
));
997 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
998 return(DispAllErrors(henv
, hdbc
));
1000 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1001 return(DispAllErrors(henv
, hdbc
));
1003 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1007 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1008 return(DispAllErrors(henv
, hdbc
));
1010 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1011 return(DispAllErrors(henv
, hdbc
));
1013 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1014 return(DispAllErrors(henv
, hdbc
));
1016 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1017 // return(DispAllErrors(henv, hdbc));
1019 // Not all drivers support this call - Nick Gorham(unixODBC)
1020 dbInf
.cliConfLvl
= 0;
1023 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1024 return(DispAllErrors(henv
, hdbc
));
1026 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1027 return(DispAllErrors(henv
, hdbc
));
1029 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1030 return(DispAllErrors(henv
, hdbc
));
1032 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1033 return(DispAllErrors(henv
, hdbc
));
1035 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1036 return(DispAllErrors(henv
, hdbc
));
1038 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1039 return(DispAllErrors(henv
, hdbc
));
1041 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1042 return(DispAllErrors(henv
, hdbc
));
1044 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1045 return(DispAllErrors(henv
, hdbc
));
1047 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1048 return(DispAllErrors(henv
, hdbc
));
1050 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1051 return(DispAllErrors(henv
, hdbc
));
1053 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1054 return(DispAllErrors(henv
, hdbc
));
1056 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1057 return(DispAllErrors(henv
, hdbc
));
1059 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1060 return(DispAllErrors(henv
, hdbc
));
1062 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1063 return(DispAllErrors(henv
, hdbc
));
1065 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1066 return(DispAllErrors(henv
, hdbc
));
1068 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1069 return(DispAllErrors(henv
, hdbc
));
1071 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1072 return(DispAllErrors(henv
, hdbc
));
1074 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1075 return(DispAllErrors(henv
, hdbc
));
1077 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1078 return(DispAllErrors(henv
, hdbc
));
1080 #ifdef DBDEBUG_CONSOLE
1081 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1082 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1083 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1084 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1086 cout
<< wxT("API Conf. Level: ");
1087 switch(dbInf
.apiConfLvl
)
1089 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1090 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1091 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1095 cout
<< wxT("SAG CLI Conf. Level: ");
1096 switch(dbInf
.cliConfLvl
)
1098 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1099 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1103 cout
<< wxT("SQL Conf. Level: ");
1104 switch(dbInf
.sqlConfLvl
)
1106 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1107 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1108 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1112 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1113 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1114 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1115 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1116 cout
<< wxT("Cursor COMMIT Behavior: ");
1117 switch(dbInf
.cursorCommitBehavior
)
1119 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1120 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1121 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1125 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1126 switch(dbInf
.cursorRollbackBehavior
)
1128 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1129 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1130 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1134 cout
<< wxT("Support NOT NULL clause: ");
1135 switch(dbInf
.supportNotNullClause
)
1137 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1138 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1142 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1143 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1145 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1148 cout
<< wxT("Default Transaction Isolation: ";
1149 switch(dbInf
.txnIsolation
)
1151 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1152 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1153 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1154 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1156 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1161 cout
<< wxT("Transaction Isolation Options: ");
1162 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1163 cout
<< wxT("Read Uncommitted, ");
1164 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1165 cout
<< wxT("Read Committed, ");
1166 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1167 cout
<< wxT("Repeatable Read, ");
1168 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1169 cout
<< wxT("Serializable, ");
1171 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1172 cout
<< wxT("Versioning");
1176 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1177 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1178 cout
<< wxT("Next, ");
1179 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1180 cout
<< wxT("Prev, ");
1181 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1182 cout
<< wxT("First, ");
1183 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1184 cout
<< wxT("Last, ");
1185 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1186 cout
<< wxT("Absolute, ");
1187 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1188 cout
<< wxT("Relative, ");
1190 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1191 cout
<< wxT("Resume, ");
1193 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1194 cout
<< wxT("Bookmark");
1197 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1198 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1199 cout
<< wxT("No Change, ");
1200 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1201 cout
<< wxT("Exclusive, ");
1202 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1203 cout
<< wxT("UnLock");
1206 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1207 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1208 cout
<< wxT("Position, ");
1209 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1210 cout
<< wxT("Refresh, ");
1211 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1212 cout
<< wxT("Upd, "));
1213 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1214 cout
<< wxT("Del, ");
1215 if (dbInf
.posOperations
& SQL_POS_ADD
)
1219 cout
<< wxT("Positioned Statements Supported: ");
1220 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1221 cout
<< wxT("Pos delete, ");
1222 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1223 cout
<< wxT("Pos update, ");
1224 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1225 cout
<< wxT("Select for update");
1228 cout
<< wxT("Scroll Concurrency: ");
1229 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1230 cout
<< wxT("Read Only, ");
1231 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1232 cout
<< wxT("Lock, ");
1233 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1234 cout
<< wxT("Opt. Rowver, ");
1235 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1236 cout
<< wxT("Opt. Values");
1239 cout
<< wxT("Scroll Options: ");
1240 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1241 cout
<< wxT("Fwd Only, ");
1242 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1243 cout
<< wxT("Static, ");
1244 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1245 cout
<< wxT("Keyset Driven, ");
1246 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1247 cout
<< wxT("Dynamic, ");
1248 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1249 cout
<< wxT("Mixed");
1252 cout
<< wxT("Static Sensitivity: ");
1253 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1254 cout
<< wxT("Additions, ");
1255 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1256 cout
<< wxT("Deletions, ");
1257 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1258 cout
<< wxT("Updates");
1261 cout
<< wxT("Transaction Capable?: ");
1262 switch(dbInf
.txnCapable
)
1264 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1265 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1266 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1267 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1268 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1275 // Completed Successfully
1278 } // wxDb::getDbInfo()
1281 /********** wxDb::getDataTypeInfo() **********/
1282 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1285 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1286 * the data type inf. is gathered for.
1288 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1293 // Get information about the data type specified
1294 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1295 return(DispAllErrors(henv
, hdbc
, hstmt
));
1297 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
1299 #ifdef DBDEBUG_CONSOLE
1300 if (retcode
== SQL_NO_DATA_FOUND
)
1301 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1303 DispAllErrors(henv
, hdbc
, hstmt
);
1304 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1308 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1309 // Obtain columns from the record
1310 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1311 return(DispAllErrors(henv
, hdbc
, hstmt
));
1313 structSQLTypeInfo
.TypeName
= typeName
;
1315 // BJO 20000503: no more needed with new GetColumns...
1318 if (Dbms() == dbmsMY_SQL
)
1320 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1321 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1322 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1323 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1324 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1325 structSQLTypeInfo
.TypeName
= wxT("int");
1326 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1327 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1328 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1329 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1330 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1331 structSQLTypeInfo
.TypeName
= wxT("char");
1334 // BJO 20000427 : OpenLink driver
1335 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1336 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1338 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1339 structSQLTypeInfo
.TypeName
= wxT("real");
1343 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1344 return(DispAllErrors(henv
, hdbc
, hstmt
));
1345 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1346 return(DispAllErrors(henv
, hdbc
, hstmt
));
1347 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1348 // return(DispAllErrors(henv, hdbc, hstmt));
1350 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1351 return(DispAllErrors(henv
, hdbc
, hstmt
));
1353 if (structSQLTypeInfo
.MaximumScale
< 0)
1354 structSQLTypeInfo
.MaximumScale
= 0;
1356 // Close the statement handle which closes open cursors
1357 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1358 return(DispAllErrors(henv
, hdbc
, hstmt
));
1360 // Completed Successfully
1363 } // wxDb::getDataTypeInfo()
1366 /********** wxDb::Close() **********/
1367 void wxDb::Close(void)
1369 // Close the Sql Log file
1376 // Free statement handle
1379 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1380 DispAllErrors(henv
, hdbc
);
1383 // Disconnect from the datasource
1384 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1385 DispAllErrors(henv
, hdbc
);
1387 // Free the connection to the datasource
1388 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1389 DispAllErrors(henv
, hdbc
);
1391 // There should be zero Ctable objects still connected to this db object
1392 wxASSERT(nTables
== 0);
1397 pNode
= TablesInUse
.First();
1401 tiu
= (wxTablesInUse
*)pNode
->Data();
1402 if (tiu
->pDb
== this)
1404 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1405 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1408 pNode
= pNode
->Next();
1412 // Copy the error messages to a global variable
1414 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1415 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1417 dbmsType
= dbmsUNIDENTIFIED
;
1423 /********** wxDb::CommitTrans() **********/
1424 bool wxDb::CommitTrans(void)
1428 // Commit the transaction
1429 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1430 return(DispAllErrors(henv
, hdbc
));
1433 // Completed successfully
1436 } // wxDb::CommitTrans()
1439 /********** wxDb::RollbackTrans() **********/
1440 bool wxDb::RollbackTrans(void)
1442 // Rollback the transaction
1443 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1444 return(DispAllErrors(henv
, hdbc
));
1446 // Completed successfully
1449 } // wxDb::RollbackTrans()
1452 /********** wxDb::DispAllErrors() **********/
1453 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1455 * This function is called internally whenever an error condition prevents the user's
1456 * request from being executed. This function will query the datasource as to the
1457 * actual error(s) that just occured on the previous request of the datasource.
1459 * The function will retrieve each error condition from the datasource and
1460 * Printf the codes/text values into a string which it then logs via logError().
1461 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1462 * window and program execution will be paused until the user presses a key.
1464 * This function always returns a false, so that functions which call this function
1465 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1466 * of the users request, so that the calling code can then process the error msg log
1469 wxString odbcErrMsg
;
1471 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1473 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1474 logError(odbcErrMsg
, sqlState
);
1477 #ifdef DBDEBUG_CONSOLE
1478 // When run in console mode, use standard out to display errors.
1479 cout
<< odbcErrMsg
.c_str() << endl
;
1480 cout
<< wxT("Press any key to continue...") << endl
;
1485 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1490 return(false); // This function always returns false.
1492 } // wxDb::DispAllErrors()
1495 /********** wxDb::GetNextError() **********/
1496 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1498 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1503 } // wxDb::GetNextError()
1506 /********** wxDb::DispNextError() **********/
1507 void wxDb::DispNextError(void)
1509 wxString odbcErrMsg
;
1511 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1512 logError(odbcErrMsg
, sqlState
);
1517 #ifdef DBDEBUG_CONSOLE
1518 // When run in console mode, use standard out to display errors.
1519 cout
<< odbcErrMsg
.c_str() << endl
;
1520 cout
<< wxT("Press any key to continue...") << endl
;
1525 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1526 #endif // __WXDEBUG__
1528 } // wxDb::DispNextError()
1531 /********** wxDb::logError() **********/
1532 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1534 wxASSERT(errMsg
.Length());
1536 static int pLast
= -1;
1539 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1542 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1543 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1547 wxStrcpy(errorList
[pLast
], errMsg
);
1549 if (SQLState
.Length())
1550 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1551 DB_STATUS
= dbStatus
;
1553 // Add the errmsg to the sql log
1554 WriteSqlLog(errMsg
);
1556 } // wxDb::logError()
1559 /**********wxDb::TranslateSqlState() **********/
1560 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1562 if (!wxStrcmp(SQLState
, wxT("01000")))
1563 return(DB_ERR_GENERAL_WARNING
);
1564 if (!wxStrcmp(SQLState
, wxT("01002")))
1565 return(DB_ERR_DISCONNECT_ERROR
);
1566 if (!wxStrcmp(SQLState
, wxT("01004")))
1567 return(DB_ERR_DATA_TRUNCATED
);
1568 if (!wxStrcmp(SQLState
, wxT("01006")))
1569 return(DB_ERR_PRIV_NOT_REVOKED
);
1570 if (!wxStrcmp(SQLState
, wxT("01S00")))
1571 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1572 if (!wxStrcmp(SQLState
, wxT("01S01")))
1573 return(DB_ERR_ERROR_IN_ROW
);
1574 if (!wxStrcmp(SQLState
, wxT("01S02")))
1575 return(DB_ERR_OPTION_VALUE_CHANGED
);
1576 if (!wxStrcmp(SQLState
, wxT("01S03")))
1577 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1578 if (!wxStrcmp(SQLState
, wxT("01S04")))
1579 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1580 if (!wxStrcmp(SQLState
, wxT("07001")))
1581 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1582 if (!wxStrcmp(SQLState
, wxT("07006")))
1583 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1584 if (!wxStrcmp(SQLState
, wxT("08001")))
1585 return(DB_ERR_UNABLE_TO_CONNECT
);
1586 if (!wxStrcmp(SQLState
, wxT("08002")))
1587 return(DB_ERR_CONNECTION_IN_USE
);
1588 if (!wxStrcmp(SQLState
, wxT("08003")))
1589 return(DB_ERR_CONNECTION_NOT_OPEN
);
1590 if (!wxStrcmp(SQLState
, wxT("08004")))
1591 return(DB_ERR_REJECTED_CONNECTION
);
1592 if (!wxStrcmp(SQLState
, wxT("08007")))
1593 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1594 if (!wxStrcmp(SQLState
, wxT("08S01")))
1595 return(DB_ERR_COMM_LINK_FAILURE
);
1596 if (!wxStrcmp(SQLState
, wxT("21S01")))
1597 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1598 if (!wxStrcmp(SQLState
, wxT("21S02")))
1599 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1600 if (!wxStrcmp(SQLState
, wxT("22001")))
1601 return(DB_ERR_STRING_RIGHT_TRUNC
);
1602 if (!wxStrcmp(SQLState
, wxT("22003")))
1603 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1604 if (!wxStrcmp(SQLState
, wxT("22005")))
1605 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1606 if (!wxStrcmp(SQLState
, wxT("22008")))
1607 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1608 if (!wxStrcmp(SQLState
, wxT("22012")))
1609 return(DB_ERR_DIVIDE_BY_ZERO
);
1610 if (!wxStrcmp(SQLState
, wxT("22026")))
1611 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1612 if (!wxStrcmp(SQLState
, wxT("23000")))
1613 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1614 if (!wxStrcmp(SQLState
, wxT("24000")))
1615 return(DB_ERR_INVALID_CURSOR_STATE
);
1616 if (!wxStrcmp(SQLState
, wxT("25000")))
1617 return(DB_ERR_INVALID_TRANS_STATE
);
1618 if (!wxStrcmp(SQLState
, wxT("28000")))
1619 return(DB_ERR_INVALID_AUTH_SPEC
);
1620 if (!wxStrcmp(SQLState
, wxT("34000")))
1621 return(DB_ERR_INVALID_CURSOR_NAME
);
1622 if (!wxStrcmp(SQLState
, wxT("37000")))
1623 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1624 if (!wxStrcmp(SQLState
, wxT("3C000")))
1625 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1626 if (!wxStrcmp(SQLState
, wxT("40001")))
1627 return(DB_ERR_SERIALIZATION_FAILURE
);
1628 if (!wxStrcmp(SQLState
, wxT("42000")))
1629 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1630 if (!wxStrcmp(SQLState
, wxT("70100")))
1631 return(DB_ERR_OPERATION_ABORTED
);
1632 if (!wxStrcmp(SQLState
, wxT("IM001")))
1633 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1634 if (!wxStrcmp(SQLState
, wxT("IM002")))
1635 return(DB_ERR_NO_DATA_SOURCE
);
1636 if (!wxStrcmp(SQLState
, wxT("IM003")))
1637 return(DB_ERR_DRIVER_LOAD_ERROR
);
1638 if (!wxStrcmp(SQLState
, wxT("IM004")))
1639 return(DB_ERR_SQLALLOCENV_FAILED
);
1640 if (!wxStrcmp(SQLState
, wxT("IM005")))
1641 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1642 if (!wxStrcmp(SQLState
, wxT("IM006")))
1643 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1644 if (!wxStrcmp(SQLState
, wxT("IM007")))
1645 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1646 if (!wxStrcmp(SQLState
, wxT("IM008")))
1647 return(DB_ERR_DIALOG_FAILED
);
1648 if (!wxStrcmp(SQLState
, wxT("IM009")))
1649 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1650 if (!wxStrcmp(SQLState
, wxT("IM010")))
1651 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1652 if (!wxStrcmp(SQLState
, wxT("IM011")))
1653 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1654 if (!wxStrcmp(SQLState
, wxT("IM012")))
1655 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1656 if (!wxStrcmp(SQLState
, wxT("IM013")))
1657 return(DB_ERR_TRACE_FILE_ERROR
);
1658 if (!wxStrcmp(SQLState
, wxT("S0001")))
1659 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1660 if (!wxStrcmp(SQLState
, wxT("S0002")))
1661 return(DB_ERR_TABLE_NOT_FOUND
);
1662 if (!wxStrcmp(SQLState
, wxT("S0011")))
1663 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1664 if (!wxStrcmp(SQLState
, wxT("S0012")))
1665 return(DB_ERR_INDEX_NOT_FOUND
);
1666 if (!wxStrcmp(SQLState
, wxT("S0021")))
1667 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1668 if (!wxStrcmp(SQLState
, wxT("S0022")))
1669 return(DB_ERR_COLUMN_NOT_FOUND
);
1670 if (!wxStrcmp(SQLState
, wxT("S0023")))
1671 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1672 if (!wxStrcmp(SQLState
, wxT("S1000")))
1673 return(DB_ERR_GENERAL_ERROR
);
1674 if (!wxStrcmp(SQLState
, wxT("S1001")))
1675 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1676 if (!wxStrcmp(SQLState
, wxT("S1002")))
1677 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1678 if (!wxStrcmp(SQLState
, wxT("S1003")))
1679 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1680 if (!wxStrcmp(SQLState
, wxT("S1004")))
1681 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1682 if (!wxStrcmp(SQLState
, wxT("S1008")))
1683 return(DB_ERR_OPERATION_CANCELLED
);
1684 if (!wxStrcmp(SQLState
, wxT("S1009")))
1685 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1686 if (!wxStrcmp(SQLState
, wxT("S1010")))
1687 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1688 if (!wxStrcmp(SQLState
, wxT("S1011")))
1689 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1690 if (!wxStrcmp(SQLState
, wxT("S1012")))
1691 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1692 if (!wxStrcmp(SQLState
, wxT("S1015")))
1693 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1694 if (!wxStrcmp(SQLState
, wxT("S1090")))
1695 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1696 if (!wxStrcmp(SQLState
, wxT("S1091")))
1697 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1698 if (!wxStrcmp(SQLState
, wxT("S1092")))
1699 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1700 if (!wxStrcmp(SQLState
, wxT("S1093")))
1701 return(DB_ERR_INVALID_PARAM_NO
);
1702 if (!wxStrcmp(SQLState
, wxT("S1094")))
1703 return(DB_ERR_INVALID_SCALE_VALUE
);
1704 if (!wxStrcmp(SQLState
, wxT("S1095")))
1705 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1706 if (!wxStrcmp(SQLState
, wxT("S1096")))
1707 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1708 if (!wxStrcmp(SQLState
, wxT("S1097")))
1709 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1710 if (!wxStrcmp(SQLState
, wxT("S1098")))
1711 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1712 if (!wxStrcmp(SQLState
, wxT("S1099")))
1713 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1714 if (!wxStrcmp(SQLState
, wxT("S1100")))
1715 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1716 if (!wxStrcmp(SQLState
, wxT("S1101")))
1717 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1718 if (!wxStrcmp(SQLState
, wxT("S1103")))
1719 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1720 if (!wxStrcmp(SQLState
, wxT("S1104")))
1721 return(DB_ERR_INVALID_PRECISION_VALUE
);
1722 if (!wxStrcmp(SQLState
, wxT("S1105")))
1723 return(DB_ERR_INVALID_PARAM_TYPE
);
1724 if (!wxStrcmp(SQLState
, wxT("S1106")))
1725 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1726 if (!wxStrcmp(SQLState
, wxT("S1107")))
1727 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1728 if (!wxStrcmp(SQLState
, wxT("S1108")))
1729 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1730 if (!wxStrcmp(SQLState
, wxT("S1109")))
1731 return(DB_ERR_INVALID_CURSOR_POSITION
);
1732 if (!wxStrcmp(SQLState
, wxT("S1110")))
1733 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1734 if (!wxStrcmp(SQLState
, wxT("S1111")))
1735 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1736 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1737 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1738 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1739 return(DB_ERR_TIMEOUT_EXPIRED
);
1744 } // wxDb::TranslateSqlState()
1747 /********** wxDb::Grant() **********/
1748 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1752 // Build the grant statement
1753 sqlStmt
= wxT("GRANT ");
1754 if (privileges
== DB_GRANT_ALL
)
1755 sqlStmt
+= wxT("ALL");
1759 if (privileges
& DB_GRANT_SELECT
)
1761 sqlStmt
+= wxT("SELECT");
1764 if (privileges
& DB_GRANT_INSERT
)
1767 sqlStmt
+= wxT(", ");
1768 sqlStmt
+= wxT("INSERT");
1770 if (privileges
& DB_GRANT_UPDATE
)
1773 sqlStmt
+= wxT(", ");
1774 sqlStmt
+= wxT("UPDATE");
1776 if (privileges
& DB_GRANT_DELETE
)
1779 sqlStmt
+= wxT(", ");
1780 sqlStmt
+= wxT("DELETE");
1784 sqlStmt
+= wxT(" ON ");
1785 sqlStmt
+= tableName
;
1786 sqlStmt
+= wxT(" TO ");
1787 sqlStmt
+= userList
;
1789 #ifdef DBDEBUG_CONSOLE
1790 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1793 WriteSqlLog(sqlStmt
);
1795 return(ExecSql(sqlStmt
));
1800 /********** wxDb::CreateView() **********/
1801 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1802 const wxString
&pSqlStmt
, bool attemptDrop
)
1806 // Drop the view first
1807 if (attemptDrop
&& !DropView(viewName
))
1810 // Build the create view statement
1811 sqlStmt
= wxT("CREATE VIEW ");
1812 sqlStmt
+= viewName
;
1814 if (colList
.Length())
1816 sqlStmt
+= wxT(" (");
1818 sqlStmt
+= wxT(")");
1821 sqlStmt
+= wxT(" AS ");
1822 sqlStmt
+= pSqlStmt
;
1824 WriteSqlLog(sqlStmt
);
1826 #ifdef DBDEBUG_CONSOLE
1827 cout
<< sqlStmt
.c_str() << endl
;
1830 return(ExecSql(sqlStmt
));
1832 } // wxDb::CreateView()
1835 /********** wxDb::DropView() **********/
1836 bool wxDb::DropView(const wxString
&viewName
)
1839 * NOTE: This function returns true if the View does not exist, but
1840 * only for identified databases. Code will need to be added
1841 * below for any other databases when those databases are defined
1842 * to handle this situation consistently
1846 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1848 WriteSqlLog(sqlStmt
);
1850 #ifdef DBDEBUG_CONSOLE
1851 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1854 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1856 // Check for "Base table not found" error and ignore
1857 GetNextError(henv
, hdbc
, hstmt
);
1858 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1860 // Check for product specific error codes
1861 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1864 DispAllErrors(henv
, hdbc
, hstmt
);
1871 // Commit the transaction
1877 } // wxDb::DropView()
1880 /********** wxDb::ExecSql() **********/
1881 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1885 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1887 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
1888 if (retcode
== SQL_SUCCESS
||
1889 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
1895 DispAllErrors(henv
, hdbc
, hstmt
);
1899 } // wxDb::ExecSql()
1902 /********** wxDb::GetNext() **********/
1903 bool wxDb::GetNext(void)
1905 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1909 DispAllErrors(henv
, hdbc
, hstmt
);
1913 } // wxDb::GetNext()
1916 /********** wxDb::GetData() **********/
1917 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1920 wxASSERT(cbReturned
);
1922 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1926 DispAllErrors(henv
, hdbc
, hstmt
);
1930 } // wxDb::GetData()
1933 /********** wxDb::GetKeyFields() **********/
1934 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1936 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1937 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1939 // SQLSMALLINT iKeySeq;
1940 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1941 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1947 * -----------------------------------------------------------------------
1948 * -- 19991224 : mj10777 : Create ------
1949 * -- : Three things are done and stored here : ------
1950 * -- : 1) which Column(s) is/are Primary Key(s) ------
1951 * -- : 2) which tables use this Key as a Foreign Key ------
1952 * -- : 3) which columns are Foreign Key and the name ------
1953 * -- : of the Table where the Key is the Primary Key -----
1954 * -- : Called from GetColumns(const wxString &tableName, ------
1955 * -- int *numCols,const wxChar *userID ) ------
1956 * -----------------------------------------------------------------------
1959 /*---------------------------------------------------------------------*/
1960 /* Get the names of the columns in the primary key. */
1961 /*---------------------------------------------------------------------*/
1962 retcode
= SQLPrimaryKeys(hstmt
,
1963 NULL
, 0, /* Catalog name */
1964 NULL
, 0, /* Schema name */
1965 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1967 /*---------------------------------------------------------------------*/
1968 /* Fetch and display the result set. This will be a list of the */
1969 /* columns in the primary key of the tableName table. */
1970 /*---------------------------------------------------------------------*/
1971 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1973 retcode
= SQLFetch(hstmt
);
1974 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1976 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1977 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1979 for (i
=0;i
<noCols
;i
++) // Find the Column name
1980 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1981 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1984 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1986 /*---------------------------------------------------------------------*/
1987 /* Get all the foreign keys that refer to tableName primary key. */
1988 /*---------------------------------------------------------------------*/
1989 retcode
= SQLForeignKeys(hstmt
,
1990 NULL
, 0, /* Primary catalog */
1991 NULL
, 0, /* Primary schema */
1992 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
1993 NULL
, 0, /* Foreign catalog */
1994 NULL
, 0, /* Foreign schema */
1995 NULL
, 0); /* Foreign table */
1997 /*---------------------------------------------------------------------*/
1998 /* Fetch and display the result set. This will be all of the foreign */
1999 /* keys in other tables that refer to the tableName primary key. */
2000 /*---------------------------------------------------------------------*/
2003 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2005 retcode
= SQLFetch(hstmt
);
2006 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2008 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2009 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2010 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2011 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2012 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2013 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2017 tempStr
.Trim(); // Get rid of any unneeded blanks
2018 if (!tempStr
.IsEmpty())
2020 for (i
=0; i
<noCols
; i
++)
2021 { // Find the Column name
2022 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2023 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2027 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2029 /*---------------------------------------------------------------------*/
2030 /* Get all the foreign keys in the tablename table. */
2031 /*---------------------------------------------------------------------*/
2032 retcode
= SQLForeignKeys(hstmt
,
2033 NULL
, 0, /* Primary catalog */
2034 NULL
, 0, /* Primary schema */
2035 NULL
, 0, /* Primary table */
2036 NULL
, 0, /* Foreign catalog */
2037 NULL
, 0, /* Foreign schema */
2038 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2040 /*---------------------------------------------------------------------*/
2041 /* Fetch and display the result set. This will be all of the */
2042 /* primary keys in other tables that are referred to by foreign */
2043 /* keys in the tableName table. */
2044 /*---------------------------------------------------------------------*/
2046 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2048 retcode
= SQLFetch(hstmt
);
2049 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2051 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2052 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2053 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2055 for (i
=0; i
<noCols
; i
++) // Find the Column name
2057 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2059 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2060 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2065 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2069 } // wxDb::GetKeyFields()
2073 /********** wxDb::GetColumns() **********/
2074 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2076 * 1) The last array element of the tableName[] argument must be zero (null).
2077 * This is how the end of the array is detected.
2078 * 2) This function returns an array of wxDbColInf structures. If no columns
2079 * were found, or an error occured, this pointer will be zero (null). THE
2080 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2081 * IS FINISHED WITH IT. i.e.
2083 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2086 * // Use the column inf
2088 * // Destroy the memory
2092 * userID is evaluated in the following manner:
2093 * userID == NULL ... UserID is ignored
2094 * userID == "" ... UserID set equal to 'this->uid'
2095 * userID != "" ... UserID set equal to 'userID'
2097 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2098 * by this function. This function should use its own wxDb instance
2099 * to avoid undesired unbinding of columns.
2104 wxDbColInf
*colInf
= 0;
2112 convertUserID(userID
,UserID
);
2114 // Pass 1 - Determine how many columns there are.
2115 // Pass 2 - Allocate the wxDbColInf array and fill in
2116 // the array with the column information.
2118 for (pass
= 1; pass
<= 2; pass
++)
2122 if (noCols
== 0) // Probably a bogus table name(s)
2124 // Allocate n wxDbColInf objects to hold the column information
2125 colInf
= new wxDbColInf
[noCols
+1];
2128 // Mark the end of the array
2129 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2130 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2131 colInf
[noCols
].sqlDataType
= 0;
2133 // Loop through each table name
2135 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2137 TableName
= tableName
[tbl
];
2138 // Oracle and Interbase table names are uppercase only, so force
2139 // the name to uppercase just in case programmer forgot to do this
2140 if ((Dbms() == dbmsORACLE
) ||
2141 (Dbms() == dbmsINTERBASE
))
2142 TableName
= TableName
.Upper();
2144 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2146 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2147 // use the call below that leaves out the user name
2148 if (!UserID
.IsEmpty() &&
2149 Dbms() != dbmsMY_SQL
&&
2150 Dbms() != dbmsACCESS
&&
2151 Dbms() != dbmsMS_SQL_SERVER
)
2153 retcode
= SQLColumns(hstmt
,
2154 NULL
, 0, // All qualifiers
2155 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2156 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2157 NULL
, 0); // All columns
2161 retcode
= SQLColumns(hstmt
,
2162 NULL
, 0, // All qualifiers
2164 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2165 NULL
, 0); // All columns
2167 if (retcode
!= SQL_SUCCESS
)
2168 { // Error occured, abort
2169 DispAllErrors(henv
, hdbc
, hstmt
);
2172 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2176 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2178 if (pass
== 1) // First pass, just add up the number of columns
2180 else // Pass 2; Fill in the array of structures
2182 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2184 // NOTE: Only the ODBC 1.x fields are retrieved
2185 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2186 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2187 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2188 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2189 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2190 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2191 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2192 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2193 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2194 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2195 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2196 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2198 // Determine the wxDb data type that is used to represent the native data type of this data source
2199 colInf
[colNo
].dbDataType
= 0;
2200 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2203 // IODBC does not return a correct columnSize, so we set
2204 // columnSize = bufferLength if no column size was returned
2205 // IODBC returns the columnSize in bufferLength.. (bug)
2206 if (colInf
[colNo
].columnSize
< 1)
2208 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2211 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2213 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2214 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2215 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2216 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2217 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2218 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2219 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2220 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2225 if (retcode
!= SQL_NO_DATA_FOUND
)
2226 { // Error occured, abort
2227 DispAllErrors(henv
, hdbc
, hstmt
);
2230 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2236 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2239 } // wxDb::GetColumns()
2242 /********** wxDb::GetColumns() **********/
2244 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2246 // Same as the above GetColumns() function except this one gets columns
2247 // only for a single table, and if 'numCols' is not NULL, the number of
2248 // columns stored in the returned wxDbColInf is set in '*numCols'
2250 // userID is evaluated in the following manner:
2251 // userID == NULL ... UserID is ignored
2252 // userID == "" ... UserID set equal to 'this->uid'
2253 // userID != "" ... UserID set equal to 'userID'
2255 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2256 // by this function. This function should use its own wxDb instance
2257 // to avoid undesired unbinding of columns.
2262 wxDbColInf
*colInf
= 0;
2270 convertUserID(userID
,UserID
);
2272 // Pass 1 - Determine how many columns there are.
2273 // Pass 2 - Allocate the wxDbColInf array and fill in
2274 // the array with the column information.
2276 for (pass
= 1; pass
<= 2; pass
++)
2280 if (noCols
== 0) // Probably a bogus table name(s)
2282 // Allocate n wxDbColInf objects to hold the column information
2283 colInf
= new wxDbColInf
[noCols
+1];
2286 // Mark the end of the array
2287 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2288 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2289 colInf
[noCols
].sqlDataType
= 0;
2292 TableName
= tableName
;
2293 // Oracle and Interbase table names are uppercase only, so force
2294 // the name to uppercase just in case programmer forgot to do this
2295 if ((Dbms() == dbmsORACLE
) ||
2296 (Dbms() == dbmsINTERBASE
))
2297 TableName
= TableName
.Upper();
2299 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2301 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2302 // use the call below that leaves out the user name
2303 if (!UserID
.IsEmpty() &&
2304 Dbms() != dbmsMY_SQL
&&
2305 Dbms() != dbmsACCESS
&&
2306 Dbms() != dbmsMS_SQL_SERVER
)
2308 retcode
= SQLColumns(hstmt
,
2309 NULL
, 0, // All qualifiers
2310 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2311 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2312 NULL
, 0); // All columns
2316 retcode
= SQLColumns(hstmt
,
2317 NULL
, 0, // All qualifiers
2319 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2320 NULL
, 0); // All columns
2322 if (retcode
!= SQL_SUCCESS
)
2323 { // Error occured, abort
2324 DispAllErrors(henv
, hdbc
, hstmt
);
2327 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2333 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2335 if (pass
== 1) // First pass, just add up the number of columns
2337 else // Pass 2; Fill in the array of structures
2339 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2341 // NOTE: Only the ODBC 1.x fields are retrieved
2342 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2343 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2344 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2345 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2346 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2347 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2348 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2349 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2350 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2351 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2352 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2353 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2354 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2355 // Start Values for Primary/Foriegn Key (=No)
2356 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2357 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2358 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2359 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2361 // BJO 20000428 : Virtuoso returns type names with upper cases!
2362 if (Dbms() == dbmsVIRTUOSO
)
2364 wxString s
= colInf
[colNo
].typeName
;
2366 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2369 // Determine the wxDb data type that is used to represent the native data type of this data source
2370 colInf
[colNo
].dbDataType
= 0;
2371 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2374 // IODBC does not return a correct columnSize, so we set
2375 // columnSize = bufferLength if no column size was returned
2376 // IODBC returns the columnSize in bufferLength.. (bug)
2377 if (colInf
[colNo
].columnSize
< 1)
2379 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2383 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2385 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2386 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2387 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2388 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2389 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2390 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2391 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2392 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2398 if (retcode
!= SQL_NO_DATA_FOUND
)
2399 { // Error occured, abort
2400 DispAllErrors(henv
, hdbc
, hstmt
);
2403 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2410 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2412 // Store Primary and Foriegn Keys
2413 GetKeyFields(tableName
,colInf
,noCols
);
2419 } // wxDb::GetColumns()
2422 #else // New GetColumns
2427 These are tentative new GetColumns members which should be more database
2428 independant and which always returns the columns in the order they were
2431 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2432 wxChar* userID)) calls the second implementation for each separate table
2433 before merging the results. This makes the code easier to maintain as
2434 only one member (the second) makes the real work
2435 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2436 wxChar *userID) is a little bit improved
2437 - It doesn't anymore rely on the type-name to find out which database-type
2439 - It ends by sorting the columns, so that they are returned in the same
2440 order they were created
2450 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2453 // The last array element of the tableName[] argument must be zero (null).
2454 // This is how the end of the array is detected.
2458 // How many tables ?
2460 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2462 // Create a table to maintain the columns for each separate table
2463 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2466 for (i
= 0 ; i
< tbl
; i
++)
2469 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2470 if (TableColumns
[i
].colInf
== NULL
)
2472 noCols
+= TableColumns
[i
].noCols
;
2475 // Now merge all the separate table infos
2476 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2478 // Mark the end of the array
2479 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2480 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2481 colInf
[noCols
].sqlDataType
= 0;
2486 for (i
= 0 ; i
< tbl
; i
++)
2488 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2490 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2494 delete [] TableColumns
;
2497 } // wxDb::GetColumns() -- NEW
2500 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2502 // Same as the above GetColumns() function except this one gets columns
2503 // only for a single table, and if 'numCols' is not NULL, the number of
2504 // columns stored in the returned wxDbColInf is set in '*numCols'
2506 // userID is evaluated in the following manner:
2507 // userID == NULL ... UserID is ignored
2508 // userID == "" ... UserID set equal to 'this->uid'
2509 // userID != "" ... UserID set equal to 'userID'
2511 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2512 // by this function. This function should use its own wxDb instance
2513 // 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 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2566 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2567 NULL
, 0); // All columns
2571 retcode
= SQLColumns(hstmt
,
2572 NULL
, 0, // All qualifiers
2574 (UCHAR
*) 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 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2605 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2606 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2607 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2608 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2609 // Start Values for Primary/Foriegn Key (=No)
2610 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2611 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2612 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2613 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2616 // IODBC does not return a correct columnSize, so we set
2617 // columnSize = bufferLength if no column size was returned
2618 // IODBC returns the columnSize in bufferLength.. (bug)
2619 if (colInf
[colNo
].columnSize
< 1)
2621 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2625 // Determine the wxDb data type that is used to represent the native data type of this data source
2626 colInf
[colNo
].dbDataType
= 0;
2627 // Get the intern datatype
2628 switch (colInf
[colNo
].sqlDataType
)
2632 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2638 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2648 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2651 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2656 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2657 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2664 if (retcode
!= SQL_NO_DATA_FOUND
)
2665 { // Error occured, abort
2666 DispAllErrors(henv
, hdbc
, hstmt
);
2669 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2676 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2678 // Store Primary and Foreign Keys
2679 GetKeyFields(tableName
,colInf
,noCols
);
2681 ///////////////////////////////////////////////////////////////////////////
2682 // Now sort the the columns in order to make them appear in the right order
2683 ///////////////////////////////////////////////////////////////////////////
2685 // Build a generic SELECT statement which returns 0 rows
2688 Stmt
.Printf(wxT("select * from %s where 0=1"), tableName
);
2691 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2693 DispAllErrors(henv
, hdbc
, hstmt
);
2697 // Get the number of result columns
2698 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2700 DispAllErrors(henv
, hdbc
, hstmt
);
2704 if (noCols
== 0) // Probably a bogus table name
2713 for (colNum
= 0; colNum
< noCols
; colNum
++)
2715 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2717 &Sword
, &Sdword
) != SQL_SUCCESS
)
2719 DispAllErrors(henv
, hdbc
, hstmt
);
2723 wxString Name1
= name
;
2724 Name1
= Name1
.Upper();
2726 // Where is this name in the array ?
2727 for (i
= colNum
; i
< noCols
; i
++)
2729 wxString Name2
= colInf
[i
].colName
;
2730 Name2
= Name2
.Upper();
2733 if (colNum
!= i
) // swap to sort
2735 wxDbColInf tmpColInf
= colInf
[colNum
];
2736 colInf
[colNum
] = colInf
[i
];
2737 colInf
[i
] = tmpColInf
;
2743 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2745 ///////////////////////////////////////////////////////////////////////////
2747 ///////////////////////////////////////////////////////////////////////////
2753 } // wxDb::GetColumns()
2756 #endif // #else OLD_GETCOLUMNS
2759 /********** wxDb::GetColumnCount() **********/
2760 UWORD
wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2762 * Returns a count of how many columns are in a table.
2763 * If an error occurs in computing the number of columns
2764 * this function will return a -1 for the count
2766 * userID is evaluated in the following manner:
2767 * userID == NULL ... UserID is ignored
2768 * userID == "" ... UserID set equal to 'this->uid'
2769 * userID != "" ... UserID set equal to 'userID'
2771 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2772 * by this function. This function should use its own wxDb instance
2773 * to avoid undesired unbinding of columns.
2783 convertUserID(userID
,UserID
);
2785 TableName
= tableName
;
2786 // Oracle and Interbase table names are uppercase only, so force
2787 // the name to uppercase just in case programmer forgot to do this
2788 if ((Dbms() == dbmsORACLE
) ||
2789 (Dbms() == dbmsINTERBASE
))
2790 TableName
= TableName
.Upper();
2792 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2794 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2795 // use the call below that leaves out the user name
2796 if (!UserID
.IsEmpty() &&
2797 Dbms() != dbmsMY_SQL
&&
2798 Dbms() != dbmsACCESS
&&
2799 Dbms() != dbmsMS_SQL_SERVER
)
2801 retcode
= SQLColumns(hstmt
,
2802 NULL
, 0, // All qualifiers
2803 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2804 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2805 NULL
, 0); // All columns
2809 retcode
= SQLColumns(hstmt
,
2810 NULL
, 0, // All qualifiers
2812 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2813 NULL
, 0); // All columns
2815 if (retcode
!= SQL_SUCCESS
)
2816 { // Error occured, abort
2817 DispAllErrors(henv
, hdbc
, hstmt
);
2818 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2822 // Count the columns
2823 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2826 if (retcode
!= SQL_NO_DATA_FOUND
)
2827 { // Error occured, abort
2828 DispAllErrors(henv
, hdbc
, hstmt
);
2829 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2833 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2836 } // wxDb::GetColumnCount()
2839 /********** wxDb::GetCatalog() *******/
2840 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2842 * ---------------------------------------------------------------------
2843 * -- 19991203 : mj10777 : Create ------
2844 * -- : Creates a wxDbInf with Tables / Cols Array ------
2845 * -- : uses SQLTables and fills pTableInf; ------
2846 * -- : pColInf is set to NULL and numCols to 0; ------
2847 * -- : returns pDbInf (wxDbInf) ------
2848 * -- - if unsuccesfull (pDbInf == NULL) ------
2849 * -- : pColInf can be filled with GetColumns(..); ------
2850 * -- : numCols can be filled with GetColumnCount(..); ------
2851 * ---------------------------------------------------------------------
2853 * userID is evaluated in the following manner:
2854 * userID == NULL ... UserID is ignored
2855 * userID == "" ... UserID set equal to 'this->uid'
2856 * userID != "" ... UserID set equal to 'userID'
2858 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2859 * by this function. This function should use its own wxDb instance
2860 * to avoid undesired unbinding of columns.
2863 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2864 int noTab
= 0; // Counter while filling table entries
2868 wxString tblNameSave
;
2871 convertUserID(userID
,UserID
);
2873 //-------------------------------------------------------------
2874 pDbInf
= new wxDbInf
; // Create the Database Array
2875 //-------------------------------------------------------------
2876 // Table Information
2877 // Pass 1 - Determine how many Tables there are.
2878 // Pass 2 - Create the Table array and fill it
2879 // - Create the Cols array = NULL
2880 //-------------------------------------------------------------
2882 for (pass
= 1; pass
<= 2; pass
++)
2884 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2885 tblNameSave
.Empty();
2887 if (!UserID
.IsEmpty() &&
2888 Dbms() != dbmsMY_SQL
&&
2889 Dbms() != dbmsACCESS
&&
2890 Dbms() != dbmsMS_SQL_SERVER
)
2892 retcode
= SQLTables(hstmt
,
2893 NULL
, 0, // All qualifiers
2894 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2895 NULL
, 0, // All tables
2896 NULL
, 0); // All columns
2900 retcode
= SQLTables(hstmt
,
2901 NULL
, 0, // All qualifiers
2902 NULL
, 0, // User specified
2903 NULL
, 0, // All tables
2904 NULL
, 0); // All columns
2907 if (retcode
!= SQL_SUCCESS
)
2909 DispAllErrors(henv
, hdbc
, hstmt
);
2911 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2915 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2917 if (pass
== 1) // First pass, just count the Tables
2919 if (pDbInf
->numTables
== 0)
2921 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2922 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2924 pDbInf
->numTables
++; // Counter for Tables
2926 if (pass
== 2) // Create and fill the Table entries
2928 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2929 { // no, then create the Array
2930 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2932 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2934 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2935 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2936 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2942 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2944 // Query how many columns are in each table
2945 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2947 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2952 } // wxDb::GetCatalog()
2955 /********** wxDb::Catalog() **********/
2956 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2958 * Creates the text file specified in 'filename' which will contain
2959 * a minimal data dictionary of all tables accessible by the user specified
2962 * userID is evaluated in the following manner:
2963 * userID == NULL ... UserID is ignored
2964 * userID == "" ... UserID set equal to 'this->uid'
2965 * userID != "" ... UserID set equal to 'userID'
2967 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2968 * by this function. This function should use its own wxDb instance
2969 * to avoid undesired unbinding of columns.
2972 wxASSERT(fileName
.Length());
2976 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2977 wxString tblNameSave
;
2978 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2980 wxChar typeName
[30+1];
2981 SWORD precision
, length
;
2983 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
2987 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2990 convertUserID(userID
,UserID
);
2992 if (!UserID
.IsEmpty() &&
2993 Dbms() != dbmsMY_SQL
&&
2994 Dbms() != dbmsACCESS
&&
2995 Dbms() != dbmsINTERBASE
&&
2996 Dbms() != dbmsMS_SQL_SERVER
)
2998 retcode
= SQLColumns(hstmt
,
2999 NULL
, 0, // All qualifiers
3000 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3001 NULL
, 0, // All tables
3002 NULL
, 0); // All columns
3006 retcode
= SQLColumns(hstmt
,
3007 NULL
, 0, // All qualifiers
3008 NULL
, 0, // User specified
3009 NULL
, 0, // All tables
3010 NULL
, 0); // All columns
3012 if (retcode
!= SQL_SUCCESS
)
3014 DispAllErrors(henv
, hdbc
, hstmt
);
3020 tblNameSave
.Empty();
3023 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3025 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3028 fputs(wxT("\n"), fp
);
3029 fputs(wxT("================================ "), fp
);
3030 fputs(wxT("================================ "), fp
);
3031 fputs(wxT("===================== "), fp
);
3032 fputs(wxT("========= "), fp
);
3033 fputs(wxT("=========\n"), fp
);
3034 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3035 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3036 fputs(outStr
.c_str(), fp
);
3037 fputs(wxT("================================ "), fp
);
3038 fputs(wxT("================================ "), fp
);
3039 fputs(wxT("===================== "), fp
);
3040 fputs(wxT("========= "), fp
);
3041 fputs(wxT("=========\n"), fp
);
3042 tblNameSave
= tblName
;
3045 GetData(3,SQL_C_CHAR
, (UCHAR
*)tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3046 GetData(4,SQL_C_CHAR
, (UCHAR
*)colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3047 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
,0, &cb
);
3048 GetData(6,SQL_C_CHAR
, (UCHAR
*)typeName
, sizeof(typeName
), &cb
);
3049 GetData(7,SQL_C_SSHORT
,(UCHAR
*)&precision
, 0, &cb
);
3050 GetData(8,SQL_C_SSHORT
,(UCHAR
*)&length
, 0, &cb
);
3052 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
3053 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3054 if (fputs(outStr
.c_str(), fp
) == EOF
)
3056 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3063 if (retcode
!= SQL_NO_DATA_FOUND
)
3064 DispAllErrors(henv
, hdbc
, hstmt
);
3066 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3069 return(retcode
== SQL_NO_DATA_FOUND
);
3071 } // wxDb::Catalog()
3074 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3076 * Table name can refer to a table, view, alias or synonym. Returns true
3077 * if the object exists in the database. This function does not indicate
3078 * whether or not the user has privleges to query or perform other functions
3081 * userID is evaluated in the following manner:
3082 * userID == NULL ... UserID is ignored
3083 * userID == "" ... UserID set equal to 'this->uid'
3084 * userID != "" ... UserID set equal to 'userID'
3087 wxASSERT(tableName
.Length());
3091 if (Dbms() == dbmsDBASE
)
3094 if (tablePath
.Length())
3095 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3097 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3100 exists
= wxFileExists(dbName
);
3105 convertUserID(userID
,UserID
);
3107 TableName
= tableName
;
3108 // Oracle and Interbase table names are uppercase only, so force
3109 // the name to uppercase just in case programmer forgot to do this
3110 if ((Dbms() == dbmsORACLE
) ||
3111 (Dbms() == dbmsINTERBASE
))
3112 TableName
= TableName
.Upper();
3114 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3117 // Some databases cannot accept a user name when looking up table names,
3118 // so we use the call below that leaves out the user name
3119 if (!UserID
.IsEmpty() &&
3120 Dbms() != dbmsMY_SQL
&&
3121 Dbms() != dbmsACCESS
&&
3122 Dbms() != dbmsMS_SQL_SERVER
&&
3123 Dbms() != dbmsDB2
&&
3124 Dbms() != dbmsINTERBASE
&&
3125 Dbms() != dbmsPERVASIVE_SQL
)
3127 retcode
= SQLTables(hstmt
,
3128 NULL
, 0, // All qualifiers
3129 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3130 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3131 NULL
, 0); // All table types
3135 retcode
= SQLTables(hstmt
,
3136 NULL
, 0, // All qualifiers
3137 NULL
, 0, // All owners
3138 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3139 NULL
, 0); // All table types
3141 if (retcode
!= SQL_SUCCESS
)
3142 return(DispAllErrors(henv
, hdbc
, hstmt
));
3144 retcode
= SQLFetch(hstmt
);
3145 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3147 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3148 return(DispAllErrors(henv
, hdbc
, hstmt
));
3151 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3155 } // wxDb::TableExists()
3158 /********** wxDb::TablePrivileges() **********/
3159 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3160 const wxChar
*schema
, const wxString
&tablePath
)
3162 wxASSERT(tableName
.Length());
3164 wxDbTablePrivilegeInfo result
;
3168 // We probably need to be able to dynamically set this based on
3169 // the driver type, and state.
3170 wxChar curRole
[]=wxT("public");
3174 wxString UserID
,Schema
;
3175 convertUserID(userID
,UserID
);
3176 convertUserID(schema
,Schema
);
3178 TableName
= tableName
;
3179 // Oracle and Interbase table names are uppercase only, so force
3180 // the name to uppercase just in case programmer forgot to do this
3181 if ((Dbms() == dbmsORACLE
) ||
3182 (Dbms() == dbmsINTERBASE
))
3183 TableName
= TableName
.Upper();
3185 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3187 // Some databases cannot accept a user name when looking up table names,
3188 // so we use the call below that leaves out the user name
3189 if (!Schema
.IsEmpty() &&
3190 Dbms() != dbmsMY_SQL
&&
3191 Dbms() != dbmsACCESS
&&
3192 Dbms() != dbmsMS_SQL_SERVER
)
3194 retcode
= SQLTablePrivileges(hstmt
,
3196 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3197 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3201 retcode
= SQLTablePrivileges(hstmt
,
3204 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3207 #ifdef DBDEBUG_CONSOLE
3208 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3211 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3212 return(DispAllErrors(henv
, hdbc
, hstmt
));
3214 bool failed
= false;
3215 retcode
= SQLFetch(hstmt
);
3216 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3218 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3221 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3224 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3227 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3230 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3233 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3236 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3241 return(DispAllErrors(henv
, hdbc
, hstmt
));
3243 #ifdef DBDEBUG_CONSOLE
3244 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3245 result
.privilege
,result
.tableOwner
,result
.tableName
,
3246 result
.grantor
, result
.grantee
);
3249 if (UserID
.IsSameAs(result
.tableOwner
,false))
3251 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3255 if (UserID
.IsSameAs(result
.grantee
,false) &&
3256 !wxStrcmp(result
.privilege
,priv
))
3258 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3262 if (!wxStrcmp(result
.grantee
,curRole
) &&
3263 !wxStrcmp(result
.privilege
,priv
))
3265 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3269 retcode
= SQLFetch(hstmt
);
3272 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3275 } // wxDb::TablePrivileges
3278 /********** wxDb::SetSqlLogging() **********/
3279 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3281 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3282 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3284 if (state
== sqlLogON
)
3288 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3289 if (fpSqlLog
== NULL
)
3297 if (fclose(fpSqlLog
))
3303 sqlLogState
= state
;
3306 } // wxDb::SetSqlLogging()
3309 /********** wxDb::WriteSqlLog() **********/
3310 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3312 wxASSERT(logMsg
.Length());
3314 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3317 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3319 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3321 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3326 } // wxDb::WriteSqlLog()
3329 /********** wxDb::Dbms() **********/
3330 wxDBMS
wxDb::Dbms(void)
3332 * Be aware that not all database engines use the exact same syntax, and not
3333 * every ODBC compliant database is compliant to the same level of compliancy.
3334 * Some manufacturers support the minimum Level 1 compliancy, and others up
3335 * through Level 3. Others support subsets of features for levels above 1.
3337 * If you find an inconsistency between the wxDb class and a specific database
3338 * engine, and an identifier to this section, and special handle the database in
3339 * the area where behavior is non-conforming with the other databases.
3342 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3343 * ---------------------------------------------------
3346 * - Currently the only database supported by the class to support VIEWS
3349 * - Does not support the SQL_TIMESTAMP structure
3350 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3351 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3352 * is true. The user must create ALL indexes from their program.
3353 * - Table names can only be 8 characters long
3354 * - Column names can only be 10 characters long
3357 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3358 * after every table name involved in the query/join if that tables matching record(s)
3360 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3362 * SYBASE (Enterprise)
3363 * - If a column is part of the Primary Key, the column cannot be NULL
3364 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3367 * - If a column is part of the Primary Key, the column cannot be NULL
3368 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns false
3369 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3370 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3371 * column definition if it is not defined correctly, but it is experimental
3372 * - Does not support sub-queries in SQL statements
3375 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3376 * - Does not support sub-queries in SQL statements
3379 * - Primary keys must be declared as NOT NULL
3380 * - Table and index names must not be longer than 13 characters in length (technically
3381 * table names can be up to 18 characters, but the primary index is created using the
3382 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3387 * - Columns that are part of primary keys must be defined as being NOT NULL
3388 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3389 * column definition if it is not defined correctly, but it is experimental
3392 // Should only need to do this once for each new database connection
3393 // so return the value we already determined it to be to save time
3394 // and lots of string comparisons
3395 if (dbmsType
!= dbmsUNIDENTIFIED
)
3398 wxChar baseName
[25+1];
3399 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3402 // RGG 20001025 : add support for Interbase
3403 // GT : Integrated to base classes on 20001121
3404 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3405 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3407 // BJO 20000428 : add support for Virtuoso
3408 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3409 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3411 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3412 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3414 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3415 // connected through an OpenLink driver.
3416 // Is it also returned by Sybase Adapatitve server?
3417 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3418 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3420 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3421 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3422 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3424 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3427 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3428 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3429 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3430 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3431 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3432 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3435 if (!wxStricmp(dbInf
.dbmsName
,wxT("Pervasive")))
3436 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3439 if (!wxStricmp(baseName
,wxT("Informix")))
3440 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3443 if (!wxStricmp(baseName
,wxT("Oracle")))
3444 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3445 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3446 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3447 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3448 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3449 if (!wxStricmp(baseName
,wxT("Sybase")))
3450 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3453 if (!wxStricmp(baseName
,wxT("DBASE")))
3454 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3457 if (!wxStricmp(baseName
,wxT("DB2")))
3458 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3460 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3465 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3466 int dataType
, ULONG columnLength
,
3467 const wxString
&optionalParam
)
3469 wxASSERT(tableName
.Length());
3470 wxASSERT(columnName
.Length());
3471 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3472 dataType
!= DB_DATA_TYPE_VARCHAR
);
3474 // Must specify a columnLength if modifying a VARCHAR type column
3475 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3478 wxString dataTypeName
;
3480 wxString alterSlashModify
;
3484 case DB_DATA_TYPE_VARCHAR
:
3485 dataTypeName
= typeInfVarchar
.TypeName
;
3487 case DB_DATA_TYPE_INTEGER
:
3488 dataTypeName
= typeInfInteger
.TypeName
;
3490 case DB_DATA_TYPE_FLOAT
:
3491 dataTypeName
= typeInfFloat
.TypeName
;
3493 case DB_DATA_TYPE_DATE
:
3494 dataTypeName
= typeInfDate
.TypeName
;
3496 case DB_DATA_TYPE_BLOB
:
3497 dataTypeName
= typeInfBlob
.TypeName
;
3503 // Set the modify or alter syntax depending on the type of database connected to
3507 alterSlashModify
= "MODIFY";
3509 case dbmsMS_SQL_SERVER
:
3510 alterSlashModify
= "ALTER COLUMN";
3512 case dbmsUNIDENTIFIED
:
3514 case dbmsSYBASE_ASA
:
3515 case dbmsSYBASE_ASE
:
3521 alterSlashModify
= "MODIFY";
3525 // create the SQL statement
3526 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3527 columnName
.c_str(), dataTypeName
.c_str());
3529 // For varchars only, append the size of the column
3530 if (dataType
== DB_DATA_TYPE_VARCHAR
)
3533 s
.Printf(wxT("(%d)"), columnLength
);
3537 // for passing things like "NOT NULL"
3538 if (optionalParam
.Length())
3540 sqlStmt
+= wxT(" ");
3541 sqlStmt
+= optionalParam
;
3544 return ExecSql(sqlStmt
);
3546 } // wxDb::ModifyColumn()
3549 /********** wxDbGetConnection() **********/
3550 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3554 // Used to keep a pointer to a DB connection that matches the requested
3555 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3556 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3557 // rather than having to re-query the datasource to get all the values
3558 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3559 wxDb
*matchingDbConnection
= NULL
;
3561 // Scan the linked list searching for an available database connection
3562 // that's already been opened but is currently not in use.
3563 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3565 // The database connection must be for the same datasource
3566 // name and must currently not be in use.
3568 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3569 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3571 pList
->Free
= false;
3572 return(pList
->PtrDb
);
3575 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3576 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3577 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3578 matchingDbConnection
= pList
->PtrDb
;
3581 // No available connections. A new connection must be made and
3582 // appended to the end of the linked list.
3585 // Find the end of the list
3586 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3587 // Append a new list item
3588 pList
->PtrNext
= new wxDbList
;
3589 pList
->PtrNext
->PtrPrev
= pList
;
3590 pList
= pList
->PtrNext
;
3594 // Create the first node on the list
3595 pList
= PtrBegDbList
= new wxDbList
;
3599 // Initialize new node in the linked list
3601 pList
->Free
= false;
3602 pList
->Dsn
= pDbConfig
->GetDsn();
3603 pList
->Uid
= pDbConfig
->GetUserID();
3604 pList
->AuthStr
= pDbConfig
->GetPassword();
3606 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3608 bool opened
= false;
3610 if (!matchingDbConnection
)
3611 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3613 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3615 // Connect to the datasource
3618 pList
->PtrDb
->setCached(true); // Prevent a user from deleting a cached connection
3619 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,true);
3620 return(pList
->PtrDb
);
3622 else // Unable to connect, destroy list item
3625 pList
->PtrPrev
->PtrNext
= 0;
3627 PtrBegDbList
= 0; // Empty list again
3628 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3629 pList
->PtrDb
->Close(); // Close the wxDb object
3630 delete pList
->PtrDb
; // Deletes the wxDb object
3631 delete pList
; // Deletes the linked list object
3635 } // wxDbGetConnection()
3638 /********** wxDbFreeConnection() **********/
3639 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3643 // Scan the linked list searching for the database connection
3644 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3646 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3647 return (pList
->Free
= true);
3650 // Never found the database object, return failure
3653 } // wxDbFreeConnection()
3656 /********** wxDbCloseConnections() **********/
3657 void WXDLLEXPORT
wxDbCloseConnections(void)
3659 wxDbList
*pList
, *pNext
;
3661 // Traverse the linked list closing database connections and freeing memory as I go.
3662 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3664 pNext
= pList
->PtrNext
; // Save the pointer to next
3665 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3666 pList
->PtrDb
->Close(); // Close the wxDb object
3667 pList
->PtrDb
->setCached(false); // Allows deletion of the wxDb instance
3668 delete pList
->PtrDb
; // Deletes the wxDb object
3669 delete pList
; // Deletes the linked list object
3672 // Mark the list as empty
3675 } // wxDbCloseConnections()
3678 /********** wxDbConnectionsInUse() **********/
3679 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3684 // Scan the linked list counting db connections that are currently in use
3685 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3687 if (pList
->Free
== false)
3693 } // wxDbConnectionsInUse()
3697 /********** wxDbLogExtendedErrorMsg() **********/
3698 // DEBUG ONLY function
3699 const wxChar WXDLLEXPORT
*wxDbLogExtendedErrorMsg(const wxChar
*userText
, wxDb
*pDb
,
3700 char *ErrFile
, int ErrLine
)
3702 static wxString msg
;
3707 if (ErrFile
|| ErrLine
)
3709 msg
+= wxT("File: ");
3711 msg
+= wxT(" Line: ");
3712 tStr
.Printf(wxT("%d"),ErrLine
);
3713 msg
+= tStr
.c_str();
3717 msg
.Append (wxT("\nODBC errors:\n"));
3720 // Display errors for this connection
3722 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3724 if (pDb
->errorList
[i
])
3726 msg
.Append(pDb
->errorList
[i
]);
3727 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3728 msg
.Append(wxT("\n"));
3729 // Clear the errmsg buffer so the next error will not
3730 // end up showing the previous error that have occurred
3731 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3736 wxLogDebug(msg
.c_str());
3739 } // wxDbLogExtendedErrorMsg()
3742 /********** wxDbSqlLog() **********/
3743 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3745 bool append
= false;
3748 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3750 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3755 SQLLOGstate
= state
;
3756 SQLLOGfn
= filename
;
3764 /********** wxDbCreateDataSource() **********/
3765 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3766 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3768 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3769 * Very rudimentary creation of an ODBC data source.
3771 * ODBC driver must be ODBC 3.0 compliant to use this function
3776 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3782 dsnLocation
= ODBC_ADD_SYS_DSN
;
3784 dsnLocation
= ODBC_ADD_DSN
;
3786 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3787 // so that is why I used it, as wxString does not deal well with
3788 // embedded nulls in strings
3789 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3791 // Replace the separator from above with the '\0' seperator needed
3792 // by the SQLConfigDataSource() function
3796 k
= setupStr
.Find((wxChar
)2,true);
3797 if (k
!= wxNOT_FOUND
)
3798 setupStr
[(UINT
)k
] = wxT('\0');
3800 while (k
!= wxNOT_FOUND
);
3802 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3803 driverName
, setupStr
.c_str());
3805 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3807 // check for errors caused by ConfigDSN based functions
3810 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3811 errMsg
[0] = wxT('\0');
3813 // This function is only supported in ODBC drivers v3.0 compliant and above
3814 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3817 #ifdef DBDEBUG_CONSOLE
3818 // When run in console mode, use standard out to display errors.
3819 cout
<< errMsg
<< endl
;
3820 cout
<< wxT("Press any key to continue...") << endl
;
3822 #endif // DBDEBUG_CONSOLE
3825 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3826 #endif // __WXDEBUG__
3832 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3833 // necessary to use this function, so this function is not supported
3835 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3838 #endif // __VISUALC__
3842 } // wxDbCreateDataSource()
3846 /********** wxDbGetDataSource() **********/
3847 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3848 SWORD DsDescMax
, DWORD direction
)
3850 * Dsn and DsDesc will contain the data source name and data source
3851 * description upon return
3856 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3857 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3862 } // wxDbGetDataSource()
3865 // Change this to 0 to remove use of all deprecated functions
3866 #if wxODBC_BACKWARD_COMPATABILITY
3867 /********************************************************************
3868 ********************************************************************
3870 * The following functions are all DEPRECATED and are included for
3871 * backward compatability reasons only
3873 ********************************************************************
3874 ********************************************************************/
3875 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3877 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3879 /***** DEPRECATED: use wxGetDataSource() *****/
3880 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3883 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3885 /***** DEPRECATED: use wxDbGetConnection() *****/
3886 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3888 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3890 /***** DEPRECATED: use wxDbFreeConnection() *****/
3891 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3893 return wxDbFreeConnection(pDb
);
3895 /***** DEPRECATED: use wxDbCloseConnections() *****/
3896 void WXDLLEXPORT
CloseDbConnections(void)
3898 wxDbCloseConnections();
3900 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3901 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3903 return wxDbConnectionsInUse();