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
;
774 wxUnusedVar(retcode
);
778 // Connect to the data source
779 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1]; // MS recommends at least 1k buffer
780 short outConnectBufferLen
;
782 inConnectionStr
= inConnectStr
;
784 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
785 inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
786 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
788 if ((retcode
!= SQL_SUCCESS
) &&
789 (retcode
!= SQL_SUCCESS_WITH_INFO
))
790 return(DispAllErrors(henv
, hdbc
));
792 outConnectBuffer
[outConnectBufferLen
] = 0;
793 outConnectionStr
= outConnectBuffer
;
794 dbOpenedWithConnectionString
= TRUE
;
796 return open(failOnDataTypeUnsupported
);
799 /********** wxDb::Open() **********/
800 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
, bool failOnDataTypeUnsupported
)
802 wxASSERT(Dsn
.Length());
807 inConnectionStr
= wxT("");
808 outConnectionStr
= wxT("");
812 if (!FwdOnlyCursors())
814 // Specify that the ODBC cursor library be used, if needed. This must be
815 // specified before the connection is made.
816 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
818 #ifdef DBDEBUG_CONSOLE
819 if (retcode
== SQL_SUCCESS
)
820 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
822 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
824 wxUnusedVar( retcode
);
828 // Connect to the data source
829 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
830 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
831 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
833 if ((retcode
!= SQL_SUCCESS
) &&
834 (retcode
!= SQL_SUCCESS_WITH_INFO
))
835 return(DispAllErrors(henv
, hdbc
));
837 return open(failOnDataTypeUnsupported
);
842 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
, bool failOnDataTypeUnsupported
)
844 wxASSERT(dbConnectInf
);
846 // Use the connection string if one is present
847 if (dbConnectInf
->UseConnectionStr())
848 return Open(GetConnectionInStr(), failOnDataTypeUnsupported
);
850 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
851 dbConnectInf
->GetPassword(), failOnDataTypeUnsupported
);
855 bool wxDb::Open(wxDb
*copyDb
)
857 dsn
= copyDb
->GetDatasourceName();
858 uid
= copyDb
->GetUsername();
859 authStr
= copyDb
->GetPassword();
860 inConnectionStr
= copyDb
->GetConnectionInStr();
861 outConnectionStr
= copyDb
->GetConnectionOutStr();
865 if (!FwdOnlyCursors())
867 // Specify that the ODBC cursor library be used, if needed. This must be
868 // specified before the connection is made.
869 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
871 #ifdef DBDEBUG_CONSOLE
872 if (retcode
== SQL_SUCCESS
)
873 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
875 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
877 wxUnusedVar( retcode
);
881 if (copyDb
->OpenedWithConnectionString())
883 // Connect to the data source
884 SQLTCHAR outConnectBuffer
[SQL_MAX_CONNECTSTR_LEN
+1];
885 short outConnectBufferLen
;
887 inConnectionStr
= copyDb
->GetConnectionInStr();
889 retcode
= SQLDriverConnect(hdbc
, NULL
, (SQLTCHAR FAR
*)inConnectionStr
.c_str(),
890 inConnectionStr
.Length(), (SQLTCHAR FAR
*)outConnectBuffer
,
891 sizeof(outConnectBuffer
), &outConnectBufferLen
, SQL_DRIVER_COMPLETE
);
893 if ((retcode
!= SQL_SUCCESS
) &&
894 (retcode
!= SQL_SUCCESS_WITH_INFO
))
895 return(DispAllErrors(henv
, hdbc
));
897 outConnectBuffer
[outConnectBufferLen
] = 0;
898 outConnectionStr
= outConnectBuffer
;
899 dbOpenedWithConnectionString
= TRUE
;
903 // Connect to the data source
904 retcode
= SQLConnect(hdbc
, (SQLTCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
905 (SQLTCHAR FAR
*) uid
.c_str(), SQL_NTS
,
906 (SQLTCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
909 if ((retcode
!= SQL_SUCCESS
) &&
910 (retcode
!= SQL_SUCCESS_WITH_INFO
))
911 return(DispAllErrors(henv
, hdbc
));
914 If using Intersolv branded ODBC drivers, this is the place where you would substitute
915 your branded driver license information
917 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
918 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
921 // Mark database as open
924 // Allocate a statement handle for the database connection
925 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
926 return(DispAllErrors(henv
, hdbc
));
928 // Set Connection Options
929 if (!setConnectionOptions())
932 // Instead of Querying the data source for info about itself, it can just be copied
933 // from the wxDb instance that was passed in (copyDb).
934 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
935 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
936 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
937 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
938 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
939 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
940 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
941 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
942 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
943 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
944 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
945 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
946 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
947 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
948 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
949 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
950 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
951 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
952 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
953 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
954 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
955 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
956 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
957 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
958 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
959 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
960 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
961 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
962 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
963 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
964 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
966 // VARCHAR = Variable length character string
967 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
968 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
969 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
970 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
971 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
974 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
975 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
976 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
977 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
978 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
981 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
982 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
983 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
984 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
985 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
988 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
989 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
990 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
991 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
992 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
995 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
996 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
997 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
998 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
999 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
1001 #ifdef DBDEBUG_CONSOLE
1002 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
1003 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
1004 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
1005 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
1006 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
1010 // Completed Successfully
1015 /********** wxDb::setConnectionOptions() **********/
1016 bool wxDb::setConnectionOptions(void)
1018 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1023 // I need to get the DBMS name here, because some of the connection options
1024 // are database specific and need to call the Dbms() function.
1025 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
1026 return(DispAllErrors(henv
, hdbc
));
1028 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
1029 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
1030 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1032 // By default, MS Sql Server closes cursors on commit and rollback. The following
1033 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1034 // after a transaction. This is a driver specific option and is not part of the
1035 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1036 // The database settings don't have any effect one way or the other.
1037 if (Dbms() == dbmsMS_SQL_SERVER
)
1039 const long SQL_PRESERVE_CURSORS
= 1204L;
1040 const long SQL_PC_ON
= 1L;
1041 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
1044 // Display the connection options to verify them
1045 #ifdef DBDEBUG_CONSOLE
1047 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
1049 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
1050 return(DispAllErrors(henv
, hdbc
));
1051 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
1053 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
1054 return(DispAllErrors(henv
, hdbc
));
1055 cout
<< wxT("ODBC CURSORS: ");
1058 case(SQL_CUR_USE_IF_NEEDED
):
1059 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
1061 case(SQL_CUR_USE_ODBC
):
1062 cout
<< wxT("SQL_CUR_USE_ODBC");
1064 case(SQL_CUR_USE_DRIVER
):
1065 cout
<< wxT("SQL_CUR_USE_DRIVER");
1070 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
1071 return(DispAllErrors(henv
, hdbc
));
1072 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
1077 // Completed Successfully
1080 } // wxDb::setConnectionOptions()
1083 /********** wxDb::getDbInfo() **********/
1084 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported
)
1089 retcode
= SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
);
1090 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1092 DispAllErrors(henv
, hdbc
);
1093 if (failOnDataTypeUnsupported
)
1097 retcode
= SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
);
1098 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1100 DispAllErrors(henv
, hdbc
);
1101 if (failOnDataTypeUnsupported
)
1105 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
);
1106 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1108 DispAllErrors(henv
, hdbc
);
1109 if (failOnDataTypeUnsupported
)
1114 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1115 // causing database connectivity to fail in some cases.
1116 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1117 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1119 DispAllErrors(henv
, hdbc
);
1120 if (failOnDataTypeUnsupported
)
1124 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
);
1125 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1127 DispAllErrors(henv
, hdbc
);
1128 if (failOnDataTypeUnsupported
)
1132 retcode
= SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
);
1133 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1135 DispAllErrors(henv
, hdbc
);
1136 if (failOnDataTypeUnsupported
)
1140 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
);
1141 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1143 DispAllErrors(henv
, hdbc
);
1144 if (failOnDataTypeUnsupported
)
1148 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
);
1149 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1151 DispAllErrors(henv
, hdbc
);
1152 if (failOnDataTypeUnsupported
)
1156 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1157 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1159 DispAllErrors(henv
, hdbc
);
1160 if (failOnDataTypeUnsupported
)
1164 retcode
= SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
);
1165 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1167 DispAllErrors(henv
, hdbc
);
1168 if (failOnDataTypeUnsupported
)
1172 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
);
1173 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1175 DispAllErrors(henv
, hdbc
);
1176 if (failOnDataTypeUnsupported
)
1180 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
);
1181 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1183 // Not all drivers support this call - Nick Gorham(unixODBC)
1184 dbInf
.cliConfLvl
= 0;
1185 DispAllErrors(henv
, hdbc
);
1186 if (failOnDataTypeUnsupported
)
1190 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
);
1191 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1193 DispAllErrors(henv
, hdbc
);
1194 if (failOnDataTypeUnsupported
)
1198 retcode
= SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
);
1199 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1201 DispAllErrors(henv
, hdbc
);
1202 if (failOnDataTypeUnsupported
)
1206 retcode
= SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
);
1207 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1209 DispAllErrors(henv
, hdbc
);
1210 if (failOnDataTypeUnsupported
)
1214 retcode
= SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
);
1215 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1217 DispAllErrors(henv
, hdbc
);
1218 if (failOnDataTypeUnsupported
)
1222 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
);
1223 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1225 DispAllErrors(henv
, hdbc
);
1226 if (failOnDataTypeUnsupported
)
1230 retcode
= SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
);
1231 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1233 DispAllErrors(henv
, hdbc
);
1234 if (failOnDataTypeUnsupported
)
1238 retcode
= SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
);
1239 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1241 DispAllErrors(henv
, hdbc
);
1242 if (failOnDataTypeUnsupported
)
1246 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
);
1247 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1249 DispAllErrors(henv
, hdbc
);
1250 if (failOnDataTypeUnsupported
)
1254 retcode
= SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
);
1255 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1257 DispAllErrors(henv
, hdbc
);
1258 if (failOnDataTypeUnsupported
)
1262 retcode
= SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
);
1263 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1265 DispAllErrors(henv
, hdbc
);
1266 if (failOnDataTypeUnsupported
)
1270 retcode
= SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
);
1271 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1273 DispAllErrors(henv
, hdbc
);
1274 if (failOnDataTypeUnsupported
)
1278 retcode
= SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
);
1279 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1281 DispAllErrors(henv
, hdbc
);
1282 if (failOnDataTypeUnsupported
)
1286 retcode
= SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
);
1287 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1289 DispAllErrors(henv
, hdbc
);
1290 if (failOnDataTypeUnsupported
)
1294 retcode
= SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
);
1295 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1297 DispAllErrors(henv
, hdbc
);
1298 if (failOnDataTypeUnsupported
)
1302 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
);
1303 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1305 DispAllErrors(henv
, hdbc
);
1306 if (failOnDataTypeUnsupported
)
1310 retcode
= SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
);
1311 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1313 DispAllErrors(henv
, hdbc
);
1314 if (failOnDataTypeUnsupported
)
1318 retcode
= SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
);
1319 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1321 DispAllErrors(henv
, hdbc
);
1322 if (failOnDataTypeUnsupported
)
1326 retcode
= SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
);
1327 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1329 DispAllErrors(henv
, hdbc
);
1330 if (failOnDataTypeUnsupported
)
1334 retcode
= SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
);
1335 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1337 DispAllErrors(henv
, hdbc
);
1338 if (failOnDataTypeUnsupported
)
1342 #ifdef DBDEBUG_CONSOLE
1343 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1344 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1345 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1346 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1348 cout
<< wxT("API Conf. Level: ");
1349 switch(dbInf
.apiConfLvl
)
1351 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1352 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1353 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1357 cout
<< wxT("SAG CLI Conf. Level: ");
1358 switch(dbInf
.cliConfLvl
)
1360 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1361 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1365 cout
<< wxT("SQL Conf. Level: ");
1366 switch(dbInf
.sqlConfLvl
)
1368 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1369 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1370 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1374 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1375 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1376 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1377 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1378 cout
<< wxT("Cursor COMMIT Behavior: ");
1379 switch(dbInf
.cursorCommitBehavior
)
1381 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1382 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1383 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1387 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1388 switch(dbInf
.cursorRollbackBehavior
)
1390 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1391 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1392 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1396 cout
<< wxT("Support NOT NULL clause: ");
1397 switch(dbInf
.supportNotNullClause
)
1399 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1400 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1404 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1405 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1407 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1410 cout
<< wxT("Default Transaction Isolation: ";
1411 switch(dbInf
.txnIsolation
)
1413 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1414 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1415 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1416 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1418 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1423 cout
<< wxT("Transaction Isolation Options: ");
1424 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1425 cout
<< wxT("Read Uncommitted, ");
1426 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1427 cout
<< wxT("Read Committed, ");
1428 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1429 cout
<< wxT("Repeatable Read, ");
1430 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1431 cout
<< wxT("Serializable, ");
1433 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1434 cout
<< wxT("Versioning");
1438 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1439 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1440 cout
<< wxT("Next, ");
1441 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1442 cout
<< wxT("Prev, ");
1443 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1444 cout
<< wxT("First, ");
1445 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1446 cout
<< wxT("Last, ");
1447 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1448 cout
<< wxT("Absolute, ");
1449 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1450 cout
<< wxT("Relative, ");
1452 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1453 cout
<< wxT("Resume, ");
1455 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1456 cout
<< wxT("Bookmark");
1459 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1460 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1461 cout
<< wxT("No Change, ");
1462 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1463 cout
<< wxT("Exclusive, ");
1464 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1465 cout
<< wxT("UnLock");
1468 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1469 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1470 cout
<< wxT("Position, ");
1471 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1472 cout
<< wxT("Refresh, ");
1473 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1474 cout
<< wxT("Upd, "));
1475 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1476 cout
<< wxT("Del, ");
1477 if (dbInf
.posOperations
& SQL_POS_ADD
)
1481 cout
<< wxT("Positioned Statements Supported: ");
1482 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1483 cout
<< wxT("Pos delete, ");
1484 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1485 cout
<< wxT("Pos update, ");
1486 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1487 cout
<< wxT("Select for update");
1490 cout
<< wxT("Scroll Concurrency: ");
1491 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1492 cout
<< wxT("Read Only, ");
1493 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1494 cout
<< wxT("Lock, ");
1495 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1496 cout
<< wxT("Opt. Rowver, ");
1497 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1498 cout
<< wxT("Opt. Values");
1501 cout
<< wxT("Scroll Options: ");
1502 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1503 cout
<< wxT("Fwd Only, ");
1504 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1505 cout
<< wxT("Static, ");
1506 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1507 cout
<< wxT("Keyset Driven, ");
1508 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1509 cout
<< wxT("Dynamic, ");
1510 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1511 cout
<< wxT("Mixed");
1514 cout
<< wxT("Static Sensitivity: ");
1515 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1516 cout
<< wxT("Additions, ");
1517 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1518 cout
<< wxT("Deletions, ");
1519 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1520 cout
<< wxT("Updates");
1523 cout
<< wxT("Transaction Capable?: ");
1524 switch(dbInf
.txnCapable
)
1526 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1527 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1528 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1529 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1530 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1537 // Completed Successfully
1540 } // wxDb::getDbInfo()
1543 /********** wxDb::getDataTypeInfo() **********/
1544 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1547 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1548 * the data type inf. is gathered for.
1550 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1555 // Get information about the data type specified
1556 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1557 return(DispAllErrors(henv
, hdbc
, hstmt
));
1560 retcode
= SQLFetch(hstmt
);
1561 if (retcode
!= SQL_SUCCESS
)
1563 #ifdef DBDEBUG_CONSOLE
1564 if (retcode
== SQL_NO_DATA_FOUND
)
1565 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1567 DispAllErrors(henv
, hdbc
, hstmt
);
1568 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1572 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1574 // Obtain columns from the record
1575 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1576 return(DispAllErrors(henv
, hdbc
, hstmt
));
1578 structSQLTypeInfo
.TypeName
= typeName
;
1580 // BJO 20000503: no more needed with new GetColumns...
1583 if (Dbms() == dbmsMY_SQL
)
1585 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1586 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1587 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1588 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1589 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1590 structSQLTypeInfo
.TypeName
= wxT("int");
1591 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1592 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1593 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1594 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1595 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1596 structSQLTypeInfo
.TypeName
= wxT("char");
1599 // BJO 20000427 : OpenLink driver
1600 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1601 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1603 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1604 structSQLTypeInfo
.TypeName
= wxT("real");
1608 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1609 return(DispAllErrors(henv
, hdbc
, hstmt
));
1610 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1611 return(DispAllErrors(henv
, hdbc
, hstmt
));
1612 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1613 // return(DispAllErrors(henv, hdbc, hstmt));
1615 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1616 return(DispAllErrors(henv
, hdbc
, hstmt
));
1618 if (structSQLTypeInfo
.MaximumScale
< 0)
1619 structSQLTypeInfo
.MaximumScale
= 0;
1621 // Close the statement handle which closes open cursors
1622 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1623 return(DispAllErrors(henv
, hdbc
, hstmt
));
1625 // Completed Successfully
1628 } // wxDb::getDataTypeInfo()
1631 /********** wxDb::Close() **********/
1632 void wxDb::Close(void)
1634 // Close the Sql Log file
1641 // Free statement handle
1644 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1645 DispAllErrors(henv
, hdbc
);
1648 // Disconnect from the datasource
1649 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1650 DispAllErrors(henv
, hdbc
);
1652 // Free the connection to the datasource
1653 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1654 DispAllErrors(henv
, hdbc
);
1656 // There should be zero Ctable objects still connected to this db object
1657 wxASSERT(nTables
== 0);
1662 pNode
= TablesInUse
.GetFirst();
1666 tiu
= (wxTablesInUse
*)pNode
->GetData();
1667 if (tiu
->pDb
== this)
1669 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1670 s2
.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1671 wxLogDebug(s
.c_str(),s2
.c_str());
1673 pNode
= pNode
->GetNext();
1677 // Copy the error messages to a global variable
1679 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1680 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1682 dbmsType
= dbmsUNIDENTIFIED
;
1688 /********** wxDb::CommitTrans() **********/
1689 bool wxDb::CommitTrans(void)
1693 // Commit the transaction
1694 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1695 return(DispAllErrors(henv
, hdbc
));
1698 // Completed successfully
1701 } // wxDb::CommitTrans()
1704 /********** wxDb::RollbackTrans() **********/
1705 bool wxDb::RollbackTrans(void)
1707 // Rollback the transaction
1708 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1709 return(DispAllErrors(henv
, hdbc
));
1711 // Completed successfully
1714 } // wxDb::RollbackTrans()
1717 /********** wxDb::DispAllErrors() **********/
1718 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1720 * This function is called internally whenever an error condition prevents the user's
1721 * request from being executed. This function will query the datasource as to the
1722 * actual error(s) that just occured on the previous request of the datasource.
1724 * The function will retrieve each error condition from the datasource and
1725 * Printf the codes/text values into a string which it then logs via logError().
1726 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1727 * window and program execution will be paused until the user presses a key.
1729 * This function always returns a FALSE, so that functions which call this function
1730 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1731 * of the users request, so that the calling code can then process the error msg log
1734 wxString odbcErrMsg
;
1736 while (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1738 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1739 logError(odbcErrMsg
, sqlState
);
1742 #ifdef DBDEBUG_CONSOLE
1743 // When run in console mode, use standard out to display errors.
1744 cout
<< odbcErrMsg
.c_str() << endl
;
1745 cout
<< wxT("Press any key to continue...") << endl
;
1750 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1755 return(FALSE
); // This function always returns FALSE.
1757 } // wxDb::DispAllErrors()
1760 /********** wxDb::GetNextError() **********/
1761 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1763 if (SQLError(aHenv
, aHdbc
, aHstmt
, (SQLTCHAR FAR
*) sqlState
, &nativeError
, (SQLTCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1768 } // wxDb::GetNextError()
1771 /********** wxDb::DispNextError() **********/
1772 void wxDb::DispNextError(void)
1774 wxString odbcErrMsg
;
1776 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1777 logError(odbcErrMsg
, sqlState
);
1782 #ifdef DBDEBUG_CONSOLE
1783 // When run in console mode, use standard out to display errors.
1784 cout
<< odbcErrMsg
.c_str() << endl
;
1785 cout
<< wxT("Press any key to continue...") << endl
;
1790 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1791 #endif // __WXDEBUG__
1793 } // wxDb::DispNextError()
1796 /********** wxDb::logError() **********/
1797 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1799 wxASSERT(errMsg
.Length());
1801 static int pLast
= -1;
1804 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1807 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1808 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1812 wxStrcpy(errorList
[pLast
], errMsg
);
1814 if (SQLState
.Length())
1815 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1816 DB_STATUS
= dbStatus
;
1818 // Add the errmsg to the sql log
1819 WriteSqlLog(errMsg
);
1821 } // wxDb::logError()
1824 /**********wxDb::TranslateSqlState() **********/
1825 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1827 if (!wxStrcmp(SQLState
, wxT("01000")))
1828 return(DB_ERR_GENERAL_WARNING
);
1829 if (!wxStrcmp(SQLState
, wxT("01002")))
1830 return(DB_ERR_DISCONNECT_ERROR
);
1831 if (!wxStrcmp(SQLState
, wxT("01004")))
1832 return(DB_ERR_DATA_TRUNCATED
);
1833 if (!wxStrcmp(SQLState
, wxT("01006")))
1834 return(DB_ERR_PRIV_NOT_REVOKED
);
1835 if (!wxStrcmp(SQLState
, wxT("01S00")))
1836 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1837 if (!wxStrcmp(SQLState
, wxT("01S01")))
1838 return(DB_ERR_ERROR_IN_ROW
);
1839 if (!wxStrcmp(SQLState
, wxT("01S02")))
1840 return(DB_ERR_OPTION_VALUE_CHANGED
);
1841 if (!wxStrcmp(SQLState
, wxT("01S03")))
1842 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1843 if (!wxStrcmp(SQLState
, wxT("01S04")))
1844 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1845 if (!wxStrcmp(SQLState
, wxT("07001")))
1846 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1847 if (!wxStrcmp(SQLState
, wxT("07006")))
1848 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1849 if (!wxStrcmp(SQLState
, wxT("08001")))
1850 return(DB_ERR_UNABLE_TO_CONNECT
);
1851 if (!wxStrcmp(SQLState
, wxT("08002")))
1852 return(DB_ERR_CONNECTION_IN_USE
);
1853 if (!wxStrcmp(SQLState
, wxT("08003")))
1854 return(DB_ERR_CONNECTION_NOT_OPEN
);
1855 if (!wxStrcmp(SQLState
, wxT("08004")))
1856 return(DB_ERR_REJECTED_CONNECTION
);
1857 if (!wxStrcmp(SQLState
, wxT("08007")))
1858 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1859 if (!wxStrcmp(SQLState
, wxT("08S01")))
1860 return(DB_ERR_COMM_LINK_FAILURE
);
1861 if (!wxStrcmp(SQLState
, wxT("21S01")))
1862 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1863 if (!wxStrcmp(SQLState
, wxT("21S02")))
1864 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1865 if (!wxStrcmp(SQLState
, wxT("22001")))
1866 return(DB_ERR_STRING_RIGHT_TRUNC
);
1867 if (!wxStrcmp(SQLState
, wxT("22003")))
1868 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1869 if (!wxStrcmp(SQLState
, wxT("22005")))
1870 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1871 if (!wxStrcmp(SQLState
, wxT("22008")))
1872 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1873 if (!wxStrcmp(SQLState
, wxT("22012")))
1874 return(DB_ERR_DIVIDE_BY_ZERO
);
1875 if (!wxStrcmp(SQLState
, wxT("22026")))
1876 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1877 if (!wxStrcmp(SQLState
, wxT("23000")))
1878 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1879 if (!wxStrcmp(SQLState
, wxT("24000")))
1880 return(DB_ERR_INVALID_CURSOR_STATE
);
1881 if (!wxStrcmp(SQLState
, wxT("25000")))
1882 return(DB_ERR_INVALID_TRANS_STATE
);
1883 if (!wxStrcmp(SQLState
, wxT("28000")))
1884 return(DB_ERR_INVALID_AUTH_SPEC
);
1885 if (!wxStrcmp(SQLState
, wxT("34000")))
1886 return(DB_ERR_INVALID_CURSOR_NAME
);
1887 if (!wxStrcmp(SQLState
, wxT("37000")))
1888 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1889 if (!wxStrcmp(SQLState
, wxT("3C000")))
1890 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1891 if (!wxStrcmp(SQLState
, wxT("40001")))
1892 return(DB_ERR_SERIALIZATION_FAILURE
);
1893 if (!wxStrcmp(SQLState
, wxT("42000")))
1894 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1895 if (!wxStrcmp(SQLState
, wxT("70100")))
1896 return(DB_ERR_OPERATION_ABORTED
);
1897 if (!wxStrcmp(SQLState
, wxT("IM001")))
1898 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1899 if (!wxStrcmp(SQLState
, wxT("IM002")))
1900 return(DB_ERR_NO_DATA_SOURCE
);
1901 if (!wxStrcmp(SQLState
, wxT("IM003")))
1902 return(DB_ERR_DRIVER_LOAD_ERROR
);
1903 if (!wxStrcmp(SQLState
, wxT("IM004")))
1904 return(DB_ERR_SQLALLOCENV_FAILED
);
1905 if (!wxStrcmp(SQLState
, wxT("IM005")))
1906 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1907 if (!wxStrcmp(SQLState
, wxT("IM006")))
1908 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1909 if (!wxStrcmp(SQLState
, wxT("IM007")))
1910 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1911 if (!wxStrcmp(SQLState
, wxT("IM008")))
1912 return(DB_ERR_DIALOG_FAILED
);
1913 if (!wxStrcmp(SQLState
, wxT("IM009")))
1914 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1915 if (!wxStrcmp(SQLState
, wxT("IM010")))
1916 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1917 if (!wxStrcmp(SQLState
, wxT("IM011")))
1918 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1919 if (!wxStrcmp(SQLState
, wxT("IM012")))
1920 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1921 if (!wxStrcmp(SQLState
, wxT("IM013")))
1922 return(DB_ERR_TRACE_FILE_ERROR
);
1923 if (!wxStrcmp(SQLState
, wxT("S0001")))
1924 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1925 if (!wxStrcmp(SQLState
, wxT("S0002")))
1926 return(DB_ERR_TABLE_NOT_FOUND
);
1927 if (!wxStrcmp(SQLState
, wxT("S0011")))
1928 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1929 if (!wxStrcmp(SQLState
, wxT("S0012")))
1930 return(DB_ERR_INDEX_NOT_FOUND
);
1931 if (!wxStrcmp(SQLState
, wxT("S0021")))
1932 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1933 if (!wxStrcmp(SQLState
, wxT("S0022")))
1934 return(DB_ERR_COLUMN_NOT_FOUND
);
1935 if (!wxStrcmp(SQLState
, wxT("S0023")))
1936 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1937 if (!wxStrcmp(SQLState
, wxT("S1000")))
1938 return(DB_ERR_GENERAL_ERROR
);
1939 if (!wxStrcmp(SQLState
, wxT("S1001")))
1940 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1941 if (!wxStrcmp(SQLState
, wxT("S1002")))
1942 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1943 if (!wxStrcmp(SQLState
, wxT("S1003")))
1944 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1945 if (!wxStrcmp(SQLState
, wxT("S1004")))
1946 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1947 if (!wxStrcmp(SQLState
, wxT("S1008")))
1948 return(DB_ERR_OPERATION_CANCELLED
);
1949 if (!wxStrcmp(SQLState
, wxT("S1009")))
1950 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1951 if (!wxStrcmp(SQLState
, wxT("S1010")))
1952 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1953 if (!wxStrcmp(SQLState
, wxT("S1011")))
1954 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1955 if (!wxStrcmp(SQLState
, wxT("S1012")))
1956 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1957 if (!wxStrcmp(SQLState
, wxT("S1015")))
1958 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1959 if (!wxStrcmp(SQLState
, wxT("S1090")))
1960 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1961 if (!wxStrcmp(SQLState
, wxT("S1091")))
1962 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1963 if (!wxStrcmp(SQLState
, wxT("S1092")))
1964 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1965 if (!wxStrcmp(SQLState
, wxT("S1093")))
1966 return(DB_ERR_INVALID_PARAM_NO
);
1967 if (!wxStrcmp(SQLState
, wxT("S1094")))
1968 return(DB_ERR_INVALID_SCALE_VALUE
);
1969 if (!wxStrcmp(SQLState
, wxT("S1095")))
1970 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1971 if (!wxStrcmp(SQLState
, wxT("S1096")))
1972 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1973 if (!wxStrcmp(SQLState
, wxT("S1097")))
1974 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1975 if (!wxStrcmp(SQLState
, wxT("S1098")))
1976 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1977 if (!wxStrcmp(SQLState
, wxT("S1099")))
1978 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1979 if (!wxStrcmp(SQLState
, wxT("S1100")))
1980 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1981 if (!wxStrcmp(SQLState
, wxT("S1101")))
1982 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1983 if (!wxStrcmp(SQLState
, wxT("S1103")))
1984 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1985 if (!wxStrcmp(SQLState
, wxT("S1104")))
1986 return(DB_ERR_INVALID_PRECISION_VALUE
);
1987 if (!wxStrcmp(SQLState
, wxT("S1105")))
1988 return(DB_ERR_INVALID_PARAM_TYPE
);
1989 if (!wxStrcmp(SQLState
, wxT("S1106")))
1990 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1991 if (!wxStrcmp(SQLState
, wxT("S1107")))
1992 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1993 if (!wxStrcmp(SQLState
, wxT("S1108")))
1994 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1995 if (!wxStrcmp(SQLState
, wxT("S1109")))
1996 return(DB_ERR_INVALID_CURSOR_POSITION
);
1997 if (!wxStrcmp(SQLState
, wxT("S1110")))
1998 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1999 if (!wxStrcmp(SQLState
, wxT("S1111")))
2000 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
2001 if (!wxStrcmp(SQLState
, wxT("S1C00")))
2002 return(DB_ERR_DRIVER_NOT_CAPABLE
);
2003 if (!wxStrcmp(SQLState
, wxT("S1T00")))
2004 return(DB_ERR_TIMEOUT_EXPIRED
);
2009 } // wxDb::TranslateSqlState()
2012 /********** wxDb::Grant() **********/
2013 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
2017 // Build the grant statement
2018 sqlStmt
= wxT("GRANT ");
2019 if (privileges
== DB_GRANT_ALL
)
2020 sqlStmt
+= wxT("ALL");
2024 if (privileges
& DB_GRANT_SELECT
)
2026 sqlStmt
+= wxT("SELECT");
2029 if (privileges
& DB_GRANT_INSERT
)
2032 sqlStmt
+= wxT(", ");
2033 sqlStmt
+= wxT("INSERT");
2035 if (privileges
& DB_GRANT_UPDATE
)
2038 sqlStmt
+= wxT(", ");
2039 sqlStmt
+= wxT("UPDATE");
2041 if (privileges
& DB_GRANT_DELETE
)
2044 sqlStmt
+= wxT(", ");
2045 sqlStmt
+= wxT("DELETE");
2049 sqlStmt
+= wxT(" ON ");
2050 sqlStmt
+= SQLTableName(tableName
);
2051 sqlStmt
+= wxT(" TO ");
2052 sqlStmt
+= userList
;
2054 #ifdef DBDEBUG_CONSOLE
2055 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2058 WriteSqlLog(sqlStmt
);
2060 return(ExecSql(sqlStmt
));
2065 /********** wxDb::CreateView() **********/
2066 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
2067 const wxString
&pSqlStmt
, bool attemptDrop
)
2071 // Drop the view first
2072 if (attemptDrop
&& !DropView(viewName
))
2075 // Build the create view statement
2076 sqlStmt
= wxT("CREATE VIEW ");
2077 sqlStmt
+= viewName
;
2079 if (colList
.Length())
2081 sqlStmt
+= wxT(" (");
2083 sqlStmt
+= wxT(")");
2086 sqlStmt
+= wxT(" AS ");
2087 sqlStmt
+= pSqlStmt
;
2089 WriteSqlLog(sqlStmt
);
2091 #ifdef DBDEBUG_CONSOLE
2092 cout
<< sqlStmt
.c_str() << endl
;
2095 return(ExecSql(sqlStmt
));
2097 } // wxDb::CreateView()
2100 /********** wxDb::DropView() **********/
2101 bool wxDb::DropView(const wxString
&viewName
)
2104 * NOTE: This function returns TRUE if the View does not exist, but
2105 * only for identified databases. Code will need to be added
2106 * below for any other databases when those databases are defined
2107 * to handle this situation consistently
2111 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
2113 WriteSqlLog(sqlStmt
);
2115 #ifdef DBDEBUG_CONSOLE
2116 cout
<< endl
<< sqlStmt
.c_str() << endl
;
2119 if (SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2121 // Check for "Base table not found" error and ignore
2122 GetNextError(henv
, hdbc
, hstmt
);
2123 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
2125 // Check for product specific error codes
2126 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
2129 DispAllErrors(henv
, hdbc
, hstmt
);
2136 // Commit the transaction
2142 } // wxDb::DropView()
2145 /********** wxDb::ExecSql() **********/
2146 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
2150 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2152 retcode
= SQLExecDirect(hstmt
, (SQLTCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
);
2153 if (retcode
== SQL_SUCCESS
||
2154 (Dbms() == dbmsDB2
&& (retcode
== SQL_SUCCESS_WITH_INFO
|| retcode
== SQL_NO_DATA_FOUND
)))
2160 DispAllErrors(henv
, hdbc
, hstmt
);
2164 } // wxDb::ExecSql()
2167 /********** wxDb::GetNext() **********/
2168 bool wxDb::GetNext(void)
2170 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
2174 DispAllErrors(henv
, hdbc
, hstmt
);
2178 } // wxDb::GetNext()
2181 /********** wxDb::GetData() **********/
2182 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
2185 wxASSERT(cbReturned
);
2187 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
2191 DispAllErrors(henv
, hdbc
, hstmt
);
2195 } // wxDb::GetData()
2198 /********** wxDb::GetKeyFields() **********/
2199 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
2201 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
2202 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
2204 // SQLSMALLINT iKeySeq;
2205 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
2206 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
2212 * -----------------------------------------------------------------------
2213 * -- 19991224 : mj10777 : Create ------
2214 * -- : Three things are done and stored here : ------
2215 * -- : 1) which Column(s) is/are Primary Key(s) ------
2216 * -- : 2) which tables use this Key as a Foreign Key ------
2217 * -- : 3) which columns are Foreign Key and the name ------
2218 * -- : of the Table where the Key is the Primary Key -----
2219 * -- : Called from GetColumns(const wxString &tableName, ------
2220 * -- int *numCols,const wxChar *userID ) ------
2221 * -----------------------------------------------------------------------
2224 /*---------------------------------------------------------------------*/
2225 /* Get the names of the columns in the primary key. */
2226 /*---------------------------------------------------------------------*/
2227 retcode
= SQLPrimaryKeys(hstmt
,
2228 NULL
, 0, /* Catalog name */
2229 NULL
, 0, /* Schema name */
2230 (SQLTCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
2232 /*---------------------------------------------------------------------*/
2233 /* Fetch and display the result set. This will be a list of the */
2234 /* columns in the primary key of the tableName table. */
2235 /*---------------------------------------------------------------------*/
2236 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2238 retcode
= SQLFetch(hstmt
);
2239 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2241 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2242 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2244 for (i
=0;i
<noCols
;i
++) // Find the Column name
2245 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
2246 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
2249 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2251 /*---------------------------------------------------------------------*/
2252 /* Get all the foreign keys that refer to tableName primary key. */
2253 /*---------------------------------------------------------------------*/
2254 retcode
= SQLForeignKeys(hstmt
,
2255 NULL
, 0, /* Primary catalog */
2256 NULL
, 0, /* Primary schema */
2257 (SQLTCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
2258 NULL
, 0, /* Foreign catalog */
2259 NULL
, 0, /* Foreign schema */
2260 NULL
, 0); /* Foreign table */
2262 /*---------------------------------------------------------------------*/
2263 /* Fetch and display the result set. This will be all of the foreign */
2264 /* keys in other tables that refer to the tableName primary key. */
2265 /*---------------------------------------------------------------------*/
2268 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2270 retcode
= SQLFetch(hstmt
);
2271 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2273 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2274 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2275 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2276 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2277 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2278 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2282 tempStr
.Trim(); // Get rid of any unneeded blanks
2283 if (!tempStr
.IsEmpty())
2285 for (i
=0; i
<noCols
; i
++)
2286 { // Find the Column name
2287 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2288 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2292 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2294 /*---------------------------------------------------------------------*/
2295 /* Get all the foreign keys in the tablename table. */
2296 /*---------------------------------------------------------------------*/
2297 retcode
= SQLForeignKeys(hstmt
,
2298 NULL
, 0, /* Primary catalog */
2299 NULL
, 0, /* Primary schema */
2300 NULL
, 0, /* Primary table */
2301 NULL
, 0, /* Foreign catalog */
2302 NULL
, 0, /* Foreign schema */
2303 (SQLTCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2305 /*---------------------------------------------------------------------*/
2306 /* Fetch and display the result set. This will be all of the */
2307 /* primary keys in other tables that are referred to by foreign */
2308 /* keys in the tableName table. */
2309 /*---------------------------------------------------------------------*/
2310 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2312 retcode
= SQLFetch(hstmt
);
2313 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2315 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2316 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2317 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2319 for (i
=0; i
<noCols
; i
++) // Find the Column name
2321 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2323 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2324 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2329 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2333 } // wxDb::GetKeyFields()
2337 /********** wxDb::GetColumns() **********/
2338 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2340 * 1) The last array element of the tableName[] argument must be zero (null).
2341 * This is how the end of the array is detected.
2342 * 2) This function returns an array of wxDbColInf structures. If no columns
2343 * were found, or an error occured, this pointer will be zero (null). THE
2344 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2345 * IS FINISHED WITH IT. i.e.
2347 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2350 * // Use the column inf
2352 * // Destroy the memory
2356 * userID is evaluated in the following manner:
2357 * userID == NULL ... UserID is ignored
2358 * userID == "" ... UserID set equal to 'this->uid'
2359 * userID != "" ... UserID set equal to 'userID'
2361 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2362 * by this function. This function should use its own wxDb instance
2363 * to avoid undesired unbinding of columns.
2368 wxDbColInf
*colInf
= 0;
2376 convertUserID(userID
,UserID
);
2378 // Pass 1 - Determine how many columns there are.
2379 // Pass 2 - Allocate the wxDbColInf array and fill in
2380 // the array with the column information.
2382 for (pass
= 1; pass
<= 2; pass
++)
2386 if (noCols
== 0) // Probably a bogus table name(s)
2388 // Allocate n wxDbColInf objects to hold the column information
2389 colInf
= new wxDbColInf
[noCols
+1];
2392 // Mark the end of the array
2393 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2394 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2395 colInf
[noCols
].sqlDataType
= 0;
2397 // Loop through each table name
2399 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2401 TableName
= tableName
[tbl
];
2402 // Oracle and Interbase table names are uppercase only, so force
2403 // the name to uppercase just in case programmer forgot to do this
2404 if ((Dbms() == dbmsORACLE
) ||
2405 (Dbms() == dbmsINTERBASE
))
2406 TableName
= TableName
.Upper();
2408 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2410 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2411 // use the call below that leaves out the user name
2412 if (!UserID
.IsEmpty() &&
2413 Dbms() != dbmsMY_SQL
&&
2414 Dbms() != dbmsACCESS
&&
2415 Dbms() != dbmsMS_SQL_SERVER
)
2417 retcode
= SQLColumns(hstmt
,
2418 NULL
, 0, // All qualifiers
2419 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2420 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2421 NULL
, 0); // All columns
2425 retcode
= SQLColumns(hstmt
,
2426 NULL
, 0, // All qualifiers
2428 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2429 NULL
, 0); // All columns
2431 if (retcode
!= SQL_SUCCESS
)
2432 { // Error occured, abort
2433 DispAllErrors(henv
, hdbc
, hstmt
);
2436 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2440 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2442 if (pass
== 1) // First pass, just add up the number of columns
2444 else // Pass 2; Fill in the array of structures
2446 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2448 // NOTE: Only the ODBC 1.x fields are retrieved
2449 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2450 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2451 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2452 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2453 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2454 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2455 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2456 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2457 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2458 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2459 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2460 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2462 // Determine the wxDb data type that is used to represent the native data type of this data source
2463 colInf
[colNo
].dbDataType
= 0;
2464 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2467 // IODBC does not return a correct columnSize, so we set
2468 // columnSize = bufferLength if no column size was returned
2469 // IODBC returns the columnSize in bufferLength.. (bug)
2470 if (colInf
[colNo
].columnSize
< 1)
2472 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2475 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2477 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2478 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2479 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2480 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2481 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2482 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2483 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2484 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2489 if (retcode
!= SQL_NO_DATA_FOUND
)
2490 { // Error occured, abort
2491 DispAllErrors(henv
, hdbc
, hstmt
);
2494 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2500 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2503 } // wxDb::GetColumns()
2506 /********** wxDb::GetColumns() **********/
2508 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2510 // Same as the above GetColumns() function except this one gets columns
2511 // only for a single table, and if 'numCols' is not NULL, the number of
2512 // columns stored in the returned wxDbColInf is set in '*numCols'
2514 // userID is evaluated in the following manner:
2515 // userID == NULL ... UserID is ignored
2516 // userID == "" ... UserID set equal to 'this->uid'
2517 // userID != "" ... UserID set equal to 'userID'
2519 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2520 // by this function. This function should use its own wxDb instance
2521 // to avoid undesired unbinding of columns.
2526 wxDbColInf
*colInf
= 0;
2534 convertUserID(userID
,UserID
);
2536 // Pass 1 - Determine how many columns there are.
2537 // Pass 2 - Allocate the wxDbColInf array and fill in
2538 // the array with the column information.
2540 for (pass
= 1; pass
<= 2; pass
++)
2544 if (noCols
== 0) // Probably a bogus table name(s)
2546 // Allocate n wxDbColInf objects to hold the column information
2547 colInf
= new wxDbColInf
[noCols
+1];
2550 // Mark the end of the array
2551 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2552 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2553 colInf
[noCols
].sqlDataType
= 0;
2556 TableName
= tableName
;
2557 // Oracle and Interbase table names are uppercase only, so force
2558 // the name to uppercase just in case programmer forgot to do this
2559 if ((Dbms() == dbmsORACLE
) ||
2560 (Dbms() == dbmsINTERBASE
))
2561 TableName
= TableName
.Upper();
2563 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2565 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2566 // use the call below that leaves out the user name
2567 if (!UserID
.IsEmpty() &&
2568 Dbms() != dbmsMY_SQL
&&
2569 Dbms() != dbmsACCESS
&&
2570 Dbms() != dbmsMS_SQL_SERVER
)
2572 retcode
= SQLColumns(hstmt
,
2573 NULL
, 0, // All qualifiers
2574 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2575 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2576 NULL
, 0); // All columns
2580 retcode
= SQLColumns(hstmt
,
2581 NULL
, 0, // All qualifiers
2583 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
2584 NULL
, 0); // All columns
2586 if (retcode
!= SQL_SUCCESS
)
2587 { // Error occured, abort
2588 DispAllErrors(henv
, hdbc
, hstmt
);
2591 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2597 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2599 if (pass
== 1) // First pass, just add up the number of columns
2601 else // Pass 2; Fill in the array of structures
2603 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2605 // NOTE: Only the ODBC 1.x fields are retrieved
2606 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2607 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2608 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2609 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2610 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2611 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2612 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2613 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2614 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2615 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2616 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2617 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2618 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2619 // Start Values for Primary/Foriegn Key (=No)
2620 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2621 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2622 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2623 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2625 // BJO 20000428 : Virtuoso returns type names with upper cases!
2626 if (Dbms() == dbmsVIRTUOSO
)
2628 wxString s
= colInf
[colNo
].typeName
;
2630 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2633 // Determine the wxDb data type that is used to represent the native data type of this data source
2634 colInf
[colNo
].dbDataType
= 0;
2635 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2638 // IODBC does not return a correct columnSize, so we set
2639 // columnSize = bufferLength if no column size was returned
2640 // IODBC returns the columnSize in bufferLength.. (bug)
2641 if (colInf
[colNo
].columnSize
< 1)
2643 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2647 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2649 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2650 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2651 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2652 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2653 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2654 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2655 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2656 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2662 if (retcode
!= SQL_NO_DATA_FOUND
)
2663 { // Error occured, abort
2664 DispAllErrors(henv
, hdbc
, hstmt
);
2667 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2674 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2676 // Store Primary and Foriegn Keys
2677 GetKeyFields(tableName
,colInf
,noCols
);
2683 } // wxDb::GetColumns()
2686 #else // New GetColumns
2691 These are tentative new GetColumns members which should be more database
2692 independant and which always returns the columns in the order they were
2695 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2696 wxChar* userID)) calls the second implementation for each separate table
2697 before merging the results. This makes the code easier to maintain as
2698 only one member (the second) makes the real work
2699 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2700 wxChar *userID) is a little bit improved
2701 - It doesn't anymore rely on the type-name to find out which database-type
2703 - It ends by sorting the columns, so that they are returned in the same
2704 order they were created
2714 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2717 // The last array element of the tableName[] argument must be zero (null).
2718 // This is how the end of the array is detected.
2722 // How many tables ?
2724 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2726 // Create a table to maintain the columns for each separate table
2727 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2730 for (i
= 0 ; i
< tbl
; i
++)
2733 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2734 if (TableColumns
[i
].colInf
== NULL
)
2736 noCols
+= TableColumns
[i
].noCols
;
2739 // Now merge all the separate table infos
2740 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2742 // Mark the end of the array
2743 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2744 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2745 colInf
[noCols
].sqlDataType
= 0;
2750 for (i
= 0 ; i
< tbl
; i
++)
2752 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2754 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2758 delete [] TableColumns
;
2761 } // wxDb::GetColumns() -- NEW
2764 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2766 // Same as the above GetColumns() function except this one gets columns
2767 // only for a single table, and if 'numCols' is not NULL, the number of
2768 // columns stored in the returned wxDbColInf is set in '*numCols'
2770 // userID is evaluated in the following manner:
2771 // userID == NULL ... UserID is ignored
2772 // userID == "" ... UserID set equal to 'this->uid'
2773 // userID != "" ... UserID set equal to 'userID'
2775 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2776 // by this function. This function should use its own wxDb instance
2777 // to avoid undesired unbinding of columns.
2781 wxDbColInf
*colInf
= 0;
2789 convertUserID(userID
,UserID
);
2791 // Pass 1 - Determine how many columns there are.
2792 // Pass 2 - Allocate the wxDbColInf array and fill in
2793 // the array with the column information.
2795 for (pass
= 1; pass
<= 2; pass
++)
2799 if (noCols
== 0) // Probably a bogus table name(s)
2801 // Allocate n wxDbColInf objects to hold the column information
2802 colInf
= new wxDbColInf
[noCols
+1];
2805 // Mark the end of the array
2806 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2807 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2808 colInf
[noCols
].sqlDataType
= 0;
2811 TableName
= tableName
;
2812 // Oracle and Interbase table names are uppercase only, so force
2813 // the name to uppercase just in case programmer forgot to do this
2814 if ((Dbms() == dbmsORACLE
) ||
2815 (Dbms() == dbmsINTERBASE
))
2816 TableName
= TableName
.Upper();
2818 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2820 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2821 // use the call below that leaves out the user name
2822 if (!UserID
.IsEmpty() &&
2823 Dbms() != dbmsMY_SQL
&&
2824 Dbms() != dbmsACCESS
&&
2825 Dbms() != dbmsMS_SQL_SERVER
)
2827 retcode
= SQLColumns(hstmt
,
2828 NULL
, 0, // All qualifiers
2829 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2830 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2831 NULL
, 0); // All columns
2835 retcode
= SQLColumns(hstmt
,
2836 NULL
, 0, // All qualifiers
2838 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2839 NULL
, 0); // All columns
2841 if (retcode
!= SQL_SUCCESS
)
2842 { // Error occured, abort
2843 DispAllErrors(henv
, hdbc
, hstmt
);
2846 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2852 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2854 if (pass
== 1) // First pass, just add up the number of columns
2856 else // Pass 2; Fill in the array of structures
2858 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2860 // NOTE: Only the ODBC 1.x fields are retrieved
2861 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2862 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2863 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2864 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2865 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2866 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2867 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2868 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2869 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2870 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2871 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2872 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2873 // Start Values for Primary/Foriegn Key (=No)
2874 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2875 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2876 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2877 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2880 // IODBC does not return a correct columnSize, so we set
2881 // columnSize = bufferLength if no column size was returned
2882 // IODBC returns the columnSize in bufferLength.. (bug)
2883 if (colInf
[colNo
].columnSize
< 1)
2885 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2889 // Determine the wxDb data type that is used to represent the native data type of this data source
2890 colInf
[colNo
].dbDataType
= 0;
2891 // Get the intern datatype
2892 switch (colInf
[colNo
].sqlDataType
)
2896 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2902 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2909 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2912 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2915 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2920 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2921 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2928 if (retcode
!= SQL_NO_DATA_FOUND
)
2929 { // Error occured, abort
2930 DispAllErrors(henv
, hdbc
, hstmt
);
2933 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2940 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2942 // Store Primary and Foreign Keys
2943 GetKeyFields(tableName
,colInf
,noCols
);
2945 ///////////////////////////////////////////////////////////////////////////
2946 // Now sort the the columns in order to make them appear in the right order
2947 ///////////////////////////////////////////////////////////////////////////
2949 // Build a generic SELECT statement which returns 0 rows
2952 Stmt
.Printf(wxT("select * from \"%s\" where 0=1"), tableName
);
2955 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2957 DispAllErrors(henv
, hdbc
, hstmt
);
2961 // Get the number of result columns
2962 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2964 DispAllErrors(henv
, hdbc
, hstmt
);
2968 if (noCols
== 0) // Probably a bogus table name
2977 for (colNum
= 0; colNum
< noCols
; colNum
++)
2979 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2981 &Sword
, &Sdword
) != SQL_SUCCESS
)
2983 DispAllErrors(henv
, hdbc
, hstmt
);
2987 wxString Name1
= name
;
2988 Name1
= Name1
.Upper();
2990 // Where is this name in the array ?
2991 for (i
= colNum
; i
< noCols
; i
++)
2993 wxString Name2
= colInf
[i
].colName
;
2994 Name2
= Name2
.Upper();
2997 if (colNum
!= i
) // swap to sort
2999 wxDbColInf tmpColInf
= colInf
[colNum
];
3000 colInf
[colNum
] = colInf
[i
];
3001 colInf
[i
] = tmpColInf
;
3007 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3009 ///////////////////////////////////////////////////////////////////////////
3011 ///////////////////////////////////////////////////////////////////////////
3017 } // wxDb::GetColumns()
3020 #endif // #else OLD_GETCOLUMNS
3023 /********** wxDb::GetColumnCount() **********/
3024 int wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
3026 * Returns a count of how many columns are in a table.
3027 * If an error occurs in computing the number of columns
3028 * this function will return a -1 for the count
3030 * userID is evaluated in the following manner:
3031 * userID == NULL ... UserID is ignored
3032 * userID == "" ... UserID set equal to 'this->uid'
3033 * userID != "" ... UserID set equal to 'userID'
3035 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3036 * by this function. This function should use its own wxDb instance
3037 * to avoid undesired unbinding of columns.
3047 convertUserID(userID
,UserID
);
3049 TableName
= tableName
;
3050 // Oracle and Interbase table names are uppercase only, so force
3051 // the name to uppercase just in case programmer forgot to do this
3052 if ((Dbms() == dbmsORACLE
) ||
3053 (Dbms() == dbmsINTERBASE
))
3054 TableName
= TableName
.Upper();
3056 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3058 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3059 // use the call below that leaves out the user name
3060 if (!UserID
.IsEmpty() &&
3061 Dbms() != dbmsMY_SQL
&&
3062 Dbms() != dbmsACCESS
&&
3063 Dbms() != dbmsMS_SQL_SERVER
)
3065 retcode
= SQLColumns(hstmt
,
3066 NULL
, 0, // All qualifiers
3067 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
3068 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3069 NULL
, 0); // All columns
3073 retcode
= SQLColumns(hstmt
,
3074 NULL
, 0, // All qualifiers
3076 (SQLTCHAR
*) TableName
.c_str(), SQL_NTS
,
3077 NULL
, 0); // All columns
3079 if (retcode
!= SQL_SUCCESS
)
3080 { // Error occured, abort
3081 DispAllErrors(henv
, hdbc
, hstmt
);
3082 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3086 // Count the columns
3087 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3090 if (retcode
!= SQL_NO_DATA_FOUND
)
3091 { // Error occured, abort
3092 DispAllErrors(henv
, hdbc
, hstmt
);
3093 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3097 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3100 } // wxDb::GetColumnCount()
3103 /********** wxDb::GetCatalog() *******/
3104 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
3106 * ---------------------------------------------------------------------
3107 * -- 19991203 : mj10777 : Create ------
3108 * -- : Creates a wxDbInf with Tables / Cols Array ------
3109 * -- : uses SQLTables and fills pTableInf; ------
3110 * -- : pColInf is set to NULL and numCols to 0; ------
3111 * -- : returns pDbInf (wxDbInf) ------
3112 * -- - if unsuccesfull (pDbInf == NULL) ------
3113 * -- : pColInf can be filled with GetColumns(..); ------
3114 * -- : numCols can be filled with GetColumnCount(..); ------
3115 * ---------------------------------------------------------------------
3117 * userID is evaluated in the following manner:
3118 * userID == NULL ... UserID is ignored
3119 * userID == "" ... UserID set equal to 'this->uid'
3120 * userID != "" ... UserID set equal to 'userID'
3122 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3123 * by this function. This function should use its own wxDb instance
3124 * to avoid undesired unbinding of columns.
3127 int noTab
= 0; // Counter while filling table entries
3131 wxString tblNameSave
;
3134 convertUserID(userID
,UserID
);
3136 //-------------------------------------------------------------
3137 // Create the Database Array of catalog entries
3139 wxDbInf
*pDbInf
= new wxDbInf
;
3141 //-------------------------------------------------------------
3142 // Table Information
3143 // Pass 1 - Determine how many Tables there are.
3144 // Pass 2 - Create the Table array and fill it
3145 // - Create the Cols array = NULL
3146 //-------------------------------------------------------------
3148 for (pass
= 1; pass
<= 2; pass
++)
3150 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
3151 tblNameSave
.Empty();
3153 if (!UserID
.IsEmpty() &&
3154 Dbms() != dbmsMY_SQL
&&
3155 Dbms() != dbmsACCESS
&&
3156 Dbms() != dbmsMS_SQL_SERVER
)
3158 retcode
= SQLTables(hstmt
,
3159 NULL
, 0, // All qualifiers
3160 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3161 NULL
, 0, // All tables
3162 NULL
, 0); // All columns
3166 retcode
= SQLTables(hstmt
,
3167 NULL
, 0, // All qualifiers
3168 NULL
, 0, // User specified
3169 NULL
, 0, // All tables
3170 NULL
, 0); // All columns
3173 if (retcode
!= SQL_SUCCESS
)
3175 DispAllErrors(henv
, hdbc
, hstmt
);
3177 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3181 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
3183 if (pass
== 1) // First pass, just count the Tables
3185 if (pDbInf
->numTables
== 0)
3187 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
3188 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
3190 pDbInf
->numTables
++; // Counter for Tables
3192 if (pass
== 2) // Create and fill the Table entries
3194 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
3195 { // no, then create the Array
3196 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
3198 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3200 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3201 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
3202 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
3208 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3210 // Query how many columns are in each table
3211 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
3213 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
3218 } // wxDb::GetCatalog()
3221 /********** wxDb::Catalog() **********/
3222 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
3224 * Creates the text file specified in 'filename' which will contain
3225 * a minimal data dictionary of all tables accessible by the user specified
3228 * userID is evaluated in the following manner:
3229 * userID == NULL ... UserID is ignored
3230 * userID == "" ... UserID set equal to 'this->uid'
3231 * userID != "" ... UserID set equal to 'userID'
3233 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3234 * by this function. This function should use its own wxDb instance
3235 * to avoid undesired unbinding of columns.
3238 wxASSERT(fileName
.Length());
3242 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
3243 wxString tblNameSave
;
3244 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
3246 wxChar typeName
[30+1];
3247 SDWORD precision
, length
;
3249 FILE *fp
= wxFopen(fileName
.c_str(),wxT("wt"));
3253 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3256 convertUserID(userID
,UserID
);
3258 if (!UserID
.IsEmpty() &&
3259 Dbms() != dbmsMY_SQL
&&
3260 Dbms() != dbmsACCESS
&&
3261 Dbms() != dbmsINTERBASE
&&
3262 Dbms() != dbmsMS_SQL_SERVER
)
3264 retcode
= SQLColumns(hstmt
,
3265 NULL
, 0, // All qualifiers
3266 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3267 NULL
, 0, // All tables
3268 NULL
, 0); // All columns
3272 retcode
= SQLColumns(hstmt
,
3273 NULL
, 0, // All qualifiers
3274 NULL
, 0, // User specified
3275 NULL
, 0, // All tables
3276 NULL
, 0); // All columns
3278 if (retcode
!= SQL_SUCCESS
)
3280 DispAllErrors(henv
, hdbc
, hstmt
);
3286 tblNameSave
.Empty();
3291 retcode
= SQLFetch(hstmt
);
3292 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3295 GetData(3,SQL_C_CHAR
, (UCHAR
*) tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3296 GetData(4,SQL_C_CHAR
, (UCHAR
*) colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3297 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
, 0, &cb
);
3298 GetData(6,SQL_C_CHAR
, (UCHAR
*) typeName
, sizeof(typeName
), &cb
);
3299 GetData(7,SQL_C_SLONG
, (UCHAR
*)&precision
, 0, &cb
);
3300 GetData(8,SQL_C_SLONG
, (UCHAR
*)&length
, 0, &cb
);
3302 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3305 wxFputs(wxT("\n"), fp
);
3306 wxFputs(wxT("================================ "), fp
);
3307 wxFputs(wxT("================================ "), fp
);
3308 wxFputs(wxT("===================== "), fp
);
3309 wxFputs(wxT("========= "), fp
);
3310 wxFputs(wxT("=========\n"), fp
);
3311 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3312 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3313 wxFputs(outStr
.c_str(), fp
);
3314 wxFputs(wxT("================================ "), fp
);
3315 wxFputs(wxT("================================ "), fp
);
3316 wxFputs(wxT("===================== "), fp
);
3317 wxFputs(wxT("========= "), fp
);
3318 wxFputs(wxT("=========\n"), fp
);
3319 tblNameSave
= tblName
;
3322 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3323 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3324 if (wxFputs(outStr
.c_str(), fp
) == EOF
)
3326 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3333 if (retcode
!= SQL_NO_DATA_FOUND
)
3334 DispAllErrors(henv
, hdbc
, hstmt
);
3336 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3339 return(retcode
== SQL_NO_DATA_FOUND
);
3341 } // wxDb::Catalog()
3344 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3346 * Table name can refer to a table, view, alias or synonym. Returns TRUE
3347 * if the object exists in the database. This function does not indicate
3348 * whether or not the user has privleges to query or perform other functions
3351 * userID is evaluated in the following manner:
3352 * userID == NULL ... UserID is ignored
3353 * userID == "" ... UserID set equal to 'this->uid'
3354 * userID != "" ... UserID set equal to 'userID'
3357 wxASSERT(tableName
.Length());
3361 if (Dbms() == dbmsDBASE
)
3364 if (tablePath
.Length())
3365 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3367 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3370 exists
= wxFileExists(dbName
);
3375 convertUserID(userID
,UserID
);
3377 TableName
= tableName
;
3378 // Oracle and Interbase table names are uppercase only, so force
3379 // the name to uppercase just in case programmer forgot to do this
3380 if ((Dbms() == dbmsORACLE
) ||
3381 (Dbms() == dbmsINTERBASE
))
3382 TableName
= TableName
.Upper();
3384 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3387 // Some databases cannot accept a user name when looking up table names,
3388 // so we use the call below that leaves out the user name
3389 if (!UserID
.IsEmpty() &&
3390 Dbms() != dbmsMY_SQL
&&
3391 Dbms() != dbmsACCESS
&&
3392 Dbms() != dbmsMS_SQL_SERVER
&&
3393 Dbms() != dbmsDB2
&&
3394 Dbms() != dbmsINTERBASE
&&
3395 Dbms() != dbmsPERVASIVE_SQL
)
3397 retcode
= SQLTables(hstmt
,
3398 NULL
, 0, // All qualifiers
3399 (SQLTCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3400 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3401 NULL
, 0); // All table types
3405 retcode
= SQLTables(hstmt
,
3406 NULL
, 0, // All qualifiers
3407 NULL
, 0, // All owners
3408 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3409 NULL
, 0); // All table types
3411 if (retcode
!= SQL_SUCCESS
)
3412 return(DispAllErrors(henv
, hdbc
, hstmt
));
3414 retcode
= SQLFetch(hstmt
);
3415 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3417 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3418 return(DispAllErrors(henv
, hdbc
, hstmt
));
3421 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3425 } // wxDb::TableExists()
3428 /********** wxDb::TablePrivileges() **********/
3429 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3430 const wxChar
*schema
, const wxString
&WXUNUSED(tablePath
))
3432 wxASSERT(tableName
.Length());
3434 wxDbTablePrivilegeInfo result
;
3438 // We probably need to be able to dynamically set this based on
3439 // the driver type, and state.
3440 wxChar curRole
[]=wxT("public");
3444 wxString UserID
,Schema
;
3445 convertUserID(userID
,UserID
);
3446 convertUserID(schema
,Schema
);
3448 TableName
= tableName
;
3449 // Oracle and Interbase table names are uppercase only, so force
3450 // the name to uppercase just in case programmer forgot to do this
3451 if ((Dbms() == dbmsORACLE
) ||
3452 (Dbms() == dbmsINTERBASE
))
3453 TableName
= TableName
.Upper();
3455 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3457 // Some databases cannot accept a user name when looking up table names,
3458 // so we use the call below that leaves out the user name
3459 if (!Schema
.IsEmpty() &&
3460 Dbms() != dbmsMY_SQL
&&
3461 Dbms() != dbmsACCESS
&&
3462 Dbms() != dbmsMS_SQL_SERVER
)
3464 retcode
= SQLTablePrivileges(hstmt
,
3466 (SQLTCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3467 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3471 retcode
= SQLTablePrivileges(hstmt
,
3474 (SQLTCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3477 #ifdef DBDEBUG_CONSOLE
3478 wxFprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3481 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3482 return(DispAllErrors(henv
, hdbc
, hstmt
));
3484 bool failed
= FALSE
;
3485 retcode
= SQLFetch(hstmt
);
3486 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3488 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3491 if (!failed
&& SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3494 if (!failed
&& SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3497 if (!failed
&& SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3500 if (!failed
&& SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3503 if (!failed
&& SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3506 if (!failed
&& SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3511 return(DispAllErrors(henv
, hdbc
, hstmt
));
3513 #ifdef DBDEBUG_CONSOLE
3514 wxFprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3515 result
.privilege
,result
.tableOwner
,result
.tableName
,
3516 result
.grantor
, result
.grantee
);
3519 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3521 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3525 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3526 !wxStrcmp(result
.privilege
,priv
))
3528 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3532 if (!wxStrcmp(result
.grantee
,curRole
) &&
3533 !wxStrcmp(result
.privilege
,priv
))
3535 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3539 retcode
= SQLFetch(hstmt
);
3542 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3545 } // wxDb::TablePrivileges
3548 const wxString
wxDb::SQLTableName(const wxChar
*tableName
)
3552 if (Dbms() == dbmsACCESS
)
3553 TableName
= _T("\"");
3554 TableName
+= tableName
;
3555 if (Dbms() == dbmsACCESS
)
3556 TableName
+= _T("\"");
3559 } // wxDb::SQLTableName()
3562 const wxString
wxDb::SQLColumnName(const wxChar
*colName
)
3566 if (Dbms() == dbmsACCESS
)
3569 if (Dbms() == dbmsACCESS
)
3570 ColName
+= _T("\"");
3573 } // wxDb::SQLColumnName()
3576 /********** wxDb::SetSqlLogging() **********/
3577 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3579 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3580 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3582 if (state
== sqlLogON
)
3586 fpSqlLog
= wxFopen(filename
, (append
? wxT("at") : wxT("wt")));
3587 if (fpSqlLog
== NULL
)
3595 if (fclose(fpSqlLog
))
3601 sqlLogState
= state
;
3604 } // wxDb::SetSqlLogging()
3607 /********** wxDb::WriteSqlLog() **********/
3608 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3610 wxASSERT(logMsg
.Length());
3612 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3615 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3617 if (wxFputs(logMsg
, fpSqlLog
) == EOF
)
3619 if (wxFputs(wxT("\n"), fpSqlLog
) == EOF
)
3624 } // wxDb::WriteSqlLog()
3627 /********** wxDb::Dbms() **********/
3628 wxDBMS
wxDb::Dbms(void)
3630 * Be aware that not all database engines use the exact same syntax, and not
3631 * every ODBC compliant database is compliant to the same level of compliancy.
3632 * Some manufacturers support the minimum Level 1 compliancy, and others up
3633 * through Level 3. Others support subsets of features for levels above 1.
3635 * If you find an inconsistency between the wxDb class and a specific database
3636 * engine, and an identifier to this section, and special handle the database in
3637 * the area where behavior is non-conforming with the other databases.
3640 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3641 * ---------------------------------------------------
3644 * - Currently the only database supported by the class to support VIEWS
3647 * - Does not support the SQL_TIMESTAMP structure
3648 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3649 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3650 * is TRUE. The user must create ALL indexes from their program.
3651 * - Table names can only be 8 characters long
3652 * - Column names can only be 10 characters long
3655 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3656 * after every table name involved in the query/join if that tables matching record(s)
3658 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3660 * SYBASE (Enterprise)
3661 * - If a column is part of the Primary Key, the column cannot be NULL
3662 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3665 * - If a column is part of the Primary Key, the column cannot be NULL
3666 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3667 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3668 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3669 * column definition if it is not defined correctly, but it is experimental
3670 * - Does not support sub-queries in SQL statements
3673 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3674 * - Does not support sub-queries in SQL statements
3677 * - Primary keys must be declared as NOT NULL
3678 * - Table and index names must not be longer than 13 characters in length (technically
3679 * table names can be up to 18 characters, but the primary index is created using the
3680 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3685 * - Columns that are part of primary keys must be defined as being NOT NULL
3686 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3687 * column definition if it is not defined correctly, but it is experimental
3690 // Should only need to do this once for each new database connection
3691 // so return the value we already determined it to be to save time
3692 // and lots of string comparisons
3693 if (dbmsType
!= dbmsUNIDENTIFIED
)
3696 wxChar baseName
[25+1];
3697 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3700 // RGG 20001025 : add support for Interbase
3701 // GT : Integrated to base classes on 20001121
3702 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3703 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3705 // BJO 20000428 : add support for Virtuoso
3706 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3707 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3709 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3710 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3712 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3713 // connected through an OpenLink driver.
3714 // Is it also returned by Sybase Adapatitve server?
3715 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3716 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3718 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3719 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3720 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3722 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3725 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3726 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3729 if (!wxStricmp(baseName
,wxT("PostgreSQL"))) // v6.5.0
3730 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3733 if (!wxStricmp(baseName
,wxT("Pervasive")))
3734 return((wxDBMS
)(dbmsType
= dbmsPERVASIVE_SQL
));
3737 if (!wxStricmp(baseName
,wxT("Informix")))
3738 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3741 if (!wxStricmp(baseName
,wxT("Oracle")))
3742 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3743 if (!wxStricmp(baseName
,wxT("ACCESS")))
3744 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3745 if (!wxStricmp(baseName
,wxT("Sybase")))
3746 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3749 if (!wxStricmp(baseName
,wxT("DBASE")))
3750 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3751 if (!wxStricmp(baseName
,wxT("xBase")))
3752 return((wxDBMS
)(dbmsType
= dbmsXBASE_SEQUITER
));
3753 if (!wxStricmp(baseName
,wxT("MySQL")))
3754 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3757 if (!wxStricmp(baseName
,wxT("DB2")))
3758 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3760 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3765 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3766 int dataType
, ULONG columnLength
,
3767 const wxString
&optionalParam
)
3769 wxASSERT(tableName
.Length());
3770 wxASSERT(columnName
.Length());
3771 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3772 dataType
!= DB_DATA_TYPE_VARCHAR
);
3774 // Must specify a columnLength if modifying a VARCHAR type column
3775 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3778 wxString dataTypeName
;
3780 wxString alterSlashModify
;
3784 case DB_DATA_TYPE_VARCHAR
:
3785 dataTypeName
= typeInfVarchar
.TypeName
;
3787 case DB_DATA_TYPE_INTEGER
:
3788 dataTypeName
= typeInfInteger
.TypeName
;
3790 case DB_DATA_TYPE_FLOAT
:
3791 dataTypeName
= typeInfFloat
.TypeName
;
3793 case DB_DATA_TYPE_DATE
:
3794 dataTypeName
= typeInfDate
.TypeName
;
3796 case DB_DATA_TYPE_BLOB
:
3797 dataTypeName
= typeInfBlob
.TypeName
;
3803 // Set the modify or alter syntax depending on the type of database connected to
3807 alterSlashModify
= _T("MODIFY");
3809 case dbmsMS_SQL_SERVER
:
3810 alterSlashModify
= _T("ALTER COLUMN");
3812 case dbmsUNIDENTIFIED
:
3814 case dbmsSYBASE_ASA
:
3815 case dbmsSYBASE_ASE
:
3820 case dbmsXBASE_SEQUITER
:
3822 alterSlashModify
= _T("MODIFY");
3826 // create the SQL statement
3827 if ( Dbms() == dbmsMY_SQL
)
3829 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3830 columnName
.c_str(), dataTypeName
.c_str());
3834 sqlStmt
.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3835 columnName
.c_str(), dataTypeName
.c_str());
3838 // For varchars only, append the size of the column
3839 if (dataType
== DB_DATA_TYPE_VARCHAR
&&
3840 (Dbms() != dbmsMY_SQL
|| dataTypeName
!= _T("text")))
3843 s
.Printf(wxT("(%lu)"), columnLength
);
3847 // for passing things like "NOT NULL"
3848 if (optionalParam
.Length())
3850 sqlStmt
+= wxT(" ");
3851 sqlStmt
+= optionalParam
;
3854 return ExecSql(sqlStmt
);
3856 } // wxDb::ModifyColumn()
3859 /********** wxDbGetConnection() **********/
3860 wxDb WXDLLIMPEXP_ODBC
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3864 // Used to keep a pointer to a DB connection that matches the requested
3865 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3866 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3867 // rather than having to re-query the datasource to get all the values
3868 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3869 wxDb
*matchingDbConnection
= NULL
;
3871 // Scan the linked list searching for an available database connection
3872 // that's already been opened but is currently not in use.
3873 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3875 // The database connection must be for the same datasource
3876 // name and must currently not be in use.
3878 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
))
3880 if (pDbConfig
->UseConnectionStr())
3882 if (pList
->PtrDb
->OpenedWithConnectionString() &&
3883 (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
)))
3885 // Found a free connection
3886 pList
->Free
= FALSE
;
3887 return(pList
->PtrDb
);
3892 if (!pList
->PtrDb
->OpenedWithConnectionString() &&
3893 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
)))
3895 // Found a free connection
3896 pList
->Free
= FALSE
;
3897 return(pList
->PtrDb
);
3902 if (pDbConfig
->UseConnectionStr())
3904 if (!wxStrcmp(pDbConfig
->GetConnectionStr(), pList
->ConnectionStr
))
3905 matchingDbConnection
= pList
->PtrDb
;
3909 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3910 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3911 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3912 matchingDbConnection
= pList
->PtrDb
;
3916 // No available connections. A new connection must be made and
3917 // appended to the end of the linked list.
3920 // Find the end of the list
3921 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3922 // Append a new list item
3923 pList
->PtrNext
= new wxDbList
;
3924 pList
->PtrNext
->PtrPrev
= pList
;
3925 pList
= pList
->PtrNext
;
3929 // Create the first node on the list
3930 pList
= PtrBegDbList
= new wxDbList
;
3934 // Initialize new node in the linked list
3936 pList
->Free
= FALSE
;
3937 pList
->Dsn
= pDbConfig
->GetDsn();
3938 pList
->Uid
= pDbConfig
->GetUserID();
3939 pList
->AuthStr
= pDbConfig
->GetPassword();
3940 pList
->ConnectionStr
= pDbConfig
->GetConnectionStr();
3942 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3946 if (!matchingDbConnection
)
3948 if (pDbConfig
->UseConnectionStr())
3950 opened
= pList
->PtrDb
->Open(pDbConfig
->GetConnectionStr());
3954 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3958 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3960 // Connect to the datasource
3963 pList
->PtrDb
->setCached(TRUE
); // Prevent a user from deleting a cached connection
3964 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
, SQLLOGfn
, TRUE
);
3965 return(pList
->PtrDb
);
3967 else // Unable to connect, destroy list item
3970 pList
->PtrPrev
->PtrNext
= 0;
3972 PtrBegDbList
= 0; // Empty list again
3974 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3975 pList
->PtrDb
->Close(); // Close the wxDb object
3976 delete pList
->PtrDb
; // Deletes the wxDb object
3977 delete pList
; // Deletes the linked list object
3981 } // wxDbGetConnection()
3984 /********** wxDbFreeConnection() **********/
3985 bool WXDLLIMPEXP_ODBC
wxDbFreeConnection(wxDb
*pDb
)
3989 // Scan the linked list searching for the database connection
3990 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3992 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3993 return (pList
->Free
= TRUE
);
3996 // Never found the database object, return failure
3999 } // wxDbFreeConnection()
4002 /********** wxDbCloseConnections() **********/
4003 void WXDLLIMPEXP_ODBC
wxDbCloseConnections(void)
4005 wxDbList
*pList
, *pNext
;
4007 // Traverse the linked list closing database connections and freeing memory as I go.
4008 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
4010 pNext
= pList
->PtrNext
; // Save the pointer to next
4011 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
4012 pList
->PtrDb
->Close(); // Close the wxDb object
4013 pList
->PtrDb
->setCached(FALSE
); // Allows deletion of the wxDb instance
4014 delete pList
->PtrDb
; // Deletes the wxDb object
4015 delete pList
; // Deletes the linked list object
4018 // Mark the list as empty
4021 } // wxDbCloseConnections()
4024 /********** wxDbConnectionsInUse() **********/
4025 int WXDLLIMPEXP_ODBC
wxDbConnectionsInUse(void)
4030 // Scan the linked list counting db connections that are currently in use
4031 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4033 if (pList
->Free
== FALSE
)
4039 } // wxDbConnectionsInUse()
4043 /********** wxDbLogExtendedErrorMsg() **********/
4044 // DEBUG ONLY function
4045 const wxChar WXDLLIMPEXP_ODBC
*wxDbLogExtendedErrorMsg(const wxChar
*userText
,
4047 const wxChar
*ErrFile
,
4050 static wxString msg
;
4055 if (ErrFile
|| ErrLine
)
4057 msg
+= wxT("File: ");
4059 msg
+= wxT(" Line: ");
4060 tStr
.Printf(wxT("%d"),ErrLine
);
4061 msg
+= tStr
.c_str();
4065 msg
.Append (wxT("\nODBC errors:\n"));
4068 // Display errors for this connection
4070 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
4072 if (pDb
->errorList
[i
])
4074 msg
.Append(pDb
->errorList
[i
]);
4075 if (wxStrcmp(pDb
->errorList
[i
],wxT("")) != 0)
4076 msg
.Append(wxT("\n"));
4077 // Clear the errmsg buffer so the next error will not
4078 // end up showing the previous error that have occurred
4079 wxStrcpy(pDb
->errorList
[i
],wxT(""));
4084 wxLogDebug(msg
.c_str());
4087 } // wxDbLogExtendedErrorMsg()
4090 /********** wxDbSqlLog() **********/
4091 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
4093 bool append
= FALSE
;
4096 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
4098 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
4103 SQLLOGstate
= state
;
4104 SQLLOGfn
= filename
;
4112 /********** wxDbCreateDataSource() **********/
4113 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
4114 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
4116 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4117 * Very rudimentary creation of an ODBC data source.
4119 * ODBC driver must be ODBC 3.0 compliant to use this function
4124 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4130 dsnLocation
= ODBC_ADD_SYS_DSN
;
4132 dsnLocation
= ODBC_ADD_DSN
;
4134 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4135 // so that is why I used it, as wxString does not deal well with
4136 // embedded nulls in strings
4137 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
4139 // Replace the separator from above with the '\0' seperator needed
4140 // by the SQLConfigDataSource() function
4144 k
= setupStr
.Find((wxChar
)2,TRUE
);
4145 if (k
!= wxNOT_FOUND
)
4146 setupStr
[(UINT
)k
] = wxT('\0');
4148 while (k
!= wxNOT_FOUND
);
4150 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
4151 driverName
, setupStr
.c_str());
4153 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
4155 // check for errors caused by ConfigDSN based functions
4158 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
4159 errMsg
[0] = wxT('\0');
4161 // This function is only supported in ODBC drivers v3.0 compliant and above
4162 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
4165 #ifdef DBDEBUG_CONSOLE
4166 // When run in console mode, use standard out to display errors.
4167 cout
<< errMsg
<< endl
;
4168 cout
<< wxT("Press any key to continue...") << endl
;
4170 #endif // DBDEBUG_CONSOLE
4173 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
4174 #endif // __WXDEBUG__
4180 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4181 // necessary to use this function, so this function is not supported
4183 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4186 #endif // __VISUALC__
4190 } // wxDbCreateDataSource()
4194 /********** wxDbGetDataSource() **********/
4195 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
4196 SWORD DsDescMax
, UWORD direction
)
4198 * Dsn and DsDesc will contain the data source name and data source
4199 * description upon return
4204 if (SQLDataSources(henv
, direction
, (SQLTCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
4205 (SQLTCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
4210 } // wxDbGetDataSource()
4213 // Change this to 0 to remove use of all deprecated functions
4214 #if wxODBC_BACKWARD_COMPATABILITY
4215 /********************************************************************
4216 ********************************************************************
4218 * The following functions are all DEPRECATED and are included for
4219 * backward compatability reasons only
4221 ********************************************************************
4222 ********************************************************************/
4223 bool SqlLog(sqlLog state
, const wxChar
*filename
)
4225 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
4227 /***** DEPRECATED: use wxGetDataSource() *****/
4228 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
4231 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
4233 /***** DEPRECATED: use wxDbGetConnection() *****/
4234 wxDb WXDLLIMPEXP_ODBC
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
4236 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
4238 /***** DEPRECATED: use wxDbFreeConnection() *****/
4239 bool WXDLLIMPEXP_ODBC
FreeDbConnection(wxDb
*pDb
)
4241 return wxDbFreeConnection(pDb
);
4243 /***** DEPRECATED: use wxDbCloseConnections() *****/
4244 void WXDLLIMPEXP_ODBC
CloseDbConnections(void)
4246 wxDbCloseConnections();
4248 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4249 int WXDLLIMPEXP_ODBC
NumberDbConnectionsInUse(void)
4251 return wxDbConnectionsInUse();