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 ///////////////////////////////////////////////////////////////////////////////
37 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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"
58 #include "wx/filefn.h"
59 #include "wx/wxchar.h"
71 // DLL options compatibility check:
73 WX_CHECK_BUILD_OPTIONS("wxODBC")
75 WXDLLIMPEXP_DATA_ODBC(wxDbList
*) PtrBegDbList
= 0;
78 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
79 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
82 extern wxList TablesInUse
;
85 // SQL Log defaults to be used by GetDbConnection
86 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
88 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
90 // The wxDb::errorList is copied to this variable when the wxDb object
91 // is closed. This way, the error list is still available after the
92 // database object is closed. This is necessary if the database
93 // connection fails so the calling application can show the operator
94 // why the connection failed. Note: as each wxDb object is closed, it
95 // will overwrite the errors of the previously destroyed wxDb object in
96 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
98 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
101 // This type defines the return row-struct form
102 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
105 wxChar tableQual
[128+1];
106 wxChar tableOwner
[128+1];
107 wxChar tableName
[128+1];
108 wxChar grantor
[128+1];
109 wxChar grantee
[128+1];
110 wxChar privilege
[128+1];
111 wxChar grantable
[3+1];
112 } wxDbTablePrivilegeInfo
;
115 /********** wxDbConnectInf Constructor - form 1 **********/
116 wxDbConnectInf::wxDbConnectInf()
119 freeHenvOnDestroy
= FALSE
;
125 /********** wxDbConnectInf Constructor - form 2 **********/
126 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
127 const wxString
&password
, const wxString
&defaultDir
,
128 const wxString
&fileType
, const wxString
&description
)
131 freeHenvOnDestroy
= FALSE
;
142 SetPassword(password
);
143 SetDescription(description
);
144 SetFileType(fileType
);
145 SetDefaultDir(defaultDir
);
146 } // wxDbConnectInf Constructor
149 wxDbConnectInf::~wxDbConnectInf()
151 if (freeHenvOnDestroy
)
155 } // wxDbConnectInf Destructor
159 /********** wxDbConnectInf::Initialize() **********/
160 bool wxDbConnectInf::Initialize()
162 freeHenvOnDestroy
= FALSE
;
164 if (freeHenvOnDestroy
&& Henv
)
176 } // wxDbConnectInf::Initialize()
179 /********** wxDbConnectInf::AllocHenv() **********/
180 bool wxDbConnectInf::AllocHenv()
182 // This is here to help trap if you are getting a new henv
183 // without releasing an existing henv
186 // Initialize the ODBC Environment for Database Operations
187 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
189 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
193 freeHenvOnDestroy
= TRUE
;
196 } // wxDbConnectInf::AllocHenv()
199 void wxDbConnectInf::FreeHenv()
207 freeHenvOnDestroy
= FALSE
;
209 } // wxDbConnectInf::FreeHenv()
212 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
214 wxASSERT(dsn
.Length() < sizeof(Dsn
));
217 } // wxDbConnectInf::SetDsn()
220 void wxDbConnectInf::SetUserID(const wxString
&uid
)
222 wxASSERT(uid
.Length() < sizeof(Uid
));
224 } // wxDbConnectInf::SetUserID()
227 void wxDbConnectInf::SetPassword(const wxString
&password
)
229 wxASSERT(password
.Length() < sizeof(AuthStr
));
231 wxStrcpy(AuthStr
,password
);
232 } // wxDbConnectInf::SetPassword()
236 /********** wxDbColFor Constructor **********/
237 wxDbColFor::wxDbColFor()
240 } // wxDbColFor::wxDbColFor()
243 wxDbColFor::~wxDbColFor()
245 } // wxDbColFor::~wxDbColFor()
248 /********** wxDbColFor::Initialize() **********/
249 void wxDbColFor::Initialize()
259 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
262 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
263 } // wxDbColFor::Initialize()
266 /********** wxDbColFor::Format() **********/
267 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
268 short columnSize
, short decimalDigits
)
270 // ----------------------------------------------------------------------------------------
271 // -- 19991224 : mj10777 : Create
272 // There is still a lot of work to do here, but it is a start
273 // It handles all the basic data-types that I have run into up to now
274 // The main work will have be with Dates and float Formatting
275 // (US 1,000.00 ; EU 1.000,00)
276 // There are wxWindow plans for locale support and the new wxDateTime. If
277 // they define some constants (wxEUROPEAN) that can be gloably used,
278 // they should be used here.
279 // ----------------------------------------------------------------------------------------
280 // There should also be a function to scan in a string to fill the variable
281 // ----------------------------------------------------------------------------------------
283 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
284 i_dbDataType
= dbDataType
;
285 i_sqlDataType
= sqlDataType
;
286 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
288 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
290 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
291 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
292 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
293 i_dbDataType
= DB_DATA_TYPE_DATE
;
294 if (i_sqlDataType
== SQL_C_BIT
)
295 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
296 if (i_sqlDataType
== SQL_NUMERIC
)
297 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
298 if (i_sqlDataType
== SQL_REAL
)
299 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
300 if (i_sqlDataType
== SQL_C_BINARY
)
301 i_dbDataType
= DB_DATA_TYPE_BLOB
;
304 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
306 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
309 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
311 case DB_DATA_TYPE_VARCHAR
:
314 case DB_DATA_TYPE_INTEGER
:
317 case DB_DATA_TYPE_FLOAT
:
318 if (decimalDigits
== 0)
321 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
322 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
324 case DB_DATA_TYPE_DATE
:
325 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
327 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
329 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
331 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
333 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
335 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
337 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
339 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
341 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
343 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
346 case DB_DATA_TYPE_BLOB
:
347 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
350 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
354 } // wxDbColFor::Format()
358 /********** wxDbColInf Constructor **********/
359 wxDbColInf::wxDbColInf()
362 } // wxDbColInf::wxDbColInf()
365 /********** wxDbColInf Destructor ********/
366 wxDbColInf::~wxDbColInf()
371 } // wxDbColInf::~wxDbColInf()
374 bool wxDbColInf::Initialize()
396 } // wxDbColInf::Initialize()
399 /********** wxDbTableInf Constructor ********/
400 wxDbTableInf::wxDbTableInf()
403 } // wxDbTableInf::wxDbTableInf()
406 /********** wxDbTableInf Constructor ********/
407 wxDbTableInf::~wxDbTableInf()
412 } // wxDbTableInf::~wxDbTableInf()
415 bool wxDbTableInf::Initialize()
424 } // wxDbTableInf::Initialize()
427 /********** wxDbInf Constructor *************/
431 } // wxDbInf::wxDbInf()
434 /********** wxDbInf Destructor *************/
440 } // wxDbInf::~wxDbInf()
443 /********** wxDbInf::Initialize() *************/
444 bool wxDbInf::Initialize()
452 } // wxDbInf::Initialize()
455 /********** wxDb Constructor **********/
456 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
458 // Copy the HENV into the db class
460 fwdOnlyCursors
= FwdOnlyCursors
;
466 /********** wxDb Destructor **********/
469 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
479 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
480 /********** wxDb::initialize() **********/
481 void wxDb::initialize()
483 * Private member function that sets all wxDb member variables to
484 * known values at creation of the wxDb
489 fpSqlLog
= 0; // Sql Log file pointer
490 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
492 dbmsType
= dbmsUNIDENTIFIED
;
494 wxStrcpy(sqlState
,wxEmptyString
);
495 wxStrcpy(errorMsg
,wxEmptyString
);
496 nativeError
= cbErrorMsg
= 0;
497 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
498 wxStrcpy(errorList
[i
], wxEmptyString
);
500 // Init typeInf structures
501 typeInfVarchar
.TypeName
.Empty();
502 typeInfVarchar
.FsqlType
= 0;
503 typeInfVarchar
.Precision
= 0;
504 typeInfVarchar
.CaseSensitive
= 0;
505 typeInfVarchar
.MaximumScale
= 0;
507 typeInfInteger
.TypeName
.Empty();
508 typeInfInteger
.FsqlType
= 0;
509 typeInfInteger
.Precision
= 0;
510 typeInfInteger
.CaseSensitive
= 0;
511 typeInfInteger
.MaximumScale
= 0;
513 typeInfFloat
.TypeName
.Empty();
514 typeInfFloat
.FsqlType
= 0;
515 typeInfFloat
.Precision
= 0;
516 typeInfFloat
.CaseSensitive
= 0;
517 typeInfFloat
.MaximumScale
= 0;
519 typeInfDate
.TypeName
.Empty();
520 typeInfDate
.FsqlType
= 0;
521 typeInfDate
.Precision
= 0;
522 typeInfDate
.CaseSensitive
= 0;
523 typeInfDate
.MaximumScale
= 0;
525 typeInfBlob
.TypeName
.Empty();
526 typeInfBlob
.FsqlType
= 0;
527 typeInfBlob
.Precision
= 0;
528 typeInfBlob
.CaseSensitive
= 0;
529 typeInfBlob
.MaximumScale
= 0;
531 // Error reporting is turned OFF by default
534 // Allocate a data source connection handle
535 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
538 // Initialize the db status flag
541 // Mark database as not open as of yet
544 } // wxDb::initialize()
547 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
549 // NOTE: Return value from this function MUST be copied
550 // immediately, as the value is not good after
551 // this function has left scope.
553 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
557 if (!wxStrlen(userID
))
565 // dBase does not use user names, and some drivers fail if you try to pass one
566 if ( Dbms() == dbmsDBASE
567 || Dbms() == dbmsXBASE_SEQUITER
)
570 // Oracle user names may only be in uppercase, so force
571 // the name to uppercase
572 if (Dbms() == dbmsORACLE
)
573 UserID
= UserID
.Upper();
575 return UserID
.c_str();
576 } // wxDb::convertUserID()
579 /********** wxDb::Open() **********/
580 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
582 wxASSERT(Dsn
.Length());
589 if (!FwdOnlyCursors())
591 // Specify that the ODBC cursor library be used, if needed. This must be
592 // specified before the connection is made.
593 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
595 #ifdef DBDEBUG_CONSOLE
596 if (retcode
== SQL_SUCCESS
)
597 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
599 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
603 // Connect to the data source
604 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
605 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
606 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
608 if ((retcode
!= SQL_SUCCESS
) &&
609 (retcode
!= SQL_SUCCESS_WITH_INFO
))
610 return(DispAllErrors(henv
, hdbc
));
613 If using Intersolv branded ODBC drivers, this is the place where you would substitute
614 your branded driver license information
616 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
617 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
620 // Mark database as open
623 // Allocate a statement handle for the database connection
624 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
625 return(DispAllErrors(henv
, hdbc
));
627 // Set Connection Options
628 if (!setConnectionOptions())
631 // Query the data source for inf. about itself
635 // Query the data source regarding data type information
638 // The way it was determined which SQL data types to use was by calling SQLGetInfo
639 // for all of the possible SQL data types to see which ones were supported. If
640 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
641 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
642 // types I've selected below will not alway's be what we want. These are just
643 // what happened to work against an Oracle 7/Intersolv combination. The following is
644 // a complete list of the results I got back against the Oracle 7 database:
646 // SQL_BIGINT SQL_NO_DATA_FOUND
647 // SQL_BINARY SQL_NO_DATA_FOUND
648 // SQL_BIT SQL_NO_DATA_FOUND
649 // SQL_CHAR type name = 'CHAR', Precision = 255
650 // SQL_DATE SQL_NO_DATA_FOUND
651 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
652 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
653 // SQL_FLOAT SQL_NO_DATA_FOUND
654 // SQL_INTEGER SQL_NO_DATA_FOUND
655 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
656 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
657 // SQL_NUMERIC SQL_NO_DATA_FOUND
658 // SQL_REAL SQL_NO_DATA_FOUND
659 // SQL_SMALLINT SQL_NO_DATA_FOUND
660 // SQL_TIME SQL_NO_DATA_FOUND
661 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
662 // SQL_VARBINARY type name = 'RAW', Precision = 255
663 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
664 // =====================================================================
665 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
667 // SQL_VARCHAR type name = 'TEXT', Precision = 255
668 // SQL_TIMESTAMP type name = 'DATETIME'
669 // SQL_DECIMAL SQL_NO_DATA_FOUND
670 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
671 // SQL_FLOAT SQL_NO_DATA_FOUND
672 // SQL_REAL type name = 'SINGLE', Precision = 7
673 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
674 // SQL_INTEGER type name = 'LONG', Precision = 10
676 // VARCHAR = Variable length character string
677 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
678 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
681 typeInfVarchar
.FsqlType
= SQL_CHAR
;
683 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
686 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
687 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
688 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
689 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
690 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
692 if (failOnDataTypeUnsupported
)
696 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
698 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
700 typeInfFloat
.FsqlType
= SQL_FLOAT
;
702 typeInfFloat
.FsqlType
= SQL_REAL
;
704 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
707 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
709 // If SQL_INTEGER is not supported, use the floating point
710 // data type to store integers as well as floats
711 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
713 if (failOnDataTypeUnsupported
)
717 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
720 typeInfInteger
.FsqlType
= SQL_INTEGER
;
723 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
725 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
728 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
730 typeInfDate
.FsqlType
= SQL_TIME
;
733 #endif // SQL_DATETIME defined
735 if (failOnDataTypeUnsupported
)
740 typeInfDate
.FsqlType
= SQL_DATE
;
743 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
746 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
748 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
750 if (failOnDataTypeUnsupported
)
754 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
757 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
759 //typeInfBlob.TypeName = "BLOB";
761 #ifdef DBDEBUG_CONSOLE
762 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
763 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
764 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
765 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
766 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
770 // Completed Successfully
776 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
778 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
779 dbConnectInf
->GetPassword());
783 bool wxDb::Open(wxDb
*copyDb
)
785 dsn
= copyDb
->GetDatasourceName();
786 uid
= copyDb
->GetUsername();
787 authStr
= copyDb
->GetPassword();
791 if (!FwdOnlyCursors())
793 // Specify that the ODBC cursor library be used, if needed. This must be
794 // specified before the connection is made.
795 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
797 #ifdef DBDEBUG_CONSOLE
798 if (retcode
== SQL_SUCCESS
)
799 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
801 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
805 // Connect to the data source
806 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
807 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
808 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
810 if (retcode
== SQL_ERROR
)
811 return(DispAllErrors(henv
, hdbc
));
814 If using Intersolv branded ODBC drivers, this is the place where you would substitute
815 your branded driver license information
817 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
818 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
821 // Mark database as open
824 // Allocate a statement handle for the database connection
825 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
826 return(DispAllErrors(henv
, hdbc
));
828 // Set Connection Options
829 if (!setConnectionOptions())
832 // Instead of Querying the data source for info about itself, it can just be copied
833 // from the wxDb instance that was passed in (copyDb).
834 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
835 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
836 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
837 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
838 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
839 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
840 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
841 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
842 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
843 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
844 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
845 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
846 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
847 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
848 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
849 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
850 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
851 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
852 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
853 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
854 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
855 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
856 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
857 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
858 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
859 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
860 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
861 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
862 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
863 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
864 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
866 // VARCHAR = Variable length character string
867 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
868 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
869 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
870 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
871 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
874 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
875 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
876 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
877 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
878 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
881 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
882 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
883 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
884 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
885 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
888 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
889 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
890 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
891 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
892 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
895 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
896 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
897 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
898 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
899 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
901 #ifdef DBDEBUG_CONSOLE
902 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
903 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
904 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
905 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
906 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
910 // Completed Successfully
915 /********** wxDb::setConnectionOptions() **********/
916 bool wxDb::setConnectionOptions(void)
918 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
923 // I need to get the DBMS name here, because some of the connection options
924 // are database specific and need to call the Dbms() function.
925 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
926 return(DispAllErrors(henv
, hdbc
));
928 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
929 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
930 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
932 // By default, MS Sql Server closes cursors on commit and rollback. The following
933 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
934 // after a transaction. This is a driver specific option and is not part of the
935 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
936 // The database settings don't have any effect one way or the other.
937 if (Dbms() == dbmsMS_SQL_SERVER
)
939 const long SQL_PRESERVE_CURSORS
= 1204L;
940 const long SQL_PC_ON
= 1L;
941 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
944 // Display the connection options to verify them
945 #ifdef DBDEBUG_CONSOLE
947 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
949 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
950 return(DispAllErrors(henv
, hdbc
));
951 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
953 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
954 return(DispAllErrors(henv
, hdbc
));
955 cout
<< wxT("ODBC CURSORS: ");
958 case(SQL_CUR_USE_IF_NEEDED
):
959 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
961 case(SQL_CUR_USE_ODBC
):
962 cout
<< wxT("SQL_CUR_USE_ODBC");
964 case(SQL_CUR_USE_DRIVER
):
965 cout
<< wxT("SQL_CUR_USE_DRIVER");
970 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
971 return(DispAllErrors(henv
, hdbc
));
972 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
977 // Completed Successfully
980 } // wxDb::setConnectionOptions()
983 /********** wxDb::getDbInfo() **********/
984 bool wxDb::getDbInfo(void)
989 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
990 return(DispAllErrors(henv
, hdbc
));
992 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
993 return(DispAllErrors(henv
, hdbc
));
995 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
996 return(DispAllErrors(henv
, hdbc
));
999 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1000 // causing database connectivity to fail in some cases.
1001 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1003 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1007 return(DispAllErrors(henv
, hdbc
));
1009 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1010 return(DispAllErrors(henv
, hdbc
));
1012 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1013 return(DispAllErrors(henv
, hdbc
));
1015 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1016 return(DispAllErrors(henv
, hdbc
));
1018 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1019 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1020 return(DispAllErrors(henv
, hdbc
));
1022 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1023 return(DispAllErrors(henv
, hdbc
));
1025 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1026 return(DispAllErrors(henv
, hdbc
));
1028 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1029 // return(DispAllErrors(henv, hdbc));
1031 // Not all drivers support this call - Nick Gorham(unixODBC)
1032 dbInf
.cliConfLvl
= 0;
1035 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1036 return(DispAllErrors(henv
, hdbc
));
1038 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1039 return(DispAllErrors(henv
, hdbc
));
1041 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1042 return(DispAllErrors(henv
, hdbc
));
1044 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1045 return(DispAllErrors(henv
, hdbc
));
1047 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1048 return(DispAllErrors(henv
, hdbc
));
1050 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1051 return(DispAllErrors(henv
, hdbc
));
1053 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1054 return(DispAllErrors(henv
, hdbc
));
1056 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1057 return(DispAllErrors(henv
, hdbc
));
1059 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1060 return(DispAllErrors(henv
, hdbc
));
1062 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1063 return(DispAllErrors(henv
, hdbc
));
1065 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1066 return(DispAllErrors(henv
, hdbc
));
1068 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1069 return(DispAllErrors(henv
, hdbc
));
1071 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1072 return(DispAllErrors(henv
, hdbc
));
1074 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1075 return(DispAllErrors(henv
, hdbc
));
1077 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1078 return(DispAllErrors(henv
, hdbc
));
1080 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1081 return(DispAllErrors(henv
, hdbc
));
1083 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1084 return(DispAllErrors(henv
, hdbc
));
1086 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1087 return(DispAllErrors(henv
, hdbc
));
1089 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1090 return(DispAllErrors(henv
, hdbc
));
1092 #ifdef DBDEBUG_CONSOLE
1093 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1094 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1095 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1096 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1098 cout
<< wxT("API Conf. Level: ");
1099 switch(dbInf
.apiConfLvl
)
1101 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1102 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1103 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1107 cout
<< wxT("SAG CLI Conf. Level: ");
1108 switch(dbInf
.cliConfLvl
)
1110 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1111 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1115 cout
<< wxT("SQL Conf. Level: ");
1116 switch(dbInf
.sqlConfLvl
)
1118 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1119 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1120 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1124 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1125 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1126 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1127 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1128 cout
<< wxT("Cursor COMMIT Behavior: ");
1129 switch(dbInf
.cursorCommitBehavior
)
1131 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1132 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1133 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1137 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1138 switch(dbInf
.cursorRollbackBehavior
)
1140 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1141 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1142 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1146 cout
<< wxT("Support NOT NULL clause: ");
1147 switch(dbInf
.supportNotNullClause
)
1149 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1150 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1154 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1155 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1157 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1160 cout
<< wxT("Default Transaction Isolation: ";
1161 switch(dbInf
.txnIsolation
)
1163 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1164 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1165 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1166 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1168 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1173 cout
<< wxT("Transaction Isolation Options: ");
1174 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1175 cout
<< wxT("Read Uncommitted, ");
1176 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1177 cout
<< wxT("Read Committed, ");
1178 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1179 cout
<< wxT("Repeatable Read, ");
1180 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1181 cout
<< wxT("Serializable, ");
1183 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1184 cout
<< wxT("Versioning");
1188 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1189 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1190 cout
<< wxT("Next, ");
1191 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1192 cout
<< wxT("Prev, ");
1193 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1194 cout
<< wxT("First, ");
1195 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1196 cout
<< wxT("Last, ");
1197 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1198 cout
<< wxT("Absolute, ");
1199 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1200 cout
<< wxT("Relative, ");
1202 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1203 cout
<< wxT("Resume, ");
1205 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1206 cout
<< wxT("Bookmark");
1209 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1210 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1211 cout
<< wxT("No Change, ");
1212 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1213 cout
<< wxT("Exclusive, ");
1214 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1215 cout
<< wxT("UnLock");
1218 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1219 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1220 cout
<< wxT("Position, ");
1221 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1222 cout
<< wxT("Refresh, ");
1223 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1224 cout
<< wxT("Upd, "));
1225 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1226 cout
<< wxT("Del, ");
1227 if (dbInf
.posOperations
& SQL_POS_ADD
)
1231 cout
<< wxT("Positioned Statements Supported: ");
1232 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1233 cout
<< wxT("Pos delete, ");
1234 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1235 cout
<< wxT("Pos update, ");
1236 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1237 cout
<< wxT("Select for update");
1240 cout
<< wxT("Scroll Concurrency: ");
1241 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1242 cout
<< wxT("Read Only, ");
1243 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1244 cout
<< wxT("Lock, ");
1245 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1246 cout
<< wxT("Opt. Rowver, ");
1247 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1248 cout
<< wxT("Opt. Values");
1251 cout
<< wxT("Scroll Options: ");
1252 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1253 cout
<< wxT("Fwd Only, ");
1254 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1255 cout
<< wxT("Static, ");
1256 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1257 cout
<< wxT("Keyset Driven, ");
1258 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1259 cout
<< wxT("Dynamic, ");
1260 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1261 cout
<< wxT("Mixed");
1264 cout
<< wxT("Static Sensitivity: ");
1265 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1266 cout
<< wxT("Additions, ");
1267 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1268 cout
<< wxT("Deletions, ");
1269 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1270 cout
<< wxT("Updates");
1273 cout
<< wxT("Transaction Capable?: ");
1274 switch(dbInf
.txnCapable
)
1276 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1277 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1278 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1279 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1280 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1287 // Completed Successfully
1290 } // wxDb::getDbInfo()
1293 /********** wxDb::getDataTypeInfo() **********/
1294 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1297 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1298 * the data type inf. is gathered for.
1300 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1305 // Get information about the data type specified
1306 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1307 return(DispAllErrors(henv
, hdbc
, hstmt
));
1310 retcode
= SQLFetch(hstmt
);
1311 if (retcode
!= SQL_SUCCESS
)
1313 #ifdef DBDEBUG_CONSOLE
1314 if (retcode
== SQL_NO_DATA_FOUND
)
1315 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1317 DispAllErrors(henv
, hdbc
, hstmt
);
1318 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1322 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1324 // Obtain columns from the record
1325 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1326 return(DispAllErrors(henv
, hdbc
, hstmt
));
1328 structSQLTypeInfo
.TypeName
= typeName
;
1330 // BJO 20000503: no more needed with new GetColumns...
1333 if (Dbms() == dbmsMY_SQL
)
1335 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1336 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1337 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1338 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1339 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1340 structSQLTypeInfo
.TypeName
= wxT("int");
1341 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1342 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1343 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1344 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1345 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1346 structSQLTypeInfo
.TypeName
= wxT("char");
1349 // BJO 20000427 : OpenLink driver
1350 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1351 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1353 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1354 structSQLTypeInfo
.TypeName
= wxT("real");
1358 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1359 return(DispAllErrors(henv
, hdbc
, hstmt
));
1360 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1361 return(DispAllErrors(henv
, hdbc
, hstmt
));
1362 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1363 // return(DispAllErrors(henv, hdbc, hstmt));
1365 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1366 return(DispAllErrors(henv
, hdbc
, hstmt
));
1368 if (structSQLTypeInfo
.MaximumScale
< 0)
1369 structSQLTypeInfo
.MaximumScale
= 0;
1371 // Close the statement handle which closes open cursors
1372 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1373 return(DispAllErrors(henv
, hdbc
, hstmt
));
1375 // Completed Successfully
1378 } // wxDb::getDataTypeInfo()
1381 /********** wxDb::Close() **********/
1382 void wxDb::Close(void)
1384 // Close the Sql Log file
1391 // Free statement handle
1394 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1395 DispAllErrors(henv
, hdbc
);
1398 // Disconnect from the datasource
1399 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1400 DispAllErrors(henv
, hdbc
);
1402 // Free the connection to the datasource
1403 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1404 DispAllErrors(henv
, hdbc
);
1406 // There should be zero Ctable objects still connected to this db object
1407 wxASSERT(nTables
== 0);
1412 pNode
= TablesInUse
.First();
1416 tiu
= (wxTablesInUse
*)pNode
->Data();
1417 if (tiu
->pDb
== this)
1419 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1420 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1421 wxLogDebug (s
.c_str(),s2
.c_str());
1423 pNode
= pNode
->Next();
1427 // Copy the error messages to a global variable
1429 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1430 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1432 dbmsType
= dbmsUNIDENTIFIED
;
1438 /********** wxDb::CommitTrans() **********/
1439 bool wxDb::CommitTrans(void)
1443 // Commit the transaction
1444 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1445 return(DispAllErrors(henv
, hdbc
));
1448 // Completed successfully
1451 } // wxDb::CommitTrans()
1454 /********** wxDb::RollbackTrans() **********/
1455 bool wxDb::RollbackTrans(void)
1457 // Rollback the transaction
1458 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1459 return(DispAllErrors(henv
, hdbc
));
1461 // Completed successfully
1464 } // wxDb::RollbackTrans()
1467 /********** wxDb::DispAllErrors() **********/
1468 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1470 * This function is called internally whenever an error condition prevents the user's
1471 * request from being executed. This function will query the datasource as to the
1472 * actual error(s) that just occured on the previous request of the datasource.
1474 * The function will retrieve each error condition from the datasource and
1475 * Printf the codes/text values into a string which it then logs via logError().
1476 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1477 * window and program execution will be paused until the user presses a key.
1479 * This function always returns a FALSE, so that functions which call this function
1480 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1481 * of the users request, so that the calling code can then process the error msg log
1484 wxString odbcErrMsg
;
1486 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1488 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1489 logError(odbcErrMsg
, sqlState
);
1492 #ifdef DBDEBUG_CONSOLE
1493 // When run in console mode, use standard out to display errors.
1494 cout
<< odbcErrMsg
.c_str() << endl
;
1495 cout
<< wxT("Press any key to continue...") << endl
;
1500 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1505 return(FALSE
); // This function always returns FALSE.
1507 } // wxDb::DispAllErrors()
1510 /********** wxDb::GetNextError() **********/
1511 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1513 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1518 } // wxDb::GetNextError()
1521 /********** wxDb::DispNextError() **********/
1522 void wxDb::DispNextError(void)
1524 wxString odbcErrMsg
;
1526 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1527 logError(odbcErrMsg
, sqlState
);
1532 #ifdef DBDEBUG_CONSOLE
1533 // When run in console mode, use standard out to display errors.
1534 cout
<< odbcErrMsg
.c_str() << endl
;
1535 cout
<< wxT("Press any key to continue...") << endl
;
1540 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1541 #endif // __WXDEBUG__
1543 } // wxDb::DispNextError()
1546 /********** wxDb::logError() **********/
1547 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1549 wxASSERT(errMsg
.Length());
1551 static int pLast
= -1;
1554 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1557 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1558 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1562 wxStrcpy(errorList
[pLast
], errMsg
);
1564 if (SQLState
.Length())
1565 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1566 DB_STATUS
= dbStatus
;
1568 // Add the errmsg to the sql log
1569 WriteSqlLog(errMsg
);
1571 } // wxDb::logError()
1574 /**********wxDb::TranslateSqlState() **********/
1575 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1577 if (!wxStrcmp(SQLState
, wxT("01000")))
1578 return(DB_ERR_GENERAL_WARNING
);
1579 if (!wxStrcmp(SQLState
, wxT("01002")))
1580 return(DB_ERR_DISCONNECT_ERROR
);
1581 if (!wxStrcmp(SQLState
, wxT("01004")))
1582 return(DB_ERR_DATA_TRUNCATED
);
1583 if (!wxStrcmp(SQLState
, wxT("01006")))
1584 return(DB_ERR_PRIV_NOT_REVOKED
);
1585 if (!wxStrcmp(SQLState
, wxT("01S00")))
1586 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1587 if (!wxStrcmp(SQLState
, wxT("01S01")))
1588 return(DB_ERR_ERROR_IN_ROW
);
1589 if (!wxStrcmp(SQLState
, wxT("01S02")))
1590 return(DB_ERR_OPTION_VALUE_CHANGED
);
1591 if (!wxStrcmp(SQLState
, wxT("01S03")))
1592 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1593 if (!wxStrcmp(SQLState
, wxT("01S04")))
1594 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1595 if (!wxStrcmp(SQLState
, wxT("07001")))
1596 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1597 if (!wxStrcmp(SQLState
, wxT("07006")))
1598 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1599 if (!wxStrcmp(SQLState
, wxT("08001")))
1600 return(DB_ERR_UNABLE_TO_CONNECT
);
1601 if (!wxStrcmp(SQLState
, wxT("08002")))
1602 return(DB_ERR_CONNECTION_IN_USE
);
1603 if (!wxStrcmp(SQLState
, wxT("08003")))
1604 return(DB_ERR_CONNECTION_NOT_OPEN
);
1605 if (!wxStrcmp(SQLState
, wxT("08004")))
1606 return(DB_ERR_REJECTED_CONNECTION
);
1607 if (!wxStrcmp(SQLState
, wxT("08007")))
1608 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1609 if (!wxStrcmp(SQLState
, wxT("08S01")))
1610 return(DB_ERR_COMM_LINK_FAILURE
);
1611 if (!wxStrcmp(SQLState
, wxT("21S01")))
1612 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1613 if (!wxStrcmp(SQLState
, wxT("21S02")))
1614 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1615 if (!wxStrcmp(SQLState
, wxT("22001")))
1616 return(DB_ERR_STRING_RIGHT_TRUNC
);
1617 if (!wxStrcmp(SQLState
, wxT("22003")))
1618 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1619 if (!wxStrcmp(SQLState
, wxT("22005")))
1620 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1621 if (!wxStrcmp(SQLState
, wxT("22008")))
1622 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1623 if (!wxStrcmp(SQLState
, wxT("22012")))
1624 return(DB_ERR_DIVIDE_BY_ZERO
);
1625 if (!wxStrcmp(SQLState
, wxT("22026")))
1626 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1627 if (!wxStrcmp(SQLState
, wxT("23000")))
1628 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1629 if (!wxStrcmp(SQLState
, wxT("24000")))
1630 return(DB_ERR_INVALID_CURSOR_STATE
);
1631 if (!wxStrcmp(SQLState
, wxT("25000")))
1632 return(DB_ERR_INVALID_TRANS_STATE
);
1633 if (!wxStrcmp(SQLState
, wxT("28000")))
1634 return(DB_ERR_INVALID_AUTH_SPEC
);
1635 if (!wxStrcmp(SQLState
, wxT("34000")))
1636 return(DB_ERR_INVALID_CURSOR_NAME
);
1637 if (!wxStrcmp(SQLState
, wxT("37000")))
1638 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1639 if (!wxStrcmp(SQLState
, wxT("3C000")))
1640 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1641 if (!wxStrcmp(SQLState
, wxT("40001")))
1642 return(DB_ERR_SERIALIZATION_FAILURE
);
1643 if (!wxStrcmp(SQLState
, wxT("42000")))
1644 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1645 if (!wxStrcmp(SQLState
, wxT("70100")))
1646 return(DB_ERR_OPERATION_ABORTED
);
1647 if (!wxStrcmp(SQLState
, wxT("IM001")))
1648 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1649 if (!wxStrcmp(SQLState
, wxT("IM002")))
1650 return(DB_ERR_NO_DATA_SOURCE
);
1651 if (!wxStrcmp(SQLState
, wxT("IM003")))
1652 return(DB_ERR_DRIVER_LOAD_ERROR
);
1653 if (!wxStrcmp(SQLState
, wxT("IM004")))
1654 return(DB_ERR_SQLALLOCENV_FAILED
);
1655 if (!wxStrcmp(SQLState
, wxT("IM005")))
1656 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1657 if (!wxStrcmp(SQLState
, wxT("IM006")))
1658 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1659 if (!wxStrcmp(SQLState
, wxT("IM007")))
1660 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1661 if (!wxStrcmp(SQLState
, wxT("IM008")))
1662 return(DB_ERR_DIALOG_FAILED
);
1663 if (!wxStrcmp(SQLState
, wxT("IM009")))
1664 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1665 if (!wxStrcmp(SQLState
, wxT("IM010")))
1666 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1667 if (!wxStrcmp(SQLState
, wxT("IM011")))
1668 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1669 if (!wxStrcmp(SQLState
, wxT("IM012")))
1670 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1671 if (!wxStrcmp(SQLState
, wxT("IM013")))
1672 return(DB_ERR_TRACE_FILE_ERROR
);
1673 if (!wxStrcmp(SQLState
, wxT("S0001")))
1674 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1675 if (!wxStrcmp(SQLState
, wxT("S0002")))
1676 return(DB_ERR_TABLE_NOT_FOUND
);
1677 if (!wxStrcmp(SQLState
, wxT("S0011")))
1678 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1679 if (!wxStrcmp(SQLState
, wxT("S0012")))
1680 return(DB_ERR_INDEX_NOT_FOUND
);
1681 if (!wxStrcmp(SQLState
, wxT("S0021")))
1682 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1683 if (!wxStrcmp(SQLState
, wxT("S0022")))
1684 return(DB_ERR_COLUMN_NOT_FOUND
);
1685 if (!wxStrcmp(SQLState
, wxT("S0023")))
1686 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1687 if (!wxStrcmp(SQLState
, wxT("S1000")))
1688 return(DB_ERR_GENERAL_ERROR
);
1689 if (!wxStrcmp(SQLState
, wxT("S1001")))
1690 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1691 if (!wxStrcmp(SQLState
, wxT("S1002")))
1692 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1693 if (!wxStrcmp(SQLState
, wxT("S1003")))
1694 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1695 if (!wxStrcmp(SQLState
, wxT("S1004")))
1696 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1697 if (!wxStrcmp(SQLState
, wxT("S1008")))
1698 return(DB_ERR_OPERATION_CANCELLED
);
1699 if (!wxStrcmp(SQLState
, wxT("S1009")))
1700 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1701 if (!wxStrcmp(SQLState
, wxT("S1010")))
1702 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1703 if (!wxStrcmp(SQLState
, wxT("S1011")))
1704 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1705 if (!wxStrcmp(SQLState
, wxT("S1012")))
1706 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1707 if (!wxStrcmp(SQLState
, wxT("S1015")))
1708 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1709 if (!wxStrcmp(SQLState
, wxT("S1090")))
1710 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1711 if (!wxStrcmp(SQLState
, wxT("S1091")))
1712 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1713 if (!wxStrcmp(SQLState
, wxT("S1092")))
1714 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1715 if (!wxStrcmp(SQLState
, wxT("S1093")))
1716 return(DB_ERR_INVALID_PARAM_NO
);
1717 if (!wxStrcmp(SQLState
, wxT("S1094")))
1718 return(DB_ERR_INVALID_SCALE_VALUE
);
1719 if (!wxStrcmp(SQLState
, wxT("S1095")))
1720 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1721 if (!wxStrcmp(SQLState
, wxT("S1096")))
1722 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1723 if (!wxStrcmp(SQLState
, wxT("S1097")))
1724 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1725 if (!wxStrcmp(SQLState
, wxT("S1098")))
1726 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1727 if (!wxStrcmp(SQLState
, wxT("S1099")))
1728 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1729 if (!wxStrcmp(SQLState
, wxT("S1100")))
1730 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1731 if (!wxStrcmp(SQLState
, wxT("S1101")))
1732 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1733 if (!wxStrcmp(SQLState
, wxT("S1103")))
1734 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1735 if (!wxStrcmp(SQLState
, wxT("S1104")))
1736 return(DB_ERR_INVALID_PRECISION_VALUE
);
1737 if (!wxStrcmp(SQLState
, wxT("S1105")))
1738 return(DB_ERR_INVALID_PARAM_TYPE
);
1739 if (!wxStrcmp(SQLState
, wxT("S1106")))
1740 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1741 if (!wxStrcmp(SQLState
, wxT("S1107")))
1742 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1743 if (!wxStrcmp(SQLState
, wxT("S1108")))
1744 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1745 if (!wxStrcmp(SQLState
, wxT("S1109")))
1746 return(DB_ERR_INVALID_CURSOR_POSITION
);
1747 if (!wxStrcmp(SQLState
, wxT("S1110")))
1748 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1749 if (!wxStrcmp(SQLState
, wxT("S1111")))
1750 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1751 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1752 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1753 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1754 return(DB_ERR_TIMEOUT_EXPIRED
);
1759 } // wxDb::TranslateSqlState()
1762 /********** wxDb::Grant() **********/
1763 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1767 // Build the grant statement
1768 sqlStmt
= wxT("GRANT ");
1769 if (privileges
== DB_GRANT_ALL
)
1770 sqlStmt
+= wxT("ALL");
1774 if (privileges
& DB_GRANT_SELECT
)
1776 sqlStmt
+= wxT("SELECT");
1779 if (privileges
& DB_GRANT_INSERT
)
1782 sqlStmt
+= wxT(", ");
1783 sqlStmt
+= wxT("INSERT");
1785 if (privileges
& DB_GRANT_UPDATE
)
1788 sqlStmt
+= wxT(", ");
1789 sqlStmt
+= wxT("UPDATE");
1791 if (privileges
& DB_GRANT_DELETE
)
1794 sqlStmt
+= wxT(", ");
1795 sqlStmt
+= wxT("DELETE");
1799 sqlStmt
+= wxT(" ON ");
1800 sqlStmt
+= SQLTableName(tableName
);
1801 sqlStmt
+= wxT(" TO ");
1802 sqlStmt
+= userList
;
1804 #ifdef DBDEBUG_CONSOLE
1805 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1808 WriteSqlLog(sqlStmt
);
1810 return(ExecSql(sqlStmt
));
1815 /********** wxDb::CreateView() **********/
1816 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1817 const wxString
&pSqlStmt
, bool attemptDrop
)
1821 // Drop the view first
1822 if (attemptDrop
&& !DropView(viewName
))
1825 // Build the create view statement
1826 sqlStmt
= wxT("CREATE VIEW ");
1827 sqlStmt
+= viewName
;
1829 if (colList
.Length())
1831 sqlStmt
+= wxT(" (");
1833 sqlStmt
+= wxT(")");
1836 sqlStmt
+= wxT(" AS ");
1837 sqlStmt
+= pSqlStmt
;
1839 WriteSqlLog(sqlStmt
);
1841 #ifdef DBDEBUG_CONSOLE
1842 cout
<< sqlStmt
.c_str() << endl
;
1845 return(ExecSql(sqlStmt
));
1847 } // wxDb::CreateView()
1850 /********** wxDb::DropView() **********/
1851 bool wxDb::DropView(const wxString
&viewName
)
1854 * NOTE: This function returns TRUE if the View does not exist, but
1855 * only for identified databases. Code will need to be added
1856 * below for any other databases when those databases are defined
1857 * to handle this situation consistently
1861 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1863 WriteSqlLog(sqlStmt
);
1865 #ifdef DBDEBUG_CONSOLE
1866 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1869 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1871 // Check for "Base table not found" error and ignore
1872 GetNextError(henv
, hdbc
, hstmt
);
1873 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1875 // Check for product specific error codes
1876 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1879 DispAllErrors(henv
, hdbc
, hstmt
);
1886 // Commit the transaction
1892 } // wxDb::DropView()
1895 /********** wxDb::ExecSql() **********/
1896 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1900 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1902 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
1903 if (retcode
== SQL_SUCCESS
||
1904 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
1910 DispAllErrors(henv
, hdbc
, hstmt
);
1914 } // wxDb::ExecSql()
1917 /********** wxDb::GetNext() **********/
1918 bool wxDb::GetNext(void)
1920 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1924 DispAllErrors(henv
, hdbc
, hstmt
);
1928 } // wxDb::GetNext()
1931 /********** wxDb::GetData() **********/
1932 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1935 wxASSERT(cbReturned
);
1937 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1941 DispAllErrors(henv
, hdbc
, hstmt
);
1945 } // wxDb::GetData()
1948 /********** wxDb::GetKeyFields() **********/
1949 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1951 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1952 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1954 // SQLSMALLINT iKeySeq;
1955 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1956 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1962 * -----------------------------------------------------------------------
1963 * -- 19991224 : mj10777 : Create ------
1964 * -- : Three things are done and stored here : ------
1965 * -- : 1) which Column(s) is/are Primary Key(s) ------
1966 * -- : 2) which tables use this Key as a Foreign Key ------
1967 * -- : 3) which columns are Foreign Key and the name ------
1968 * -- : of the Table where the Key is the Primary Key -----
1969 * -- : Called from GetColumns(const wxString &tableName, ------
1970 * -- int *numCols,const wxChar *userID ) ------
1971 * -----------------------------------------------------------------------
1974 /*---------------------------------------------------------------------*/
1975 /* Get the names of the columns in the primary key. */
1976 /*---------------------------------------------------------------------*/
1977 retcode
= SQLPrimaryKeys(hstmt
,
1978 NULL
, 0, /* Catalog name */
1979 NULL
, 0, /* Schema name */
1980 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1982 /*---------------------------------------------------------------------*/
1983 /* Fetch and display the result set. This will be a list of the */
1984 /* columns in the primary key of the tableName table. */
1985 /*---------------------------------------------------------------------*/
1986 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1988 retcode
= SQLFetch(hstmt
);
1989 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1991 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1992 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1994 for (i
=0;i
<noCols
;i
++) // Find the Column name
1995 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1996 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1999 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2001 /*---------------------------------------------------------------------*/
2002 /* Get all the foreign keys that refer to tableName primary key. */
2003 /*---------------------------------------------------------------------*/
2004 retcode
= SQLForeignKeys(hstmt
,
2005 NULL
, 0, /* Primary catalog */
2006 NULL
, 0, /* Primary schema */
2007 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2008 NULL
, 0, /* Foreign catalog */
2009 NULL
, 0, /* Foreign schema */
2010 NULL
, 0); /* Foreign table */
2012 /*---------------------------------------------------------------------*/
2013 /* Fetch and display the result set. This will be all of the foreign */
2014 /* keys in other tables that refer to the tableName primary key. */
2015 /*---------------------------------------------------------------------*/
2018 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2020 retcode
= SQLFetch(hstmt
);
2021 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2023 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2024 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2025 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2026 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2027 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2028 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2032 tempStr
.Trim(); // Get rid of any unneeded blanks
2033 if (!tempStr
.IsEmpty())
2035 for (i
=0; i
<noCols
; i
++)
2036 { // Find the Column name
2037 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2038 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2042 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2044 /*---------------------------------------------------------------------*/
2045 /* Get all the foreign keys in the tablename table. */
2046 /*---------------------------------------------------------------------*/
2047 retcode
= SQLForeignKeys(hstmt
,
2048 NULL
, 0, /* Primary catalog */
2049 NULL
, 0, /* Primary schema */
2050 NULL
, 0, /* Primary table */
2051 NULL
, 0, /* Foreign catalog */
2052 NULL
, 0, /* Foreign schema */
2053 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2055 /*---------------------------------------------------------------------*/
2056 /* Fetch and display the result set. This will be all of the */
2057 /* primary keys in other tables that are referred to by foreign */
2058 /* keys in the tableName table. */
2059 /*---------------------------------------------------------------------*/
2061 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2063 retcode
= SQLFetch(hstmt
);
2064 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2066 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2067 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2068 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2070 for (i
=0; i
<noCols
; i
++) // Find the Column name
2072 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2074 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2075 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2080 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2084 } // wxDb::GetKeyFields()
2088 /********** wxDb::GetColumns() **********/
2089 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2091 * 1) The last array element of the tableName[] argument must be zero (null).
2092 * This is how the end of the array is detected.
2093 * 2) This function returns an array of wxDbColInf structures. If no columns
2094 * were found, or an error occured, this pointer will be zero (null). THE
2095 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2096 * IS FINISHED WITH IT. i.e.
2098 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2101 * // Use the column inf
2103 * // Destroy the memory
2107 * userID is evaluated in the following manner:
2108 * userID == NULL ... UserID is ignored
2109 * userID == "" ... UserID set equal to 'this->uid'
2110 * userID != "" ... UserID set equal to 'userID'
2112 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2113 * by this function. This function should use its own wxDb instance
2114 * to avoid undesired unbinding of columns.
2119 wxDbColInf
*colInf
= 0;
2127 convertUserID(userID
,UserID
);
2129 // Pass 1 - Determine how many columns there are.
2130 // Pass 2 - Allocate the wxDbColInf array and fill in
2131 // the array with the column information.
2133 for (pass
= 1; pass
<= 2; pass
++)
2137 if (noCols
== 0) // Probably a bogus table name(s)
2139 // Allocate n wxDbColInf objects to hold the column information
2140 colInf
= new wxDbColInf
[noCols
+1];
2143 // Mark the end of the array
2144 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2145 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2146 colInf
[noCols
].sqlDataType
= 0;
2148 // Loop through each table name
2150 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2152 TableName
= tableName
[tbl
];
2153 // Oracle and Interbase table names are uppercase only, so force
2154 // the name to uppercase just in case programmer forgot to do this
2155 if ((Dbms() == dbmsORACLE
) ||
2156 (Dbms() == dbmsINTERBASE
))
2157 TableName
= TableName
.Upper();
2159 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2161 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2162 // use the call below that leaves out the user name
2163 if (!UserID
.IsEmpty() &&
2164 Dbms() != dbmsMY_SQL
&&
2165 Dbms() != dbmsACCESS
&&
2166 Dbms() != dbmsMS_SQL_SERVER
)
2168 retcode
= SQLColumns(hstmt
,
2169 NULL
, 0, // All qualifiers
2170 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2171 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2172 NULL
, 0); // All columns
2176 retcode
= SQLColumns(hstmt
,
2177 NULL
, 0, // All qualifiers
2179 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2180 NULL
, 0); // All columns
2182 if (retcode
!= SQL_SUCCESS
)
2183 { // Error occured, abort
2184 DispAllErrors(henv
, hdbc
, hstmt
);
2187 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2191 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2193 if (pass
== 1) // First pass, just add up the number of columns
2195 else // Pass 2; Fill in the array of structures
2197 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2199 // NOTE: Only the ODBC 1.x fields are retrieved
2200 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2201 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2202 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2203 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2204 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2205 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2206 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2207 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2208 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2209 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2210 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2211 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2213 // Determine the wxDb data type that is used to represent the native data type of this data source
2214 colInf
[colNo
].dbDataType
= 0;
2215 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2218 // IODBC does not return a correct columnSize, so we set
2219 // columnSize = bufferLength if no column size was returned
2220 // IODBC returns the columnSize in bufferLength.. (bug)
2221 if (colInf
[colNo
].columnSize
< 1)
2223 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2226 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2228 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2229 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2230 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2231 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2232 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2233 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2234 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2235 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2240 if (retcode
!= SQL_NO_DATA_FOUND
)
2241 { // Error occured, abort
2242 DispAllErrors(henv
, hdbc
, hstmt
);
2245 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2251 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2254 } // wxDb::GetColumns()
2257 /********** wxDb::GetColumns() **********/
2259 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2261 // Same as the above GetColumns() function except this one gets columns
2262 // only for a single table, and if 'numCols' is not NULL, the number of
2263 // columns stored in the returned wxDbColInf is set in '*numCols'
2265 // userID is evaluated in the following manner:
2266 // userID == NULL ... UserID is ignored
2267 // userID == "" ... UserID set equal to 'this->uid'
2268 // userID != "" ... UserID set equal to 'userID'
2270 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2271 // by this function. This function should use its own wxDb instance
2272 // to avoid undesired unbinding of columns.
2277 wxDbColInf
*colInf
= 0;
2285 convertUserID(userID
,UserID
);
2287 // Pass 1 - Determine how many columns there are.
2288 // Pass 2 - Allocate the wxDbColInf array and fill in
2289 // the array with the column information.
2291 for (pass
= 1; pass
<= 2; pass
++)
2295 if (noCols
== 0) // Probably a bogus table name(s)
2297 // Allocate n wxDbColInf objects to hold the column information
2298 colInf
= new wxDbColInf
[noCols
+1];
2301 // Mark the end of the array
2302 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2303 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2304 colInf
[noCols
].sqlDataType
= 0;
2307 TableName
= tableName
;
2308 // Oracle and Interbase table names are uppercase only, so force
2309 // the name to uppercase just in case programmer forgot to do this
2310 if ((Dbms() == dbmsORACLE
) ||
2311 (Dbms() == dbmsINTERBASE
))
2312 TableName
= TableName
.Upper();
2314 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2316 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2317 // use the call below that leaves out the user name
2318 if (!UserID
.IsEmpty() &&
2319 Dbms() != dbmsMY_SQL
&&
2320 Dbms() != dbmsACCESS
&&
2321 Dbms() != dbmsMS_SQL_SERVER
)
2323 retcode
= SQLColumns(hstmt
,
2324 NULL
, 0, // All qualifiers
2325 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2326 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2327 NULL
, 0); // All columns
2331 retcode
= SQLColumns(hstmt
,
2332 NULL
, 0, // All qualifiers
2334 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2335 NULL
, 0); // All columns
2337 if (retcode
!= SQL_SUCCESS
)
2338 { // Error occured, abort
2339 DispAllErrors(henv
, hdbc
, hstmt
);
2342 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2348 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2350 if (pass
== 1) // First pass, just add up the number of columns
2352 else // Pass 2; Fill in the array of structures
2354 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2356 // NOTE: Only the ODBC 1.x fields are retrieved
2357 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2358 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2359 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2360 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2361 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2362 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2363 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2364 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2365 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2366 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2367 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2368 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2369 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2370 // Start Values for Primary/Foriegn Key (=No)
2371 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2372 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2373 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2374 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2376 // BJO 20000428 : Virtuoso returns type names with upper cases!
2377 if (Dbms() == dbmsVIRTUOSO
)
2379 wxString s
= colInf
[colNo
].typeName
;
2381 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2384 // Determine the wxDb data type that is used to represent the native data type of this data source
2385 colInf
[colNo
].dbDataType
= 0;
2386 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2389 // IODBC does not return a correct columnSize, so we set
2390 // columnSize = bufferLength if no column size was returned
2391 // IODBC returns the columnSize in bufferLength.. (bug)
2392 if (colInf
[colNo
].columnSize
< 1)
2394 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2398 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2400 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2401 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2402 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2403 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2404 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2405 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2406 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2407 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2413 if (retcode
!= SQL_NO_DATA_FOUND
)
2414 { // Error occured, abort
2415 DispAllErrors(henv
, hdbc
, hstmt
);
2418 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2425 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2427 // Store Primary and Foriegn Keys
2428 GetKeyFields(tableName
,colInf
,noCols
);
2434 } // wxDb::GetColumns()
2437 #else // New GetColumns
2442 These are tentative new GetColumns members which should be more database
2443 independant and which always returns the columns in the order they were
2446 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2447 wxChar* userID)) calls the second implementation for each separate table
2448 before merging the results. This makes the code easier to maintain as
2449 only one member (the second) makes the real work
2450 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2451 wxChar *userID) is a little bit improved
2452 - It doesn't anymore rely on the type-name to find out which database-type
2454 - It ends by sorting the columns, so that they are returned in the same
2455 order they were created
2465 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2468 // The last array element of the tableName[] argument must be zero (null).
2469 // This is how the end of the array is detected.
2473 // How many tables ?
2475 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2477 // Create a table to maintain the columns for each separate table
2478 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2481 for (i
= 0 ; i
< tbl
; i
++)
2484 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2485 if (TableColumns
[i
].colInf
== NULL
)
2487 noCols
+= TableColumns
[i
].noCols
;
2490 // Now merge all the separate table infos
2491 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2493 // Mark the end of the array
2494 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2495 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2496 colInf
[noCols
].sqlDataType
= 0;
2501 for (i
= 0 ; i
< tbl
; i
++)
2503 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2505 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2509 delete [] TableColumns
;
2512 } // wxDb::GetColumns() -- NEW
2515 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2517 // Same as the above GetColumns() function except this one gets columns
2518 // only for a single table, and if 'numCols' is not NULL, the number of
2519 // columns stored in the returned wxDbColInf is set in '*numCols'
2521 // userID is evaluated in the following manner:
2522 // userID == NULL ... UserID is ignored
2523 // userID == "" ... UserID set equal to 'this->uid'
2524 // userID != "" ... UserID set equal to 'userID'
2526 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2527 // by this function. This function should use its own wxDb instance
2528 // to avoid undesired unbinding of columns.
2532 wxDbColInf
*colInf
= 0;
2540 convertUserID(userID
,UserID
);
2542 // Pass 1 - Determine how many columns there are.
2543 // Pass 2 - Allocate the wxDbColInf array and fill in
2544 // the array with the column information.
2546 for (pass
= 1; pass
<= 2; pass
++)
2550 if (noCols
== 0) // Probably a bogus table name(s)
2552 // Allocate n wxDbColInf objects to hold the column information
2553 colInf
= new wxDbColInf
[noCols
+1];
2556 // Mark the end of the array
2557 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2558 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2559 colInf
[noCols
].sqlDataType
= 0;
2562 TableName
= tableName
;
2563 // Oracle and Interbase table names are uppercase only, so force
2564 // the name to uppercase just in case programmer forgot to do this
2565 if ((Dbms() == dbmsORACLE
) ||
2566 (Dbms() == dbmsINTERBASE
))
2567 TableName
= TableName
.Upper();
2569 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2571 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2572 // use the call below that leaves out the user name
2573 if (!UserID
.IsEmpty() &&
2574 Dbms() != dbmsMY_SQL
&&
2575 Dbms() != dbmsACCESS
&&
2576 Dbms() != dbmsMS_SQL_SERVER
)
2578 retcode
= SQLColumns(hstmt
,
2579 NULL
, 0, // All qualifiers
2580 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2581 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2582 NULL
, 0); // All columns
2586 retcode
= SQLColumns(hstmt
,
2587 NULL
, 0, // All qualifiers
2589 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2590 NULL
, 0); // All columns
2592 if (retcode
!= SQL_SUCCESS
)
2593 { // Error occured, abort
2594 DispAllErrors(henv
, hdbc
, hstmt
);
2597 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2603 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2605 if (pass
== 1) // First pass, just add up the number of columns
2607 else // Pass 2; Fill in the array of structures
2609 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2611 // NOTE: Only the ODBC 1.x fields are retrieved
2612 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2613 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2614 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2615 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2616 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2617 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2618 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2619 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2620 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2621 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2622 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2623 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2624 // Start Values for Primary/Foriegn Key (=No)
2625 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2626 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2627 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2628 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2631 // IODBC does not return a correct columnSize, so we set
2632 // columnSize = bufferLength if no column size was returned
2633 // IODBC returns the columnSize in bufferLength.. (bug)
2634 if (colInf
[colNo
].columnSize
< 1)
2636 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2640 // Determine the wxDb data type that is used to represent the native data type of this data source
2641 colInf
[colNo
].dbDataType
= 0;
2642 // Get the intern datatype
2643 switch (colInf
[colNo
].sqlDataType
)
2647 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2653 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2660 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2663 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2666 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2671 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2672 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2679 if (retcode
!= SQL_NO_DATA_FOUND
)
2680 { // Error occured, abort
2681 DispAllErrors(henv
, hdbc
, hstmt
);
2684 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2691 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2693 // Store Primary and Foreign Keys
2694 GetKeyFields(tableName
,colInf
,noCols
);
2696 ///////////////////////////////////////////////////////////////////////////
2697 // Now sort the the columns in order to make them appear in the right order
2698 ///////////////////////////////////////////////////////////////////////////
2700 // Build a generic SELECT statement which returns 0 rows
2703 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2706 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2708 DispAllErrors(henv
, hdbc
, hstmt
);
2712 // Get the number of result columns
2713 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2715 DispAllErrors(henv
, hdbc
, hstmt
);
2719 if (noCols
== 0) // Probably a bogus table name
2728 for (colNum
= 0; colNum
< noCols
; colNum
++)
2730 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2732 &Sword
, &Sdword
) != SQL_SUCCESS
)
2734 DispAllErrors(henv
, hdbc
, hstmt
);
2738 wxString Name1
= name
;
2739 Name1
= Name1
.Upper();
2741 // Where is this name in the array ?
2742 for (i
= colNum
; i
< noCols
; i
++)
2744 wxString Name2
= colInf
[i
].colName
;
2745 Name2
= Name2
.Upper();
2748 if (colNum
!= i
) // swap to sort
2750 wxDbColInf tmpColInf
= colInf
[colNum
];
2751 colInf
[colNum
] = colInf
[i
];
2752 colInf
[i
] = tmpColInf
;
2758 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2760 ///////////////////////////////////////////////////////////////////////////
2762 ///////////////////////////////////////////////////////////////////////////
2768 } // wxDb::GetColumns()
2771 #endif // #else OLD_GETCOLUMNS
2774 /********** wxDb::GetColumnCount() **********/
2775 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2777 * Returns a count of how many columns are in a table.
2778 * If an error occurs in computing the number of columns
2779 * this function will return a -1 for the count
2781 * userID is evaluated in the following manner:
2782 * userID == NULL ... UserID is ignored
2783 * userID == "" ... UserID set equal to 'this->uid'
2784 * userID != "" ... UserID set equal to 'userID'
2786 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2787 * by this function. This function should use its own wxDb instance
2788 * to avoid undesired unbinding of columns.
2798 convertUserID(userID
,UserID
);
2800 TableName
= tableName
;
2801 // Oracle and Interbase table names are uppercase only, so force
2802 // the name to uppercase just in case programmer forgot to do this
2803 if ((Dbms() == dbmsORACLE
) ||
2804 (Dbms() == dbmsINTERBASE
))
2805 TableName
= TableName
.Upper();
2807 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2809 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2810 // use the call below that leaves out the user name
2811 if (!UserID
.IsEmpty() &&
2812 Dbms() != dbmsMY_SQL
&&
2813 Dbms() != dbmsACCESS
&&
2814 Dbms() != dbmsMS_SQL_SERVER
)
2816 retcode
= SQLColumns(hstmt
,
2817 NULL
, 0, // All qualifiers
2818 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2819 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2820 NULL
, 0); // All columns
2824 retcode
= SQLColumns(hstmt
,
2825 NULL
, 0, // All qualifiers
2827 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2828 NULL
, 0); // All columns
2830 if (retcode
!= SQL_SUCCESS
)
2831 { // Error occured, abort
2832 DispAllErrors(henv
, hdbc
, hstmt
);
2833 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2837 // Count the columns
2838 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2841 if (retcode
!= SQL_NO_DATA_FOUND
)
2842 { // Error occured, abort
2843 DispAllErrors(henv
, hdbc
, hstmt
);
2844 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2848 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2851 } // wxDb::GetColumnCount()
2854 /********** wxDb::GetCatalog() *******/
2855 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2857 * ---------------------------------------------------------------------
2858 * -- 19991203 : mj10777 : Create ------
2859 * -- : Creates a wxDbInf with Tables / Cols Array ------
2860 * -- : uses SQLTables and fills pTableInf; ------
2861 * -- : pColInf is set to NULL and numCols to 0; ------
2862 * -- : returns pDbInf (wxDbInf) ------
2863 * -- - if unsuccesfull (pDbInf == NULL) ------
2864 * -- : pColInf can be filled with GetColumns(..); ------
2865 * -- : numCols can be filled with GetColumnCount(..); ------
2866 * ---------------------------------------------------------------------
2868 * userID is evaluated in the following manner:
2869 * userID == NULL ... UserID is ignored
2870 * userID == "" ... UserID set equal to 'this->uid'
2871 * userID != "" ... UserID set equal to 'userID'
2873 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2874 * by this function. This function should use its own wxDb instance
2875 * to avoid undesired unbinding of columns.
2878 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2879 int noTab
= 0; // Counter while filling table entries
2883 wxString tblNameSave
;
2886 convertUserID(userID
,UserID
);
2888 //-------------------------------------------------------------
2889 pDbInf
= new wxDbInf
; // Create the Database Array
2890 //-------------------------------------------------------------
2891 // Table Information
2892 // Pass 1 - Determine how many Tables there are.
2893 // Pass 2 - Create the Table array and fill it
2894 // - Create the Cols array = NULL
2895 //-------------------------------------------------------------
2897 for (pass
= 1; pass
<= 2; pass
++)
2899 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2900 tblNameSave
.Empty();
2902 if (!UserID
.IsEmpty() &&
2903 Dbms() != dbmsMY_SQL
&&
2904 Dbms() != dbmsACCESS
&&
2905 Dbms() != dbmsMS_SQL_SERVER
)
2907 retcode
= SQLTables(hstmt
,
2908 NULL
, 0, // All qualifiers
2909 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2910 NULL
, 0, // All tables
2911 NULL
, 0); // All columns
2915 retcode
= SQLTables(hstmt
,
2916 NULL
, 0, // All qualifiers
2917 NULL
, 0, // User specified
2918 NULL
, 0, // All tables
2919 NULL
, 0); // All columns
2922 if (retcode
!= SQL_SUCCESS
)
2924 DispAllErrors(henv
, hdbc
, hstmt
);
2926 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2930 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2932 if (pass
== 1) // First pass, just count the Tables
2934 if (pDbInf
->numTables
== 0)
2936 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2937 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2939 pDbInf
->numTables
++; // Counter for Tables
2941 if (pass
== 2) // Create and fill the Table entries
2943 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2944 { // no, then create the Array
2945 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2947 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2949 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2950 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2951 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2957 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2959 // Query how many columns are in each table
2960 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2962 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2967 } // wxDb::GetCatalog()
2970 /********** wxDb::Catalog() **********/
2971 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2973 * Creates the text file specified in 'filename' which will contain
2974 * a minimal data dictionary of all tables accessible by the user specified
2977 * userID is evaluated in the following manner:
2978 * userID == NULL ... UserID is ignored
2979 * userID == "" ... UserID set equal to 'this->uid'
2980 * userID != "" ... UserID set equal to 'userID'
2982 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2983 * by this function. This function should use its own wxDb instance
2984 * to avoid undesired unbinding of columns.
2987 wxASSERT(fileName
.Length());
2991 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2992 wxString tblNameSave
;
2993 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2995 wxChar typeName
[30+1];
2996 SDWORD precision
, length
;
2998 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
3002 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3005 convertUserID(userID
,UserID
);
3007 if (!UserID
.IsEmpty() &&
3008 Dbms() != dbmsMY_SQL
&&
3009 Dbms() != dbmsACCESS
&&
3010 Dbms() != dbmsINTERBASE
&&
3011 Dbms() != dbmsMS_SQL_SERVER
)
3013 retcode
= SQLColumns(hstmt
,
3014 NULL
, 0, // All qualifiers
3015 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3016 NULL
, 0, // All tables
3017 NULL
, 0); // All columns
3021 retcode
= SQLColumns(hstmt
,
3022 NULL
, 0, // All qualifiers
3023 NULL
, 0, // User specified
3024 NULL
, 0, // All tables
3025 NULL
, 0); // All columns
3027 if (retcode
!= SQL_SUCCESS
)
3029 DispAllErrors(henv
, hdbc
, hstmt
);
3035 tblNameSave
.Empty();
3040 retcode
= SQLFetch(hstmt
);
3041 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3044 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3047 fputs(wxT("\n"), fp
);
3048 fputs(wxT("================================ "), fp
);
3049 fputs(wxT("================================ "), fp
);
3050 fputs(wxT("===================== "), fp
);
3051 fputs(wxT("========= "), fp
);
3052 fputs(wxT("=========\n"), fp
);
3053 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3054 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3055 fputs(outStr
.c_str(), fp
);
3056 fputs(wxT("================================ "), fp
);
3057 fputs(wxT("================================ "), fp
);
3058 fputs(wxT("===================== "), fp
);
3059 fputs(wxT("========= "), fp
);
3060 fputs(wxT("=========\n"), fp
);
3061 tblNameSave
= tblName
;
3064 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3065 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3066 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3067 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3068 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3069 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3071 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3072 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3073 if (fputs(outStr
.c_str(), fp
) == EOF
)
3075 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3082 if (retcode
!= SQL_NO_DATA_FOUND
)
3083 DispAllErrors(henv
, hdbc
, hstmt
);
3085 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3088 return(retcode
== SQL_NO_DATA_FOUND
);
3090 } // wxDb::Catalog()
3093 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3095 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3096 * if the object exists in the database. This function does not indicate
3097 * whether or not the user has privleges to query or perform other functions
3100 * userID is evaluated in the following manner:
3101 * userID == NULL ... UserID is ignored
3102 * userID == "" ... UserID set equal to 'this->uid'
3103 * userID != "" ... UserID set equal to 'userID'
3106 wxASSERT(tableName
.Length());
3110 if (Dbms() == dbmsDBASE
)
3113 if (tablePath
.Length())
3114 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3116 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3119 exists
= wxFileExists(dbName
);
3124 convertUserID(userID
,UserID
);
3126 TableName
= tableName
;
3127 // Oracle and Interbase table names are uppercase only, so force
3128 // the name to uppercase just in case programmer forgot to do this
3129 if ((Dbms() == dbmsORACLE
) ||
3130 (Dbms() == dbmsINTERBASE
))
3131 TableName
= TableName
.Upper();
3133 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3136 // Some databases cannot accept a user name when looking up table names,
3137 // so we use the call below that leaves out the user name
3138 if (!UserID
.IsEmpty() &&
3139 Dbms() != dbmsMY_SQL
&&
3140 Dbms() != dbmsACCESS
&&
3141 Dbms() != dbmsMS_SQL_SERVER
&&
3142 Dbms() != dbmsDB2
&&
3143 Dbms() != dbmsINTERBASE
&&
3144 Dbms() != dbmsPERVASIVE_SQL
)
3146 retcode
= SQLTables(hstmt
,
3147 NULL
, 0, // All qualifiers
3148 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3149 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3150 NULL
, 0); // All table types
3154 retcode
= SQLTables(hstmt
,
3155 NULL
, 0, // All qualifiers
3156 NULL
, 0, // All owners
3157 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3158 NULL
, 0); // All table types
3160 if (retcode
!= SQL_SUCCESS
)
3161 return(DispAllErrors(henv
, hdbc
, hstmt
));
3163 retcode
= SQLFetch(hstmt
);
3164 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3166 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3167 return(DispAllErrors(henv
, hdbc
, hstmt
));
3170 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3174 } // wxDb::TableExists()
3177 /********** wxDb::TablePrivileges() **********/
3178 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3179 const wxChar
*schema
, const wxString
&tablePath
)
3181 wxASSERT(tableName
.Length());
3183 wxDbTablePrivilegeInfo result
;
3187 // We probably need to be able to dynamically set this based on
3188 // the driver type, and state.
3189 wxChar curRole
[]=wxT("public");
3193 wxString UserID
,Schema
;
3194 convertUserID(userID
,UserID
);
3195 convertUserID(schema
,Schema
);
3197 TableName
= tableName
;
3198 // Oracle and Interbase table names are uppercase only, so force
3199 // the name to uppercase just in case programmer forgot to do this
3200 if ((Dbms() == dbmsORACLE
) ||
3201 (Dbms() == dbmsINTERBASE
))
3202 TableName
= TableName
.Upper();
3204 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3206 // Some databases cannot accept a user name when looking up table names,
3207 // so we use the call below that leaves out the user name
3208 if (!Schema
.IsEmpty() &&
3209 Dbms() != dbmsMY_SQL
&&
3210 Dbms() != dbmsACCESS
&&
3211 Dbms() != dbmsMS_SQL_SERVER
)
3213 retcode
= SQLTablePrivileges(hstmt
,
3215 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3216 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3220 retcode
= SQLTablePrivileges(hstmt
,
3223 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3226 #ifdef DBDEBUG_CONSOLE
3227 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3230 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3231 return(DispAllErrors(henv
, hdbc
, hstmt
));
3233 bool failed
= FALSE
;
3234 retcode
= SQLFetch(hstmt
);
3235 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3237 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3240 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3243 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3246 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3249 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3252 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3255 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3260 return(DispAllErrors(henv
, hdbc
, hstmt
));
3262 #ifdef DBDEBUG_CONSOLE
3263 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3264 result
.privilege
,result
.tableOwner
,result
.tableName
,
3265 result
.grantor
, result
.grantee
);
3268 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3270 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3274 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3275 !wxStrcmp(result
.privilege
,priv
))
3277 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3281 if (!wxStrcmp(result
.grantee
,curRole
) &&
3282 !wxStrcmp(result
.privilege
,priv
))
3284 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3288 retcode
= SQLFetch(hstmt
);
3291 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3294 } // wxDb::TablePrivileges
3297 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3301 if (Dbms() == dbmsACCESS
)
3303 TableName
+= tableName
;
3304 if (Dbms() == dbmsACCESS
)
3308 } // wxDb::SQLTableName()
3311 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3315 if (Dbms() == dbmsACCESS
)
3318 if (Dbms() == dbmsACCESS
)
3322 } // wxDb::SQLColumnName()
3325 /********** wxDb::SetSqlLogging() **********/
3326 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3328 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3329 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3331 if (state
== sqlLogON
)
3335 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3336 if (fpSqlLog
== NULL
)
3344 if (fclose(fpSqlLog
))
3350 sqlLogState
= state
;
3353 } // wxDb::SetSqlLogging()
3356 /********** wxDb::WriteSqlLog() **********/
3357 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3359 wxASSERT(logMsg
.Length());
3361 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3364 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3366 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3368 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3373 } // wxDb::WriteSqlLog()
3376 /********** wxDb::Dbms() **********/
3377 wxDBMS
wxDb::Dbms(void)
3379 * Be aware that not all database engines use the exact same syntax, and not
3380 * every ODBC compliant database is compliant to the same level of compliancy.
3381 * Some manufacturers support the minimum Level 1 compliancy, and others up
3382 * through Level 3. Others support subsets of features for levels above 1.
3384 * If you find an inconsistency between the wxDb class and a specific database
3385 * engine, and an identifier to this section, and special handle the database in
3386 * the area where behavior is non-conforming with the other databases.
3389 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3390 * ---------------------------------------------------
3393 * - Currently the only database supported by the class to support VIEWS
3396 * - Does not support the SQL_TIMESTAMP structure
3397 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3398 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3399 * is TRUE. The user must create ALL indexes from their program.
3400 * - Table names can only be 8 characters long
3401 * - Column names can only be 10 characters long
3404 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3405 * after every table name involved in the query/join if that tables matching record(s)
3407 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3409 * SYBASE (Enterprise)
3410 * - If a column is part of the Primary Key, the column cannot be NULL
3411 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3414 * - If a column is part of the Primary Key, the column cannot be NULL
3415 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3416 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3417 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3418 * column definition if it is not defined correctly, but it is experimental
3419 * - Does not support sub-queries in SQL statements
3422 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3423 * - Does not support sub-queries in SQL statements
3426 * - Primary keys must be declared as NOT NULL
3427 * - Table and index names must not be longer than 13 characters in length (technically
3428 * table names can be up to 18 characters, but the primary index is created using the
3429 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3434 * - Columns that are part of primary keys must be defined as being NOT NULL
3435 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3436 * column definition if it is not defined correctly, but it is experimental
3439 // Should only need to do this once for each new database connection
3440 // so return the value we already determined it to be to save time
3441 // and lots of string comparisons
3442 if (dbmsType
!= dbmsUNIDENTIFIED
)
3445 wxChar baseName
[25+1];
3446 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3449 // RGG 20001025 : add support for Interbase
3450 // GT : Integrated to base classes on 20001121
3451 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3452 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3454 // BJO 20000428 : add support for Virtuoso
3455 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3456 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3458 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3459 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3461 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3462 // connected through an OpenLink driver.
3463 // Is it also returned by Sybase Adapatitve server?
3464 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3465 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3467 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3468 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3469 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3471 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3474 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3475 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3476 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3477 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3478 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3479 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3482 if (!wxStricmp(dbInf
.dbmsName
,wxT("Pervasive")))
3483 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3486 if (!wxStricmp(baseName
,wxT("Informix")))
3487 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3490 if (!wxStricmp(baseName
,wxT("Oracle")))
3491 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3492 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3493 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3494 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3495 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3496 if (!wxStricmp(baseName
,wxT("Sybase")))
3497 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3500 if (!wxStricmp(baseName
,wxT("DBASE")))
3501 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3503 if (!wxStricmp(baseName
,wxT("xBase")))
3504 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3506 if (!wxStricmp(baseName
,wxT("MySQL")))
3507 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3510 if (!wxStricmp(baseName
,wxT("DB2")))
3511 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3513 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3518 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3519 int dataType
, ULONG columnLength
,
3520 const wxString
&optionalParam
)
3522 wxASSERT(tableName
.Length());
3523 wxASSERT(columnName
.Length());
3524 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3525 dataType
!= DB_DATA_TYPE_VARCHAR
);
3527 // Must specify a columnLength if modifying a VARCHAR type column
3528 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3531 wxString dataTypeName
;
3533 wxString alterSlashModify
;
3537 case DB_DATA_TYPE_VARCHAR
:
3538 dataTypeName
= typeInfVarchar
.TypeName
;
3540 case DB_DATA_TYPE_INTEGER
:
3541 dataTypeName
= typeInfInteger
.TypeName
;
3543 case DB_DATA_TYPE_FLOAT
:
3544 dataTypeName
= typeInfFloat
.TypeName
;
3546 case DB_DATA_TYPE_DATE
:
3547 dataTypeName
= typeInfDate
.TypeName
;
3549 case DB_DATA_TYPE_BLOB
:
3550 dataTypeName
= typeInfBlob
.TypeName
;
3556 // Set the modify or alter syntax depending on the type of database connected to
3560 alterSlashModify
= "MODIFY";
3562 case dbmsMS_SQL_SERVER
:
3563 alterSlashModify
= "ALTER COLUMN";
3565 case dbmsUNIDENTIFIED
:
3567 case dbmsSYBASE_ASA
:
3568 case dbmsSYBASE_ASE
:
3573 case dbmsXBASE_SEQUITER
:
3575 alterSlashModify
= "MODIFY";
3579 // create the SQL statement
3580 if ( Dbms() == dbmsMY_SQL
)
3582 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3583 columnName
.c_str(), dataTypeName
.c_str());
3587 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3588 columnName
.c_str(), dataTypeName
.c_str());
3591 // For varchars only, append the size of the column
3592 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3593 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= "text"))
3596 s
.Printf(wxT("(%lu)"), columnLength
);
3600 // for passing things like "NOT NULL"
3601 if (optionalParam
.Length())
3603 sqlStmt
+= wxT(" ");
3604 sqlStmt
+= optionalParam
;
3607 return ExecSql(sqlStmt
);
3609 } // wxDb::ModifyColumn()
3612 /********** wxDbGetConnection() **********/
3613 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3617 // Used to keep a pointer to a DB connection that matches the requested
3618 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3619 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3620 // rather than having to re-query the datasource to get all the values
3621 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3622 wxDb
*matchingDbConnection
= NULL
;
3624 // Scan the linked list searching for an available database connection
3625 // that's already been opened but is currently not in use.
3626 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3628 // The database connection must be for the same datasource
3629 // name and must currently not be in use.
3631 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3632 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3634 pList
->Free
= FALSE
;
3635 return(pList
->PtrDb
);
3638 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3639 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3640 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3641 matchingDbConnection
= pList
->PtrDb
;
3644 // No available connections. A new connection must be made and
3645 // appended to the end of the linked list.
3648 // Find the end of the list
3649 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3650 // Append a new list item
3651 pList
->PtrNext
= new wxDbList
;
3652 pList
->PtrNext
->PtrPrev
= pList
;
3653 pList
= pList
->PtrNext
;
3657 // Create the first node on the list
3658 pList
= PtrBegDbList
= new wxDbList
;
3662 // Initialize new node in the linked list
3664 pList
->Free
= FALSE
;
3665 pList
->Dsn
= pDbConfig
->GetDsn();
3666 pList
->Uid
= pDbConfig
->GetUserID();
3667 pList
->AuthStr
= pDbConfig
->GetPassword();
3669 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3671 bool opened
= FALSE
;
3673 if (!matchingDbConnection
)
3674 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3676 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3678 // Connect to the datasource
3681 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3682 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3683 return(pList
->PtrDb
);
3685 else // Unable to connect, destroy list item
3688 pList
->PtrPrev
->PtrNext
= 0;
3690 PtrBegDbList
= 0; // Empty list again
3691 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3692 pList
->PtrDb
->Close(); // Close the wxDb object
3693 delete pList
->PtrDb
; // Deletes the wxDb object
3694 delete pList
; // Deletes the linked list object
3698 } // wxDbGetConnection()
3701 /********** wxDbFreeConnection() **********/
3702 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3706 // Scan the linked list searching for the database connection
3707 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3709 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3710 return (pList
->Free
= TRUE
);
3713 // Never found the database object, return failure
3716 } // wxDbFreeConnection()
3719 /********** wxDbCloseConnections() **********/
3720 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
3722 wxDbList
*pList
, *pNext
;
3724 // Traverse the linked list closing database connections and freeing memory as I go.
3725 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3727 pNext
= pList
->PtrNext
; // Save the pointer to next
3728 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3729 pList
->PtrDb
->Close(); // Close the wxDb object
3730 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
3731 delete pList
->PtrDb
; // Deletes the wxDb object
3732 delete pList
; // Deletes the linked list object
3735 // Mark the list as empty
3738 } // wxDbCloseConnections()
3741 /********** wxDbConnectionsInUse() **********/
3742 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
3747 // Scan the linked list counting db connections that are currently in use
3748 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3750 if (pList
->Free
== FALSE
)
3756 } // wxDbConnectionsInUse()
3760 /********** wxDbLogExtendedErrorMsg() **********/
3761 // DEBUG ONLY function
3762 const wxChar
* WXDLLIMPEXP_ODBC
wxDbLogExtendedErrorMsg(const wxChar
*userText
,
3764 const wxChar
*ErrFile
,
3767 static wxString msg
;
3772 if (ErrFile
|| ErrLine
)
3774 msg
+= wxT("File: ");
3776 msg
+= wxT(" Line: ");
3777 tStr
.Printf(wxT("%d"),ErrLine
);
3778 msg
+= tStr
.c_str();
3782 msg
.Append (wxT("\nODBC errors:\n"));
3785 // Display errors for this connection
3787 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
3789 if (pDb
->errorList
[i
])
3791 msg
.Append(pDb
->errorList
[i
]);
3792 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
3793 msg
.Append(wxT("\n"));
3794 // Clear the errmsg buffer so the next error will not
3795 // end up showing the previous error that have occurred
3796 wxStrcpy(pDb
->errorList
[i
],wxT(""));
3801 wxLogDebug(msg
.c_str());
3804 } // wxDbLogExtendedErrorMsg()
3807 /********** wxDbSqlLog() **********/
3808 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3810 bool append
= FALSE
;
3813 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3815 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3820 SQLLOGstate
= state
;
3821 SQLLOGfn
= filename
;
3829 /********** wxDbCreateDataSource() **********/
3830 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3831 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3833 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3834 * Very rudimentary creation of an ODBC data source.
3836 * ODBC driver must be ODBC 3.0 compliant to use this function
3841 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3847 dsnLocation
= ODBC_ADD_SYS_DSN
;
3849 dsnLocation
= ODBC_ADD_DSN
;
3851 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3852 // so that is why I used it, as wxString does not deal well with
3853 // embedded nulls in strings
3854 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3856 // Replace the separator from above with the '\0' seperator needed
3857 // by the SQLConfigDataSource() function
3861 k
= setupStr
.Find((wxChar
)2,TRUE
);
3862 if (k
!= wxNOT_FOUND
)
3863 setupStr
[(UINT
)k
] = wxT('\0');
3865 while (k
!= wxNOT_FOUND
);
3867 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3868 driverName
, setupStr
.c_str());
3870 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3872 // check for errors caused by ConfigDSN based functions
3875 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3876 errMsg
[0] = wxT('\0');
3878 // This function is only supported in ODBC drivers v3.0 compliant and above
3879 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3882 #ifdef DBDEBUG_CONSOLE
3883 // When run in console mode, use standard out to display errors.
3884 cout
<< errMsg
<< endl
;
3885 cout
<< wxT("Press any key to continue...") << endl
;
3887 #endif // DBDEBUG_CONSOLE
3890 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3891 #endif // __WXDEBUG__
3897 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3898 // necessary to use this function, so this function is not supported
3900 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3903 #endif // __VISUALC__
3907 } // wxDbCreateDataSource()
3911 /********** wxDbGetDataSource() **********/
3912 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3913 SWORD DsDescMax
, UWORD direction
)
3915 * Dsn and DsDesc will contain the data source name and data source
3916 * description upon return
3921 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3922 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3927 } // wxDbGetDataSource()
3930 // Change this to 0 to remove use of all deprecated functions
3931 #if wxODBC_BACKWARD_COMPATABILITY
3932 /********************************************************************
3933 ********************************************************************
3935 * The following functions are all DEPRECATED and are included for
3936 * backward compatability reasons only
3938 ********************************************************************
3939 ********************************************************************/
3940 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3942 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3944 /***** DEPRECATED: use wxGetDataSource() *****/
3945 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3948 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3950 /***** DEPRECATED: use wxDbGetConnection() *****/
3951 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3953 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3955 /***** DEPRECATED: use wxDbFreeConnection() *****/
3956 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
3958 return wxDbFreeConnection(pDb
);
3960 /***** DEPRECATED: use wxDbCloseConnections() *****/
3961 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
3963 wxDbCloseConnections();
3965 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3966 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
3968 return wxDbConnectionsInUse();