1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
7 // Modified by: George Tasker
9 // Mark Johnson, wxWindows@mj10777.de
11 // -Added support for SQL statement logging and database cataloging
13 // -Added QUERY_ONLY mode support to reduce default number of cursors
14 // -Added additional SQL logging code
15 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
16 // -Set ODBC option to only read committed writes to the DB so all
17 // databases operate the same in that respect
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence, plus:
22 // Notice: This class library and its intellectual design are free of charge for use,
23 // modification, enhancement, debugging under the following conditions:
24 // 1) These classes may only be used as part of the implementation of a
25 // wxWindows-based application
26 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
27 // user groups free of all charges for use with the wxWindows library.
28 // 3) These classes may not be distributed as part of any other class library,
29 // DLL, text (written or electronic), other than a complete distribution of
30 // the wxWindows GUI development toolkit.
31 ///////////////////////////////////////////////////////////////////////////////
38 #include "wx/wxprec.h"
41 // Use this line for wxWindows v1.x
43 // Use this line for wxWindows v2.x
44 #include "wx/version.h"
46 #if wxMAJOR_VERSION == 2
48 #pragma implementation "db.h"
52 #ifdef DBDEBUG_CONSOLE
53 #include "wx/ioswrap.h"
60 #if wxMAJOR_VERSION == 2
62 #include "wx/string.h"
63 #include "wx/object.h"
66 #include "wx/msgdlg.h"
69 #include "wx/filefn.h"
70 #include "wx/wxchar.h"
74 #if wxMAJOR_VERSION == 1
75 # if defined(wx_msw) || defined(wx_x)
93 #if wxMAJOR_VERSION == 1
95 #elif wxMAJOR_VERSION == 2
99 WXDLLEXPORT_DATA(wxDbList
*) PtrBegDbList
= 0;
102 wxChar
const *SQL_LOG_FILENAME
= wxT("sqllog.txt");
103 wxChar
const *SQL_CATALOG_FILENAME
= wxT("catalog.txt");
106 extern wxList TablesInUse
;
109 // SQL Log defaults to be used by GetDbConnection
110 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
112 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
114 // The wxDb::errorList is copied to this variable when the wxDb object
115 // is closed. This way, the error list is still available after the
116 // database object is closed. This is necessary if the database
117 // connection fails so the calling application can show the operator
118 // why the connection failed. Note: as each wxDb object is closed, it
119 // will overwrite the errors of the previously destroyed wxDb object in
120 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
122 wxChar DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
124 // This type defines the return row-struct form
125 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
128 wxChar tableQual
[128+1];
129 wxChar tableOwner
[128+1];
130 wxChar tableName
[128+1];
131 wxChar grantor
[128+1];
132 wxChar grantee
[128+1];
133 wxChar privilege
[128+1];
134 wxChar grantable
[3+1];
135 } wxDbTablePrivilegeInfo
;
138 /********** wxDbConnectInf Constructor - form 1 **********/
139 wxDbConnectInf::wxDbConnectInf()
142 freeHenvOnDestroy
= FALSE
;
148 /********** wxDbConnectInf Constructor - form 2 **********/
149 wxDbConnectInf::wxDbConnectInf(HENV henv
, const wxString
&dsn
, const wxString
&userID
,
150 const wxString
&password
, const wxString
&defaultDir
,
151 const wxString
&fileType
, const wxString
&description
)
154 freeHenvOnDestroy
= FALSE
;
165 SetPassword(password
);
166 SetDescription(description
);
167 SetFileType(fileType
);
168 SetDefaultDir(defaultDir
);
169 } // wxDbConnectInf Constructor
172 wxDbConnectInf::~wxDbConnectInf()
174 if (freeHenvOnDestroy
)
178 } // wxDbConnectInf Destructor
182 /********** wxDbConnectInf::Initialize() **********/
183 bool wxDbConnectInf::Initialize()
185 freeHenvOnDestroy
= FALSE
;
187 if (freeHenvOnDestroy
&& Henv
)
199 } // wxDbConnectInf::Initialize()
202 /********** wxDbConnectInf::AllocHenv() **********/
203 bool wxDbConnectInf::AllocHenv()
205 // This is here to help trap if you are getting a new henv
206 // without releasing an existing henv
209 // Initialize the ODBC Environment for Database Operations
210 if (SQLAllocEnv(&Henv
) != SQL_SUCCESS
)
212 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
216 freeHenvOnDestroy
= TRUE
;
219 } // wxDbConnectInf::AllocHenv()
222 void wxDbConnectInf::FreeHenv()
230 freeHenvOnDestroy
= FALSE
;
232 } // wxDbConnectInf::FreeHenv()
235 void wxDbConnectInf::SetDsn(const wxString
&dsn
)
237 wxASSERT(dsn
.Length() < sizeof(Dsn
));
240 } // wxDbConnectInf::SetDsn()
243 void wxDbConnectInf::SetUserID(const wxString
&uid
)
245 wxASSERT(uid
.Length() < sizeof(Uid
));
247 } // wxDbConnectInf::SetUserID()
250 void wxDbConnectInf::SetPassword(const wxString
&password
)
252 wxASSERT(password
.Length() < sizeof(AuthStr
));
254 wxStrcpy(AuthStr
,password
);
255 } // wxDbConnectInf::SetPassword()
259 /********** wxDbColFor Constructor **********/
260 wxDbColFor::wxDbColFor()
263 } // wxDbColFor::wxDbColFor()
266 wxDbColFor::~wxDbColFor()
268 } // wxDbColFor::~wxDbColFor()
271 /********** wxDbColFor::Initialize() **********/
272 void wxDbColFor::Initialize()
282 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
285 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
286 } // wxDbColFor::Initialize()
289 /********** wxDbColFor::Format() **********/
290 int wxDbColFor::Format(int Nation
, int dbDataType
, SWORD sqlDataType
,
291 short columnSize
, short decimalDigits
)
293 // ----------------------------------------------------------------------------------------
294 // -- 19991224 : mj10777 : Create
295 // There is still a lot of work to do here, but it is a start
296 // It handles all the basic data-types that I have run into up to now
297 // The main work will have be with Dates and float Formatting
298 // (US 1,000.00 ; EU 1.000,00)
299 // There are wxWindow plans for locale support and the new wxDateTime. If
300 // they define some constants (wxEUROPEAN) that can be gloably used,
301 // they should be used here.
302 // ----------------------------------------------------------------------------------------
303 // There should also be a function to scan in a string to fill the variable
304 // ----------------------------------------------------------------------------------------
306 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
307 i_dbDataType
= dbDataType
;
308 i_sqlDataType
= sqlDataType
;
309 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
311 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
313 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
314 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
315 if ((i_sqlDataType
== SQL_C_DATE
) || (i_sqlDataType
== SQL_C_TIMESTAMP
))
316 i_dbDataType
= DB_DATA_TYPE_DATE
;
317 if (i_sqlDataType
== SQL_C_BIT
)
318 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
319 if (i_sqlDataType
== SQL_NUMERIC
)
320 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
321 if (i_sqlDataType
== SQL_REAL
)
322 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
323 if (i_sqlDataType
== SQL_C_BINARY
)
324 i_dbDataType
= DB_DATA_TYPE_BLOB
;
327 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
329 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
332 switch(i_dbDataType
) // TBD: Still a lot of proper formatting to do
334 case DB_DATA_TYPE_VARCHAR
:
337 case DB_DATA_TYPE_INTEGER
:
340 case DB_DATA_TYPE_FLOAT
:
341 if (decimalDigits
== 0)
344 tempStr
.Printf(wxT("%s%d.%d"),tempStr
.c_str(),columnSize
,decimalDigits
);
345 s_Field
.Printf(wxT("%sf"),tempStr
.c_str());
347 case DB_DATA_TYPE_DATE
:
348 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
350 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
352 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
354 s_Field
= wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
356 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
358 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
360 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
362 s_Field
= wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
364 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
366 s_Field
= wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
369 case DB_DATA_TYPE_BLOB
:
370 s_Field
.Printf(wxT("Unable to format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
373 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
377 } // wxDbColFor::Format()
381 /********** wxDbColInf Constructor **********/
382 wxDbColInf::wxDbColInf()
385 } // wxDbColInf::wxDbColInf()
388 /********** wxDbColInf Destructor ********/
389 wxDbColInf::~wxDbColInf()
394 } // wxDbColInf::~wxDbColInf()
397 bool wxDbColInf::Initialize()
419 } // wxDbColInf::Initialize()
422 /********** wxDbTableInf Constructor ********/
423 wxDbTableInf::wxDbTableInf()
426 } // wxDbTableInf::wxDbTableInf()
429 /********** wxDbTableInf Constructor ********/
430 wxDbTableInf::~wxDbTableInf()
435 } // wxDbTableInf::~wxDbTableInf()
438 bool wxDbTableInf::Initialize()
447 } // wxDbTableInf::Initialize()
450 /********** wxDbInf Constructor *************/
454 } // wxDbInf::wxDbInf()
457 /********** wxDbInf Destructor *************/
463 } // wxDbInf::~wxDbInf()
466 /********** wxDbInf::Initialize() *************/
467 bool wxDbInf::Initialize()
475 } // wxDbInf::Initialize()
478 /********** wxDb Constructors **********/
479 wxDb::wxDb(const HENV
&aHenv
, bool FwdOnlyCursors
)
481 // Copy the HENV into the db class
483 fwdOnlyCursors
= FwdOnlyCursors
;
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
553 } // wxDb::initialize()
556 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
558 // NOTE: Return value from this function MUST be copied
559 // immediately, as the value is not good after
560 // this function has left scope.
562 const wxChar
*wxDb::convertUserID(const wxChar
*userID
, wxString
&UserID
)
566 if (!wxStrlen(userID
))
574 // dBase does not use user names, and some drivers fail if you try to pass one
575 if (Dbms() == dbmsDBASE
)
578 // Oracle user names may only be in uppercase, so force
579 // the name to uppercase
580 if (Dbms() == dbmsORACLE
)
581 UserID
= UserID
.Upper();
583 return UserID
.c_str();
584 } // wxDb::convertUserID()
587 /********** wxDb::Open() **********/
588 bool wxDb::Open(const wxString
&Dsn
, const wxString
&Uid
, const wxString
&AuthStr
)
590 wxASSERT(Dsn
.Length());
597 if (!FwdOnlyCursors())
599 // Specify that the ODBC cursor library be used, if needed. This must be
600 // specified before the connection is made.
601 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
603 #ifdef DBDEBUG_CONSOLE
604 if (retcode
== SQL_SUCCESS
)
605 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
607 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
611 // Connect to the data source
612 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
613 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
614 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
617 if (retcode == SQL_SUCCESS_WITH_INFO)
618 DispAllErrors(henv, hdbc);
619 else if (retcode != SQL_SUCCESS)
620 return(DispAllErrors(henv, hdbc));
622 if (retcode == SQL_ERROR)
623 return(DispAllErrors(henv, hdbc));
625 if ((retcode
!= SQL_SUCCESS
) &&
626 (retcode
!= SQL_SUCCESS_WITH_INFO
))
627 return(DispAllErrors(henv
, hdbc
));
630 If using Intersolv branded ODBC drivers, this is the place where you would substitute
631 your branded driver license information
633 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
634 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
637 // Mark database as open
640 // Allocate a statement handle for the database connection
641 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
642 return(DispAllErrors(henv
, hdbc
));
644 // Set Connection Options
645 if (!setConnectionOptions())
648 // Query the data source for inf. about itself
652 // Query the data source regarding data type information
655 // The way it was determined which SQL data types to use was by calling SQLGetInfo
656 // for all of the possible SQL data types to see which ones were supported. If
657 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
658 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
659 // types I've selected below will not alway's be what we want. These are just
660 // what happened to work against an Oracle 7/Intersolv combination. The following is
661 // a complete list of the results I got back against the Oracle 7 database:
663 // SQL_BIGINT SQL_NO_DATA_FOUND
664 // SQL_BINARY SQL_NO_DATA_FOUND
665 // SQL_BIT SQL_NO_DATA_FOUND
666 // SQL_CHAR type name = 'CHAR', Precision = 255
667 // SQL_DATE SQL_NO_DATA_FOUND
668 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
669 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
670 // SQL_FLOAT SQL_NO_DATA_FOUND
671 // SQL_INTEGER SQL_NO_DATA_FOUND
672 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
673 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
674 // SQL_NUMERIC SQL_NO_DATA_FOUND
675 // SQL_REAL SQL_NO_DATA_FOUND
676 // SQL_SMALLINT SQL_NO_DATA_FOUND
677 // SQL_TIME SQL_NO_DATA_FOUND
678 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
679 // SQL_VARBINARY type name = 'RAW', Precision = 255
680 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
681 // =====================================================================
682 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
684 // SQL_VARCHAR type name = 'TEXT', Precision = 255
685 // SQL_TIMESTAMP type name = 'DATETIME'
686 // SQL_DECIMAL SQL_NO_DATA_FOUND
687 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
688 // SQL_FLOAT SQL_NO_DATA_FOUND
689 // SQL_REAL type name = 'SINGLE', Precision = 7
690 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
691 // SQL_INTEGER type name = 'LONG', Precision = 10
693 // VARCHAR = Variable length character string
694 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
695 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
698 typeInfVarchar
.FsqlType
= SQL_CHAR
;
700 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
703 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
704 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
705 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
706 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
707 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
710 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
712 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
714 typeInfFloat
.FsqlType
= SQL_FLOAT
;
716 typeInfFloat
.FsqlType
= SQL_REAL
;
718 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
721 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
723 // If SQL_INTEGER is not supported, use the floating point
724 // data type to store integers as well as floats
725 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
728 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
731 typeInfInteger
.FsqlType
= SQL_INTEGER
;
734 if (Dbms() != dbmsDBASE
)
736 if (! getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
739 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
743 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
746 typeInfDate
.FsqlType
= SQL_DATE
;
749 if (!getDataTypeInfo(SQL_LONGVARBINARY
, typeInfBlob
))
751 if (!getDataTypeInfo(SQL_VARBINARY
,typeInfBlob
))
754 typeInfBlob
.FsqlType
= SQL_VARBINARY
;
757 typeInfBlob
.FsqlType
= SQL_LONGVARBINARY
;
759 //typeInfBlob.TypeName = "BLOB";
761 #ifdef DBDEBUG_CONSOLE
762 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
763 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
764 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
765 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
766 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
770 // Completed Successfully
776 bool wxDb::Open(wxDbConnectInf
*dbConnectInf
)
778 return Open(dbConnectInf
->GetDsn(), dbConnectInf
->GetUserID(),
779 dbConnectInf
->GetPassword());
783 bool wxDb::Open(wxDb
*copyDb
)
785 dsn
= copyDb
->GetDatasourceName();
786 uid
= copyDb
->GetUsername();
787 authStr
= copyDb
->GetPassword();
791 if (!FwdOnlyCursors())
793 // Specify that the ODBC cursor library be used, if needed. This must be
794 // specified before the connection is made.
795 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
797 #ifdef DBDEBUG_CONSOLE
798 if (retcode
== SQL_SUCCESS
)
799 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl
;
801 cout
<< wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl
;
805 // Connect to the data source
806 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
.c_str(), SQL_NTS
,
807 (UCHAR FAR
*) uid
.c_str(), SQL_NTS
,
808 (UCHAR FAR
*) authStr
.c_str(), SQL_NTS
);
810 if (retcode
== SQL_ERROR
)
811 return(DispAllErrors(henv
, hdbc
));
814 If using Intersolv branded ODBC drivers, this is the place where you would substitute
815 your branded driver license information
817 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
818 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
821 // Mark database as open
824 // Allocate a statement handle for the database connection
825 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
826 return(DispAllErrors(henv
, hdbc
));
828 // Set Connection Options
829 if (!setConnectionOptions())
832 // Instead of Querying the data source for info about itself, it can just be copied
833 // from the wxDb instance that was passed in (copyDb).
834 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
835 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
836 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
837 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
838 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
839 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
840 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
841 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
842 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
843 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
844 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
845 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
846 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
847 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
848 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
849 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
850 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
851 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
852 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
853 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
854 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
855 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
856 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
857 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
858 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
859 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
860 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
861 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
862 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
863 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
864 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
866 // VARCHAR = Variable length character string
867 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
868 typeInfVarchar
.TypeName
= copyDb
->typeInfVarchar
.TypeName
;
869 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
870 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
871 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
874 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
875 typeInfFloat
.TypeName
= copyDb
->typeInfFloat
.TypeName
;
876 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
877 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
878 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
881 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
882 typeInfInteger
.TypeName
= copyDb
->typeInfInteger
.TypeName
;
883 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
884 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
885 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
888 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
889 typeInfDate
.TypeName
= copyDb
->typeInfDate
.TypeName
;
890 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
891 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
892 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
895 typeInfBlob
.FsqlType
= copyDb
->typeInfBlob
.FsqlType
;
896 typeInfBlob
.TypeName
= copyDb
->typeInfBlob
.TypeName
;
897 typeInfBlob
.Precision
= copyDb
->typeInfBlob
.Precision
;
898 typeInfBlob
.CaseSensitive
= copyDb
->typeInfBlob
.CaseSensitive
;
899 typeInfBlob
.MaximumScale
= copyDb
->typeInfBlob
.MaximumScale
;
901 #ifdef DBDEBUG_CONSOLE
902 cout
<< wxT("VARCHAR DATA TYPE: ") << typeInfVarchar
.TypeName
<< endl
;
903 cout
<< wxT("INTEGER DATA TYPE: ") << typeInfInteger
.TypeName
<< endl
;
904 cout
<< wxT("FLOAT DATA TYPE: ") << typeInfFloat
.TypeName
<< endl
;
905 cout
<< wxT("DATE DATA TYPE: ") << typeInfDate
.TypeName
<< endl
;
906 cout
<< wxT("BLOB DATA TYPE: ") << typeInfBlob
.TypeName
<< endl
;
910 // Completed Successfully
915 /********** wxDb::setConnectionOptions() **********/
916 bool wxDb::setConnectionOptions(void)
918 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
923 // I need to get the DBMS name here, because some of the connection options
924 // are database specific and need to call the Dbms() function.
925 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
926 return(DispAllErrors(henv
, hdbc
));
928 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
929 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
930 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
932 // By default, MS Sql Server closes cursors on commit and rollback. The following
933 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
934 // after a transaction. This is a driver specific option and is not part of the
935 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
936 // The database settings don't have any effect one way or the other.
937 if (Dbms() == dbmsMS_SQL_SERVER
)
939 const long SQL_PRESERVE_CURSORS
= 1204L;
940 const long SQL_PC_ON
= 1L;
941 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
944 // Display the connection options to verify them
945 #ifdef DBDEBUG_CONSOLE
947 cout
<< wxT("****** CONNECTION OPTIONS ******") << endl
;
949 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
950 return(DispAllErrors(henv
, hdbc
));
951 cout
<< wxT("AUTOCOMMIT: ") << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
953 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
954 return(DispAllErrors(henv
, hdbc
));
955 cout
<< wxT("ODBC CURSORS: ");
958 case(SQL_CUR_USE_IF_NEEDED
):
959 cout
<< wxT("SQL_CUR_USE_IF_NEEDED");
961 case(SQL_CUR_USE_ODBC
):
962 cout
<< wxT("SQL_CUR_USE_ODBC");
964 case(SQL_CUR_USE_DRIVER
):
965 cout
<< wxT("SQL_CUR_USE_DRIVER");
970 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
971 return(DispAllErrors(henv
, hdbc
));
972 cout
<< wxT("TRACING: ") << (l
== SQL_OPT_TRACE_OFF
? wxT("OFF") : wxT("ON")) << endl
;
977 // Completed Successfully
980 } // wxDb::setConnectionOptions()
983 /********** wxDb::getDbInfo() **********/
984 bool wxDb::getDbInfo(void)
989 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
990 return(DispAllErrors(henv
, hdbc
));
992 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
993 return(DispAllErrors(henv
, hdbc
));
995 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
996 return(DispAllErrors(henv
, hdbc
));
999 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1000 // causing database connectivity to fail in some cases.
1001 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
1003 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1004 return(DispAllErrors(henv
, hdbc
));
1006 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
1007 return(DispAllErrors(henv
, hdbc
));
1009 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
1010 return(DispAllErrors(henv
, hdbc
));
1012 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
1013 return(DispAllErrors(henv
, hdbc
));
1015 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
1016 return(DispAllErrors(henv
, hdbc
));
1018 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
1019 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1020 return(DispAllErrors(henv
, hdbc
));
1022 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
1023 return(DispAllErrors(henv
, hdbc
));
1025 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
1026 return(DispAllErrors(henv
, hdbc
));
1028 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
1029 // return(DispAllErrors(henv, hdbc));
1031 // Not all drivers support this call - Nick Gorham(unixODBC)
1032 dbInf
.cliConfLvl
= 0;
1035 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
1036 return(DispAllErrors(henv
, hdbc
));
1038 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
1039 return(DispAllErrors(henv
, hdbc
));
1041 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
1042 return(DispAllErrors(henv
, hdbc
));
1044 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
1045 return(DispAllErrors(henv
, hdbc
));
1047 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
1048 return(DispAllErrors(henv
, hdbc
));
1050 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
1051 return(DispAllErrors(henv
, hdbc
));
1053 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
1054 return(DispAllErrors(henv
, hdbc
));
1056 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
1057 return(DispAllErrors(henv
, hdbc
));
1059 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
1060 return(DispAllErrors(henv
, hdbc
));
1062 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
1063 return(DispAllErrors(henv
, hdbc
));
1065 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
1066 return(DispAllErrors(henv
, hdbc
));
1068 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
1069 return(DispAllErrors(henv
, hdbc
));
1071 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
1072 return(DispAllErrors(henv
, hdbc
));
1074 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
1075 return(DispAllErrors(henv
, hdbc
));
1077 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
1078 return(DispAllErrors(henv
, hdbc
));
1080 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
1081 return(DispAllErrors(henv
, hdbc
));
1083 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
1084 return(DispAllErrors(henv
, hdbc
));
1086 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
1087 return(DispAllErrors(henv
, hdbc
));
1089 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
1090 return(DispAllErrors(henv
, hdbc
));
1092 #ifdef DBDEBUG_CONSOLE
1093 cout
<< wxT("***** DATA SOURCE INFORMATION *****") << endl
;
1094 cout
<< wxT(wxT("SERVER Name: ") << dbInf
.serverName
<< endl
;
1095 cout
<< wxT("DBMS Name: ") << dbInf
.dbmsName
<< wxT("; DBMS Version: ") << dbInf
.dbmsVer
<< endl
;
1096 cout
<< wxT("ODBC Version: ") << dbInf
.odbcVer
<< wxT("; Driver Version: ") << dbInf
.driverVer
<< endl
;
1098 cout
<< wxT("API Conf. Level: ");
1099 switch(dbInf
.apiConfLvl
)
1101 case SQL_OAC_NONE
: cout
<< wxT("None"); break;
1102 case SQL_OAC_LEVEL1
: cout
<< wxT("Level 1"); break;
1103 case SQL_OAC_LEVEL2
: cout
<< wxT("Level 2"); break;
1107 cout
<< wxT("SAG CLI Conf. Level: ");
1108 switch(dbInf
.cliConfLvl
)
1110 case SQL_OSCC_NOT_COMPLIANT
: cout
<< wxT("Not Compliant"); break;
1111 case SQL_OSCC_COMPLIANT
: cout
<< wxT("Compliant"); break;
1115 cout
<< wxT("SQL Conf. Level: ");
1116 switch(dbInf
.sqlConfLvl
)
1118 case SQL_OSC_MINIMUM
: cout
<< wxT("Minimum Grammar"); break;
1119 case SQL_OSC_CORE
: cout
<< wxT("Core Grammar"); break;
1120 case SQL_OSC_EXTENDED
: cout
<< wxT("Extended Grammar"); break;
1124 cout
<< wxT("Max. Connections: ") << dbInf
.maxConnections
<< endl
;
1125 cout
<< wxT("Outer Joins: ") << dbInf
.outerJoins
<< endl
;
1126 cout
<< wxT("Support for Procedures: ") << dbInf
.procedureSupport
<< endl
;
1127 cout
<< wxT("All tables accessible : ") << dbInf
.accessibleTables
<< endl
;
1128 cout
<< wxT("Cursor COMMIT Behavior: ");
1129 switch(dbInf
.cursorCommitBehavior
)
1131 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1132 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1133 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1137 cout
<< wxT("Cursor ROLLBACK Behavior: ");
1138 switch(dbInf
.cursorRollbackBehavior
)
1140 case SQL_CB_DELETE
: cout
<< wxT("Delete cursors"); break;
1141 case SQL_CB_CLOSE
: cout
<< wxT("Close cursors"); break;
1142 case SQL_CB_PRESERVE
: cout
<< wxT("Preserve cursors"); break;
1146 cout
<< wxT("Support NOT NULL clause: ");
1147 switch(dbInf
.supportNotNullClause
)
1149 case SQL_NNC_NULL
: cout
<< wxT("No"); break;
1150 case SQL_NNC_NON_NULL
: cout
<< wxT("Yes"); break;
1154 cout
<< wxT("Support IEF (Ref. Integrity): ") << dbInf
.supportIEF
<< endl
;
1155 cout
<< wxT("Login Timeout: ") << dbInf
.loginTimeout
<< endl
;
1157 cout
<< endl
<< endl
<< wxT("more ...") << endl
;
1160 cout
<< wxT("Default Transaction Isolation: ";
1161 switch(dbInf
.txnIsolation
)
1163 case SQL_TXN_READ_UNCOMMITTED
: cout
<< wxT("Read Uncommitted"); break;
1164 case SQL_TXN_READ_COMMITTED
: cout
<< wxT("Read Committed"); break;
1165 case SQL_TXN_REPEATABLE_READ
: cout
<< wxT("Repeatable Read"); break;
1166 case SQL_TXN_SERIALIZABLE
: cout
<< wxT("Serializable"); break;
1168 case SQL_TXN_VERSIONING
: cout
<< wxT("Versioning"); break;
1173 cout
<< wxT("Transaction Isolation Options: ");
1174 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
1175 cout
<< wxT("Read Uncommitted, ");
1176 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
1177 cout
<< wxT("Read Committed, ");
1178 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
1179 cout
<< wxT("Repeatable Read, ");
1180 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
1181 cout
<< wxT("Serializable, ");
1183 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
1184 cout
<< wxT("Versioning");
1188 cout
<< wxT("Fetch Directions Supported:") << endl
<< wxT(" ");
1189 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
1190 cout
<< wxT("Next, ");
1191 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
1192 cout
<< wxT("Prev, ");
1193 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
1194 cout
<< wxT("First, ");
1195 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
1196 cout
<< wxT("Last, ");
1197 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
1198 cout
<< wxT("Absolute, ");
1199 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
1200 cout
<< wxT("Relative, ");
1202 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
1203 cout
<< wxT("Resume, ");
1205 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
1206 cout
<< wxT("Bookmark");
1209 cout
<< wxT("Lock Types Supported (SQLSetPos): ");
1210 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
1211 cout
<< wxT("No Change, ");
1212 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
1213 cout
<< wxT("Exclusive, ");
1214 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
1215 cout
<< wxT("UnLock");
1218 cout
<< wxT("Position Operations Supported (SQLSetPos): ");
1219 if (dbInf
.posOperations
& SQL_POS_POSITION
)
1220 cout
<< wxT("Position, ");
1221 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
1222 cout
<< wxT("Refresh, ");
1223 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
1224 cout
<< wxT("Upd, "));
1225 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1226 cout
<< wxT("Del, ");
1227 if (dbInf
.posOperations
& SQL_POS_ADD
)
1231 cout
<< wxT("Positioned Statements Supported: ");
1232 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1233 cout
<< wxT("Pos delete, ");
1234 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1235 cout
<< wxT("Pos update, ");
1236 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1237 cout
<< wxT("Select for update");
1240 cout
<< wxT("Scroll Concurrency: ");
1241 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1242 cout
<< wxT("Read Only, ");
1243 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1244 cout
<< wxT("Lock, ");
1245 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1246 cout
<< wxT("Opt. Rowver, ");
1247 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1248 cout
<< wxT("Opt. Values");
1251 cout
<< wxT("Scroll Options: ");
1252 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1253 cout
<< wxT("Fwd Only, ");
1254 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1255 cout
<< wxT("Static, ");
1256 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1257 cout
<< wxT("Keyset Driven, ");
1258 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1259 cout
<< wxT("Dynamic, ");
1260 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1261 cout
<< wxT("Mixed");
1264 cout
<< wxT("Static Sensitivity: ");
1265 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1266 cout
<< wxT("Additions, ");
1267 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1268 cout
<< wxT("Deletions, ");
1269 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1270 cout
<< wxT("Updates");
1273 cout
<< wxT("Transaction Capable?: ");
1274 switch(dbInf
.txnCapable
)
1276 case SQL_TC_NONE
: cout
<< wxT("No"); break;
1277 case SQL_TC_DML
: cout
<< wxT("DML Only"); break;
1278 case SQL_TC_DDL_COMMIT
: cout
<< wxT("DDL Commit"); break;
1279 case SQL_TC_DDL_IGNORE
: cout
<< wxT("DDL Ignore"); break;
1280 case SQL_TC_ALL
: cout
<< wxT("DDL & DML"); break;
1287 // Completed Successfully
1290 } // wxDb::getDbInfo()
1293 /********** wxDb::getDataTypeInfo() **********/
1294 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1297 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1298 * the data type inf. is gathered for.
1300 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1305 // Get information about the data type specified
1306 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1307 return(DispAllErrors(henv
, hdbc
, hstmt
));
1309 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
1311 #ifdef DBDEBUG_CONSOLE
1312 if (retcode
== SQL_NO_DATA_FOUND
)
1313 cout
<< wxT("SQL_NO_DATA_FOUND fetching inf. about data type.") << endl
;
1315 DispAllErrors(henv
, hdbc
, hstmt
);
1316 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1320 wxChar typeName
[DB_TYPE_NAME_LEN
+1];
1321 // Obtain columns from the record
1322 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) typeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1323 return(DispAllErrors(henv
, hdbc
, hstmt
));
1325 structSQLTypeInfo
.TypeName
= typeName
;
1327 // BJO 20000503: no more needed with new GetColumns...
1330 if (Dbms() == dbmsMY_SQL
)
1332 if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1333 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1334 else if (structSQLTypeInfo
.TypeName
== wxT("middleint unsigned"))
1335 structSQLTypeInfo
.TypeName
= wxT("mediumint unsigned");
1336 else if (structSQLTypeInfo
.TypeName
== wxT("integer"))
1337 structSQLTypeInfo
.TypeName
= wxT("int");
1338 else if (structSQLTypeInfo
.TypeName
== wxT("integer unsigned"))
1339 structSQLTypeInfo
.TypeName
= wxT("int unsigned");
1340 else if (structSQLTypeInfo
.TypeName
== wxT("middleint"))
1341 structSQLTypeInfo
.TypeName
= wxT("mediumint");
1342 else if (structSQLTypeInfo
.TypeName
== wxT("varchar"))
1343 structSQLTypeInfo
.TypeName
= wxT("char");
1346 // BJO 20000427 : OpenLink driver
1347 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
1348 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
1350 if (structSQLTypeInfo
.TypeName
== wxT("double precision"))
1351 structSQLTypeInfo
.TypeName
= wxT("real");
1355 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1356 return(DispAllErrors(henv
, hdbc
, hstmt
));
1357 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1358 return(DispAllErrors(henv
, hdbc
, hstmt
));
1359 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1360 // return(DispAllErrors(henv, hdbc, hstmt));
1362 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1363 return(DispAllErrors(henv
, hdbc
, hstmt
));
1365 if (structSQLTypeInfo
.MaximumScale
< 0)
1366 structSQLTypeInfo
.MaximumScale
= 0;
1368 // Close the statement handle which closes open cursors
1369 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1370 return(DispAllErrors(henv
, hdbc
, hstmt
));
1372 // Completed Successfully
1375 } // wxDb::getDataTypeInfo()
1378 /********** wxDb::Close() **********/
1379 void wxDb::Close(void)
1381 // Close the Sql Log file
1388 // Free statement handle
1391 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1392 DispAllErrors(henv
, hdbc
);
1395 // Disconnect from the datasource
1396 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1397 DispAllErrors(henv
, hdbc
);
1399 // Free the connection to the datasource
1400 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1401 DispAllErrors(henv
, hdbc
);
1403 // There should be zero Ctable objects still connected to this db object
1404 wxASSERT(nTables
== 0);
1409 pNode
= TablesInUse
.First();
1413 tiu
= (wxTablesInUse
*)pNode
->Data();
1414 if (tiu
->pDb
== this)
1416 s
.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1417 s2
.Printf(wxT("Orphaned found using pDb:[%p]"),this);
1420 pNode
= pNode
->Next();
1424 // Copy the error messages to a global variable
1426 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1427 wxStrcpy(DBerrorList
[i
], errorList
[i
]);
1429 dbmsType
= dbmsUNIDENTIFIED
;
1435 /********** wxDb::CommitTrans() **********/
1436 bool wxDb::CommitTrans(void)
1440 // Commit the transaction
1441 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1442 return(DispAllErrors(henv
, hdbc
));
1445 // Completed successfully
1448 } // wxDb::CommitTrans()
1451 /********** wxDb::RollbackTrans() **********/
1452 bool wxDb::RollbackTrans(void)
1454 // Rollback the transaction
1455 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1456 return(DispAllErrors(henv
, hdbc
));
1458 // Completed successfully
1461 } // wxDb::RollbackTrans()
1464 /********** wxDb::DispAllErrors() **********/
1465 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1467 * This function is called internally whenever an error condition prevents the user's
1468 * request from being executed. This function will query the datasource as to the
1469 * actual error(s) that just occured on the previous request of the datasource.
1471 * The function will retrieve each error condition from the datasource and
1472 * Printf the codes/text values into a string which it then logs via logError().
1473 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1474 * window and program execution will be paused until the user presses a key.
1476 * This function always returns a FALSE, so that functions which call this function
1477 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1478 * of the users request, so that the calling code can then process the error msg log
1481 wxString odbcErrMsg
;
1483 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1485 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1486 logError(odbcErrMsg
, sqlState
);
1489 #ifdef DBDEBUG_CONSOLE
1490 // When run in console mode, use standard out to display errors.
1491 cout
<< odbcErrMsg
.c_str() << endl
;
1492 cout
<< wxT("Press any key to continue...") << endl
;
1497 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1502 return(FALSE
); // This function always returns false.
1504 } // wxDb::DispAllErrors()
1507 /********** wxDb::GetNextError() **********/
1508 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1510 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1515 } // wxDb::GetNextError()
1518 /********** wxDb::DispNextError() **********/
1519 void wxDb::DispNextError(void)
1521 wxString odbcErrMsg
;
1523 odbcErrMsg
.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState
, nativeError
, errorMsg
);
1524 logError(odbcErrMsg
, sqlState
);
1529 #ifdef DBDEBUG_CONSOLE
1530 // When run in console mode, use standard out to display errors.
1531 cout
<< odbcErrMsg
.c_str() << endl
;
1532 cout
<< wxT("Press any key to continue...") << endl
;
1537 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1538 #endif // __WXDEBUG__
1540 } // wxDb::DispNextError()
1543 /********** wxDb::logError() **********/
1544 void wxDb::logError(const wxString
&errMsg
, const wxString
&SQLState
)
1546 wxASSERT(errMsg
.Length());
1548 static int pLast
= -1;
1551 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1554 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1555 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1559 wxStrcpy(errorList
[pLast
], errMsg
);
1561 if (SQLState
.Length())
1562 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1563 DB_STATUS
= dbStatus
;
1565 // Add the errmsg to the sql log
1566 WriteSqlLog(errMsg
);
1568 } // wxDb::logError()
1571 /**********wxDb::TranslateSqlState() **********/
1572 int wxDb::TranslateSqlState(const wxString
&SQLState
)
1574 if (!wxStrcmp(SQLState
, wxT("01000")))
1575 return(DB_ERR_GENERAL_WARNING
);
1576 if (!wxStrcmp(SQLState
, wxT("01002")))
1577 return(DB_ERR_DISCONNECT_ERROR
);
1578 if (!wxStrcmp(SQLState
, wxT("01004")))
1579 return(DB_ERR_DATA_TRUNCATED
);
1580 if (!wxStrcmp(SQLState
, wxT("01006")))
1581 return(DB_ERR_PRIV_NOT_REVOKED
);
1582 if (!wxStrcmp(SQLState
, wxT("01S00")))
1583 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1584 if (!wxStrcmp(SQLState
, wxT("01S01")))
1585 return(DB_ERR_ERROR_IN_ROW
);
1586 if (!wxStrcmp(SQLState
, wxT("01S02")))
1587 return(DB_ERR_OPTION_VALUE_CHANGED
);
1588 if (!wxStrcmp(SQLState
, wxT("01S03")))
1589 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1590 if (!wxStrcmp(SQLState
, wxT("01S04")))
1591 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1592 if (!wxStrcmp(SQLState
, wxT("07001")))
1593 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1594 if (!wxStrcmp(SQLState
, wxT("07006")))
1595 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1596 if (!wxStrcmp(SQLState
, wxT("08001")))
1597 return(DB_ERR_UNABLE_TO_CONNECT
);
1598 if (!wxStrcmp(SQLState
, wxT("08002")))
1599 return(DB_ERR_CONNECTION_IN_USE
);
1600 if (!wxStrcmp(SQLState
, wxT("08003")))
1601 return(DB_ERR_CONNECTION_NOT_OPEN
);
1602 if (!wxStrcmp(SQLState
, wxT("08004")))
1603 return(DB_ERR_REJECTED_CONNECTION
);
1604 if (!wxStrcmp(SQLState
, wxT("08007")))
1605 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1606 if (!wxStrcmp(SQLState
, wxT("08S01")))
1607 return(DB_ERR_COMM_LINK_FAILURE
);
1608 if (!wxStrcmp(SQLState
, wxT("21S01")))
1609 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1610 if (!wxStrcmp(SQLState
, wxT("21S02")))
1611 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1612 if (!wxStrcmp(SQLState
, wxT("22001")))
1613 return(DB_ERR_STRING_RIGHT_TRUNC
);
1614 if (!wxStrcmp(SQLState
, wxT("22003")))
1615 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1616 if (!wxStrcmp(SQLState
, wxT("22005")))
1617 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1618 if (!wxStrcmp(SQLState
, wxT("22008")))
1619 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1620 if (!wxStrcmp(SQLState
, wxT("22012")))
1621 return(DB_ERR_DIVIDE_BY_ZERO
);
1622 if (!wxStrcmp(SQLState
, wxT("22026")))
1623 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1624 if (!wxStrcmp(SQLState
, wxT("23000")))
1625 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1626 if (!wxStrcmp(SQLState
, wxT("24000")))
1627 return(DB_ERR_INVALID_CURSOR_STATE
);
1628 if (!wxStrcmp(SQLState
, wxT("25000")))
1629 return(DB_ERR_INVALID_TRANS_STATE
);
1630 if (!wxStrcmp(SQLState
, wxT("28000")))
1631 return(DB_ERR_INVALID_AUTH_SPEC
);
1632 if (!wxStrcmp(SQLState
, wxT("34000")))
1633 return(DB_ERR_INVALID_CURSOR_NAME
);
1634 if (!wxStrcmp(SQLState
, wxT("37000")))
1635 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1636 if (!wxStrcmp(SQLState
, wxT("3C000")))
1637 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1638 if (!wxStrcmp(SQLState
, wxT("40001")))
1639 return(DB_ERR_SERIALIZATION_FAILURE
);
1640 if (!wxStrcmp(SQLState
, wxT("42000")))
1641 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1642 if (!wxStrcmp(SQLState
, wxT("70100")))
1643 return(DB_ERR_OPERATION_ABORTED
);
1644 if (!wxStrcmp(SQLState
, wxT("IM001")))
1645 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1646 if (!wxStrcmp(SQLState
, wxT("IM002")))
1647 return(DB_ERR_NO_DATA_SOURCE
);
1648 if (!wxStrcmp(SQLState
, wxT("IM003")))
1649 return(DB_ERR_DRIVER_LOAD_ERROR
);
1650 if (!wxStrcmp(SQLState
, wxT("IM004")))
1651 return(DB_ERR_SQLALLOCENV_FAILED
);
1652 if (!wxStrcmp(SQLState
, wxT("IM005")))
1653 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1654 if (!wxStrcmp(SQLState
, wxT("IM006")))
1655 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1656 if (!wxStrcmp(SQLState
, wxT("IM007")))
1657 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1658 if (!wxStrcmp(SQLState
, wxT("IM008")))
1659 return(DB_ERR_DIALOG_FAILED
);
1660 if (!wxStrcmp(SQLState
, wxT("IM009")))
1661 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1662 if (!wxStrcmp(SQLState
, wxT("IM010")))
1663 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1664 if (!wxStrcmp(SQLState
, wxT("IM011")))
1665 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1666 if (!wxStrcmp(SQLState
, wxT("IM012")))
1667 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1668 if (!wxStrcmp(SQLState
, wxT("IM013")))
1669 return(DB_ERR_TRACE_FILE_ERROR
);
1670 if (!wxStrcmp(SQLState
, wxT("S0001")))
1671 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1672 if (!wxStrcmp(SQLState
, wxT("S0002")))
1673 return(DB_ERR_TABLE_NOT_FOUND
);
1674 if (!wxStrcmp(SQLState
, wxT("S0011")))
1675 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1676 if (!wxStrcmp(SQLState
, wxT("S0012")))
1677 return(DB_ERR_INDEX_NOT_FOUND
);
1678 if (!wxStrcmp(SQLState
, wxT("S0021")))
1679 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1680 if (!wxStrcmp(SQLState
, wxT("S0022")))
1681 return(DB_ERR_COLUMN_NOT_FOUND
);
1682 if (!wxStrcmp(SQLState
, wxT("S0023")))
1683 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1684 if (!wxStrcmp(SQLState
, wxT("S1000")))
1685 return(DB_ERR_GENERAL_ERROR
);
1686 if (!wxStrcmp(SQLState
, wxT("S1001")))
1687 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1688 if (!wxStrcmp(SQLState
, wxT("S1002")))
1689 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1690 if (!wxStrcmp(SQLState
, wxT("S1003")))
1691 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1692 if (!wxStrcmp(SQLState
, wxT("S1004")))
1693 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1694 if (!wxStrcmp(SQLState
, wxT("S1008")))
1695 return(DB_ERR_OPERATION_CANCELLED
);
1696 if (!wxStrcmp(SQLState
, wxT("S1009")))
1697 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1698 if (!wxStrcmp(SQLState
, wxT("S1010")))
1699 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1700 if (!wxStrcmp(SQLState
, wxT("S1011")))
1701 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1702 if (!wxStrcmp(SQLState
, wxT("S1012")))
1703 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1704 if (!wxStrcmp(SQLState
, wxT("S1015")))
1705 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1706 if (!wxStrcmp(SQLState
, wxT("S1090")))
1707 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1708 if (!wxStrcmp(SQLState
, wxT("S1091")))
1709 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1710 if (!wxStrcmp(SQLState
, wxT("S1092")))
1711 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1712 if (!wxStrcmp(SQLState
, wxT("S1093")))
1713 return(DB_ERR_INVALID_PARAM_NO
);
1714 if (!wxStrcmp(SQLState
, wxT("S1094")))
1715 return(DB_ERR_INVALID_SCALE_VALUE
);
1716 if (!wxStrcmp(SQLState
, wxT("S1095")))
1717 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1718 if (!wxStrcmp(SQLState
, wxT("S1096")))
1719 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1720 if (!wxStrcmp(SQLState
, wxT("S1097")))
1721 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1722 if (!wxStrcmp(SQLState
, wxT("S1098")))
1723 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1724 if (!wxStrcmp(SQLState
, wxT("S1099")))
1725 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1726 if (!wxStrcmp(SQLState
, wxT("S1100")))
1727 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1728 if (!wxStrcmp(SQLState
, wxT("S1101")))
1729 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1730 if (!wxStrcmp(SQLState
, wxT("S1103")))
1731 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1732 if (!wxStrcmp(SQLState
, wxT("S1104")))
1733 return(DB_ERR_INVALID_PRECISION_VALUE
);
1734 if (!wxStrcmp(SQLState
, wxT("S1105")))
1735 return(DB_ERR_INVALID_PARAM_TYPE
);
1736 if (!wxStrcmp(SQLState
, wxT("S1106")))
1737 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1738 if (!wxStrcmp(SQLState
, wxT("S1107")))
1739 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1740 if (!wxStrcmp(SQLState
, wxT("S1108")))
1741 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1742 if (!wxStrcmp(SQLState
, wxT("S1109")))
1743 return(DB_ERR_INVALID_CURSOR_POSITION
);
1744 if (!wxStrcmp(SQLState
, wxT("S1110")))
1745 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1746 if (!wxStrcmp(SQLState
, wxT("S1111")))
1747 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1748 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1749 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1750 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1751 return(DB_ERR_TIMEOUT_EXPIRED
);
1756 } // wxDb::TranslateSqlState()
1759 /********** wxDb::Grant() **********/
1760 bool wxDb::Grant(int privileges
, const wxString
&tableName
, const wxString
&userList
)
1764 // Build the grant statement
1765 sqlStmt
= wxT("GRANT ");
1766 if (privileges
== DB_GRANT_ALL
)
1767 sqlStmt
+= wxT("ALL");
1771 if (privileges
& DB_GRANT_SELECT
)
1773 sqlStmt
+= wxT("SELECT");
1776 if (privileges
& DB_GRANT_INSERT
)
1779 sqlStmt
+= wxT(", ");
1780 sqlStmt
+= wxT("INSERT");
1782 if (privileges
& DB_GRANT_UPDATE
)
1785 sqlStmt
+= wxT(", ");
1786 sqlStmt
+= wxT("UPDATE");
1788 if (privileges
& DB_GRANT_DELETE
)
1791 sqlStmt
+= wxT(", ");
1792 sqlStmt
+= wxT("DELETE");
1796 sqlStmt
+= wxT(" ON ");
1797 sqlStmt
+= tableName
;
1798 sqlStmt
+= wxT(" TO ");
1799 sqlStmt
+= userList
;
1801 #ifdef DBDEBUG_CONSOLE
1802 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1805 WriteSqlLog(sqlStmt
);
1807 return(ExecSql(sqlStmt
));
1812 /********** wxDb::CreateView() **********/
1813 bool wxDb::CreateView(const wxString
&viewName
, const wxString
&colList
,
1814 const wxString
&pSqlStmt
, bool attemptDrop
)
1818 // Drop the view first
1819 if (attemptDrop
&& !DropView(viewName
))
1822 // Build the create view statement
1823 sqlStmt
= wxT("CREATE VIEW ");
1824 sqlStmt
+= viewName
;
1826 if (colList
.Length())
1828 sqlStmt
+= wxT(" (");
1830 sqlStmt
+= wxT(")");
1833 sqlStmt
+= wxT(" AS ");
1834 sqlStmt
+= pSqlStmt
;
1836 WriteSqlLog(sqlStmt
);
1838 #ifdef DBDEBUG_CONSOLE
1839 cout
<< sqlStmt
.c_str() << endl
;
1842 return(ExecSql(sqlStmt
));
1844 } // wxDb::CreateView()
1847 /********** wxDb::DropView() **********/
1848 bool wxDb::DropView(const wxString
&viewName
)
1851 * NOTE: This function returns TRUE if the View does not exist, but
1852 * only for identified databases. Code will need to be added
1853 * below for any other databases when those databases are defined
1854 * to handle this situation consistently
1858 sqlStmt
.Printf(wxT("DROP VIEW %s"), viewName
.c_str());
1860 WriteSqlLog(sqlStmt
);
1862 #ifdef DBDEBUG_CONSOLE
1863 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1866 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1868 // Check for "Base table not found" error and ignore
1869 GetNextError(henv
, hdbc
, hstmt
);
1870 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1872 // Check for product specific error codes
1873 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1876 DispAllErrors(henv
, hdbc
, hstmt
);
1883 // Commit the transaction
1884 if (! CommitTrans())
1889 } // wxDb::DropView()
1892 /********** wxDb::ExecSql() **********/
1893 bool wxDb::ExecSql(const wxString
&pSqlStmt
)
1895 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1896 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
.c_str(), SQL_NTS
) == SQL_SUCCESS
)
1900 DispAllErrors(henv
, hdbc
, hstmt
);
1904 } // wxDb::ExecSql()
1907 /********** wxDb::GetNext() **********/
1908 bool wxDb::GetNext(void)
1910 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1914 DispAllErrors(henv
, hdbc
, hstmt
);
1918 } // wxDb::GetNext()
1921 /********** wxDb::GetData() **********/
1922 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1925 wxASSERT(cbReturned
);
1927 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1931 DispAllErrors(henv
, hdbc
, hstmt
);
1935 } // wxDb::GetData()
1938 /********** wxDb::GetKeyFields() **********/
1939 int wxDb::GetKeyFields(const wxString
&tableName
, wxDbColInf
* colInf
, UWORD noCols
)
1941 wxChar szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1942 wxChar szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1944 // SQLSMALLINT iKeySeq;
1945 wxChar szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1946 wxChar szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1952 * -----------------------------------------------------------------------
1953 * -- 19991224 : mj10777 : Create ------
1954 * -- : Three things are done and stored here : ------
1955 * -- : 1) which Column(s) is/are Primary Key(s) ------
1956 * -- : 2) which tables use this Key as a Foreign Key ------
1957 * -- : 3) which columns are Foreign Key and the name ------
1958 * -- : of the Table where the Key is the Primary Key -----
1959 * -- : Called from GetColumns(const wxString &tableName, ------
1960 * -- int *numCols,const wxChar *userID ) ------
1961 * -----------------------------------------------------------------------
1964 /*---------------------------------------------------------------------*/
1965 /* Get the names of the columns in the primary key. */
1966 /*---------------------------------------------------------------------*/
1967 retcode
= SQLPrimaryKeys(hstmt
,
1968 NULL
, 0, /* Catalog name */
1969 NULL
, 0, /* Schema name */
1970 (UCHAR FAR
*) tableName
.c_str(), SQL_NTS
); /* Table name */
1972 /*---------------------------------------------------------------------*/
1973 /* Fetch and display the result set. This will be a list of the */
1974 /* columns in the primary key of the tableName table. */
1975 /*---------------------------------------------------------------------*/
1976 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1978 retcode
= SQLFetch(hstmt
);
1979 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1981 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1982 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1984 for (i
=0;i
<noCols
;i
++) // Find the Column name
1985 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1986 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1989 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1991 /*---------------------------------------------------------------------*/
1992 /* Get all the foreign keys that refer to tableName primary key. */
1993 /*---------------------------------------------------------------------*/
1994 retcode
= SQLForeignKeys(hstmt
,
1995 NULL
, 0, /* Primary catalog */
1996 NULL
, 0, /* Primary schema */
1997 (UCHAR FAR
*)tableName
.c_str(), SQL_NTS
,/* Primary table */
1998 NULL
, 0, /* Foreign catalog */
1999 NULL
, 0, /* Foreign schema */
2000 NULL
, 0); /* Foreign table */
2002 /*---------------------------------------------------------------------*/
2003 /* Fetch and display the result set. This will be all of the foreign */
2004 /* keys in other tables that refer to the tableName primary key. */
2005 /*---------------------------------------------------------------------*/
2008 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2010 retcode
= SQLFetch(hstmt
);
2011 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2013 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2014 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2015 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2016 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2017 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2018 tempStr
.Printf(wxT("%s[%s] "),tempStr
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
2022 tempStr
.Trim(); // Get rid of any unneeded blanks
2023 if (!tempStr
.IsEmpty())
2025 for (i
=0; i
<noCols
; i
++)
2026 { // Find the Column name
2027 if (!wxStrcmp(colInf
[i
].colName
, szPkCol
)) // We have found the Column, store the Information
2028 wxStrcpy(colInf
[i
].PkTableName
, tempStr
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
2032 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2034 /*---------------------------------------------------------------------*/
2035 /* Get all the foreign keys in the tablename table. */
2036 /*---------------------------------------------------------------------*/
2037 retcode
= SQLForeignKeys(hstmt
,
2038 NULL
, 0, /* Primary catalog */
2039 NULL
, 0, /* Primary schema */
2040 NULL
, 0, /* Primary table */
2041 NULL
, 0, /* Foreign catalog */
2042 NULL
, 0, /* Foreign schema */
2043 (UCHAR
*)tableName
.c_str(), SQL_NTS
);/* Foreign table */
2045 /*---------------------------------------------------------------------*/
2046 /* Fetch and display the result set. This will be all of the */
2047 /* primary keys in other tables that are referred to by foreign */
2048 /* keys in the tableName table. */
2049 /*---------------------------------------------------------------------*/
2051 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
2053 retcode
= SQLFetch(hstmt
);
2054 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
2056 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2057 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
2058 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2060 for (i
=0; i
<noCols
; i
++) // Find the Column name
2062 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
2064 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
2065 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
2070 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
2074 } // wxDb::GetKeyFields()
2078 /********** wxDb::GetColumns() **********/
2079 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2081 * 1) The last array element of the tableName[] argument must be zero (null).
2082 * This is how the end of the array is detected.
2083 * 2) This function returns an array of wxDbColInf structures. If no columns
2084 * were found, or an error occured, this pointer will be zero (null). THE
2085 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2086 * IS FINISHED WITH IT. i.e.
2088 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2091 * // Use the column inf
2093 * // Destroy the memory
2097 * userID is evaluated in the following manner:
2098 * userID == NULL ... UserID is ignored
2099 * userID == "" ... UserID set equal to 'this->uid'
2100 * userID != "" ... UserID set equal to 'userID'
2102 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2103 * by this function. This function should use its own wxDb instance
2104 * to avoid undesired unbinding of columns.
2109 wxDbColInf
*colInf
= 0;
2117 convertUserID(userID
,UserID
);
2119 // Pass 1 - Determine how many columns there are.
2120 // Pass 2 - Allocate the wxDbColInf array and fill in
2121 // the array with the column information.
2123 for (pass
= 1; pass
<= 2; pass
++)
2127 if (noCols
== 0) // Probably a bogus table name(s)
2129 // Allocate n wxDbColInf objects to hold the column information
2130 colInf
= new wxDbColInf
[noCols
+1];
2133 // Mark the end of the array
2134 wxStrcpy(colInf
[noCols
].tableName
,wxEmptyString
);
2135 wxStrcpy(colInf
[noCols
].colName
,wxEmptyString
);
2136 colInf
[noCols
].sqlDataType
= 0;
2138 // Loop through each table name
2140 for (tbl
= 0; tableName
[tbl
]; tbl
++)
2142 TableName
= tableName
[tbl
];
2143 // Oracle and Interbase table names are uppercase only, so force
2144 // the name to uppercase just in case programmer forgot to do this
2145 if ((Dbms() == dbmsORACLE
) ||
2146 (Dbms() == dbmsINTERBASE
))
2147 TableName
= TableName
.Upper();
2149 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2151 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2152 // use the call below that leaves out the user name
2153 if (!UserID
.IsEmpty() &&
2154 Dbms() != dbmsMY_SQL
&&
2155 Dbms() != dbmsACCESS
&&
2156 Dbms() != dbmsMS_SQL_SERVER
)
2158 retcode
= SQLColumns(hstmt
,
2159 NULL
, 0, // All qualifiers
2160 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2161 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2162 NULL
, 0); // All columns
2166 retcode
= SQLColumns(hstmt
,
2167 NULL
, 0, // All qualifiers
2169 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2170 NULL
, 0); // All columns
2172 if (retcode
!= SQL_SUCCESS
)
2173 { // Error occured, abort
2174 DispAllErrors(henv
, hdbc
, hstmt
);
2177 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2181 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2183 if (pass
== 1) // First pass, just add up the number of columns
2185 else // Pass 2; Fill in the array of structures
2187 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2189 // NOTE: Only the ODBC 1.x fields are retrieved
2190 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2191 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2192 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2193 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2194 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2195 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2196 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2197 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2198 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2199 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2200 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2201 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2203 // Determine the wxDb data type that is used to represent the native data type of this data source
2204 colInf
[colNo
].dbDataType
= 0;
2205 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2208 // IODBC does not return a correct columnSize, so we set
2209 // columnSize = bufferLength if no column size was returned
2210 // IODBC returns the columnSize in bufferLength.. (bug)
2211 if (colInf
[colNo
].columnSize
< 1)
2213 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2216 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2218 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2219 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2220 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2221 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2222 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2223 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2224 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2225 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2230 if (retcode
!= SQL_NO_DATA_FOUND
)
2231 { // Error occured, abort
2232 DispAllErrors(henv
, hdbc
, hstmt
);
2235 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2241 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2244 } // wxDb::GetColumns()
2247 /********** wxDb::GetColumns() **********/
2249 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, UWORD
*numCols
, const wxChar
*userID
)
2251 // Same as the above GetColumns() function except this one gets columns
2252 // only for a single table, and if 'numCols' is not NULL, the number of
2253 // columns stored in the returned wxDbColInf is set in '*numCols'
2255 // userID is evaluated in the following manner:
2256 // userID == NULL ... UserID is ignored
2257 // userID == "" ... UserID set equal to 'this->uid'
2258 // userID != "" ... UserID set equal to 'userID'
2260 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2261 // by this function. This function should use its own wxDb instance
2262 // to avoid undesired unbinding of columns.
2267 wxDbColInf
*colInf
= 0;
2275 convertUserID(userID
,UserID
);
2277 // Pass 1 - Determine how many columns there are.
2278 // Pass 2 - Allocate the wxDbColInf array and fill in
2279 // the array with the column information.
2281 for (pass
= 1; pass
<= 2; pass
++)
2285 if (noCols
== 0) // Probably a bogus table name(s)
2287 // Allocate n wxDbColInf objects to hold the column information
2288 colInf
= new wxDbColInf
[noCols
+1];
2291 // Mark the end of the array
2292 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2293 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2294 colInf
[noCols
].sqlDataType
= 0;
2297 TableName
= tableName
;
2298 // Oracle and Interbase table names are uppercase only, so force
2299 // the name to uppercase just in case programmer forgot to do this
2300 if ((Dbms() == dbmsORACLE
) ||
2301 (Dbms() == dbmsINTERBASE
))
2302 TableName
= TableName
.Upper();
2304 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2306 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2307 // use the call below that leaves out the user name
2308 if (!UserID
.IsEmpty() &&
2309 Dbms() != dbmsMY_SQL
&&
2310 Dbms() != dbmsACCESS
&&
2311 Dbms() != dbmsMS_SQL_SERVER
)
2313 retcode
= SQLColumns(hstmt
,
2314 NULL
, 0, // All qualifiers
2315 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2316 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2317 NULL
, 0); // All columns
2321 retcode
= SQLColumns(hstmt
,
2322 NULL
, 0, // All qualifiers
2324 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2325 NULL
, 0); // All columns
2327 if (retcode
!= SQL_SUCCESS
)
2328 { // Error occured, abort
2329 DispAllErrors(henv
, hdbc
, hstmt
);
2332 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2338 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2340 if (pass
== 1) // First pass, just add up the number of columns
2342 else // Pass 2; Fill in the array of structures
2344 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2346 // NOTE: Only the ODBC 1.x fields are retrieved
2347 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2348 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2349 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2350 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2351 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2352 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2353 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2354 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2355 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2356 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2357 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2358 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2359 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2360 // Start Values for Primary/Foriegn Key (=No)
2361 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2362 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2363 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2364 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2366 // BJO 20000428 : Virtuoso returns type names with upper cases!
2367 if (Dbms() == dbmsVIRTUOSO
)
2369 wxString s
= colInf
[colNo
].typeName
;
2371 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2374 // Determine the wxDb data type that is used to represent the native data type of this data source
2375 colInf
[colNo
].dbDataType
= 0;
2376 if (!wxStricmp(typeInfVarchar
.TypeName
, colInf
[colNo
].typeName
))
2379 // IODBC does not return a correct columnSize, so we set
2380 // columnSize = bufferLength if no column size was returned
2381 // IODBC returns the columnSize in bufferLength.. (bug)
2382 if (colInf
[colNo
].columnSize
< 1)
2384 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2388 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2390 else if (!wxStricmp(typeInfInteger
.TypeName
, colInf
[colNo
].typeName
))
2391 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2392 else if (!wxStricmp(typeInfFloat
.TypeName
, colInf
[colNo
].typeName
))
2393 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2394 else if (!wxStricmp(typeInfDate
.TypeName
, colInf
[colNo
].typeName
))
2395 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2396 else if (!wxStricmp(typeInfBlob
.TypeName
, colInf
[colNo
].typeName
))
2397 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2403 if (retcode
!= SQL_NO_DATA_FOUND
)
2404 { // Error occured, abort
2405 DispAllErrors(henv
, hdbc
, hstmt
);
2408 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2415 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2417 // Store Primary and Foriegn Keys
2418 GetKeyFields(tableName
,colInf
,noCols
);
2424 } // wxDb::GetColumns()
2427 #else // New GetColumns
2432 These are tentative new GetColumns members which should be more database
2433 independant and which always returns the columns in the order they were
2436 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2437 wxChar* userID)) calls the second implementation for each separate table
2438 before merging the results. This makes the code easier to maintain as
2439 only one member (the second) makes the real work
2440 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2441 wxChar *userID) is a little bit improved
2442 - It doesn't anymore rely on the type-name to find out which database-type
2444 - It ends by sorting the columns, so that they are returned in the same
2445 order they were created
2455 wxDbColInf
*wxDb::GetColumns(wxChar
*tableName
[], const wxChar
*userID
)
2458 // The last array element of the tableName[] argument must be zero (null).
2459 // This is how the end of the array is detected.
2463 // How many tables ?
2465 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2467 // Create a table to maintain the columns for each separate table
2468 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2471 for (i
= 0 ; i
< tbl
; i
++)
2474 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2475 if (TableColumns
[i
].colInf
== NULL
)
2477 noCols
+= TableColumns
[i
].noCols
;
2480 // Now merge all the separate table infos
2481 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2483 // Mark the end of the array
2484 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2485 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2486 colInf
[noCols
].sqlDataType
= 0;
2491 for (i
= 0 ; i
< tbl
; i
++)
2493 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2495 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2499 delete [] TableColumns
;
2502 } // wxDb::GetColumns() -- NEW
2505 wxDbColInf
*wxDb::GetColumns(const wxString
&tableName
, int *numCols
, const wxChar
*userID
)
2507 // Same as the above GetColumns() function except this one gets columns
2508 // only for a single table, and if 'numCols' is not NULL, the number of
2509 // columns stored in the returned wxDbColInf is set in '*numCols'
2511 // userID is evaluated in the following manner:
2512 // userID == NULL ... UserID is ignored
2513 // userID == "" ... UserID set equal to 'this->uid'
2514 // userID != "" ... UserID set equal to 'userID'
2516 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2517 // by this function. This function should use its own wxDb instance
2518 // to avoid undesired unbinding of columns.
2522 wxDbColInf
*colInf
= 0;
2530 convertUserID(userID
,UserID
);
2532 // Pass 1 - Determine how many columns there are.
2533 // Pass 2 - Allocate the wxDbColInf array and fill in
2534 // the array with the column information.
2536 for (pass
= 1; pass
<= 2; pass
++)
2540 if (noCols
== 0) // Probably a bogus table name(s)
2542 // Allocate n wxDbColInf objects to hold the column information
2543 colInf
= new wxDbColInf
[noCols
+1];
2546 // Mark the end of the array
2547 wxStrcpy(colInf
[noCols
].tableName
, wxEmptyString
);
2548 wxStrcpy(colInf
[noCols
].colName
, wxEmptyString
);
2549 colInf
[noCols
].sqlDataType
= 0;
2552 TableName
= tableName
;
2553 // Oracle and Interbase table names are uppercase only, so force
2554 // the name to uppercase just in case programmer forgot to do this
2555 if ((Dbms() == dbmsORACLE
) ||
2556 (Dbms() == dbmsINTERBASE
))
2557 TableName
= TableName
.Upper();
2559 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2561 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2562 // use the call below that leaves out the user name
2563 if (!UserID
.IsEmpty() &&
2564 Dbms() != dbmsMY_SQL
&&
2565 Dbms() != dbmsACCESS
&&
2566 Dbms() != dbmsMS_SQL_SERVER
)
2568 retcode
= SQLColumns(hstmt
,
2569 NULL
, 0, // All qualifiers
2570 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2571 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2572 NULL
, 0); // All columns
2576 retcode
= SQLColumns(hstmt
,
2577 NULL
, 0, // All qualifiers
2579 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2580 NULL
, 0); // All columns
2582 if (retcode
!= SQL_SUCCESS
)
2583 { // Error occured, abort
2584 DispAllErrors(henv
, hdbc
, hstmt
);
2587 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2593 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2595 if (pass
== 1) // First pass, just add up the number of columns
2597 else // Pass 2; Fill in the array of structures
2599 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2601 // NOTE: Only the ODBC 1.x fields are retrieved
2602 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2603 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2604 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2605 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2606 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2607 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2608 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2609 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2610 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2611 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2612 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2613 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2614 // Start Values for Primary/Foriegn Key (=No)
2615 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2616 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2617 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2618 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2621 // IODBC does not return a correct columnSize, so we set
2622 // columnSize = bufferLength if no column size was returned
2623 // IODBC returns the columnSize in bufferLength.. (bug)
2624 if (colInf
[colNo
].columnSize
< 1)
2626 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2630 // Determine the wxDb data type that is used to represent the native data type of this data source
2631 colInf
[colNo
].dbDataType
= 0;
2632 // Get the intern datatype
2633 switch (colInf
[colNo
].sqlDataType
)
2637 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2643 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2650 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2653 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2656 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_BLOB
;
2661 errMsg
.Printf(wxT("SQL Data type %d currently not supported by wxWindows"), colInf
[colNo
].sqlDataType
);
2662 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2669 if (retcode
!= SQL_NO_DATA_FOUND
)
2670 { // Error occured, abort
2671 DispAllErrors(henv
, hdbc
, hstmt
);
2674 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2681 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2683 // Store Primary and Foreign Keys
2684 GetKeyFields(tableName
,colInf
,noCols
);
2686 ///////////////////////////////////////////////////////////////////////////
2687 // Now sort the the columns in order to make them appear in the right order
2688 ///////////////////////////////////////////////////////////////////////////
2690 // Build a generic SELECT statement which returns 0 rows
2693 Stmt
.Printf(wxT("select * from %s where 0=1"), tableName
);
2696 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2698 DispAllErrors(henv
, hdbc
, hstmt
);
2702 // Get the number of result columns
2703 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2705 DispAllErrors(henv
, hdbc
, hstmt
);
2709 if (noCols
== 0) // Probably a bogus table name
2718 for (colNum
= 0; colNum
< noCols
; colNum
++)
2720 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2722 &Sword
, &Sdword
) != SQL_SUCCESS
)
2724 DispAllErrors(henv
, hdbc
, hstmt
);
2728 wxString Name1
= name
;
2729 Name1
= Name1
.Upper();
2731 // Where is this name in the array ?
2732 for (i
= colNum
; i
< noCols
; i
++)
2734 wxString Name2
= colInf
[i
].colName
;
2735 Name2
= Name2
.Upper();
2738 if (colNum
!= i
) // swap to sort
2740 wxDbColInf tmpColInf
= colInf
[colNum
];
2741 colInf
[colNum
] = colInf
[i
];
2742 colInf
[i
] = tmpColInf
;
2748 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2750 ///////////////////////////////////////////////////////////////////////////
2752 ///////////////////////////////////////////////////////////////////////////
2758 } // wxDb::GetColumns()
2761 #endif // #else OLD_GETCOLUMNS
2764 /********** wxDb::GetColumnCount() **********/
2765 UWORD
wxDb::GetColumnCount(const wxString
&tableName
, const wxChar
*userID
)
2767 * Returns a count of how many columns are in a table.
2768 * If an error occurs in computing the number of columns
2769 * this function will return a -1 for the count
2771 * userID is evaluated in the following manner:
2772 * userID == NULL ... UserID is ignored
2773 * userID == "" ... UserID set equal to 'this->uid'
2774 * userID != "" ... UserID set equal to 'userID'
2776 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2777 * by this function. This function should use its own wxDb instance
2778 * to avoid undesired unbinding of columns.
2788 convertUserID(userID
,UserID
);
2790 TableName
= tableName
;
2791 // Oracle and Interbase table names are uppercase only, so force
2792 // the name to uppercase just in case programmer forgot to do this
2793 if ((Dbms() == dbmsORACLE
) ||
2794 (Dbms() == dbmsINTERBASE
))
2795 TableName
= TableName
.Upper();
2797 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2799 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2800 // use the call below that leaves out the user name
2801 if (!UserID
.IsEmpty() &&
2802 Dbms() != dbmsMY_SQL
&&
2803 Dbms() != dbmsACCESS
&&
2804 Dbms() != dbmsMS_SQL_SERVER
)
2806 retcode
= SQLColumns(hstmt
,
2807 NULL
, 0, // All qualifiers
2808 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2809 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2810 NULL
, 0); // All columns
2814 retcode
= SQLColumns(hstmt
,
2815 NULL
, 0, // All qualifiers
2817 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2818 NULL
, 0); // All columns
2820 if (retcode
!= SQL_SUCCESS
)
2821 { // Error occured, abort
2822 DispAllErrors(henv
, hdbc
, hstmt
);
2823 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2827 // Count the columns
2828 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2831 if (retcode
!= SQL_NO_DATA_FOUND
)
2832 { // Error occured, abort
2833 DispAllErrors(henv
, hdbc
, hstmt
);
2834 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2838 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2841 } // wxDb::GetColumnCount()
2844 /********** wxDb::GetCatalog() *******/
2845 wxDbInf
*wxDb::GetCatalog(const wxChar
*userID
)
2847 * ---------------------------------------------------------------------
2848 * -- 19991203 : mj10777 : Create ------
2849 * -- : Creates a wxDbInf with Tables / Cols Array ------
2850 * -- : uses SQLTables and fills pTableInf; ------
2851 * -- : pColInf is set to NULL and numCols to 0; ------
2852 * -- : returns pDbInf (wxDbInf) ------
2853 * -- - if unsuccesfull (pDbInf == NULL) ------
2854 * -- : pColInf can be filled with GetColumns(..); ------
2855 * -- : numCols can be filled with GetColumnCount(..); ------
2856 * ---------------------------------------------------------------------
2858 * userID is evaluated in the following manner:
2859 * userID == NULL ... UserID is ignored
2860 * userID == "" ... UserID set equal to 'this->uid'
2861 * userID != "" ... UserID set equal to 'userID'
2863 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2864 * by this function. This function should use its own wxDb instance
2865 * to avoid undesired unbinding of columns.
2868 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2869 int noTab
= 0; // Counter while filling table entries
2873 wxString tblNameSave
;
2876 convertUserID(userID
,UserID
);
2878 //-------------------------------------------------------------
2879 pDbInf
= new wxDbInf
; // Create the Database Array
2880 //-------------------------------------------------------------
2881 // Table Information
2882 // Pass 1 - Determine how many Tables there are.
2883 // Pass 2 - Create the Table array and fill it
2884 // - Create the Cols array = NULL
2885 //-------------------------------------------------------------
2887 for (pass
= 1; pass
<= 2; pass
++)
2889 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2890 tblNameSave
.Empty();
2892 if (!UserID
.IsEmpty() &&
2893 Dbms() != dbmsMY_SQL
&&
2894 Dbms() != dbmsACCESS
&&
2895 Dbms() != dbmsMS_SQL_SERVER
)
2897 retcode
= SQLTables(hstmt
,
2898 NULL
, 0, // All qualifiers
2899 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2900 NULL
, 0, // All tables
2901 NULL
, 0); // All columns
2905 retcode
= SQLTables(hstmt
,
2906 NULL
, 0, // All qualifiers
2907 NULL
, 0, // User specified
2908 NULL
, 0, // All tables
2909 NULL
, 0); // All columns
2912 if (retcode
!= SQL_SUCCESS
)
2914 DispAllErrors(henv
, hdbc
, hstmt
);
2916 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2920 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2922 if (pass
== 1) // First pass, just count the Tables
2924 if (pDbInf
->numTables
== 0)
2926 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2927 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2929 pDbInf
->numTables
++; // Counter for Tables
2931 if (pass
== 2) // Create and fill the Table entries
2933 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2934 { // no, then create the Array
2935 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2937 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2939 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2940 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2941 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2947 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2949 // Query how many columns are in each table
2950 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2952 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2957 } // wxDb::GetCatalog()
2960 /********** wxDb::Catalog() **********/
2961 bool wxDb::Catalog(const wxChar
*userID
, const wxString
&fileName
)
2963 * Creates the text file specified in 'filename' which will contain
2964 * a minimal data dictionary of all tables accessible by the user specified
2967 * userID is evaluated in the following manner:
2968 * userID == NULL ... UserID is ignored
2969 * userID == "" ... UserID set equal to 'this->uid'
2970 * userID != "" ... UserID set equal to 'userID'
2972 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2973 * by this function. This function should use its own wxDb instance
2974 * to avoid undesired unbinding of columns.
2977 wxASSERT(fileName
.Length());
2981 wxChar tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2982 wxString tblNameSave
;
2983 wxChar colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2985 wxChar typeName
[30+1];
2986 SWORD precision
, length
;
2988 FILE *fp
= fopen(fileName
.c_str(),wxT("wt"));
2992 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2995 convertUserID(userID
,UserID
);
2997 if (!UserID
.IsEmpty() &&
2998 Dbms() != dbmsMY_SQL
&&
2999 Dbms() != dbmsACCESS
&&
3000 Dbms() != dbmsMS_SQL_SERVER
)
3002 retcode
= SQLColumns(hstmt
,
3003 NULL
, 0, // All qualifiers
3004 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
3005 NULL
, 0, // All tables
3006 NULL
, 0); // All columns
3010 retcode
= SQLColumns(hstmt
,
3011 NULL
, 0, // All qualifiers
3012 NULL
, 0, // User specified
3013 NULL
, 0, // All tables
3014 NULL
, 0); // All columns
3016 if (retcode
!= SQL_SUCCESS
)
3018 DispAllErrors(henv
, hdbc
, hstmt
);
3024 tblNameSave
.Empty();
3027 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
3029 if (wxStrcmp(tblName
, tblNameSave
.c_str()))
3032 fputs(wxT("\n"), fp
);
3033 fputs(wxT("================================ "), fp
);
3034 fputs(wxT("================================ "), fp
);
3035 fputs(wxT("===================== "), fp
);
3036 fputs(wxT("========= "), fp
);
3037 fputs(wxT("=========\n"), fp
);
3038 outStr
.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3039 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3040 fputs(outStr
.c_str(), fp
);
3041 fputs(wxT("================================ "), fp
);
3042 fputs(wxT("================================ "), fp
);
3043 fputs(wxT("===================== "), fp
);
3044 fputs(wxT("========= "), fp
);
3045 fputs(wxT("=========\n"), fp
);
3046 tblNameSave
= tblName
;
3049 GetData(3,SQL_C_CHAR
, (UCHAR
*)tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
3050 GetData(4,SQL_C_CHAR
, (UCHAR
*)colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
3051 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
,0, &cb
);
3052 GetData(6,SQL_C_CHAR
, (UCHAR
*)typeName
, sizeof(typeName
), &cb
);
3053 GetData(7,SQL_C_SSHORT
,(UCHAR
*)&precision
, 0, &cb
);
3054 GetData(8,SQL_C_SSHORT
,(UCHAR
*)&length
, 0, &cb
);
3056 outStr
.Printf(wxT("%-32s %-32s (%04d)%-15s %9d %9d\n"),
3057 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
3058 if (fputs(outStr
.c_str(), fp
) == EOF
)
3060 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3067 if (retcode
!= SQL_NO_DATA_FOUND
)
3068 DispAllErrors(henv
, hdbc
, hstmt
);
3070 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3073 return(retcode
== SQL_NO_DATA_FOUND
);
3075 } // wxDb::Catalog()
3078 bool wxDb::TableExists(const wxString
&tableName
, const wxChar
*userID
, const wxString
&tablePath
)
3080 * Table name can refer to a table, view, alias or synonym. Returns true
3081 * if the object exists in the database. This function does not indicate
3082 * whether or not the user has privleges to query or perform other functions
3085 * userID is evaluated in the following manner:
3086 * userID == NULL ... UserID is ignored
3087 * userID == "" ... UserID set equal to 'this->uid'
3088 * userID != "" ... UserID set equal to 'userID'
3091 wxASSERT(tableName
.Length());
3095 if (Dbms() == dbmsDBASE
)
3098 if (tablePath
.Length())
3099 dbName
.Printf(wxT("%s/%s.dbf"), tablePath
.c_str(), tableName
.c_str());
3101 dbName
.Printf(wxT("%s.dbf"), tableName
.c_str());
3104 exists
= wxFileExists(dbName
);
3109 convertUserID(userID
,UserID
);
3111 TableName
= tableName
;
3112 // Oracle and Interbase table names are uppercase only, so force
3113 // the name to uppercase just in case programmer forgot to do this
3114 if ((Dbms() == dbmsORACLE
) ||
3115 (Dbms() == dbmsINTERBASE
))
3116 TableName
= TableName
.Upper();
3118 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3121 // Some databases cannot accept a user name when looking up table names,
3122 // so we use the call below that leaves out the user name
3123 if (!UserID
.IsEmpty() &&
3124 Dbms() != dbmsMY_SQL
&&
3125 Dbms() != dbmsACCESS
&&
3126 Dbms() != dbmsMS_SQL_SERVER
)
3128 retcode
= SQLTables(hstmt
,
3129 NULL
, 0, // All qualifiers
3130 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3131 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3132 NULL
, 0); // All table types
3136 retcode
= SQLTables(hstmt
,
3137 NULL
, 0, // All qualifiers
3138 NULL
, 0, // All owners
3139 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3140 NULL
, 0); // All table types
3142 if (retcode
!= SQL_SUCCESS
)
3143 return(DispAllErrors(henv
, hdbc
, hstmt
));
3145 retcode
= SQLFetch(hstmt
);
3146 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3148 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3149 return(DispAllErrors(henv
, hdbc
, hstmt
));
3152 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3156 } // wxDb::TableExists()
3159 /********** wxDb::TablePrivileges() **********/
3160 bool wxDb::TablePrivileges(const wxString
&tableName
, const wxString
&priv
, const wxChar
*userID
,
3161 const wxChar
*schema
, const wxString
&tablePath
)
3163 wxASSERT(tableName
.Length());
3165 wxDbTablePrivilegeInfo result
;
3169 // We probably need to be able to dynamically set this based on
3170 // the driver type, and state.
3171 wxChar curRole
[]=wxT("public");
3175 wxString UserID
,Schema
;
3176 convertUserID(userID
,UserID
);
3177 convertUserID(schema
,Schema
);
3179 TableName
= tableName
;
3180 // Oracle and Interbase table names are uppercase only, so force
3181 // the name to uppercase just in case programmer forgot to do this
3182 if ((Dbms() == dbmsORACLE
) ||
3183 (Dbms() == dbmsINTERBASE
))
3184 TableName
= TableName
.Upper();
3186 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3188 // Some databases cannot accept a user name when looking up table names,
3189 // so we use the call below that leaves out the user name
3190 if (!Schema
.IsEmpty() &&
3191 Dbms() != dbmsMY_SQL
&&
3192 Dbms() != dbmsACCESS
&&
3193 Dbms() != dbmsMS_SQL_SERVER
)
3195 retcode
= SQLTablePrivileges(hstmt
,
3197 (UCHAR FAR
*)Schema
.c_str(), SQL_NTS
, // Schema
3198 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3202 retcode
= SQLTablePrivileges(hstmt
,
3205 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3208 #ifdef DBDEBUG_CONSOLE
3209 fprintf(stderr
,wxT("SQLTablePrivileges() returned %i \n"),retcode
);
3212 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3213 return(DispAllErrors(henv
, hdbc
, hstmt
));
3215 retcode
= SQLFetch(hstmt
);
3216 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3218 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3219 return(DispAllErrors(henv
, hdbc
, hstmt
));
3221 if (SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3222 return(DispAllErrors(henv
, hdbc
, hstmt
));
3224 if (SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3225 return(DispAllErrors(henv
, hdbc
, hstmt
));
3227 if (SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3228 return(DispAllErrors(henv
, hdbc
, hstmt
));
3230 if (SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3231 return(DispAllErrors(henv
, hdbc
, hstmt
));
3233 if (SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3234 return(DispAllErrors(henv
, hdbc
, hstmt
));
3236 if (SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3237 return(DispAllErrors(henv
, hdbc
, hstmt
));
3239 #ifdef DBDEBUG_CONSOLE
3240 fprintf(stderr
,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3241 result
.privilege
,result
.tableOwner
,result
.tableName
,
3242 result
.grantor
, result
.grantee
);
3245 if (UserID
.IsSameAs(result
.tableOwner
,FALSE
))
3247 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3251 if (UserID
.IsSameAs(result
.grantee
,FALSE
) &&
3252 !wxStrcmp(result
.privilege
,priv
))
3254 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3258 if (!wxStrcmp(result
.grantee
,curRole
) &&
3259 !wxStrcmp(result
.privilege
,priv
))
3261 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3265 retcode
= SQLFetch(hstmt
);
3268 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3271 } // wxDb::TablePrivileges
3274 /********** wxDb::SetSqlLogging() **********/
3275 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const wxString
&filename
, bool append
)
3277 wxASSERT(state
== sqlLogON
|| state
== sqlLogOFF
);
3278 wxASSERT(state
== sqlLogOFF
|| filename
.Length());
3280 if (state
== sqlLogON
)
3284 fpSqlLog
= fopen(filename
, (append
? wxT("at") : wxT("wt")));
3285 if (fpSqlLog
== NULL
)
3293 if (fclose(fpSqlLog
))
3299 sqlLogState
= state
;
3302 } // wxDb::SetSqlLogging()
3305 /********** wxDb::WriteSqlLog() **********/
3306 bool wxDb::WriteSqlLog(const wxString
&logMsg
)
3308 wxASSERT(logMsg
.Length());
3310 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3313 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3315 if (fputs(logMsg
, fpSqlLog
) == EOF
)
3317 if (fputs(wxT("\n"), fpSqlLog
) == EOF
)
3322 } // wxDb::WriteSqlLog()
3325 /********** wxDb::Dbms() **********/
3326 wxDBMS
wxDb::Dbms(void)
3328 * Be aware that not all database engines use the exact same syntax, and not
3329 * every ODBC compliant database is compliant to the same level of compliancy.
3330 * Some manufacturers support the minimum Level 1 compliancy, and others up
3331 * through Level 3. Others support subsets of features for levels above 1.
3333 * If you find an inconsistency between the wxDb class and a specific database
3334 * engine, and an identifier to this section, and special handle the database in
3335 * the area where behavior is non-conforming with the other databases.
3338 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3339 * ---------------------------------------------------
3342 * - Currently the only database supported by the class to support VIEWS
3345 * - Does not support the SQL_TIMESTAMP structure
3346 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3347 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3348 * is TRUE. The user must create ALL indexes from their program.
3349 * - Table names can only be 8 characters long
3350 * - Column names can only be 10 characters long
3353 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3354 * after every table name involved in the query/join if that tables matching record(s)
3356 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3358 * SYBASE (Enterprise)
3359 * - If a column is part of the Primary Key, the column cannot be NULL
3360 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3363 * - If a column is part of the Primary Key, the column cannot be NULL
3364 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3365 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3366 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3367 * column definition if it is not defined correctly, but it is experimental
3368 * - Does not support sub-queries in SQL statements
3371 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3372 * - Does not support sub-queries in SQL statements
3375 * - Primary keys must be declared as NOT NULL
3379 // Should only need to do this once for each new database connection
3380 // so return the value we already determined it to be to save time
3381 // and lots of string comparisons
3382 if (dbmsType
!= dbmsUNIDENTIFIED
)
3385 wxChar baseName
[25+1];
3386 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3389 // RGG 20001025 : add support for Interbase
3390 // GT : Integrated to base classes on 20001121
3391 if (!wxStricmp(dbInf
.dbmsName
,wxT("Interbase")))
3392 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3394 // BJO 20000428 : add support for Virtuoso
3395 if (!wxStricmp(dbInf
.dbmsName
,wxT("OpenLink Virtuoso VDBMS")))
3396 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3398 if (!wxStricmp(dbInf
.dbmsName
,wxT("Adaptive Server Anywhere")))
3399 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3401 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3402 // connected through an OpenLink driver.
3403 // Is it also returned by Sybase Adapatitve server?
3404 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3405 if (!wxStricmp(dbInf
.dbmsName
,wxT("SQL Server")))
3407 if (!wxStrncmp(dbInf
.driverName
, wxT("oplodbc"), 7) ||
3408 !wxStrncmp(dbInf
.driverName
, wxT("OLOD"), 4))
3409 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3411 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3414 if (!wxStricmp(dbInf
.dbmsName
,wxT("Microsoft SQL Server")))
3415 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3416 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3417 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3418 if (!wxStricmp(dbInf
.dbmsName
,wxT("PostgreSQL"))) // v6.5.0
3419 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3422 if (!wxStricmp(baseName
,wxT("Informix")))
3423 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3426 if (!wxStricmp(baseName
,wxT("Oracle")))
3427 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3428 if (!wxStricmp(dbInf
.dbmsName
,wxT("ACCESS")))
3429 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3430 if (!wxStricmp(dbInf
.dbmsName
,wxT("MySQL")))
3431 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3432 if (!wxStricmp(baseName
,wxT("Sybase")))
3433 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3436 if (!wxStricmp(baseName
,wxT("DBASE")))
3437 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3440 if (!wxStricmp(baseName
,wxT("DB2")))
3441 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3443 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3448 bool wxDb::ModifyColumn(const wxString
&tableName
, const wxString
&columnName
,
3449 int dataType
, ULONG columnLength
,
3450 const wxString
&optionalParam
)
3452 wxASSERT(tableName
.Length());
3453 wxASSERT(columnName
.Length());
3454 wxASSERT((dataType
== DB_DATA_TYPE_VARCHAR
&& columnLength
> 0) ||
3455 dataType
!= DB_DATA_TYPE_VARCHAR
);
3457 // Must specify a columnLength if modifying a VARCHAR type column
3458 if (dataType
== DB_DATA_TYPE_VARCHAR
&& !columnLength
)
3461 wxString dataTypeName
;
3463 wxString alterSlashModify
;
3467 case DB_DATA_TYPE_VARCHAR
:
3468 dataTypeName
= typeInfVarchar
.TypeName
;
3470 case DB_DATA_TYPE_INTEGER
:
3471 dataTypeName
= typeInfInteger
.TypeName
;
3473 case DB_DATA_TYPE_FLOAT
:
3474 dataTypeName
= typeInfFloat
.TypeName
;
3476 case DB_DATA_TYPE_DATE
:
3477 dataTypeName
= typeInfDate
.TypeName
;
3479 case DB_DATA_TYPE_BLOB
:
3480 dataTypeName
= typeInfBlob
.TypeName
;
3486 // Set the modify or alter syntax depending on the type of database connected to
3490 alterSlashModify
= "MODIFY";
3492 case dbmsMS_SQL_SERVER
:
3493 alterSlashModify
= "ALTER COLUMN";
3495 case dbmsUNIDENTIFIED
:
3497 case dbmsSYBASE_ASA
:
3498 case dbmsSYBASE_ASE
:
3504 alterSlashModify
= "MODIFY";
3508 // create the SQL statement
3509 sqlStmt
.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName
.c_str(), alterSlashModify
.c_str(),
3510 columnName
.c_str(), dataTypeName
.c_str());
3512 // For varchars only, append the size of the column
3513 if (dataType
== DB_DATA_TYPE_VARCHAR
)
3516 s
.Printf(wxT("(%d)"), columnLength
);
3520 // for passing things like "NOT NULL"
3521 if (optionalParam
.Length())
3523 sqlStmt
+= wxT(" ");
3524 sqlStmt
+= optionalParam
;
3527 return ExecSql(sqlStmt
);
3529 } // wxDb::ModifyColumn()
3532 /********** wxDbGetConnection() **********/
3533 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3537 // Used to keep a pointer to a DB connection that matches the requested
3538 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3539 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3540 // rather than having to re-query the datasource to get all the values
3541 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3542 wxDb
*matchingDbConnection
= NULL
;
3544 // Scan the linked list searching for an available database connection
3545 // that's already been opened but is currently not in use.
3546 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3548 // The database connection must be for the same datasource
3549 // name and must currently not be in use.
3551 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3552 (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
))) // Found a free connection
3554 pList
->Free
= FALSE
;
3555 return(pList
->PtrDb
);
3558 if (!wxStrcmp(pDbConfig
->GetDsn(), pList
->Dsn
) &&
3559 !wxStrcmp(pDbConfig
->GetUserID(), pList
->Uid
) &&
3560 !wxStrcmp(pDbConfig
->GetPassword(), pList
->AuthStr
))
3561 matchingDbConnection
= pList
->PtrDb
;
3564 // No available connections. A new connection must be made and
3565 // appended to the end of the linked list.
3568 // Find the end of the list
3569 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3570 // Append a new list item
3571 pList
->PtrNext
= new wxDbList
;
3572 pList
->PtrNext
->PtrPrev
= pList
;
3573 pList
= pList
->PtrNext
;
3577 // Create the first node on the list
3578 pList
= PtrBegDbList
= new wxDbList
;
3582 // Initialize new node in the linked list
3584 pList
->Free
= FALSE
;
3585 pList
->Dsn
= pDbConfig
->GetDsn(); //glt - will this assignment work?
3586 pList
->Uid
= pDbConfig
->GetUserID();
3587 pList
->AuthStr
= pDbConfig
->GetPassword();
3589 pList
->PtrDb
= new wxDb(pDbConfig
->GetHenv(), FwdOnlyCursors
);
3591 bool opened
= FALSE
;
3593 if (!matchingDbConnection
)
3594 opened
= pList
->PtrDb
->Open(pDbConfig
->GetDsn(), pDbConfig
->GetUserID(), pDbConfig
->GetPassword());
3596 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3598 // Connect to the datasource
3601 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3602 return(pList
->PtrDb
);
3604 else // Unable to connect, destroy list item
3607 pList
->PtrPrev
->PtrNext
= 0;
3609 PtrBegDbList
= 0; // Empty list again
3610 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3611 pList
->PtrDb
->Close(); // Close the wxDb object
3612 delete pList
->PtrDb
; // Deletes the wxDb object
3613 delete pList
; // Deletes the linked list object
3617 } // wxDbGetConnection()
3620 /********** wxDbFreeConnection() **********/
3621 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3625 // Scan the linked list searching for the database connection
3626 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3628 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3629 return (pList
->Free
= TRUE
);
3632 // Never found the database object, return failure
3635 } // wxDbFreeConnection()
3638 /********** wxDbCloseConnections() **********/
3639 void WXDLLEXPORT
wxDbCloseConnections(void)
3641 wxDbList
*pList
, *pNext
;
3643 // Traverse the linked list closing database connections and freeing memory as I go.
3644 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3646 pNext
= pList
->PtrNext
; // Save the pointer to next
3647 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3648 pList
->PtrDb
->Close(); // Close the wxDb object
3649 delete pList
->PtrDb
; // Deletes the wxDb object
3650 delete pList
; // Deletes the linked list object
3653 // Mark the list as empty
3656 } // wxDbCloseConnections()
3659 /********** wxDbConnectionsInUse() **********/
3660 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3665 // Scan the linked list counting db connections that are currently in use
3666 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3668 if (pList
->Free
== FALSE
)
3674 } // wxDbConnectionsInUse()
3677 /********** wxDbSqlLog() **********/
3678 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3680 bool append
= FALSE
;
3683 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3685 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3690 SQLLOGstate
= state
;
3691 SQLLOGfn
= filename
;
3699 /********** wxDbCreateDataSource() **********/
3700 int wxDbCreateDataSource(const wxString
&driverName
, const wxString
&dsn
, const wxString
&description
,
3701 bool sysDSN
, const wxString
&defDir
, wxWindow
*parent
)
3703 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3704 * Very rudimentary creation of an ODBC data source.
3706 * ODBC driver must be ODBC 3.0 compliant to use this function
3711 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3717 dsnLocation
= ODBC_ADD_SYS_DSN
;
3719 dsnLocation
= ODBC_ADD_DSN
;
3721 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3722 // so that is why I used it, as wxString does not deal well with
3723 // embedded nulls in strings
3724 setupStr
.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn
,2,description
,2,defDir
,2);
3726 // Replace the separator from above with the '\0' seperator needed
3727 // by the SQLConfigDataSource() function
3731 k
= setupStr
.Find((wxChar
)2,TRUE
);
3732 if (k
!= wxNOT_FOUND
)
3733 setupStr
[(UINT
)k
] = wxT('\0');
3735 while (k
!= wxNOT_FOUND
);
3737 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3738 driverName
, setupStr
.c_str());
3740 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3742 // check for errors caused by ConfigDSN based functions
3745 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3746 errMsg
[0] = wxT('\0');
3748 // This function is only supported in ODBC drivers v3.0 compliant and above
3749 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3752 #ifdef DBDEBUG_CONSOLE
3753 // When run in console mode, use standard out to display errors.
3754 cout
<< errMsg
<< endl
;
3755 cout
<< wxT("Press any key to continue...") << endl
;
3757 #endif // DBDEBUG_CONSOLE
3760 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3761 #endif // __WXDEBUG__
3767 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3768 // necessary to use this function, so this function is not supported
3770 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
3773 #endif // __VISUALC__
3777 } // wxDbCreateDataSource()
3781 /********** wxDbGetDataSource() **********/
3782 bool wxDbGetDataSource(HENV henv
, wxChar
*Dsn
, SWORD DsnMax
, wxChar
*DsDesc
,
3783 SWORD DsDescMax
, UWORD direction
)
3785 * Dsn and DsDesc will contain the data source name and data source
3786 * description upon return
3791 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3792 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3797 } // wxDbGetDataSource()
3800 // Change this to 0 to remove use of all deprecated functions
3801 #if wxODBC_BACKWARD_COMPATABILITY
3802 /********************************************************************
3803 ********************************************************************
3805 * The following functions are all DEPRECATED and are included for
3806 * backward compatability reasons only
3808 ********************************************************************
3809 ********************************************************************/
3810 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3812 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3814 /***** DEPRECATED: use wxGetDataSource() *****/
3815 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3818 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3820 /***** DEPRECATED: use wxDbGetConnection() *****/
3821 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3823 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3825 /***** DEPRECATED: use wxDbFreeConnection() *****/
3826 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3828 return wxDbFreeConnection(pDb
);
3830 /***** DEPRECATED: use wxDbCloseConnections() *****/
3831 void WXDLLEXPORT
CloseDbConnections(void)
3833 wxDbCloseConnections();
3835 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3836 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3838 return wxDbConnectionsInUse();