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;
77 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
78 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
81 extern wxList TablesInUse
;
84 // SQL Log defaults to be used by GetDbConnection
85 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
87 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
89 // The wxDb::errorList is copied to this variable when the wxDb object
90 // is closed. This way, the error list is still available after the
91 // database object is closed. This is necessary if the database
92 // connection fails so the calling application can show the operator
93 // why the connection failed. Note: as each wxDb object is closed, it
94 // will overwrite the errors of the previously destroyed wxDb object in
95 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
97 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
100 // This type defines the return row-struct form
101 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
104 wxChar tableQual
[128+1];
105 wxChar tableOwner
[128+1];
106 wxChar tableName
[128+1];
107 wxChar grantor
[128+1];
108 wxChar grantee
[128+1];
109 wxChar privilege
[128+1];
110 wxChar grantable
[3+1];
111 } wxDbTablePrivilegeInfo
;
114 /********** wxDbConnectInf Constructor - form 1 **********/
115 wxDbConnectInf::wxDbConnectInf()
118 freeHenvOnDestroy
= FALSE
;
124 /********** wxDbConnectInf Constructor - form 2 **********/
125 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
126 const wxString
&password
, const wxString
&defaultDir
,
127 const wxString
&fileType
, const wxString
&description
)
130 freeHenvOnDestroy
= FALSE
;
141 SetPassword(password
);
142 SetDescription(description
);
143 SetFileType(fileType
);
144 SetDefaultDir(defaultDir
);
145 } // wxDbConnectInf Constructor
148 wxDbConnectInf::~wxDbConnectInf()
150 if (freeHenvOnDestroy
)
154 } // wxDbConnectInf Destructor
158 /********** wxDbConnectInf::Initialize() **********/
159 bool wxDbConnectInf::Initialize()
161 freeHenvOnDestroy
= FALSE
;
163 if (freeHenvOnDestroy
&& Henv
)
170 ConnectionStr
[0] = 0;
175 useConnectionStr
= FALSE
;
178 } // wxDbConnectInf::Initialize()
181 /********** wxDbConnectInf::AllocHenv() **********/
182 bool wxDbConnectInf::AllocHenv()
184 // This is here to help trap if you are getting a new henv
185 // without releasing an existing henv
188 // Initialize the ODBC Environment for Database Operations
189 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
191 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
195 freeHenvOnDestroy
= TRUE
;
198 } // wxDbConnectInf::AllocHenv()
201 void wxDbConnectInf::FreeHenv()
209 freeHenvOnDestroy
= FALSE
;
211 } // wxDbConnectInf::FreeHenv()
214 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
216 wxASSERT(dsn
.Length() < sizeof(Dsn
));
219 } // wxDbConnectInf::SetDsn()
222 void wxDbConnectInf::SetUserID(const wxString
&uid
)
224 wxASSERT(uid
.Length() < sizeof(Uid
));
226 } // wxDbConnectInf::SetUserID()
229 void wxDbConnectInf::SetPassword(const wxString
&password
)
231 wxASSERT(password
.Length() < sizeof(AuthStr
));
233 wxStrcpy(AuthStr
, password
);
234 } // wxDbConnectInf::SetPassword()
236 void wxDbConnectInf::SetConnectionStr(const wxString
&connectStr
)
238 wxASSERT(connectStr
.Length() < sizeof(ConnectionStr
));
240 useConnectionStr
= wxStrlen(connectStr
) > 0;
242 wxStrcpy(ConnectionStr
, connectStr
);
243 } // wxDbConnectInf::SetConnectionStr()
246 /********** wxDbColFor Constructor **********/
247 wxDbColFor::wxDbColFor()
250 } // wxDbColFor::wxDbColFor()
253 wxDbColFor::~wxDbColFor()
255 } // wxDbColFor::~wxDbColFor()
258 /********** wxDbColFor::Initialize() **********/
259 void wxDbColFor::Initialize()
269 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
272 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
273 } // wxDbColFor::Initialize()
276 /********** wxDbColFor::Format() **********/
277 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
278 short columnSize
, short decimalDigits
)
280 // ----------------------------------------------------------------------------------------
281 // -- 19991224 : mj10777 : Create
282 // There is still a lot of work to do here, but it is a start
283 // It handles all the basic data-types that I have run into up to now
284 // The main work will have be with Dates and float Formatting
285 // (US 1,000.00 ; EU 1.000,00)
286 // There are wxWindow plans for locale support and the new wxDateTime. If
287 // they define some constants (wxEUROPEAN) that can be gloably used,
288 // they should be used here.
289 // ----------------------------------------------------------------------------------------
290 // There should also be a function to scan in a string to fill the variable
291 // ----------------------------------------------------------------------------------------
293 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
294 i_dbDataType
= dbDataType
;
295 i_sqlDataType
= sqlDataType
;
296 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
298 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
300 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
301 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
302 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
303 i_dbDataType
= DB_DATA_TYPE_DATE
;
304 if (i_sqlDataType
== SQL_C_BIT
)
305 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
306 if (i_sqlDataType
== SQL_NUMERIC
)
307 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
308 if (i_sqlDataType
== SQL_REAL
)
309 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
310 if (i_sqlDataType
== SQL_C_BINARY
)
311 i_dbDataType
= DB_DATA_TYPE_BLOB
;
314 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
316 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
319 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
321 case DB_DATA_TYPE_VARCHAR
:
324 case DB_DATA_TYPE_INTEGER
:
327 case DB_DATA_TYPE_FLOAT
:
328 if (decimalDigits
== 0)
331 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
332 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
334 case DB_DATA_TYPE_DATE
:
335 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
337 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
339 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
341 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
343 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
345 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
347 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
349 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
351 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
353 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
356 case DB_DATA_TYPE_BLOB
:
357 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
360 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
364 } // wxDbColFor::Format()
368 /********** wxDbColInf Constructor **********/
369 wxDbColInf::wxDbColInf()
372 } // wxDbColInf::wxDbColInf()
375 /********** wxDbColInf Destructor ********/
376 wxDbColInf::~wxDbColInf()
381 } // wxDbColInf::~wxDbColInf()
384 bool wxDbColInf::Initialize()
406 } // wxDbColInf::Initialize()
409 /********** wxDbTableInf Constructor ********/
410 wxDbTableInf::wxDbTableInf()
413 } // wxDbTableInf::wxDbTableInf()
416 /********** wxDbTableInf Constructor ********/
417 wxDbTableInf::~wxDbTableInf()
422 } // wxDbTableInf::~wxDbTableInf()
425 bool wxDbTableInf::Initialize()
434 } // wxDbTableInf::Initialize()
437 /********** wxDbInf Constructor *************/
441 } // wxDbInf::wxDbInf()
444 /********** wxDbInf Destructor *************/
450 } // wxDbInf::~wxDbInf()
453 /********** wxDbInf::Initialize() *************/
454 bool wxDbInf::Initialize()
462 } // wxDbInf::Initialize()
465 /********** wxDb Constructor **********/
466 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
468 // Copy the HENV into the db class
470 fwdOnlyCursors
= FwdOnlyCursors
;
476 /********** wxDb Destructor **********/
479 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
489 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
490 /********** wxDb::initialize() **********/
491 void wxDb::initialize()
493 * Private member function that sets all wxDb member variables to
494 * known values at creation of the wxDb
499 fpSqlLog
= 0; // Sql Log file pointer
500 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
502 dbmsType
= dbmsUNIDENTIFIED
;
504 wxStrcpy(sqlState
,wxEmptyString
);
505 wxStrcpy(errorMsg
,wxEmptyString
);
506 nativeError
= cbErrorMsg
= 0;
507 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
508 wxStrcpy(errorList
[i
], wxEmptyString
);
510 // Init typeInf structures
511 typeInfVarchar
.TypeName
.Empty();
512 typeInfVarchar
.FsqlType
= 0;
513 typeInfVarchar
.Precision
= 0;
514 typeInfVarchar
.CaseSensitive
= 0;
515 typeInfVarchar
.MaximumScale
= 0;
517 typeInfInteger
.TypeName
.Empty();
518 typeInfInteger
.FsqlType
= 0;
519 typeInfInteger
.Precision
= 0;
520 typeInfInteger
.CaseSensitive
= 0;
521 typeInfInteger
.MaximumScale
= 0;
523 typeInfFloat
.TypeName
.Empty();
524 typeInfFloat
.FsqlType
= 0;
525 typeInfFloat
.Precision
= 0;
526 typeInfFloat
.CaseSensitive
= 0;
527 typeInfFloat
.MaximumScale
= 0;
529 typeInfDate
.TypeName
.Empty();
530 typeInfDate
.FsqlType
= 0;
531 typeInfDate
.Precision
= 0;
532 typeInfDate
.CaseSensitive
= 0;
533 typeInfDate
.MaximumScale
= 0;
535 typeInfBlob
.TypeName
.Empty();
536 typeInfBlob
.FsqlType
= 0;
537 typeInfBlob
.Precision
= 0;
538 typeInfBlob
.CaseSensitive
= 0;
539 typeInfBlob
.MaximumScale
= 0;
541 // Error reporting is turned OFF by default
544 // Allocate a data source connection handle
545 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
548 // Initialize the db status flag
551 // Mark database as not open as of yet
554 dbOpenedWithConnectionString
= FALSE
;
555 } // wxDb::initialize()
558 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
560 // NOTE: Return value from this function MUST be copied
561 // immediately, as the value is not good after
562 // this function has left scope.
564 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
568 if (!wxStrlen(userID
))
576 // dBase does not use user names, and some drivers fail if you try to pass one
577 if ( Dbms() == dbmsDBASE
578 || Dbms() == dbmsXBASE_SEQUITER
)
581 // Oracle user names may only be in uppercase, so force
582 // the name to uppercase
583 if (Dbms() == dbmsORACLE
)
584 UserID
= UserID
.Upper();
586 return UserID
.c_str();
587 } // wxDb::convertUserID()
590 bool wxDb::open(bool failOnDataTypeUnsupported
)
593 If using Intersolv branded ODBC drivers, this is the place where you would substitute
594 your branded driver license information
596 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
597 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
600 // Mark database as open
603 // Allocate a statement handle for the database connection
604 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
605 return(DispAllErrors(henv
, hdbc
));
607 // Set Connection Options
608 if (!setConnectionOptions())
611 // Query the data source for inf. about itself
612 if (!getDbInfo(failOnDataTypeUnsupported
))
615 // Query the data source regarding data type information
618 // The way it was determined which SQL data types to use was by calling SQLGetInfo
619 // for all of the possible SQL data types to see which ones were supported. If
620 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
621 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
622 // types I've selected below will not alway's be what we want. These are just
623 // what happened to work against an Oracle 7/Intersolv combination. The following is
624 // a complete list of the results I got back against the Oracle 7 database:
626 // SQL_BIGINT SQL_NO_DATA_FOUND
627 // SQL_BINARY SQL_NO_DATA_FOUND
628 // SQL_BIT SQL_NO_DATA_FOUND
629 // SQL_CHAR type name = 'CHAR', Precision = 255
630 // SQL_DATE SQL_NO_DATA_FOUND
631 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
632 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
633 // SQL_FLOAT SQL_NO_DATA_FOUND
634 // SQL_INTEGER SQL_NO_DATA_FOUND
635 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
636 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
637 // SQL_NUMERIC SQL_NO_DATA_FOUND
638 // SQL_REAL SQL_NO_DATA_FOUND
639 // SQL_SMALLINT SQL_NO_DATA_FOUND
640 // SQL_TIME SQL_NO_DATA_FOUND
641 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
642 // SQL_VARBINARY type name = 'RAW', Precision = 255
643 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
644 // =====================================================================
645 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
647 // SQL_VARCHAR type name = 'TEXT', Precision = 255
648 // SQL_TIMESTAMP type name = 'DATETIME'
649 // SQL_DECIMAL SQL_NO_DATA_FOUND
650 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
651 // SQL_FLOAT SQL_NO_DATA_FOUND
652 // SQL_REAL type name = 'SINGLE', Precision = 7
653 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
654 // SQL_INTEGER type name = 'LONG', Precision = 10
656 // VARCHAR = Variable length character string
657 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
658 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
661 typeInfVarchar
.FsqlType
= SQL_CHAR
;
663 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
666 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
667 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
668 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
669 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
670 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
672 if (failOnDataTypeUnsupported
)
676 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
678 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
680 typeInfFloat
.FsqlType
= SQL_FLOAT
;
682 typeInfFloat
.FsqlType
= SQL_REAL
;
684 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
687 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
689 // If SQL_INTEGER is not supported, use the floating point
690 // data type to store integers as well as floats
691 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
693 if (failOnDataTypeUnsupported
)
697 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
700 typeInfInteger
.FsqlType
= SQL_INTEGER
;
703 if (!getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
705 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
708 if (getDataTypeInfo(SQL_DATETIME
,typeInfDate
))
710 typeInfDate
.FsqlType
= SQL_TIME
;
713 #endif // SQL_DATETIME defined
715 if (failOnDataTypeUnsupported
)
720 typeInfDate
.FsqlType
= SQL_DATE
;
723 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
726 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
728 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
730 if (failOnDataTypeUnsupported
)
734 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
737 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
740 #ifdef DBDEBUG_CONSOLE
741 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
742 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
743 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
744 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
745 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
749 // Completed Successfully
753 bool wxDb::Open(const wxString
& inConnectStr
, bool failOnDataTypeUnsupported
)
755 wxASSERT(inConnectStr
.Length());
762 if (!FwdOnlyCursors())
764 // Specify that the ODBC cursor library be used, if needed. This must be
765 // specified before the connection is made.
766 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
768 #ifdef DBDEBUG_CONSOLE
769 if (retcode
== SQL_SUCCESS
)
770 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
772 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
776 // Connect to the data source
777 UCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
778 short outConnectBufferLen
;
780 inConnectionStr
= inConnectStr
;
782 retcode
= SQLDriverConnect(hdbc
, NULL
, (UCHAR FAR
*)inConnectionStr
.c_str(),
783 inConnectionStr
.Length(), (UCHAR FAR
*)outConnectBuffer
,
784 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
786 if ((retcode
!= SQL_SUCCESS
) &&
787 (retcode
!= SQL_SUCCESS_WITH_INFO
))
788 return(DispAllErrors(henv
, hdbc
));
790 outConnectBuffer
[outConnectBufferLen
] = 0;
791 outConnectionStr
= outConnectBuffer
;
792 dbOpenedWithConnectionString
= TRUE
;
794 return open(failOnDataTypeUnsupported
);
797 /********** wxDb::Open() **********/
798 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
800 wxASSERT(Dsn
.Length());
805 inConnectionStr
= "";
806 outConnectionStr
= "";
810 if (!FwdOnlyCursors())
812 // Specify that the ODBC cursor library be used, if needed. This must be
813 // specified before the connection is made.
814 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
816 #ifdef DBDEBUG_CONSOLE
817 if (retcode
== SQL_SUCCESS
)
818 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
820 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
822 wxUnusedVar( retcode
);
826 // Connect to the data source
827 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
828 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
829 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
831 if ((retcode
!= SQL_SUCCESS
) &&
832 (retcode
!= SQL_SUCCESS_WITH_INFO
))
833 return(DispAllErrors(henv
, hdbc
));
835 return open(failOnDataTypeUnsupported
);
840 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
842 wxASSERT(dbConnectInf
);
844 // Use the connection string if one is present
845 if (dbConnectInf
->UseConnectionStr())
846 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
848 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
849 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
853 bool wxDb::Open(wxDb
*copyDb
)
855 dsn
= copyDb
->GetDatasourceName();
856 uid
= copyDb
->GetUsername();
857 authStr
= copyDb
->GetPassword();
858 inConnectionStr
= copyDb
->GetConnectionInStr();
859 outConnectionStr
= copyDb
->GetConnectionOutStr();
863 if (!FwdOnlyCursors())
865 // Specify that the ODBC cursor library be used, if needed. This must be
866 // specified before the connection is made.
867 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
869 #ifdef DBDEBUG_CONSOLE
870 if (retcode
== SQL_SUCCESS
)
871 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
873 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
875 wxUnusedVar( retcode
);
879 if (copyDb
->OpenedWithConnectionString())
881 // Connect to the data source
882 UCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
883 short outConnectBufferLen
;
885 inConnectionStr
= copyDb
->GetConnectionInStr();
887 retcode
= SQLDriverConnect(hdbc
, NULL
, (UCHAR FAR
*)inConnectionStr
.c_str(),
888 inConnectionStr
.Length(), (UCHAR FAR
*)outConnectBuffer
,
889 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
891 if ((retcode
!= SQL_SUCCESS
) &&
892 (retcode
!= SQL_SUCCESS_WITH_INFO
))
893 return(DispAllErrors(henv
, hdbc
));
895 outConnectBuffer
[outConnectBufferLen
] = 0;
896 outConnectionStr
= outConnectBuffer
;
897 dbOpenedWithConnectionString
= TRUE
;
901 // Connect to the data source
902 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
903 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
904 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
907 if ((retcode
!= SQL_SUCCESS
) &&
908 (retcode
!= SQL_SUCCESS_WITH_INFO
))
909 return(DispAllErrors(henv
, hdbc
));
912 If using Intersolv branded ODBC drivers, this is the place where you would substitute
913 your branded driver license information
915 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
916 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
919 // Mark database as open
922 // Allocate a statement handle for the database connection
923 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
924 return(DispAllErrors(henv
, hdbc
));
926 // Set Connection Options
927 if (!setConnectionOptions())
930 // Instead of Querying the data source for info about itself, it can just be copied
931 // from the wxDb instance that was passed in (copyDb).
932 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
933 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
934 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
935 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
936 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
937 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
938 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
939 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
940 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
941 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
942 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
943 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
944 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
945 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
946 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
947 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
948 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
949 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
950 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
951 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
952 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
953 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
954 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
955 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
956 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
957 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
958 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
959 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
960 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
961 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
962 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
964 // VARCHAR = Variable length character string
965 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
966 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
967 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
968 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
969 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
972 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
973 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
974 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
975 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
976 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
979 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
980 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
981 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
982 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
983 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
986 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
987 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
988 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
989 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
990 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
993 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
994 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
995 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
996 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
997 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
999 #ifdef DBDEBUG_CONSOLE
1000 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1001 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1002 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1003 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1004 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1008 // Completed Successfully
1013 /********** wxDb::setConnectionOptions() **********/
1014 bool wxDb::setConnectionOptions(void)
1016 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1021 // I need to get the DBMS name here, because some of the connection options
1022 // are database specific and need to call the Dbms() function.
1023 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1024 return(DispAllErrors(henv
, hdbc
));
1026 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1027 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1028 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1030 // By default, MS Sql Server closes cursors on commit and rollback. The following
1031 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1032 // after a transaction. This is a driver specific option and is not part of the
1033 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1034 // The database settings don't have any effect one way or the other.
1035 if (Dbms() == dbmsMS_SQL_SERVER
)
1037 const long SQL_PRESERVE_CURSORS
= 1204L;
1038 const long SQL_PC_ON
= 1L;
1039 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1042 // Display the connection options to verify them
1043 #ifdef DBDEBUG_CONSOLE
1045 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1047 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
1048 return(DispAllErrors(henv
, hdbc
));
1049 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1051 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
1052 return(DispAllErrors(henv
, hdbc
));
1053 cout
<< wxT("ODBC CURSORS: ");
1056 case(SQL_CUR_USE_IF_NEEDED
):
1057 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1059 case(SQL_CUR_USE_ODBC
):
1060 cout
<< wxT("SQL_CUR_USE_ODBC");
1062 case(SQL_CUR_USE_DRIVER
):
1063 cout
<< wxT("SQL_CUR_USE_DRIVER");
1068 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
1069 return(DispAllErrors(henv
, hdbc
));
1070 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1075 // Completed Successfully
1078 } // wxDb::setConnectionOptions()
1081 /********** wxDb::getDbInfo() **********/
1082 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1087 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
);
1088 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1090 DispAllErrors(henv
, hdbc
);
1091 if (failOnDataTypeUnsupported
)
1095 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
);
1096 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1098 DispAllErrors(henv
, hdbc
);
1099 if (failOnDataTypeUnsupported
)
1103 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
);
1104 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1106 DispAllErrors(henv
, hdbc
);
1107 if (failOnDataTypeUnsupported
)
1112 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1113 // causing database connectivity to fail in some cases.
1114 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1115 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1117 DispAllErrors(henv
, hdbc
);
1118 if (failOnDataTypeUnsupported
)
1122 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1123 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1125 DispAllErrors(henv
, hdbc
);
1126 if (failOnDataTypeUnsupported
)
1130 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1131 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1133 DispAllErrors(henv
, hdbc
);
1134 if (failOnDataTypeUnsupported
)
1138 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
);
1139 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1141 DispAllErrors(henv
, hdbc
);
1142 if (failOnDataTypeUnsupported
)
1146 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
);
1147 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1149 DispAllErrors(henv
, hdbc
);
1150 if (failOnDataTypeUnsupported
)
1154 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1155 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1157 DispAllErrors(henv
, hdbc
);
1158 if (failOnDataTypeUnsupported
)
1162 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
);
1163 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1165 DispAllErrors(henv
, hdbc
);
1166 if (failOnDataTypeUnsupported
)
1170 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1171 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1173 DispAllErrors(henv
, hdbc
);
1174 if (failOnDataTypeUnsupported
)
1178 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1179 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1181 // Not all drivers support this call - Nick Gorham(unixODBC)
1182 dbInf
.cliConfLvl
= 0;
1183 DispAllErrors(henv
, hdbc
);
1184 if (failOnDataTypeUnsupported
)
1188 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1189 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1191 DispAllErrors(henv
, hdbc
);
1192 if (failOnDataTypeUnsupported
)
1196 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
);
1197 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1199 DispAllErrors(henv
, hdbc
);
1200 if (failOnDataTypeUnsupported
)
1204 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
);
1205 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1207 DispAllErrors(henv
, hdbc
);
1208 if (failOnDataTypeUnsupported
)
1212 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
);
1213 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1215 DispAllErrors(henv
, hdbc
);
1216 if (failOnDataTypeUnsupported
)
1220 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1221 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1223 DispAllErrors(henv
, hdbc
);
1224 if (failOnDataTypeUnsupported
)
1228 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1229 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1231 DispAllErrors(henv
, hdbc
);
1232 if (failOnDataTypeUnsupported
)
1236 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1237 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1239 DispAllErrors(henv
, hdbc
);
1240 if (failOnDataTypeUnsupported
)
1244 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
);
1245 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1247 DispAllErrors(henv
, hdbc
);
1248 if (failOnDataTypeUnsupported
)
1252 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1253 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1255 DispAllErrors(henv
, hdbc
);
1256 if (failOnDataTypeUnsupported
)
1260 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1261 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1263 DispAllErrors(henv
, hdbc
);
1264 if (failOnDataTypeUnsupported
)
1268 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1269 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1271 DispAllErrors(henv
, hdbc
);
1272 if (failOnDataTypeUnsupported
)
1276 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1277 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1279 DispAllErrors(henv
, hdbc
);
1280 if (failOnDataTypeUnsupported
)
1284 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1285 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1287 DispAllErrors(henv
, hdbc
);
1288 if (failOnDataTypeUnsupported
)
1292 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1293 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1295 DispAllErrors(henv
, hdbc
);
1296 if (failOnDataTypeUnsupported
)
1300 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1301 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1303 DispAllErrors(henv
, hdbc
);
1304 if (failOnDataTypeUnsupported
)
1308 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1309 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1311 DispAllErrors(henv
, hdbc
);
1312 if (failOnDataTypeUnsupported
)
1316 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1317 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1319 DispAllErrors(henv
, hdbc
);
1320 if (failOnDataTypeUnsupported
)
1324 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1325 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1327 DispAllErrors(henv
, hdbc
);
1328 if (failOnDataTypeUnsupported
)
1332 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1333 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1335 DispAllErrors(henv
, hdbc
);
1336 if (failOnDataTypeUnsupported
)
1340 #ifdef DBDEBUG_CONSOLE
1341 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1342 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1343 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1344 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1346 cout
<< wxT("API Conf. Level: ");
1347 switch(dbInf
.apiConfLvl
)
1349 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1350 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1351 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1355 cout
<< wxT("SAG CLI Conf. Level: ");
1356 switch(dbInf
.cliConfLvl
)
1358 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1359 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1363 cout
<< wxT("SQL Conf. Level: ");
1364 switch(dbInf
.sqlConfLvl
)
1366 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1367 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1368 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1372 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1373 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1374 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1375 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1376 cout
<< wxT("Cursor COMMIT Behavior: ");
1377 switch(dbInf
.cursorCommitBehavior
)
1379 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1380 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1381 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1385 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1386 switch(dbInf
.cursorRollbackBehavior
)
1388 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1389 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1390 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1394 cout
<< wxT("Support NOT NULL clause: ");
1395 switch(dbInf
.supportNotNullClause
)
1397 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1398 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1402 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1403 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1405 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1408 cout
<< wxT("Default Transaction Isolation: ";
1409 switch(dbInf
.txnIsolation
)
1411 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1412 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1413 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1414 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1416 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1421 cout
<< wxT("Transaction Isolation Options: ");
1422 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1423 cout
<< wxT("Read Uncommitted, ");
1424 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1425 cout
<< wxT("Read Committed, ");
1426 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1427 cout
<< wxT("Repeatable Read, ");
1428 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1429 cout
<< wxT("Serializable, ");
1431 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1432 cout
<< wxT("Versioning");
1436 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1437 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1438 cout
<< wxT("Next, ");
1439 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1440 cout
<< wxT("Prev, ");
1441 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1442 cout
<< wxT("First, ");
1443 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1444 cout
<< wxT("Last, ");
1445 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1446 cout
<< wxT("Absolute, ");
1447 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1448 cout
<< wxT("Relative, ");
1450 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1451 cout
<< wxT("Resume, ");
1453 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1454 cout
<< wxT("Bookmark");
1457 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1458 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1459 cout
<< wxT("No Change, ");
1460 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1461 cout
<< wxT("Exclusive, ");
1462 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1463 cout
<< wxT("UnLock");
1466 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1467 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1468 cout
<< wxT("Position, ");
1469 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1470 cout
<< wxT("Refresh, ");
1471 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1472 cout
<< wxT("Upd, "));
1473 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1474 cout
<< wxT("Del, ");
1475 if (dbInf
.posOperations
& SQL_POS_ADD
)
1479 cout
<< wxT("Positioned Statements Supported: ");
1480 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1481 cout
<< wxT("Pos delete, ");
1482 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1483 cout
<< wxT("Pos update, ");
1484 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1485 cout
<< wxT("Select for update");
1488 cout
<< wxT("Scroll Concurrency: ");
1489 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1490 cout
<< wxT("Read Only, ");
1491 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1492 cout
<< wxT("Lock, ");
1493 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1494 cout
<< wxT("Opt. Rowver, ");
1495 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1496 cout
<< wxT("Opt. Values");
1499 cout
<< wxT("Scroll Options: ");
1500 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1501 cout
<< wxT("Fwd Only, ");
1502 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1503 cout
<< wxT("Static, ");
1504 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1505 cout
<< wxT("Keyset Driven, ");
1506 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1507 cout
<< wxT("Dynamic, ");
1508 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1509 cout
<< wxT("Mixed");
1512 cout
<< wxT("Static Sensitivity: ");
1513 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1514 cout
<< wxT("Additions, ");
1515 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1516 cout
<< wxT("Deletions, ");
1517 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1518 cout
<< wxT("Updates");
1521 cout
<< wxT("Transaction Capable?: ");
1522 switch(dbInf
.txnCapable
)
1524 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1525 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1526 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1527 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1528 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1535 // Completed Successfully
1538 } // wxDb::getDbInfo()
1541 /********** wxDb::getDataTypeInfo() **********/
1542 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1545 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1546 * the data type inf. is gathered for.
1548 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1553 // Get information about the data type specified
1554 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1555 return(DispAllErrors(henv
, hdbc
, hstmt
));
1558 retcode
= SQLFetch(hstmt
);
1559 if (retcode
!= SQL_SUCCESS
)
1561 #ifdef DBDEBUG_CONSOLE
1562 if (retcode
== SQL_NO_DATA_FOUND
)
1563 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1565 DispAllErrors(henv
, hdbc
, hstmt
);
1566 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1570 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1572 // Obtain columns from the record
1573 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1574 return(DispAllErrors(henv
, hdbc
, hstmt
));
1576 structSQLTypeInfo
.TypeName
= typeName
;
1578 // BJO 20000503: no more needed with new GetColumns...
1581 if (Dbms() == dbmsMY_SQL
)
1583 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1584 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1585 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1586 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1587 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1588 structSQLTypeInfo
.TypeName
= wxT("int");
1589 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1590 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1591 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1592 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1593 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1594 structSQLTypeInfo
.TypeName
= wxT("char");
1597 // BJO 20000427 : OpenLink driver
1598 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1599 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1601 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1602 structSQLTypeInfo
.TypeName
= wxT("real");
1606 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1607 return(DispAllErrors(henv
, hdbc
, hstmt
));
1608 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1609 return(DispAllErrors(henv
, hdbc
, hstmt
));
1610 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1611 // return(DispAllErrors(henv, hdbc, hstmt));
1613 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1614 return(DispAllErrors(henv
, hdbc
, hstmt
));
1616 if (structSQLTypeInfo
.MaximumScale
< 0)
1617 structSQLTypeInfo
.MaximumScale
= 0;
1619 // Close the statement handle which closes open cursors
1620 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1621 return(DispAllErrors(henv
, hdbc
, hstmt
));
1623 // Completed Successfully
1626 } // wxDb::getDataTypeInfo()
1629 /********** wxDb::Close() **********/
1630 void wxDb::Close(void)
1632 // Close the Sql Log file
1639 // Free statement handle
1642 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1643 DispAllErrors(henv
, hdbc
);
1646 // Disconnect from the datasource
1647 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1648 DispAllErrors(henv
, hdbc
);
1650 // Free the connection to the datasource
1651 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1652 DispAllErrors(henv
, hdbc
);
1654 // There should be zero Ctable objects still connected to this db object
1655 wxASSERT(nTables
== 0);
1660 pNode
= TablesInUse
.GetFirst();
1664 tiu
= (wxTablesInUse
*)pNode
->GetData();
1665 if (tiu
->pDb
== this)
1667 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1668 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1669 wxLogDebug(s
.c_str(),s2
.c_str());
1671 pNode
= pNode
->GetNext();
1675 // Copy the error messages to a global variable
1677 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1678 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1680 dbmsType
= dbmsUNIDENTIFIED
;
1686 /********** wxDb::CommitTrans() **********/
1687 bool wxDb::CommitTrans(void)
1691 // Commit the transaction
1692 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1693 return(DispAllErrors(henv
, hdbc
));
1696 // Completed successfully
1699 } // wxDb::CommitTrans()
1702 /********** wxDb::RollbackTrans() **********/
1703 bool wxDb::RollbackTrans(void)
1705 // Rollback the transaction
1706 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1707 return(DispAllErrors(henv
, hdbc
));
1709 // Completed successfully
1712 } // wxDb::RollbackTrans()
1715 /********** wxDb::DispAllErrors() **********/
1716 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1718 * This function is called internally whenever an error condition prevents the user's
1719 * request from being executed. This function will query the datasource as to the
1720 * actual error(s) that just occured on the previous request of the datasource.
1722 * The function will retrieve each error condition from the datasource and
1723 * Printf the codes/text values into a string which it then logs via logError().
1724 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1725 * window and program execution will be paused until the user presses a key.
1727 * This function always returns a FALSE, so that functions which call this function
1728 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1729 * of the users request, so that the calling code can then process the error msg log
1732 wxString odbcErrMsg
;
1734 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1736 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1737 logError(odbcErrMsg
, sqlState
);
1740 #ifdef DBDEBUG_CONSOLE
1741 // When run in console mode, use standard out to display errors.
1742 cout
<< odbcErrMsg
.c_str() << endl
;
1743 cout
<< wxT("Press any key to continue...") << endl
;
1748 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1753 return(FALSE
); // This function always returns FALSE.
1755 } // wxDb::DispAllErrors()
1758 /********** wxDb::GetNextError() **********/
1759 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1761 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1766 } // wxDb::GetNextError()
1769 /********** wxDb::DispNextError() **********/
1770 void wxDb::DispNextError(void)
1772 wxString odbcErrMsg
;
1774 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1775 logError(odbcErrMsg
, sqlState
);
1780 #ifdef DBDEBUG_CONSOLE
1781 // When run in console mode, use standard out to display errors.
1782 cout
<< odbcErrMsg
.c_str() << endl
;
1783 cout
<< wxT("Press any key to continue...") << endl
;
1788 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1789 #endif // __WXDEBUG__
1791 } // wxDb::DispNextError()
1794 /********** wxDb::logError() **********/
1795 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1797 wxASSERT(errMsg
.Length());
1799 static int pLast
= -1;
1802 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1805 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1806 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1810 wxStrcpy(errorList
[pLast
], errMsg
);
1812 if (SQLState
.Length())
1813 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1814 DB_STATUS
= dbStatus
;
1816 // Add the errmsg to the sql log
1817 WriteSqlLog(errMsg
);
1819 } // wxDb::logError()
1822 /**********wxDb::TranslateSqlState() **********/
1823 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1825 if (!wxStrcmp(SQLState
, wxT("01000")))
1826 return(DB_ERR_GENERAL_WARNING
);
1827 if (!wxStrcmp(SQLState
, wxT("01002")))
1828 return(DB_ERR_DISCONNECT_ERROR
);
1829 if (!wxStrcmp(SQLState
, wxT("01004")))
1830 return(DB_ERR_DATA_TRUNCATED
);
1831 if (!wxStrcmp(SQLState
, wxT("01006")))
1832 return(DB_ERR_PRIV_NOT_REVOKED
);
1833 if (!wxStrcmp(SQLState
, wxT("01S00")))
1834 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1835 if (!wxStrcmp(SQLState
, wxT("01S01")))
1836 return(DB_ERR_ERROR_IN_ROW
);
1837 if (!wxStrcmp(SQLState
, wxT("01S02")))
1838 return(DB_ERR_OPTION_VALUE_CHANGED
);
1839 if (!wxStrcmp(SQLState
, wxT("01S03")))
1840 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1841 if (!wxStrcmp(SQLState
, wxT("01S04")))
1842 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1843 if (!wxStrcmp(SQLState
, wxT("07001")))
1844 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1845 if (!wxStrcmp(SQLState
, wxT("07006")))
1846 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1847 if (!wxStrcmp(SQLState
, wxT("08001")))
1848 return(DB_ERR_UNABLE_TO_CONNECT
);
1849 if (!wxStrcmp(SQLState
, wxT("08002")))
1850 return(DB_ERR_CONNECTION_IN_USE
);
1851 if (!wxStrcmp(SQLState
, wxT("08003")))
1852 return(DB_ERR_CONNECTION_NOT_OPEN
);
1853 if (!wxStrcmp(SQLState
, wxT("08004")))
1854 return(DB_ERR_REJECTED_CONNECTION
);
1855 if (!wxStrcmp(SQLState
, wxT("08007")))
1856 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1857 if (!wxStrcmp(SQLState
, wxT("08S01")))
1858 return(DB_ERR_COMM_LINK_FAILURE
);
1859 if (!wxStrcmp(SQLState
, wxT("21S01")))
1860 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1861 if (!wxStrcmp(SQLState
, wxT("21S02")))
1862 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1863 if (!wxStrcmp(SQLState
, wxT("22001")))
1864 return(DB_ERR_STRING_RIGHT_TRUNC
);
1865 if (!wxStrcmp(SQLState
, wxT("22003")))
1866 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1867 if (!wxStrcmp(SQLState
, wxT("22005")))
1868 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1869 if (!wxStrcmp(SQLState
, wxT("22008")))
1870 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1871 if (!wxStrcmp(SQLState
, wxT("22012")))
1872 return(DB_ERR_DIVIDE_BY_ZERO
);
1873 if (!wxStrcmp(SQLState
, wxT("22026")))
1874 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1875 if (!wxStrcmp(SQLState
, wxT("23000")))
1876 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1877 if (!wxStrcmp(SQLState
, wxT("24000")))
1878 return(DB_ERR_INVALID_CURSOR_STATE
);
1879 if (!wxStrcmp(SQLState
, wxT("25000")))
1880 return(DB_ERR_INVALID_TRANS_STATE
);
1881 if (!wxStrcmp(SQLState
, wxT("28000")))
1882 return(DB_ERR_INVALID_AUTH_SPEC
);
1883 if (!wxStrcmp(SQLState
, wxT("34000")))
1884 return(DB_ERR_INVALID_CURSOR_NAME
);
1885 if (!wxStrcmp(SQLState
, wxT("37000")))
1886 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1887 if (!wxStrcmp(SQLState
, wxT("3C000")))
1888 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1889 if (!wxStrcmp(SQLState
, wxT("40001")))
1890 return(DB_ERR_SERIALIZATION_FAILURE
);
1891 if (!wxStrcmp(SQLState
, wxT("42000")))
1892 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1893 if (!wxStrcmp(SQLState
, wxT("70100")))
1894 return(DB_ERR_OPERATION_ABORTED
);
1895 if (!wxStrcmp(SQLState
, wxT("IM001")))
1896 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1897 if (!wxStrcmp(SQLState
, wxT("IM002")))
1898 return(DB_ERR_NO_DATA_SOURCE
);
1899 if (!wxStrcmp(SQLState
, wxT("IM003")))
1900 return(DB_ERR_DRIVER_LOAD_ERROR
);
1901 if (!wxStrcmp(SQLState
, wxT("IM004")))
1902 return(DB_ERR_SQLALLOCENV_FAILED
);
1903 if (!wxStrcmp(SQLState
, wxT("IM005")))
1904 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1905 if (!wxStrcmp(SQLState
, wxT("IM006")))
1906 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1907 if (!wxStrcmp(SQLState
, wxT("IM007")))
1908 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1909 if (!wxStrcmp(SQLState
, wxT("IM008")))
1910 return(DB_ERR_DIALOG_FAILED
);
1911 if (!wxStrcmp(SQLState
, wxT("IM009")))
1912 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1913 if (!wxStrcmp(SQLState
, wxT("IM010")))
1914 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1915 if (!wxStrcmp(SQLState
, wxT("IM011")))
1916 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1917 if (!wxStrcmp(SQLState
, wxT("IM012")))
1918 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1919 if (!wxStrcmp(SQLState
, wxT("IM013")))
1920 return(DB_ERR_TRACE_FILE_ERROR
);
1921 if (!wxStrcmp(SQLState
, wxT("S0001")))
1922 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1923 if (!wxStrcmp(SQLState
, wxT("S0002")))
1924 return(DB_ERR_TABLE_NOT_FOUND
);
1925 if (!wxStrcmp(SQLState
, wxT("S0011")))
1926 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1927 if (!wxStrcmp(SQLState
, wxT("S0012")))
1928 return(DB_ERR_INDEX_NOT_FOUND
);
1929 if (!wxStrcmp(SQLState
, wxT("S0021")))
1930 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1931 if (!wxStrcmp(SQLState
, wxT("S0022")))
1932 return(DB_ERR_COLUMN_NOT_FOUND
);
1933 if (!wxStrcmp(SQLState
, wxT("S0023")))
1934 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1935 if (!wxStrcmp(SQLState
, wxT("S1000")))
1936 return(DB_ERR_GENERAL_ERROR
);
1937 if (!wxStrcmp(SQLState
, wxT("S1001")))
1938 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1939 if (!wxStrcmp(SQLState
, wxT("S1002")))
1940 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1941 if (!wxStrcmp(SQLState
, wxT("S1003")))
1942 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1943 if (!wxStrcmp(SQLState
, wxT("S1004")))
1944 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1945 if (!wxStrcmp(SQLState
, wxT("S1008")))
1946 return(DB_ERR_OPERATION_CANCELLED
);
1947 if (!wxStrcmp(SQLState
, wxT("S1009")))
1948 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1949 if (!wxStrcmp(SQLState
, wxT("S1010")))
1950 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1951 if (!wxStrcmp(SQLState
, wxT("S1011")))
1952 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1953 if (!wxStrcmp(SQLState
, wxT("S1012")))
1954 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1955 if (!wxStrcmp(SQLState
, wxT("S1015")))
1956 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1957 if (!wxStrcmp(SQLState
, wxT("S1090")))
1958 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1959 if (!wxStrcmp(SQLState
, wxT("S1091")))
1960 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1961 if (!wxStrcmp(SQLState
, wxT("S1092")))
1962 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1963 if (!wxStrcmp(SQLState
, wxT("S1093")))
1964 return(DB_ERR_INVALID_PARAM_NO
);
1965 if (!wxStrcmp(SQLState
, wxT("S1094")))
1966 return(DB_ERR_INVALID_SCALE_VALUE
);
1967 if (!wxStrcmp(SQLState
, wxT("S1095")))
1968 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1969 if (!wxStrcmp(SQLState
, wxT("S1096")))
1970 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1971 if (!wxStrcmp(SQLState
, wxT("S1097")))
1972 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1973 if (!wxStrcmp(SQLState
, wxT("S1098")))
1974 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1975 if (!wxStrcmp(SQLState
, wxT("S1099")))
1976 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1977 if (!wxStrcmp(SQLState
, wxT("S1100")))
1978 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1979 if (!wxStrcmp(SQLState
, wxT("S1101")))
1980 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1981 if (!wxStrcmp(SQLState
, wxT("S1103")))
1982 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1983 if (!wxStrcmp(SQLState
, wxT("S1104")))
1984 return(DB_ERR_INVALID_PRECISION_VALUE
);
1985 if (!wxStrcmp(SQLState
, wxT("S1105")))
1986 return(DB_ERR_INVALID_PARAM_TYPE
);
1987 if (!wxStrcmp(SQLState
, wxT("S1106")))
1988 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1989 if (!wxStrcmp(SQLState
, wxT("S1107")))
1990 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1991 if (!wxStrcmp(SQLState
, wxT("S1108")))
1992 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1993 if (!wxStrcmp(SQLState
, wxT("S1109")))
1994 return(DB_ERR_INVALID_CURSOR_POSITION
);
1995 if (!wxStrcmp(SQLState
, wxT("S1110")))
1996 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1997 if (!wxStrcmp(SQLState
, wxT("S1111")))
1998 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1999 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2000 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2001 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2002 return(DB_ERR_TIMEOUT_EXPIRED
);
2007 } // wxDb::TranslateSqlState()
2010 /********** wxDb::Grant() **********/
2011 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2015 // Build the grant statement
2016 sqlStmt
= wxT("GRANT ");
2017 if (privileges
== DB_GRANT_ALL
)
2018 sqlStmt
+= wxT("ALL");
2022 if (privileges
& DB_GRANT_SELECT
)
2024 sqlStmt
+= wxT("SELECT");
2027 if (privileges
& DB_GRANT_INSERT
)
2030 sqlStmt
+= wxT(", ");
2031 sqlStmt
+= wxT("INSERT");
2033 if (privileges
& DB_GRANT_UPDATE
)
2036 sqlStmt
+= wxT(", ");
2037 sqlStmt
+= wxT("UPDATE");
2039 if (privileges
& DB_GRANT_DELETE
)
2042 sqlStmt
+= wxT(", ");
2043 sqlStmt
+= wxT("DELETE");
2047 sqlStmt
+= wxT(" ON ");
2048 sqlStmt
+= SQLTableName(tableName
);
2049 sqlStmt
+= wxT(" TO ");
2050 sqlStmt
+= userList
;
2052 #ifdef DBDEBUG_CONSOLE
2053 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2056 WriteSqlLog(sqlStmt
);
2058 return(ExecSql(sqlStmt
));
2063 /********** wxDb::CreateView() **********/
2064 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2065 const wxString
&pSqlStmt
, bool attemptDrop
)
2069 // Drop the view first
2070 if (attemptDrop
&& !DropView(viewName
))
2073 // Build the create view statement
2074 sqlStmt
= wxT("CREATE VIEW ");
2075 sqlStmt
+= viewName
;
2077 if (colList
.Length())
2079 sqlStmt
+= wxT(" (");
2081 sqlStmt
+= wxT(")");
2084 sqlStmt
+= wxT(" AS ");
2085 sqlStmt
+= pSqlStmt
;
2087 WriteSqlLog(sqlStmt
);
2089 #ifdef DBDEBUG_CONSOLE
2090 cout
<< sqlStmt
.c_str() << endl
;
2093 return(ExecSql(sqlStmt
));
2095 } // wxDb::CreateView()
2098 /********** wxDb::DropView() **********/
2099 bool wxDb::DropView(const wxString
&viewName
)
2102 * NOTE: This function returns TRUE if the View does not exist, but
2103 * only for identified databases. Code will need to be added
2104 * below for any other databases when those databases are defined
2105 * to handle this situation consistently
2109 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2111 WriteSqlLog(sqlStmt
);
2113 #ifdef DBDEBUG_CONSOLE
2114 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2117 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2119 // Check for "Base table not found" error and ignore
2120 GetNextError(henv
, hdbc
, hstmt
);
2121 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2123 // Check for product specific error codes
2124 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2127 DispAllErrors(henv
, hdbc
, hstmt
);
2134 // Commit the transaction
2140 } // wxDb::DropView()
2143 /********** wxDb::ExecSql() **********/
2144 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2148 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2150 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2151 if (retcode
== SQL_SUCCESS
||
2152 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2158 DispAllErrors(henv
, hdbc
, hstmt
);
2162 } // wxDb::ExecSql()
2165 /********** wxDb::GetNext() **********/
2166 bool wxDb::GetNext(void)
2168 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2172 DispAllErrors(henv
, hdbc
, hstmt
);
2176 } // wxDb::GetNext()
2179 /********** wxDb::GetData() **********/
2180 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2183 wxASSERT(cbReturned
);
2185 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2189 DispAllErrors(henv
, hdbc
, hstmt
);
2193 } // wxDb::GetData()
2196 /********** wxDb::GetKeyFields() **********/
2197 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2199 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2200 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2202 // SQLSMALLINT iKeySeq;
2203 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2204 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2210 * -----------------------------------------------------------------------
2211 * -- 19991224 : mj10777 : Create ------
2212 * -- : Three things are done and stored here : ------
2213 * -- : 1) which Column(s) is/are Primary Key(s) ------
2214 * -- : 2) which tables use this Key as a Foreign Key ------
2215 * -- : 3) which columns are Foreign Key and the name ------
2216 * -- : of the Table where the Key is the Primary Key -----
2217 * -- : Called from GetColumns(const wxString &tableName, ------
2218 * -- int *numCols,const wxChar *userID ) ------
2219 * -----------------------------------------------------------------------
2222 /*---------------------------------------------------------------------*/
2223 /* Get the names of the columns in the primary key. */
2224 /*---------------------------------------------------------------------*/
2225 retcode
= SQLPrimaryKeys(hstmt
,
2226 NULL
, 0, /* Catalog name */
2227 NULL
, 0, /* Schema name */
2228 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2230 /*---------------------------------------------------------------------*/
2231 /* Fetch and display the result set. This will be a list of the */
2232 /* columns in the primary key of the tableName table. */
2233 /*---------------------------------------------------------------------*/
2234 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2236 retcode
= SQLFetch(hstmt
);
2237 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2239 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2240 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2242 for (i
=0;i
<noCols
;i
++) // Find the Column name
2243 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2244 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2247 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2249 /*---------------------------------------------------------------------*/
2250 /* Get all the foreign keys that refer to tableName primary key. */
2251 /*---------------------------------------------------------------------*/
2252 retcode
= SQLForeignKeys(hstmt
,
2253 NULL
, 0, /* Primary catalog */
2254 NULL
, 0, /* Primary schema */
2255 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2256 NULL
, 0, /* Foreign catalog */
2257 NULL
, 0, /* Foreign schema */
2258 NULL
, 0); /* Foreign table */
2260 /*---------------------------------------------------------------------*/
2261 /* Fetch and display the result set. This will be all of the foreign */
2262 /* keys in other tables that refer to the tableName primary key. */
2263 /*---------------------------------------------------------------------*/
2266 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2268 retcode
= SQLFetch(hstmt
);
2269 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2271 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2272 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2273 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2274 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2275 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2276 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2280 tempStr
.Trim(); // Get rid of any unneeded blanks
2281 if (!tempStr
.IsEmpty())
2283 for (i
=0; i
<noCols
; i
++)
2284 { // Find the Column name
2285 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2286 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2290 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2292 /*---------------------------------------------------------------------*/
2293 /* Get all the foreign keys in the tablename table. */
2294 /*---------------------------------------------------------------------*/
2295 retcode
= SQLForeignKeys(hstmt
,
2296 NULL
, 0, /* Primary catalog */
2297 NULL
, 0, /* Primary schema */
2298 NULL
, 0, /* Primary table */
2299 NULL
, 0, /* Foreign catalog */
2300 NULL
, 0, /* Foreign schema */
2301 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2303 /*---------------------------------------------------------------------*/
2304 /* Fetch and display the result set. This will be all of the */
2305 /* primary keys in other tables that are referred to by foreign */
2306 /* keys in the tableName table. */
2307 /*---------------------------------------------------------------------*/
2308 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2310 retcode
= SQLFetch(hstmt
);
2311 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2313 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2314 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2315 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2317 for (i
=0; i
<noCols
; i
++) // Find the Column name
2319 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2321 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2322 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2327 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2331 } // wxDb::GetKeyFields()
2335 /********** wxDb::GetColumns() **********/
2336 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2338 * 1) The last array element of the tableName[] argument must be zero (null).
2339 * This is how the end of the array is detected.
2340 * 2) This function returns an array of wxDbColInf structures. If no columns
2341 * were found, or an error occured, this pointer will be zero (null). THE
2342 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2343 * IS FINISHED WITH IT. i.e.
2345 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2348 * // Use the column inf
2350 * // Destroy the memory
2354 * userID is evaluated in the following manner:
2355 * userID == NULL ... UserID is ignored
2356 * userID == "" ... UserID set equal to 'this->uid'
2357 * userID != "" ... UserID set equal to 'userID'
2359 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2360 * by this function. This function should use its own wxDb instance
2361 * to avoid undesired unbinding of columns.
2366 wxDbColInf
*colInf
= 0;
2374 convertUserID(userID
,UserID
);
2376 // Pass 1 - Determine how many columns there are.
2377 // Pass 2 - Allocate the wxDbColInf array and fill in
2378 // the array with the column information.
2380 for (pass
= 1; pass
<= 2; pass
++)
2384 if (noCols
== 0) // Probably a bogus table name(s)
2386 // Allocate n wxDbColInf objects to hold the column information
2387 colInf
= new wxDbColInf
[noCols
+1];
2390 // Mark the end of the array
2391 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2392 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2393 colInf
[noCols
].sqlDataType
= 0;
2395 // Loop through each table name
2397 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2399 TableName
= tableName
[tbl
];
2400 // Oracle and Interbase table names are uppercase only, so force
2401 // the name to uppercase just in case programmer forgot to do this
2402 if ((Dbms() == dbmsORACLE
) ||
2403 (Dbms() == dbmsINTERBASE
))
2404 TableName
= TableName
.Upper();
2406 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2408 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2409 // use the call below that leaves out the user name
2410 if (!UserID
.IsEmpty() &&
2411 Dbms() != dbmsMY_SQL
&&
2412 Dbms() != dbmsACCESS
&&
2413 Dbms() != dbmsMS_SQL_SERVER
)
2415 retcode
= SQLColumns(hstmt
,
2416 NULL
, 0, // All qualifiers
2417 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2418 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2419 NULL
, 0); // All columns
2423 retcode
= SQLColumns(hstmt
,
2424 NULL
, 0, // All qualifiers
2426 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2427 NULL
, 0); // All columns
2429 if (retcode
!= SQL_SUCCESS
)
2430 { // Error occured, abort
2431 DispAllErrors(henv
, hdbc
, hstmt
);
2434 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2438 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2440 if (pass
== 1) // First pass, just add up the number of columns
2442 else // Pass 2; Fill in the array of structures
2444 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2446 // NOTE: Only the ODBC 1.x fields are retrieved
2447 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2448 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2449 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2450 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2451 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2452 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2453 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2454 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2455 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2456 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2457 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2458 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2460 // Determine the wxDb data type that is used to represent the native data type of this data source
2461 colInf
[colNo
].dbDataType
= 0;
2462 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2465 // IODBC does not return a correct columnSize, so we set
2466 // columnSize = bufferLength if no column size was returned
2467 // IODBC returns the columnSize in bufferLength.. (bug)
2468 if (colInf
[colNo
].columnSize
< 1)
2470 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2473 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2475 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2476 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2477 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2478 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2479 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2480 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2481 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2482 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2487 if (retcode
!= SQL_NO_DATA_FOUND
)
2488 { // Error occured, abort
2489 DispAllErrors(henv
, hdbc
, hstmt
);
2492 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2498 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2501 } // wxDb::GetColumns()
2504 /********** wxDb::GetColumns() **********/
2506 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2508 // Same as the above GetColumns() function except this one gets columns
2509 // only for a single table, and if 'numCols' is not NULL, the number of
2510 // columns stored in the returned wxDbColInf is set in '*numCols'
2512 // userID is evaluated in the following manner:
2513 // userID == NULL ... UserID is ignored
2514 // userID == "" ... UserID set equal to 'this->uid'
2515 // userID != "" ... UserID set equal to 'userID'
2517 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2518 // by this function. This function should use its own wxDb instance
2519 // to avoid undesired unbinding of columns.
2524 wxDbColInf
*colInf
= 0;
2532 convertUserID(userID
,UserID
);
2534 // Pass 1 - Determine how many columns there are.
2535 // Pass 2 - Allocate the wxDbColInf array and fill in
2536 // the array with the column information.
2538 for (pass
= 1; pass
<= 2; pass
++)
2542 if (noCols
== 0) // Probably a bogus table name(s)
2544 // Allocate n wxDbColInf objects to hold the column information
2545 colInf
= new wxDbColInf
[noCols
+1];
2548 // Mark the end of the array
2549 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2550 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2551 colInf
[noCols
].sqlDataType
= 0;
2554 TableName
= tableName
;
2555 // Oracle and Interbase table names are uppercase only, so force
2556 // the name to uppercase just in case programmer forgot to do this
2557 if ((Dbms() == dbmsORACLE
) ||
2558 (Dbms() == dbmsINTERBASE
))
2559 TableName
= TableName
.Upper();
2561 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2563 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2564 // use the call below that leaves out the user name
2565 if (!UserID
.IsEmpty() &&
2566 Dbms() != dbmsMY_SQL
&&
2567 Dbms() != dbmsACCESS
&&
2568 Dbms() != dbmsMS_SQL_SERVER
)
2570 retcode
= SQLColumns(hstmt
,
2571 NULL
, 0, // All qualifiers
2572 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2573 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2574 NULL
, 0); // All columns
2578 retcode
= SQLColumns(hstmt
,
2579 NULL
, 0, // All qualifiers
2581 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2582 NULL
, 0); // All columns
2584 if (retcode
!= SQL_SUCCESS
)
2585 { // Error occured, abort
2586 DispAllErrors(henv
, hdbc
, hstmt
);
2589 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2595 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2597 if (pass
== 1) // First pass, just add up the number of columns
2599 else // Pass 2; Fill in the array of structures
2601 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2603 // NOTE: Only the ODBC 1.x fields are retrieved
2604 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2605 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2606 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2607 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2608 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2609 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2610 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2611 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2612 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2613 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2614 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2615 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2616 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2617 // Start Values for Primary/Foriegn Key (=No)
2618 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2619 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2620 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2621 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2623 // BJO 20000428 : Virtuoso returns type names with upper cases!
2624 if (Dbms() == dbmsVIRTUOSO
)
2626 wxString s
= colInf
[colNo
].typeName
;
2628 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2631 // Determine the wxDb data type that is used to represent the native data type of this data source
2632 colInf
[colNo
].dbDataType
= 0;
2633 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2636 // IODBC does not return a correct columnSize, so we set
2637 // columnSize = bufferLength if no column size was returned
2638 // IODBC returns the columnSize in bufferLength.. (bug)
2639 if (colInf
[colNo
].columnSize
< 1)
2641 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2645 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2647 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2648 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2649 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2650 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2651 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2652 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2653 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2654 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2660 if (retcode
!= SQL_NO_DATA_FOUND
)
2661 { // Error occured, abort
2662 DispAllErrors(henv
, hdbc
, hstmt
);
2665 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2672 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2674 // Store Primary and Foriegn Keys
2675 GetKeyFields(tableName
,colInf
,noCols
);
2681 } // wxDb::GetColumns()
2684 #else // New GetColumns
2689 These are tentative new GetColumns members which should be more database
2690 independant and which always returns the columns in the order they were
2693 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2694 wxChar* userID)) calls the second implementation for each separate table
2695 before merging the results. This makes the code easier to maintain as
2696 only one member (the second) makes the real work
2697 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2698 wxChar *userID) is a little bit improved
2699 - It doesn't anymore rely on the type-name to find out which database-type
2701 - It ends by sorting the columns, so that they are returned in the same
2702 order they were created
2712 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2715 // The last array element of the tableName[] argument must be zero (null).
2716 // This is how the end of the array is detected.
2720 // How many tables ?
2722 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2724 // Create a table to maintain the columns for each separate table
2725 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2728 for (i
= 0 ; i
< tbl
; i
++)
2731 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2732 if (TableColumns
[i
].colInf
== NULL
)
2734 noCols
+= TableColumns
[i
].noCols
;
2737 // Now merge all the separate table infos
2738 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2740 // Mark the end of the array
2741 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2742 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2743 colInf
[noCols
].sqlDataType
= 0;
2748 for (i
= 0 ; i
< tbl
; i
++)
2750 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2752 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2756 delete [] TableColumns
;
2759 } // wxDb::GetColumns() -- NEW
2762 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2764 // Same as the above GetColumns() function except this one gets columns
2765 // only for a single table, and if 'numCols' is not NULL, the number of
2766 // columns stored in the returned wxDbColInf is set in '*numCols'
2768 // userID is evaluated in the following manner:
2769 // userID == NULL ... UserID is ignored
2770 // userID == "" ... UserID set equal to 'this->uid'
2771 // userID != "" ... UserID set equal to 'userID'
2773 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2774 // by this function. This function should use its own wxDb instance
2775 // to avoid undesired unbinding of columns.
2779 wxDbColInf
*colInf
= 0;
2787 convertUserID(userID
,UserID
);
2789 // Pass 1 - Determine how many columns there are.
2790 // Pass 2 - Allocate the wxDbColInf array and fill in
2791 // the array with the column information.
2793 for (pass
= 1; pass
<= 2; pass
++)
2797 if (noCols
== 0) // Probably a bogus table name(s)
2799 // Allocate n wxDbColInf objects to hold the column information
2800 colInf
= new wxDbColInf
[noCols
+1];
2803 // Mark the end of the array
2804 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2805 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2806 colInf
[noCols
].sqlDataType
= 0;
2809 TableName
= tableName
;
2810 // Oracle and Interbase table names are uppercase only, so force
2811 // the name to uppercase just in case programmer forgot to do this
2812 if ((Dbms() == dbmsORACLE
) ||
2813 (Dbms() == dbmsINTERBASE
))
2814 TableName
= TableName
.Upper();
2816 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2818 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2819 // use the call below that leaves out the user name
2820 if (!UserID
.IsEmpty() &&
2821 Dbms() != dbmsMY_SQL
&&
2822 Dbms() != dbmsACCESS
&&
2823 Dbms() != dbmsMS_SQL_SERVER
)
2825 retcode
= SQLColumns(hstmt
,
2826 NULL
, 0, // All qualifiers
2827 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2828 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2829 NULL
, 0); // All columns
2833 retcode
= SQLColumns(hstmt
,
2834 NULL
, 0, // All qualifiers
2836 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2837 NULL
, 0); // All columns
2839 if (retcode
!= SQL_SUCCESS
)
2840 { // Error occured, abort
2841 DispAllErrors(henv
, hdbc
, hstmt
);
2844 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2850 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2852 if (pass
== 1) // First pass, just add up the number of columns
2854 else // Pass 2; Fill in the array of structures
2856 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2858 // NOTE: Only the ODBC 1.x fields are retrieved
2859 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2860 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2861 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2862 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2863 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2864 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2865 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2866 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2867 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2868 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2869 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2870 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2871 // Start Values for Primary/Foriegn Key (=No)
2872 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2873 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2874 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2875 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2878 // IODBC does not return a correct columnSize, so we set
2879 // columnSize = bufferLength if no column size was returned
2880 // IODBC returns the columnSize in bufferLength.. (bug)
2881 if (colInf
[colNo
].columnSize
< 1)
2883 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2887 // Determine the wxDb data type that is used to represent the native data type of this data source
2888 colInf
[colNo
].dbDataType
= 0;
2889 // Get the intern datatype
2890 switch (colInf
[colNo
].sqlDataType
)
2894 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2900 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2907 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2910 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2913 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2918 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2919 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2926 if (retcode
!= SQL_NO_DATA_FOUND
)
2927 { // Error occured, abort
2928 DispAllErrors(henv
, hdbc
, hstmt
);
2931 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2938 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2940 // Store Primary and Foreign Keys
2941 GetKeyFields(tableName
,colInf
,noCols
);
2943 ///////////////////////////////////////////////////////////////////////////
2944 // Now sort the the columns in order to make them appear in the right order
2945 ///////////////////////////////////////////////////////////////////////////
2947 // Build a generic SELECT statement which returns 0 rows
2950 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2953 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2955 DispAllErrors(henv
, hdbc
, hstmt
);
2959 // Get the number of result columns
2960 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2962 DispAllErrors(henv
, hdbc
, hstmt
);
2966 if (noCols
== 0) // Probably a bogus table name
2975 for (colNum
= 0; colNum
< noCols
; colNum
++)
2977 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2979 &Sword
, &Sdword
) != SQL_SUCCESS
)
2981 DispAllErrors(henv
, hdbc
, hstmt
);
2985 wxString Name1
= name
;
2986 Name1
= Name1
.Upper();
2988 // Where is this name in the array ?
2989 for (i
= colNum
; i
< noCols
; i
++)
2991 wxString Name2
= colInf
[i
].colName
;
2992 Name2
= Name2
.Upper();
2995 if (colNum
!= i
) // swap to sort
2997 wxDbColInf tmpColInf
= colInf
[colNum
];
2998 colInf
[colNum
] = colInf
[i
];
2999 colInf
[i
] = tmpColInf
;
3005 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3007 ///////////////////////////////////////////////////////////////////////////
3009 ///////////////////////////////////////////////////////////////////////////
3015 } // wxDb::GetColumns()
3018 #endif // #else OLD_GETCOLUMNS
3021 /********** wxDb::GetColumnCount() **********/
3022 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3024 * Returns a count of how many columns are in a table.
3025 * If an error occurs in computing the number of columns
3026 * this function will return a -1 for the count
3028 * userID is evaluated in the following manner:
3029 * userID == NULL ... UserID is ignored
3030 * userID == "" ... UserID set equal to 'this->uid'
3031 * userID != "" ... UserID set equal to 'userID'
3033 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3034 * by this function. This function should use its own wxDb instance
3035 * to avoid undesired unbinding of columns.
3045 convertUserID(userID
,UserID
);
3047 TableName
= tableName
;
3048 // Oracle and Interbase table names are uppercase only, so force
3049 // the name to uppercase just in case programmer forgot to do this
3050 if ((Dbms() == dbmsORACLE
) ||
3051 (Dbms() == dbmsINTERBASE
))
3052 TableName
= TableName
.Upper();
3054 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3056 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3057 // use the call below that leaves out the user name
3058 if (!UserID
.IsEmpty() &&
3059 Dbms() != dbmsMY_SQL
&&
3060 Dbms() != dbmsACCESS
&&
3061 Dbms() != dbmsMS_SQL_SERVER
)
3063 retcode
= SQLColumns(hstmt
,
3064 NULL
, 0, // All qualifiers
3065 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3066 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3067 NULL
, 0); // All columns
3071 retcode
= SQLColumns(hstmt
,
3072 NULL
, 0, // All qualifiers
3074 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3075 NULL
, 0); // All columns
3077 if (retcode
!= SQL_SUCCESS
)
3078 { // Error occured, abort
3079 DispAllErrors(henv
, hdbc
, hstmt
);
3080 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3084 // Count the columns
3085 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3088 if (retcode
!= SQL_NO_DATA_FOUND
)
3089 { // Error occured, abort
3090 DispAllErrors(henv
, hdbc
, hstmt
);
3091 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3095 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3098 } // wxDb::GetColumnCount()
3101 /********** wxDb::GetCatalog() *******/
3102 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3104 * ---------------------------------------------------------------------
3105 * -- 19991203 : mj10777 : Create ------
3106 * -- : Creates a wxDbInf with Tables / Cols Array ------
3107 * -- : uses SQLTables and fills pTableInf; ------
3108 * -- : pColInf is set to NULL and numCols to 0; ------
3109 * -- : returns pDbInf (wxDbInf) ------
3110 * -- - if unsuccesfull (pDbInf == NULL) ------
3111 * -- : pColInf can be filled with GetColumns(..); ------
3112 * -- : numCols can be filled with GetColumnCount(..); ------
3113 * ---------------------------------------------------------------------
3115 * userID is evaluated in the following manner:
3116 * userID == NULL ... UserID is ignored
3117 * userID == "" ... UserID set equal to 'this->uid'
3118 * userID != "" ... UserID set equal to 'userID'
3120 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3121 * by this function. This function should use its own wxDb instance
3122 * to avoid undesired unbinding of columns.
3125 int noTab
= 0; // Counter while filling table entries
3129 wxString tblNameSave
;
3132 convertUserID(userID
,UserID
);
3134 //-------------------------------------------------------------
3135 // Create the Database Array of catalog entries
3137 wxDbInf
*pDbInf
= new wxDbInf
;
3139 //-------------------------------------------------------------
3140 // Table Information
3141 // Pass 1 - Determine how many Tables there are.
3142 // Pass 2 - Create the Table array and fill it
3143 // - Create the Cols array = NULL
3144 //-------------------------------------------------------------
3146 for (pass
= 1; pass
<= 2; pass
++)
3148 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3149 tblNameSave
.Empty();
3151 if (!UserID
.IsEmpty() &&
3152 Dbms() != dbmsMY_SQL
&&
3153 Dbms() != dbmsACCESS
&&
3154 Dbms() != dbmsMS_SQL_SERVER
)
3156 retcode
= SQLTables(hstmt
,
3157 NULL
, 0, // All qualifiers
3158 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3159 NULL
, 0, // All tables
3160 NULL
, 0); // All columns
3164 retcode
= SQLTables(hstmt
,
3165 NULL
, 0, // All qualifiers
3166 NULL
, 0, // User specified
3167 NULL
, 0, // All tables
3168 NULL
, 0); // All columns
3171 if (retcode
!= SQL_SUCCESS
)
3173 DispAllErrors(henv
, hdbc
, hstmt
);
3175 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3179 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3181 if (pass
== 1) // First pass, just count the Tables
3183 if (pDbInf
->numTables
== 0)
3185 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3186 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3188 pDbInf
->numTables
++; // Counter for Tables
3190 if (pass
== 2) // Create and fill the Table entries
3192 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3193 { // no, then create the Array
3194 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3196 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3198 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3199 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3200 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3206 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3208 // Query how many columns are in each table
3209 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3211 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3216 } // wxDb::GetCatalog()
3219 /********** wxDb::Catalog() **********/
3220 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3222 * Creates the text file specified in 'filename' which will contain
3223 * a minimal data dictionary of all tables accessible by the user specified
3226 * userID is evaluated in the following manner:
3227 * userID == NULL ... UserID is ignored
3228 * userID == "" ... UserID set equal to 'this->uid'
3229 * userID != "" ... UserID set equal to 'userID'
3231 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3232 * by this function. This function should use its own wxDb instance
3233 * to avoid undesired unbinding of columns.
3236 wxASSERT(fileName
.Length());
3240 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3241 wxString tblNameSave
;
3242 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3244 wxChar typeName
[30+1];
3245 SDWORD precision
, length
;
3247 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3251 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3254 convertUserID(userID
,UserID
);
3256 if (!UserID
.IsEmpty() &&
3257 Dbms() != dbmsMY_SQL
&&
3258 Dbms() != dbmsACCESS
&&
3259 Dbms() != dbmsINTERBASE
&&
3260 Dbms() != dbmsMS_SQL_SERVER
)
3262 retcode
= SQLColumns(hstmt
,
3263 NULL
, 0, // All qualifiers
3264 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3265 NULL
, 0, // All tables
3266 NULL
, 0); // All columns
3270 retcode
= SQLColumns(hstmt
,
3271 NULL
, 0, // All qualifiers
3272 NULL
, 0, // User specified
3273 NULL
, 0, // All tables
3274 NULL
, 0); // All columns
3276 if (retcode
!= SQL_SUCCESS
)
3278 DispAllErrors(henv
, hdbc
, hstmt
);
3284 tblNameSave
.Empty();
3289 retcode
= SQLFetch(hstmt
);
3290 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3293 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3294 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3295 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3296 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3297 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3298 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3300 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3303 wxFputs(wxT("\n"), fp
);
3304 wxFputs(wxT("================================ "), fp
);
3305 wxFputs(wxT("================================ "), fp
);
3306 wxFputs(wxT("===================== "), fp
);
3307 wxFputs(wxT("========= "), fp
);
3308 wxFputs(wxT("=========\n"), fp
);
3309 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3310 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3311 wxFputs(outStr
.c_str(), fp
);
3312 wxFputs(wxT("================================ "), fp
);
3313 wxFputs(wxT("================================ "), fp
);
3314 wxFputs(wxT("===================== "), fp
);
3315 wxFputs(wxT("========= "), fp
);
3316 wxFputs(wxT("=========\n"), fp
);
3317 tblNameSave
= tblName
;
3320 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3321 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3322 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3324 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3331 if (retcode
!= SQL_NO_DATA_FOUND
)
3332 DispAllErrors(henv
, hdbc
, hstmt
);
3334 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3337 return(retcode
== SQL_NO_DATA_FOUND
);
3339 } // wxDb::Catalog()
3342 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3344 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3345 * if the object exists in the database. This function does not indicate
3346 * whether or not the user has privleges to query or perform other functions
3349 * userID is evaluated in the following manner:
3350 * userID == NULL ... UserID is ignored
3351 * userID == "" ... UserID set equal to 'this->uid'
3352 * userID != "" ... UserID set equal to 'userID'
3355 wxASSERT(tableName
.Length());
3359 if (Dbms() == dbmsDBASE
)
3362 if (tablePath
.Length())
3363 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3365 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3368 exists
= wxFileExists(dbName
);
3373 convertUserID(userID
,UserID
);
3375 TableName
= tableName
;
3376 // Oracle and Interbase table names are uppercase only, so force
3377 // the name to uppercase just in case programmer forgot to do this
3378 if ((Dbms() == dbmsORACLE
) ||
3379 (Dbms() == dbmsINTERBASE
))
3380 TableName
= TableName
.Upper();
3382 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3385 // Some databases cannot accept a user name when looking up table names,
3386 // so we use the call below that leaves out the user name
3387 if (!UserID
.IsEmpty() &&
3388 Dbms() != dbmsMY_SQL
&&
3389 Dbms() != dbmsACCESS
&&
3390 Dbms() != dbmsMS_SQL_SERVER
&&
3391 Dbms() != dbmsDB2
&&
3392 Dbms() != dbmsINTERBASE
&&
3393 Dbms() != dbmsPERVASIVE_SQL
)
3395 retcode
= SQLTables(hstmt
,
3396 NULL
, 0, // All qualifiers
3397 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3398 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3399 NULL
, 0); // All table types
3403 retcode
= SQLTables(hstmt
,
3404 NULL
, 0, // All qualifiers
3405 NULL
, 0, // All owners
3406 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3407 NULL
, 0); // All table types
3409 if (retcode
!= SQL_SUCCESS
)
3410 return(DispAllErrors(henv
, hdbc
, hstmt
));
3412 retcode
= SQLFetch(hstmt
);
3413 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3415 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3416 return(DispAllErrors(henv
, hdbc
, hstmt
));
3419 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3423 } // wxDb::TableExists()
3426 /********** wxDb::TablePrivileges() **********/
3427 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3428 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3430 wxASSERT(tableName
.Length());
3432 wxDbTablePrivilegeInfo result
;
3436 // We probably need to be able to dynamically set this based on
3437 // the driver type, and state.
3438 wxChar curRole
[]=wxT("public");
3442 wxString UserID
,Schema
;
3443 convertUserID(userID
,UserID
);
3444 convertUserID(schema
,Schema
);
3446 TableName
= tableName
;
3447 // Oracle and Interbase table names are uppercase only, so force
3448 // the name to uppercase just in case programmer forgot to do this
3449 if ((Dbms() == dbmsORACLE
) ||
3450 (Dbms() == dbmsINTERBASE
))
3451 TableName
= TableName
.Upper();
3453 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3455 // Some databases cannot accept a user name when looking up table names,
3456 // so we use the call below that leaves out the user name
3457 if (!Schema
.IsEmpty() &&
3458 Dbms() != dbmsMY_SQL
&&
3459 Dbms() != dbmsACCESS
&&
3460 Dbms() != dbmsMS_SQL_SERVER
)
3462 retcode
= SQLTablePrivileges(hstmt
,
3464 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3465 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3469 retcode
= SQLTablePrivileges(hstmt
,
3472 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3475 #ifdef DBDEBUG_CONSOLE
3476 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3479 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3480 return(DispAllErrors(henv
, hdbc
, hstmt
));
3482 bool failed
= FALSE
;
3483 retcode
= SQLFetch(hstmt
);
3484 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3486 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3489 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3492 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3495 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3498 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3501 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3504 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3509 return(DispAllErrors(henv
, hdbc
, hstmt
));
3511 #ifdef DBDEBUG_CONSOLE
3512 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3513 result
.privilege
,result
.tableOwner
,result
.tableName
,
3514 result
.grantor
, result
.grantee
);
3517 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3519 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3523 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3524 !wxStrcmp(result
.privilege
,priv
))
3526 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3530 if (!wxStrcmp(result
.grantee
,curRole
) &&
3531 !wxStrcmp(result
.privilege
,priv
))
3533 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3537 retcode
= SQLFetch(hstmt
);
3540 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3543 } // wxDb::TablePrivileges
3546 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3550 if (Dbms() == dbmsACCESS
)
3551 TableName
= _T("\"");
3552 TableName
+= tableName
;
3553 if (Dbms() == dbmsACCESS
)
3554 TableName
+= _T("\"");
3557 } // wxDb::SQLTableName()
3560 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3564 if (Dbms() == dbmsACCESS
)
3567 if (Dbms() == dbmsACCESS
)
3568 ColName
+= _T("\"");
3571 } // wxDb::SQLColumnName()
3574 /********** wxDb::SetSqlLogging() **********/
3575 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3577 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3578 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3580 if (state
== sqlLogON
)
3584 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3585 if (fpSqlLog
== NULL
)
3593 if (fclose(fpSqlLog
))
3599 sqlLogState
= state
;
3602 } // wxDb::SetSqlLogging()
3605 /********** wxDb::WriteSqlLog() **********/
3606 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3608 wxASSERT(logMsg
.Length());
3610 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3613 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3615 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3617 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3622 } // wxDb::WriteSqlLog()
3625 /********** wxDb::Dbms() **********/
3626 wxDBMS
wxDb::Dbms(void)
3628 * Be aware that not all database engines use the exact same syntax, and not
3629 * every ODBC compliant database is compliant to the same level of compliancy.
3630 * Some manufacturers support the minimum Level 1 compliancy, and others up
3631 * through Level 3. Others support subsets of features for levels above 1.
3633 * If you find an inconsistency between the wxDb class and a specific database
3634 * engine, and an identifier to this section, and special handle the database in
3635 * the area where behavior is non-conforming with the other databases.
3638 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3639 * ---------------------------------------------------
3642 * - Currently the only database supported by the class to support VIEWS
3645 * - Does not support the SQL_TIMESTAMP structure
3646 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3647 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3648 * is TRUE. The user must create ALL indexes from their program.
3649 * - Table names can only be 8 characters long
3650 * - Column names can only be 10 characters long
3653 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3654 * after every table name involved in the query/join if that tables matching record(s)
3656 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3658 * SYBASE (Enterprise)
3659 * - If a column is part of the Primary Key, the column cannot be NULL
3660 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3663 * - If a column is part of the Primary Key, the column cannot be NULL
3664 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3665 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3666 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3667 * column definition if it is not defined correctly, but it is experimental
3668 * - Does not support sub-queries in SQL statements
3671 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3672 * - Does not support sub-queries in SQL statements
3675 * - Primary keys must be declared as NOT NULL
3676 * - Table and index names must not be longer than 13 characters in length (technically
3677 * table names can be up to 18 characters, but the primary index is created using the
3678 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3683 * - Columns that are part of primary keys must be defined as being NOT NULL
3684 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3685 * column definition if it is not defined correctly, but it is experimental
3688 // Should only need to do this once for each new database connection
3689 // so return the value we already determined it to be to save time
3690 // and lots of string comparisons
3691 if (dbmsType
!= dbmsUNIDENTIFIED
)
3694 wxChar baseName
[25+1];
3695 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3698 // RGG 20001025 : add support for Interbase
3699 // GT : Integrated to base classes on 20001121
3700 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3701 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3703 // BJO 20000428 : add support for Virtuoso
3704 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3705 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3707 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3708 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3710 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3711 // connected through an OpenLink driver.
3712 // Is it also returned by Sybase Adapatitve server?
3713 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3714 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3716 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3717 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3718 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3720 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3723 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3724 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3727 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3728 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3731 if (!wxStricmp(baseName
,wxT("Pervasive")))
3732 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3735 if (!wxStricmp(baseName
,wxT("Informix")))
3736 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3739 if (!wxStricmp(baseName
,wxT("Oracle")))
3740 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3741 if (!wxStricmp(baseName
,wxT("ACCESS")))
3742 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3743 if (!wxStricmp(baseName
,wxT("Sybase")))
3744 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3747 if (!wxStricmp(baseName
,wxT("DBASE")))
3748 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3749 if (!wxStricmp(baseName
,wxT("xBase")))
3750 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3751 if (!wxStricmp(baseName
,wxT("MySQL")))
3752 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3755 if (!wxStricmp(baseName
,wxT("DB2")))
3756 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3758 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3763 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3764 int dataType
, ULONG columnLength
,
3765 const wxString
&optionalParam
)
3767 wxASSERT(tableName
.Length());
3768 wxASSERT(columnName
.Length());
3769 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3770 dataType
!= DB_DATA_TYPE_VARCHAR
);
3772 // Must specify a columnLength if modifying a VARCHAR type column
3773 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3776 wxString dataTypeName
;
3778 wxString alterSlashModify
;
3782 case DB_DATA_TYPE_VARCHAR
:
3783 dataTypeName
= typeInfVarchar
.TypeName
;
3785 case DB_DATA_TYPE_INTEGER
:
3786 dataTypeName
= typeInfInteger
.TypeName
;
3788 case DB_DATA_TYPE_FLOAT
:
3789 dataTypeName
= typeInfFloat
.TypeName
;
3791 case DB_DATA_TYPE_DATE
:
3792 dataTypeName
= typeInfDate
.TypeName
;
3794 case DB_DATA_TYPE_BLOB
:
3795 dataTypeName
= typeInfBlob
.TypeName
;
3801 // Set the modify or alter syntax depending on the type of database connected to
3805 alterSlashModify
= _T("MODIFY");
3807 case dbmsMS_SQL_SERVER
:
3808 alterSlashModify
= _T("ALTER COLUMN");
3810 case dbmsUNIDENTIFIED
:
3812 case dbmsSYBASE_ASA
:
3813 case dbmsSYBASE_ASE
:
3818 case dbmsXBASE_SEQUITER
:
3820 alterSlashModify
= _T("MODIFY");
3824 // create the SQL statement
3825 if ( Dbms() == dbmsMY_SQL
)
3827 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3828 columnName
.c_str(), dataTypeName
.c_str());
3832 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3833 columnName
.c_str(), dataTypeName
.c_str());
3836 // For varchars only, append the size of the column
3837 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3838 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3841 s
.Printf(wxT("(%lu)"), columnLength
);
3845 // for passing things like "NOT NULL"
3846 if (optionalParam
.Length())
3848 sqlStmt
+= wxT(" ");
3849 sqlStmt
+= optionalParam
;
3852 return ExecSql(sqlStmt
);
3854 } // wxDb::ModifyColumn()
3857 /********** wxDbGetConnection() **********/
3858 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3862 // Used to keep a pointer to a DB connection that matches the requested
3863 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3864 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3865 // rather than having to re-query the datasource to get all the values
3866 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3867 wxDb
*matchingDbConnection
= NULL
;
3869 // Scan the linked list searching for an available database connection
3870 // that's already been opened but is currently not in use.
3871 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3873 // The database connection must be for the same datasource
3874 // name and must currently not be in use.
3876 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
3878 if (pDbConfig
->UseConnectionStr())
3880 if (pList
->PtrDb
->OpenedWithConnectionString() &&
3881 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
3883 // Found a free connection
3884 pList
->Free
= FALSE
;
3885 return(pList
->PtrDb
);
3890 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
3891 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
3893 // Found a free connection
3894 pList
->Free
= FALSE
;
3895 return(pList
->PtrDb
);
3900 if (pDbConfig
->UseConnectionStr())
3902 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
3903 matchingDbConnection
= pList
->PtrDb
;
3907 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3908 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3909 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3910 matchingDbConnection
= pList
->PtrDb
;
3914 // No available connections. A new connection must be made and
3915 // appended to the end of the linked list.
3918 // Find the end of the list
3919 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3920 // Append a new list item
3921 pList
->PtrNext
= new wxDbList
;
3922 pList
->PtrNext
->PtrPrev
= pList
;
3923 pList
= pList
->PtrNext
;
3927 // Create the first node on the list
3928 pList
= PtrBegDbList
= new wxDbList
;
3932 // Initialize new node in the linked list
3934 pList
->Free
= FALSE
;
3935 pList
->Dsn
= pDbConfig
->GetDsn();
3936 pList
->Uid
= pDbConfig
->GetUserID();
3937 pList
->AuthStr
= pDbConfig
->GetPassword();
3938 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
3940 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3944 if (!matchingDbConnection
)
3946 if (pDbConfig
->UseConnectionStr())
3948 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
3952 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3956 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3958 // Connect to the datasource
3961 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3962 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, TRUE
);
3963 return(pList
->PtrDb
);
3965 else // Unable to connect, destroy list item
3968 pList
->PtrPrev
->PtrNext
= 0;
3970 PtrBegDbList
= 0; // Empty list again
3972 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3973 pList
->PtrDb
->Close(); // Close the wxDb object
3974 delete pList
->PtrDb
; // Deletes the wxDb object
3975 delete pList
; // Deletes the linked list object
3979 } // wxDbGetConnection()
3982 /********** wxDbFreeConnection() **********/
3983 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3987 // Scan the linked list searching for the database connection
3988 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3990 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3991 return (pList
->Free
= TRUE
);
3994 // Never found the database object, return failure
3997 } // wxDbFreeConnection()
4000 /********** wxDbCloseConnections() **********/
4001 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4003 wxDbList
*pList
, *pNext
;
4005 // Traverse the linked list closing database connections and freeing memory as I go.
4006 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4008 pNext
= pList
->PtrNext
; // Save the pointer to next
4009 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4010 pList
->PtrDb
->Close(); // Close the wxDb object
4011 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
4012 delete pList
->PtrDb
; // Deletes the wxDb object
4013 delete pList
; // Deletes the linked list object
4016 // Mark the list as empty
4019 } // wxDbCloseConnections()
4022 /********** wxDbConnectionsInUse() **********/
4023 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4028 // Scan the linked list counting db connections that are currently in use
4029 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4031 if (pList
->Free
== FALSE
)
4037 } // wxDbConnectionsInUse()
4041 /********** wxDbLogExtendedErrorMsg() **********/
4042 // DEBUG ONLY function
4043 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4045 const wxChar
*ErrFile
,
4048 static wxString msg
;
4053 if (ErrFile
|| ErrLine
)
4055 msg
+= wxT("File: ");
4057 msg
+= wxT(" Line: ");
4058 tStr
.Printf(wxT("%d"),ErrLine
);
4059 msg
+= tStr
.c_str();
4063 msg
.Append (wxT("\nODBC errors:\n"));
4066 // Display errors for this connection
4068 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4070 if (pDb
->errorList
[i
])
4072 msg
.Append(pDb
->errorList
[i
]);
4073 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
4074 msg
.Append(wxT("\n"));
4075 // Clear the errmsg buffer so the next error will not
4076 // end up showing the previous error that have occurred
4077 wxStrcpy(pDb
->errorList
[i
],wxT(""));
4082 wxLogDebug(msg
.c_str());
4085 } // wxDbLogExtendedErrorMsg()
4088 /********** wxDbSqlLog() **********/
4089 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4091 bool append
= FALSE
;
4094 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4096 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4101 SQLLOGstate
= state
;
4102 SQLLOGfn
= filename
;
4110 /********** wxDbCreateDataSource() **********/
4111 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4112 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4114 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4115 * Very rudimentary creation of an ODBC data source.
4117 * ODBC driver must be ODBC 3.0 compliant to use this function
4122 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4128 dsnLocation
= ODBC_ADD_SYS_DSN
;
4130 dsnLocation
= ODBC_ADD_DSN
;
4132 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4133 // so that is why I used it, as wxString does not deal well with
4134 // embedded nulls in strings
4135 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4137 // Replace the separator from above with the '\0' seperator needed
4138 // by the SQLConfigDataSource() function
4142 k
= setupStr
.Find((wxChar
)2,TRUE
);
4143 if (k
!= wxNOT_FOUND
)
4144 setupStr
[(UINT
)k
] = wxT('\0');
4146 while (k
!= wxNOT_FOUND
);
4148 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4149 driverName
, setupStr
.c_str());
4151 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4153 // check for errors caused by ConfigDSN based functions
4156 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4157 errMsg
[0] = wxT('\0');
4159 // This function is only supported in ODBC drivers v3.0 compliant and above
4160 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4163 #ifdef DBDEBUG_CONSOLE
4164 // When run in console mode, use standard out to display errors.
4165 cout
<< errMsg
<< endl
;
4166 cout
<< wxT("Press any key to continue...") << endl
;
4168 #endif // DBDEBUG_CONSOLE
4171 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4172 #endif // __WXDEBUG__
4178 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4179 // necessary to use this function, so this function is not supported
4181 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4184 #endif // __VISUALC__
4188 } // wxDbCreateDataSource()
4192 /********** wxDbGetDataSource() **********/
4193 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4194 SWORD DsDescMax
, UWORD direction
)
4196 * Dsn and DsDesc will contain the data source name and data source
4197 * description upon return
4202 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4203 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4208 } // wxDbGetDataSource()
4211 // Change this to 0 to remove use of all deprecated functions
4212 #if wxODBC_BACKWARD_COMPATABILITY
4213 /********************************************************************
4214 ********************************************************************
4216 * The following functions are all DEPRECATED and are included for
4217 * backward compatability reasons only
4219 ********************************************************************
4220 ********************************************************************/
4221 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4223 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4225 /***** DEPRECATED: use wxGetDataSource() *****/
4226 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4229 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4231 /***** DEPRECATED: use wxDbGetConnection() *****/
4232 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4234 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4236 /***** DEPRECATED: use wxDbFreeConnection() *****/
4237 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4239 return wxDbFreeConnection(pDb
);
4241 /***** DEPRECATED: use wxDbCloseConnections() *****/
4242 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4244 wxDbCloseConnections();
4246 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4247 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4249 return wxDbConnectionsInUse();