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 char const *SQL_LOG_FILENAME
= "sqllog.txt";
103 char const *SQL_CATALOG_FILENAME
= "catalog.txt";
106 extern wxList TablesInUse
;
109 // SQL Log defaults to be used by GetDbConnection
110 wxDbSqlLogState SQLLOGstate
= sqlLogOFF
;
112 //char SQLLOGfn[wxDB_PATH_MAX+1] = SQL_LOG_FILENAME;
113 //wxChar *SQLLOGfn = (wxChar*) SQL_LOG_FILENAME;
114 static wxString SQLLOGfn
= SQL_LOG_FILENAME
;
116 // The wxDb::errorList is copied to this variable when the wxDb object
117 // is closed. This way, the error list is still available after the
118 // database object is closed. This is necessary if the database
119 // connection fails so the calling application can show the operator
120 // why the connection failed. Note: as each wxDb object is closed, it
121 // will overwrite the errors of the previously destroyed wxDb object in
122 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
124 char DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
126 // This type defines the return row-struct form
127 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
130 wxChar tableQual
[128+1];
131 wxChar tableOwner
[128+1];
132 wxChar tableName
[128+1];
133 wxChar grantor
[128+1];
134 wxChar grantee
[128+1];
135 wxChar privilege
[128+1];
136 wxChar grantable
[3+1];
137 } wxDbTablePrivilegeInfo
;
140 /********** wxDbColFor Constructor **********/
141 wxDbColFor::wxDbColFor()
151 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
154 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
155 } // wxDbColFor::wxDbColFor()
158 wxDbColFor::~wxDbColFor()
160 } // wxDbColFor::~wxDbColFor()
163 /********** wxDbColInf Con / Destructor **********/
164 wxDbColInf::wxDbColInf()
184 } // wxDbColInf::wxDbColFor()
187 wxDbColInf::~wxDbColInf()
192 } // wxDbColInf::~wxDbColInf()
195 /********** wxDbTableInf Constructor ********/
196 wxDbTableInf::wxDbTableInf()
203 } // wxDbTableInf::wxDbTableFor()
206 /********** wxDbTableInf Constructor ********/
207 wxDbTableInf::~wxDbTableInf()
212 } // wxDbTableInf::~wxDbTableInf()
215 /********** wxDbInf Constructor *************/
222 } // wxDbInf::wxDbFor()
225 /********** wxDbInf Destructor *************/
231 } // wxDbInf::~wxDbInf()
234 /*************************************************/
237 int wxDbColFor::Format(int Nation
,int dbDataType
,SWORD sqlDataType
,short columnSize
,short decimalDigits
)
239 // ----------------------------------------------------------------------------------------
240 // -- 19991224 : mj10777 : Create
241 // There is still a lot of work to do here, but it is a start
242 // It handles all the basic data-types that I have run into up to now
243 // The main work will have be with Dates and float Formatting
244 // (US 1,000.00 ; EU 1.000,00)
245 // There are wxWindow plans for locale support and the new wxDateTime. If
246 // they define some constants (wxEUROPEAN) that can be gloably used,
247 // they should be used here.
248 // ----------------------------------------------------------------------------------------
249 // There should also be a function to scan in a string to fill the variable
250 // ----------------------------------------------------------------------------------------
252 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
253 i_dbDataType
= dbDataType
;
254 i_sqlDataType
= sqlDataType
;
255 s_Field
.Printf(wxT("%s%d"),s_Amount
[1].c_str(),i_Amount
[1]); // OK for VARCHAR, INTEGER and FLOAT
256 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
258 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
259 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
260 if (i_sqlDataType
== SQL_C_DATE
)
261 i_dbDataType
= DB_DATA_TYPE_DATE
;
262 if (i_sqlDataType
== SQL_C_BIT
)
263 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
264 if (i_sqlDataType
== SQL_NUMERIC
)
265 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
266 if (i_sqlDataType
== SQL_REAL
)
267 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
269 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
271 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
273 switch(i_dbDataType
) // -A-> Still a lot of proper formatting to do
275 case DB_DATA_TYPE_VARCHAR
:
278 case DB_DATA_TYPE_INTEGER
:
281 case DB_DATA_TYPE_FLOAT
:
282 if (decimalDigits
== 0)
285 Temp0
.Printf(wxT("%s%d.%d"),Temp0
.c_str(),columnSize
,decimalDigits
);
286 s_Field
.Printf(wxT("%sf"),Temp0
.c_str());
288 case DB_DATA_TYPE_DATE
:
289 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
291 s_Field
= "%04d-%02d-%02d %02d:%02d:%02d.%03d";
293 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
295 s_Field
= "%02d.%02d.%04d %02d:%02d:%02d.%03d";
297 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
299 s_Field
= "%02d/%02d/%04d %02d:%02d:%02d.%03d";
301 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
303 s_Field
= "%04d-%02d-%02d %02d:%02d:%02d.%03d";
305 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
307 s_Field
= "%02d/%02d/%04d %02d:%02d:%02d.%03d";
311 s_Field
.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType
,sqlDataType
); //
315 } // wxDbColFor::Format()
318 /********** wxDb Constructors **********/
319 wxDb::wxDb(HENV
&aHenv
, bool FwdOnlyCursors
)
321 // Copy the HENV into the db class
323 fwdOnlyCursors
= FwdOnlyCursors
;
328 void wxDb::initialize()
330 * Private member function that sets all wxDb member variables to
331 * known values at creation of the wxDb
336 fpSqlLog
= 0; // Sql Log file pointer
337 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
339 dbmsType
= dbmsUNIDENTIFIED
;
341 wxStrcpy(sqlState
,wxT(""));
342 wxStrcpy(errorMsg
,wxT(""));
343 nativeError
= cbErrorMsg
= 0;
344 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
345 wxStrcpy(errorList
[i
], wxT(""));
347 // Init typeInf structures
348 wxStrcpy(typeInfVarchar
.TypeName
,wxT(""));
349 typeInfVarchar
.FsqlType
= 0;
350 typeInfVarchar
.Precision
= 0;
351 typeInfVarchar
.CaseSensitive
= 0;
352 typeInfVarchar
.MaximumScale
= 0;
354 wxStrcpy(typeInfInteger
.TypeName
,wxT(""));
355 typeInfInteger
.FsqlType
= 0;
356 typeInfInteger
.Precision
= 0;
357 typeInfInteger
.CaseSensitive
= 0;
358 typeInfInteger
.MaximumScale
= 0;
360 wxStrcpy(typeInfFloat
.TypeName
,wxT(""));
361 typeInfFloat
.FsqlType
= 0;
362 typeInfFloat
.Precision
= 0;
363 typeInfFloat
.CaseSensitive
= 0;
364 typeInfFloat
.MaximumScale
= 0;
366 wxStrcpy(typeInfDate
.TypeName
,wxT(""));
367 typeInfDate
.FsqlType
= 0;
368 typeInfDate
.Precision
= 0;
369 typeInfDate
.CaseSensitive
= 0;
370 typeInfDate
.MaximumScale
= 0;
372 // Error reporting is turned OFF by default
375 // Allocate a data source connection handle
376 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
379 // Initialize the db status flag
382 // Mark database as not open as of yet
384 } // wxDb::initialize()
387 /********** wxDb::Open() **********/
388 bool wxDb::Open(char *Dsn
, char *Uid
, char *AuthStr
)
390 assert(Dsn
&& wxStrlen(Dsn
));
397 if (!FwdOnlyCursors())
399 // Specify that the ODBC cursor library be used, if needed. This must be
400 // specified before the connection is made.
401 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
403 #ifdef DBDEBUG_CONSOLE
404 if (retcode
== SQL_SUCCESS
)
405 cout
<< "SQLSetConnectOption(CURSOR_LIB) successful" << endl
;
407 cout
<< "SQLSetConnectOption(CURSOR_LIB) failed" << endl
;
411 // Connect to the data source
412 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
, SQL_NTS
,
413 (UCHAR FAR
*) uid
, SQL_NTS
,
414 (UCHAR FAR
*) authStr
,SQL_NTS
);
417 if (retcode == SQL_SUCCESS_WITH_INFO)
418 DispAllErrors(henv, hdbc);
419 else if (retcode != SQL_SUCCESS)
420 return(DispAllErrors(henv, hdbc));
422 if (retcode == SQL_ERROR)
423 return(DispAllErrors(henv, hdbc));
425 if ((retcode
!= SQL_SUCCESS
) &&
426 (retcode
!= SQL_SUCCESS_WITH_INFO
))
427 return(DispAllErrors(henv
, hdbc
));
430 If using Intersolv branded ODBC drivers, this is the place where you would substitute
431 your branded driver license information
433 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
434 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
437 // Mark database as open
440 // Allocate a statement handle for the database connection
441 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
442 return(DispAllErrors(henv
, hdbc
));
444 // Set Connection Options
445 if (!setConnectionOptions())
448 // Query the data source for inf. about itself
452 // Query the data source regarding data type information
455 // The way I determined which SQL data types to use was by calling SQLGetInfo
456 // for all of the possible SQL data types to see which ones were supported. If
457 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
458 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
459 // types I've selected below will not alway's be what we want. These are just
460 // what happened to work against an Oracle 7/Intersolv combination. The following is
461 // a complete list of the results I got back against the Oracle 7 database:
463 // SQL_BIGINT SQL_NO_DATA_FOUND
464 // SQL_BINARY SQL_NO_DATA_FOUND
465 // SQL_BIT SQL_NO_DATA_FOUND
466 // SQL_CHAR type name = 'CHAR', Precision = 255
467 // SQL_DATE SQL_NO_DATA_FOUND
468 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
469 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
470 // SQL_FLOAT SQL_NO_DATA_FOUND
471 // SQL_INTEGER SQL_NO_DATA_FOUND
472 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
473 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
474 // SQL_NUMERIC SQL_NO_DATA_FOUND
475 // SQL_REAL SQL_NO_DATA_FOUND
476 // SQL_SMALLINT SQL_NO_DATA_FOUND
477 // SQL_TIME SQL_NO_DATA_FOUND
478 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
479 // SQL_VARBINARY type name = 'RAW', Precision = 255
480 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
481 // =====================================================================
482 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
484 // SQL_VARCHAR type name = 'TEXT', Precision = 255
485 // SQL_TIMESTAMP type name = 'DATETIME'
486 // SQL_DECIMAL SQL_NO_DATA_FOUND
487 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
488 // SQL_FLOAT SQL_NO_DATA_FOUND
489 // SQL_REAL type name = 'SINGLE', Precision = 7
490 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
491 // SQL_INTEGER type name = 'LONG', Precision = 10
493 // VARCHAR = Variable length character string
494 if (!getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
495 if (!getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
498 typeInfVarchar
.FsqlType
= SQL_CHAR
;
500 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
503 if (!getDataTypeInfo(SQL_DOUBLE
,typeInfFloat
))
505 if (!getDataTypeInfo(SQL_REAL
,typeInfFloat
))
506 if (!getDataTypeInfo(SQL_FLOAT
,typeInfFloat
))
507 if (!getDataTypeInfo(SQL_DECIMAL
,typeInfFloat
))
508 if (!getDataTypeInfo(SQL_NUMERIC
,typeInfFloat
))
511 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
513 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
515 typeInfFloat
.FsqlType
= SQL_FLOAT
;
517 typeInfFloat
.FsqlType
= SQL_REAL
;
519 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
522 if (!getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
524 // If SQL_INTEGER is not supported, use the floating point
525 // data type to store integers as well as floats
526 if (!getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
529 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
532 typeInfInteger
.FsqlType
= SQL_INTEGER
;
535 if (Dbms() != dbmsDBASE
)
537 if (! getDataTypeInfo(SQL_TIMESTAMP
,typeInfDate
))
540 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
544 if (!getDataTypeInfo(SQL_DATE
,typeInfDate
))
547 typeInfDate
.FsqlType
= SQL_DATE
;
550 #ifdef DBDEBUG_CONSOLE
551 cout
<< "VARCHAR DATA TYPE: " << typeInfVarchar
.TypeName
<< endl
;
552 cout
<< "INTEGER DATA TYPE: " << typeInfInteger
.TypeName
<< endl
;
553 cout
<< "FLOAT DATA TYPE: " << typeInfFloat
.TypeName
<< endl
;
554 cout
<< "DATE DATA TYPE: " << typeInfDate
.TypeName
<< endl
;
558 // Completed Successfully
564 bool wxDb::Open(wxDb
*copyDb
)
566 dsn
= (char *)copyDb
->GetDataSourceName();
567 uid
= (char *)copyDb
->GetUsername();
568 authStr
= (char *)copyDb
->GetPassword();
572 if (!FwdOnlyCursors())
574 // Specify that the ODBC cursor library be used, if needed. This must be
575 // specified before the connection is made.
576 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
578 #ifdef DBDEBUG_CONSOLE
579 if (retcode
== SQL_SUCCESS
)
580 cout
<< "SQLSetConnectOption(CURSOR_LIB) successful" << endl
;
582 cout
<< "SQLSetConnectOption(CURSOR_LIB) failed" << endl
;
586 // Connect to the data source
587 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) dsn
, SQL_NTS
,
588 (UCHAR FAR
*) uid
, SQL_NTS
,
589 (UCHAR FAR
*) authStr
, SQL_NTS
);
591 if (retcode
== SQL_ERROR
)
592 return(DispAllErrors(henv
, hdbc
));
595 If using Intersolv branded ODBC drivers, this is the place where you would substitute
596 your branded driver license information
598 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
599 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
602 // Mark database as open
605 // Allocate a statement handle for the database connection
606 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
607 return(DispAllErrors(henv
, hdbc
));
609 // Set Connection Options
610 if (!setConnectionOptions())
613 // Instead of Querying the data source for info about itself, it can just be copied
614 // from the wxDb instance that was passed in (copyDb).
615 wxStrcpy(dbInf
.serverName
,copyDb
->dbInf
.serverName
);
616 wxStrcpy(dbInf
.databaseName
,copyDb
->dbInf
.databaseName
);
617 wxStrcpy(dbInf
.dbmsName
,copyDb
->dbInf
.dbmsName
);
618 wxStrcpy(dbInf
.dbmsVer
,copyDb
->dbInf
.dbmsVer
);
619 dbInf
.maxConnections
= copyDb
->dbInf
.maxConnections
;
620 dbInf
.maxStmts
= copyDb
->dbInf
.maxStmts
;
621 wxStrcpy(dbInf
.driverName
,copyDb
->dbInf
.driverName
);
622 wxStrcpy(dbInf
.odbcVer
,copyDb
->dbInf
.odbcVer
);
623 wxStrcpy(dbInf
.drvMgrOdbcVer
,copyDb
->dbInf
.drvMgrOdbcVer
);
624 wxStrcpy(dbInf
.driverVer
,copyDb
->dbInf
.driverVer
);
625 dbInf
.apiConfLvl
= copyDb
->dbInf
.apiConfLvl
;
626 dbInf
.cliConfLvl
= copyDb
->dbInf
.cliConfLvl
;
627 dbInf
.sqlConfLvl
= copyDb
->dbInf
.sqlConfLvl
;
628 wxStrcpy(dbInf
.outerJoins
,copyDb
->dbInf
.outerJoins
);
629 wxStrcpy(dbInf
.procedureSupport
,copyDb
->dbInf
.procedureSupport
);
630 wxStrcpy(dbInf
.accessibleTables
,copyDb
->dbInf
.accessibleTables
);
631 dbInf
.cursorCommitBehavior
= copyDb
->dbInf
.cursorCommitBehavior
;
632 dbInf
.cursorRollbackBehavior
= copyDb
->dbInf
.cursorRollbackBehavior
;
633 dbInf
.supportNotNullClause
= copyDb
->dbInf
.supportNotNullClause
;
634 wxStrcpy(dbInf
.supportIEF
,copyDb
->dbInf
.supportIEF
);
635 dbInf
.txnIsolation
= copyDb
->dbInf
.txnIsolation
;
636 dbInf
.txnIsolationOptions
= copyDb
->dbInf
.txnIsolationOptions
;
637 dbInf
.fetchDirections
= copyDb
->dbInf
.fetchDirections
;
638 dbInf
.lockTypes
= copyDb
->dbInf
.lockTypes
;
639 dbInf
.posOperations
= copyDb
->dbInf
.posOperations
;
640 dbInf
.posStmts
= copyDb
->dbInf
.posStmts
;
641 dbInf
.scrollConcurrency
= copyDb
->dbInf
.scrollConcurrency
;
642 dbInf
.scrollOptions
= copyDb
->dbInf
.scrollOptions
;
643 dbInf
.staticSensitivity
= copyDb
->dbInf
.staticSensitivity
;
644 dbInf
.txnCapable
= copyDb
->dbInf
.txnCapable
;
645 dbInf
.loginTimeout
= copyDb
->dbInf
.loginTimeout
;
647 // VARCHAR = Variable length character string
648 typeInfVarchar
.FsqlType
= copyDb
->typeInfVarchar
.FsqlType
;
651 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
654 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
657 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
659 #ifdef DBDEBUG_CONSOLE
660 cout
<< "VARCHAR DATA TYPE: " << typeInfVarchar
.TypeName
<< endl
;
661 cout
<< "INTEGER DATA TYPE: " << typeInfInteger
.TypeName
<< endl
;
662 cout
<< "FLOAT DATA TYPE: " << typeInfFloat
.TypeName
<< endl
;
663 cout
<< "DATE DATA TYPE: " << typeInfDate
.TypeName
<< endl
;
667 // Completed Successfully
672 /********** wxDb::setConnectionOptions() **********/
673 bool wxDb::setConnectionOptions(void)
675 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
680 // I need to get the DBMS name here, because some of the connection options
681 // are database specific and need to call the Dbms() function.
682 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
683 return(DispAllErrors(henv
, hdbc
));
685 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
686 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
687 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
689 // By default, MS Sql Server closes cursors on commit and rollback. The following
690 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
691 // after a transaction. This is a driver specific option and is not part of the
692 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
693 // The database settings don't have any effect one way or the other.
694 if (Dbms() == dbmsMS_SQL_SERVER
)
696 const long SQL_PRESERVE_CURSORS
= 1204L;
697 const long SQL_PC_ON
= 1L;
698 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
701 // Display the connection options to verify them
702 #ifdef DBDEBUG_CONSOLE
704 cout
<< "****** CONNECTION OPTIONS ******" << endl
;
706 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
707 return(DispAllErrors(henv
, hdbc
));
708 cout
<< "AUTOCOMMIT: " << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
710 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
711 return(DispAllErrors(henv
, hdbc
));
712 cout
<< "ODBC CURSORS: ";
715 case(SQL_CUR_USE_IF_NEEDED
):
716 cout
<< "SQL_CUR_USE_IF_NEEDED";
718 case(SQL_CUR_USE_ODBC
):
719 cout
<< "SQL_CUR_USE_ODBC";
721 case(SQL_CUR_USE_DRIVER
):
722 cout
<< "SQL_CUR_USE_DRIVER";
727 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
728 return(DispAllErrors(henv
, hdbc
));
729 cout
<< "TRACING: " << (l
== SQL_OPT_TRACE_OFF
? "OFF" : "ON") << endl
;
734 // Completed Successfully
737 } // wxDb::setConnectionOptions()
740 /********** wxDb::getDbInfo() **********/
741 bool wxDb::getDbInfo(void)
746 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
747 return(DispAllErrors(henv
, hdbc
));
749 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
750 return(DispAllErrors(henv
, hdbc
));
752 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
753 return(DispAllErrors(henv
, hdbc
));
756 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
757 // causing database connectivity to fail in some cases.
758 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
760 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
761 return(DispAllErrors(henv
, hdbc
));
763 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
764 return(DispAllErrors(henv
, hdbc
));
766 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
767 return(DispAllErrors(henv
, hdbc
));
769 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
770 return(DispAllErrors(henv
, hdbc
));
772 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
773 return(DispAllErrors(henv
, hdbc
));
775 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
776 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
777 return(DispAllErrors(henv
, hdbc
));
779 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
780 return(DispAllErrors(henv
, hdbc
));
782 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
783 return(DispAllErrors(henv
, hdbc
));
785 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
786 // return(DispAllErrors(henv, hdbc));
788 // Not all drivers support this call - Nick Gorham(unixODBC)
789 dbInf
.cliConfLvl
= 0;
792 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
793 return(DispAllErrors(henv
, hdbc
));
795 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
796 return(DispAllErrors(henv
, hdbc
));
798 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
799 return(DispAllErrors(henv
, hdbc
));
801 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
802 return(DispAllErrors(henv
, hdbc
));
804 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
805 return(DispAllErrors(henv
, hdbc
));
807 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
808 return(DispAllErrors(henv
, hdbc
));
810 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
811 return(DispAllErrors(henv
, hdbc
));
813 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
814 return(DispAllErrors(henv
, hdbc
));
816 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
817 return(DispAllErrors(henv
, hdbc
));
819 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
820 return(DispAllErrors(henv
, hdbc
));
822 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
823 return(DispAllErrors(henv
, hdbc
));
825 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
826 return(DispAllErrors(henv
, hdbc
));
828 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
829 return(DispAllErrors(henv
, hdbc
));
831 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
832 return(DispAllErrors(henv
, hdbc
));
834 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
835 return(DispAllErrors(henv
, hdbc
));
837 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
838 return(DispAllErrors(henv
, hdbc
));
840 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
841 return(DispAllErrors(henv
, hdbc
));
843 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
844 return(DispAllErrors(henv
, hdbc
));
846 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
847 return(DispAllErrors(henv
, hdbc
));
849 #ifdef DBDEBUG_CONSOLE
850 cout
<< "***** DATA SOURCE INFORMATION *****" << endl
;
851 cout
<< "SERVER Name: " << dbInf
.serverName
<< endl
;
852 cout
<< "DBMS Name: " << dbInf
.dbmsName
<< "; DBMS Version: " << dbInf
.dbmsVer
<< endl
;
853 cout
<< "ODBC Version: " << dbInf
.odbcVer
<< "; Driver Version: " << dbInf
.driverVer
<< endl
;
855 cout
<< "API Conf. Level: ";
856 switch(dbInf
.apiConfLvl
)
858 case SQL_OAC_NONE
: cout
<< "None"; break;
859 case SQL_OAC_LEVEL1
: cout
<< "Level 1"; break;
860 case SQL_OAC_LEVEL2
: cout
<< "Level 2"; break;
864 cout
<< "SAG CLI Conf. Level: ";
865 switch(dbInf
.cliConfLvl
)
867 case SQL_OSCC_NOT_COMPLIANT
: cout
<< "Not Compliant"; break;
868 case SQL_OSCC_COMPLIANT
: cout
<< "Compliant"; break;
872 cout
<< "SQL Conf. Level: ";
873 switch(dbInf
.sqlConfLvl
)
875 case SQL_OSC_MINIMUM
: cout
<< "Minimum Grammar"; break;
876 case SQL_OSC_CORE
: cout
<< "Core Grammar"; break;
877 case SQL_OSC_EXTENDED
: cout
<< "Extended Grammar"; break;
881 cout
<< "Max. Connections: " << dbInf
.maxConnections
<< endl
;
882 cout
<< "Outer Joins: " << dbInf
.outerJoins
<< endl
;
883 cout
<< "Support for Procedures: " << dbInf
.procedureSupport
<< endl
;
884 cout
<< "All tables accessible : " << dbInf
.accessibleTables
<< endl
;
885 cout
<< "Cursor COMMIT Behavior: ";
886 switch(dbInf
.cursorCommitBehavior
)
888 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
889 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
890 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
894 cout
<< "Cursor ROLLBACK Behavior: ";
895 switch(dbInf
.cursorRollbackBehavior
)
897 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
898 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
899 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
903 cout
<< "Support NOT NULL clause: ";
904 switch(dbInf
.supportNotNullClause
)
906 case SQL_NNC_NULL
: cout
<< "No"; break;
907 case SQL_NNC_NON_NULL
: cout
<< "Yes"; break;
911 cout
<< "Support IEF (Ref. Integrity): " << dbInf
.supportIEF
<< endl
;
912 cout
<< "Login Timeout: " << dbInf
.loginTimeout
<< endl
;
914 cout
<< endl
<< endl
<< "more ..." << endl
;
917 cout
<< "Default Transaction Isolation: ";
918 switch(dbInf
.txnIsolation
)
920 case SQL_TXN_READ_UNCOMMITTED
: cout
<< "Read Uncommitted"; break;
921 case SQL_TXN_READ_COMMITTED
: cout
<< "Read Committed"; break;
922 case SQL_TXN_REPEATABLE_READ
: cout
<< "Repeatable Read"; break;
923 case SQL_TXN_SERIALIZABLE
: cout
<< "Serializable"; break;
925 case SQL_TXN_VERSIONING
: cout
<< "Versioning"; break;
930 cout
<< "Transaction Isolation Options: ";
931 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
932 cout
<< "Read Uncommitted, ";
933 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
934 cout
<< "Read Committed, ";
935 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
936 cout
<< "Repeatable Read, ";
937 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
938 cout
<< "Serializable, ";
940 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
941 cout
<< "Versioning";
945 cout
<< "Fetch Directions Supported:" << endl
<< " ";
946 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
948 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
950 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
952 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
954 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
955 cout
<< "Absolute, ";
956 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
957 cout
<< "Relative, ";
959 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
962 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
966 cout
<< "Lock Types Supported (SQLSetPos): ";
967 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
968 cout
<< "No Change, ";
969 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
970 cout
<< "Exclusive, ";
971 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
975 cout
<< "Position Operations Supported (SQLSetPos): ";
976 if (dbInf
.posOperations
& SQL_POS_POSITION
)
977 cout
<< "Position, ";
978 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
980 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
982 if (dbInf
.posOperations
& SQL_POS_DELETE
)
984 if (dbInf
.posOperations
& SQL_POS_ADD
)
988 cout
<< "Positioned Statements Supported: ";
989 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
990 cout
<< "Pos delete, ";
991 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
992 cout
<< "Pos update, ";
993 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
994 cout
<< "Select for update";
997 cout
<< "Scroll Concurrency: ";
998 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
999 cout
<< "Read Only, ";
1000 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1002 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1003 cout
<< "Opt. Rowver, ";
1004 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1005 cout
<< "Opt. Values";
1008 cout
<< "Scroll Options: ";
1009 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1010 cout
<< "Fwd Only, ";
1011 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1013 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1014 cout
<< "Keyset Driven, ";
1015 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1016 cout
<< "Dynamic, ";
1017 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1021 cout
<< "Static Sensitivity: ";
1022 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1023 cout
<< "Additions, ";
1024 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1025 cout
<< "Deletions, ";
1026 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1030 cout
<< "Transaction Capable?: ";
1031 switch(dbInf
.txnCapable
)
1033 case SQL_TC_NONE
: cout
<< "No"; break;
1034 case SQL_TC_DML
: cout
<< "DML Only"; break;
1035 case SQL_TC_DDL_COMMIT
: cout
<< "DDL Commit"; break;
1036 case SQL_TC_DDL_IGNORE
: cout
<< "DDL Ignore"; break;
1037 case SQL_TC_ALL
: cout
<< "DDL & DML"; break;
1044 // Completed Successfully
1047 } // wxDb::getDbInfo()
1050 /********** wxDb::getDataTypeInfo() **********/
1051 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1054 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1055 * the data type inf. is gathered for.
1057 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1062 // Get information about the data type specified
1063 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1064 return(DispAllErrors(henv
, hdbc
, hstmt
));
1066 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
1068 #ifdef DBDEBUG_CONSOLE
1069 if (retcode
== SQL_NO_DATA_FOUND
)
1070 cout
<< "SQL_NO_DATA_FOUND fetching inf. about data type." << endl
;
1072 DispAllErrors(henv
, hdbc
, hstmt
);
1073 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1076 // Obtain columns from the record
1077 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) structSQLTypeInfo
.TypeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1078 return(DispAllErrors(henv
, hdbc
, hstmt
));
1080 // BJO 20000503: no more needed with new GetColumns...
1083 if (Dbms() == dbmsMY_SQL
)
1085 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint");
1086 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint unsigned");
1087 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int");
1088 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int unsigned");
1089 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint");
1090 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "varchar")) wxStrcpy(structSQLTypeInfo
.TypeName
, "char");
1093 // BJO 20000427 : OpenLink driver
1094 if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) ||
1095 !wxStrncmp(dbInf
.driverName
, "OLOD", 4))
1097 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "double precision"))
1098 wxStrcpy(structSQLTypeInfo
.TypeName
, "real");
1102 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1103 return(DispAllErrors(henv
, hdbc
, hstmt
));
1104 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1105 return(DispAllErrors(henv
, hdbc
, hstmt
));
1106 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1107 // return(DispAllErrors(henv, hdbc, hstmt));
1109 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1110 return(DispAllErrors(henv
, hdbc
, hstmt
));
1112 if (structSQLTypeInfo
.MaximumScale
< 0)
1113 structSQLTypeInfo
.MaximumScale
= 0;
1115 // Close the statement handle which closes open cursors
1116 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1117 return(DispAllErrors(henv
, hdbc
, hstmt
));
1119 // Completed Successfully
1122 } // wxDb::getDataTypeInfo()
1125 /********** wxDb::Close() **********/
1126 void wxDb::Close(void)
1128 // Close the Sql Log file
1135 // Free statement handle
1138 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1139 DispAllErrors(henv
, hdbc
);
1142 // Disconnect from the datasource
1143 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1144 DispAllErrors(henv
, hdbc
);
1146 // Free the connection to the datasource
1147 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1148 DispAllErrors(henv
, hdbc
);
1150 // There should be zero Ctable objects still connected to this db object
1151 assert(nTables
== 0);
1156 pNode
= TablesInUse
.First();
1160 tiu
= (wxTablesInUse
*)pNode
->Data();
1161 if (tiu
->pDb
== this)
1163 s
.sprintf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1164 s2
.sprintf(wxT("Orphaned found using pDb:[%p]"),this);
1165 wxLogDebug (s
.c_str(),s2
.c_str());
1167 pNode
= pNode
->Next();
1171 // Copy the error messages to a global variable
1173 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1174 wxStrcpy(DBerrorList
[i
],errorList
[i
]);
1176 dbmsType
= dbmsUNIDENTIFIED
;
1182 /********** wxDb::CommitTrans() **********/
1183 bool wxDb::CommitTrans(void)
1187 // Commit the transaction
1188 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1189 return(DispAllErrors(henv
, hdbc
));
1192 // Completed successfully
1195 } // wxDb::CommitTrans()
1198 /********** wxDb::RollbackTrans() **********/
1199 bool wxDb::RollbackTrans(void)
1201 // Rollback the transaction
1202 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1203 return(DispAllErrors(henv
, hdbc
));
1205 // Completed successfully
1208 } // wxDb::RollbackTrans()
1211 /********** wxDb::DispAllErrors() **********/
1212 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1214 * This function is called internally whenever an error condition prevents the user's
1215 * request from being executed. This function will query the datasource as to the
1216 * actual error(s) that just occured on the previous request of the datasource.
1218 * The function will retrieve each error condition from the datasource and
1219 * sprintf the codes/text values into a string which it then logs via logError().
1220 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1221 * window and program execution will be paused until the user presses a key.
1223 * This function always returns a FALSE, so that functions which call this function
1224 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1225 * of the users request, so that the calling code can then process the error msg log
1228 wxString odbcErrMsg
;
1230 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1232 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
1233 logError(odbcErrMsg
.c_str(), sqlState
);
1236 #ifdef DBDEBUG_CONSOLE
1237 // When run in console mode, use standard out to display errors.
1238 cout
<< odbcErrMsg
.c_str() << endl
;
1239 cout
<< "Press any key to continue..." << endl
;
1244 wxLogDebug(odbcErrMsg
.c_str(),wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1249 return(FALSE
); // This function always returns false.
1251 } // wxDb::DispAllErrors()
1254 /********** wxDb::GetNextError() **********/
1255 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1257 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1262 } // wxDb::GetNextError()
1265 /********** wxDb::DispNextError() **********/
1266 void wxDb::DispNextError(void)
1268 wxString odbcErrMsg
;
1270 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
1271 logError(odbcErrMsg
.c_str(), sqlState
);
1276 #ifdef DBDEBUG_CONSOLE
1277 // When run in console mode, use standard out to display errors.
1278 cout
<< odbcErrMsg
.c_str() << endl
;
1279 cout
<< "Press any key to continue..." << endl
;
1284 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1285 #endif // __WXDEBUG__
1287 } // wxDb::DispNextError()
1290 /********** wxDb::logError() **********/
1291 void wxDb::logError(const char *errMsg
, const char *SQLState
)
1293 assert(errMsg
&& wxStrlen(errMsg
));
1295 static int pLast
= -1;
1298 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1301 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1302 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1306 wxStrcpy(errorList
[pLast
], errMsg
);
1308 if (SQLState
&& wxStrlen(SQLState
))
1309 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1310 DB_STATUS
= dbStatus
;
1312 // Add the errmsg to the sql log
1313 WriteSqlLog(errMsg
);
1315 } // wxDb::logError()
1318 /**********wxDb::TranslateSqlState() **********/
1319 int wxDb::TranslateSqlState(const wxChar
*SQLState
)
1321 if (!wxStrcmp(SQLState
, wxT("01000")))
1322 return(DB_ERR_GENERAL_WARNING
);
1323 if (!wxStrcmp(SQLState
, wxT("01002")))
1324 return(DB_ERR_DISCONNECT_ERROR
);
1325 if (!wxStrcmp(SQLState
, wxT("01004")))
1326 return(DB_ERR_DATA_TRUNCATED
);
1327 if (!wxStrcmp(SQLState
, wxT("01006")))
1328 return(DB_ERR_PRIV_NOT_REVOKED
);
1329 if (!wxStrcmp(SQLState
, wxT("01S00")))
1330 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1331 if (!wxStrcmp(SQLState
, wxT("01S01")))
1332 return(DB_ERR_ERROR_IN_ROW
);
1333 if (!wxStrcmp(SQLState
, wxT("01S02")))
1334 return(DB_ERR_OPTION_VALUE_CHANGED
);
1335 if (!wxStrcmp(SQLState
, wxT("01S03")))
1336 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1337 if (!wxStrcmp(SQLState
, wxT("01S04")))
1338 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1339 if (!wxStrcmp(SQLState
, wxT("07001")))
1340 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1341 if (!wxStrcmp(SQLState
, wxT("07006")))
1342 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1343 if (!wxStrcmp(SQLState
, wxT("08001")))
1344 return(DB_ERR_UNABLE_TO_CONNECT
);
1345 if (!wxStrcmp(SQLState
, wxT("08002")))
1346 return(DB_ERR_CONNECTION_IN_USE
);
1347 if (!wxStrcmp(SQLState
, wxT("08003")))
1348 return(DB_ERR_CONNECTION_NOT_OPEN
);
1349 if (!wxStrcmp(SQLState
, wxT("08004")))
1350 return(DB_ERR_REJECTED_CONNECTION
);
1351 if (!wxStrcmp(SQLState
, wxT("08007")))
1352 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1353 if (!wxStrcmp(SQLState
, wxT("08S01")))
1354 return(DB_ERR_COMM_LINK_FAILURE
);
1355 if (!wxStrcmp(SQLState
, wxT("21S01")))
1356 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1357 if (!wxStrcmp(SQLState
, wxT("21S02")))
1358 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1359 if (!wxStrcmp(SQLState
, wxT("22001")))
1360 return(DB_ERR_STRING_RIGHT_TRUNC
);
1361 if (!wxStrcmp(SQLState
, wxT("22003")))
1362 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1363 if (!wxStrcmp(SQLState
, wxT("22005")))
1364 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1365 if (!wxStrcmp(SQLState
, wxT("22008")))
1366 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1367 if (!wxStrcmp(SQLState
, wxT("22012")))
1368 return(DB_ERR_DIVIDE_BY_ZERO
);
1369 if (!wxStrcmp(SQLState
, wxT("22026")))
1370 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1371 if (!wxStrcmp(SQLState
, wxT("23000")))
1372 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1373 if (!wxStrcmp(SQLState
, wxT("24000")))
1374 return(DB_ERR_INVALID_CURSOR_STATE
);
1375 if (!wxStrcmp(SQLState
, wxT("25000")))
1376 return(DB_ERR_INVALID_TRANS_STATE
);
1377 if (!wxStrcmp(SQLState
, wxT("28000")))
1378 return(DB_ERR_INVALID_AUTH_SPEC
);
1379 if (!wxStrcmp(SQLState
, wxT("34000")))
1380 return(DB_ERR_INVALID_CURSOR_NAME
);
1381 if (!wxStrcmp(SQLState
, wxT("37000")))
1382 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1383 if (!wxStrcmp(SQLState
, wxT("3C000")))
1384 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1385 if (!wxStrcmp(SQLState
, wxT("40001")))
1386 return(DB_ERR_SERIALIZATION_FAILURE
);
1387 if (!wxStrcmp(SQLState
, wxT("42000")))
1388 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1389 if (!wxStrcmp(SQLState
, wxT("70100")))
1390 return(DB_ERR_OPERATION_ABORTED
);
1391 if (!wxStrcmp(SQLState
, wxT("IM001")))
1392 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1393 if (!wxStrcmp(SQLState
, wxT("IM002")))
1394 return(DB_ERR_NO_DATA_SOURCE
);
1395 if (!wxStrcmp(SQLState
, wxT("IM003")))
1396 return(DB_ERR_DRIVER_LOAD_ERROR
);
1397 if (!wxStrcmp(SQLState
, wxT("IM004")))
1398 return(DB_ERR_SQLALLOCENV_FAILED
);
1399 if (!wxStrcmp(SQLState
, wxT("IM005")))
1400 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1401 if (!wxStrcmp(SQLState
, wxT("IM006")))
1402 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1403 if (!wxStrcmp(SQLState
, wxT("IM007")))
1404 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1405 if (!wxStrcmp(SQLState
, wxT("IM008")))
1406 return(DB_ERR_DIALOG_FAILED
);
1407 if (!wxStrcmp(SQLState
, wxT("IM009")))
1408 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1409 if (!wxStrcmp(SQLState
, wxT("IM010")))
1410 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1411 if (!wxStrcmp(SQLState
, wxT("IM011")))
1412 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1413 if (!wxStrcmp(SQLState
, wxT("IM012")))
1414 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1415 if (!wxStrcmp(SQLState
, wxT("IM013")))
1416 return(DB_ERR_TRACE_FILE_ERROR
);
1417 if (!wxStrcmp(SQLState
, wxT("S0001")))
1418 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1419 if (!wxStrcmp(SQLState
, wxT("S0002")))
1420 return(DB_ERR_TABLE_NOT_FOUND
);
1421 if (!wxStrcmp(SQLState
, wxT("S0011")))
1422 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1423 if (!wxStrcmp(SQLState
, wxT("S0012")))
1424 return(DB_ERR_INDEX_NOT_FOUND
);
1425 if (!wxStrcmp(SQLState
, wxT("S0021")))
1426 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1427 if (!wxStrcmp(SQLState
, wxT("S0022")))
1428 return(DB_ERR_COLUMN_NOT_FOUND
);
1429 if (!wxStrcmp(SQLState
, wxT("S0023")))
1430 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1431 if (!wxStrcmp(SQLState
, wxT("S1000")))
1432 return(DB_ERR_GENERAL_ERROR
);
1433 if (!wxStrcmp(SQLState
, wxT("S1001")))
1434 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1435 if (!wxStrcmp(SQLState
, wxT("S1002")))
1436 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1437 if (!wxStrcmp(SQLState
, wxT("S1003")))
1438 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1439 if (!wxStrcmp(SQLState
, wxT("S1004")))
1440 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1441 if (!wxStrcmp(SQLState
, wxT("S1008")))
1442 return(DB_ERR_OPERATION_CANCELLED
);
1443 if (!wxStrcmp(SQLState
, wxT("S1009")))
1444 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1445 if (!wxStrcmp(SQLState
, wxT("S1010")))
1446 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1447 if (!wxStrcmp(SQLState
, wxT("S1011")))
1448 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1449 if (!wxStrcmp(SQLState
, wxT("S1012")))
1450 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1451 if (!wxStrcmp(SQLState
, wxT("S1015")))
1452 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1453 if (!wxStrcmp(SQLState
, wxT("S1090")))
1454 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1455 if (!wxStrcmp(SQLState
, wxT("S1091")))
1456 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1457 if (!wxStrcmp(SQLState
, wxT("S1092")))
1458 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1459 if (!wxStrcmp(SQLState
, wxT("S1093")))
1460 return(DB_ERR_INVALID_PARAM_NO
);
1461 if (!wxStrcmp(SQLState
, wxT("S1094")))
1462 return(DB_ERR_INVALID_SCALE_VALUE
);
1463 if (!wxStrcmp(SQLState
, wxT("S1095")))
1464 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1465 if (!wxStrcmp(SQLState
, wxT("S1096")))
1466 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1467 if (!wxStrcmp(SQLState
, wxT("S1097")))
1468 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1469 if (!wxStrcmp(SQLState
, wxT("S1098")))
1470 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1471 if (!wxStrcmp(SQLState
, wxT("S1099")))
1472 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1473 if (!wxStrcmp(SQLState
, wxT("S1100")))
1474 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1475 if (!wxStrcmp(SQLState
, wxT("S1101")))
1476 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1477 if (!wxStrcmp(SQLState
, wxT("S1103")))
1478 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1479 if (!wxStrcmp(SQLState
, wxT("S1104")))
1480 return(DB_ERR_INVALID_PRECISION_VALUE
);
1481 if (!wxStrcmp(SQLState
, wxT("S1105")))
1482 return(DB_ERR_INVALID_PARAM_TYPE
);
1483 if (!wxStrcmp(SQLState
, wxT("S1106")))
1484 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1485 if (!wxStrcmp(SQLState
, wxT("S1107")))
1486 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1487 if (!wxStrcmp(SQLState
, wxT("S1108")))
1488 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1489 if (!wxStrcmp(SQLState
, wxT("S1109")))
1490 return(DB_ERR_INVALID_CURSOR_POSITION
);
1491 if (!wxStrcmp(SQLState
, wxT("S1110")))
1492 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1493 if (!wxStrcmp(SQLState
, wxT("S1111")))
1494 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1495 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1496 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1497 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1498 return(DB_ERR_TIMEOUT_EXPIRED
);
1503 } // wxDb::TranslateSqlState()
1506 /********** wxDb::Grant() **********/
1507 bool wxDb::Grant(int privileges
, const char *tableName
, const char *userList
)
1511 // Build the grant statement
1513 if (privileges
== DB_GRANT_ALL
)
1518 if (privileges
& DB_GRANT_SELECT
)
1520 sqlStmt
+= "SELECT";
1523 if (privileges
& DB_GRANT_INSERT
)
1527 sqlStmt
+= "INSERT";
1529 if (privileges
& DB_GRANT_UPDATE
)
1533 sqlStmt
+= "UPDATE";
1535 if (privileges
& DB_GRANT_DELETE
)
1539 sqlStmt
+= "DELETE";
1544 sqlStmt
+= tableName
;
1546 sqlStmt
+= userList
;
1548 #ifdef DBDEBUG_CONSOLE
1549 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1552 WriteSqlLog(sqlStmt
.c_str());
1554 return(ExecSql(sqlStmt
.c_str()));
1559 /********** wxDb::CreateView() **********/
1560 bool wxDb::CreateView(const char *viewName
, const char *colList
, const char *pSqlStmt
, bool attemptDrop
)
1564 // Drop the view first
1565 if (attemptDrop
&& !DropView(viewName
))
1568 // Build the create view statement
1569 sqlStmt
= "CREATE VIEW ";
1570 sqlStmt
+= viewName
;
1572 if (wxStrlen(colList
))
1580 sqlStmt
+= pSqlStmt
;
1582 WriteSqlLog(sqlStmt
.c_str());
1584 #ifdef DBDEBUG_CONSOLE
1585 cout
<< sqlStmt
.c_str() << endl
;
1588 return(ExecSql(sqlStmt
.c_str()));
1590 } // wxDb::CreateView()
1593 /********** wxDb::DropView() **********/
1594 bool wxDb::DropView(const char *viewName
)
1597 * NOTE: This function returns TRUE if the View does not exist, but
1598 * only for identified databases. Code will need to be added
1599 * below for any other databases when those databases are defined
1600 * to handle this situation consistently
1602 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1605 sqlStmt
.sprintf("DROP VIEW %s", viewName
);
1607 WriteSqlLog(sqlStmt
.c_str());
1609 #ifdef DBDEBUG_CONSOLE
1610 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1613 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1615 // Check for "Base table not found" error and ignore
1616 GetNextError(henv
, hdbc
, hstmt
);
1617 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1619 // Check for product specific error codes
1620 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1623 DispAllErrors(henv
, hdbc
, hstmt
);
1630 // Commit the transaction
1631 if (! CommitTrans())
1636 } // wxDb::DropView()
1639 /********** wxDb::ExecSql() **********/
1640 bool wxDb::ExecSql(const char *pSqlStmt
)
1642 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1643 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) == SQL_SUCCESS
)
1647 DispAllErrors(henv
, hdbc
, hstmt
);
1651 } // wxDb::ExecSql()
1654 /********** wxDb::GetNext() **********/
1655 bool wxDb::GetNext(void)
1657 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1661 DispAllErrors(henv
, hdbc
, hstmt
);
1665 } // wxDb::GetNext()
1668 /********** wxDb::GetData() **********/
1669 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1674 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1678 DispAllErrors(henv
, hdbc
, hstmt
);
1682 } // wxDb::GetData()
1685 /********** wxDb::GetKeyFields() **********/
1686 int wxDb::GetKeyFields(char *tableName
, wxDbColInf
* colInf
, int noCols
)
1688 char szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1689 char szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1691 // SQLSMALLINT iKeySeq;
1692 char szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1693 char szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1699 * ---------------------------------------------------------------------
1700 * -- 19991224 : mj10777 : Create ------
1701 * -- : Three things are done and stored here : ------
1702 * -- : 1) which Column(s) is/are Primary Key(s) ------
1703 * -- : 2) which tables use this Key as a Foreign Key ------
1704 * -- : 3) which columns are Foreign Key and the name ------
1705 * -- : of the Table where the Key is the Primary Key -----
1706 * -- : Called from GetColumns(char *tableName, ------
1707 * -- int *numCols,const char *userID ) ------
1708 * ---------------------------------------------------------------------
1711 /*---------------------------------------------------------------------*/
1712 /* Get the names of the columns in the primary key. */
1713 /*---------------------------------------------------------------------*/
1714 retcode
= SQLPrimaryKeys(hstmt
,
1715 NULL
, 0, /* Catalog name */
1716 NULL
, 0, /* Schema name */
1717 (UCHAR
*) tableName
, SQL_NTS
); /* Table name */
1719 /*---------------------------------------------------------------------*/
1720 /* Fetch and display the result set. This will be a list of the */
1721 /* columns in the primary key of the tableName table. */
1722 /*---------------------------------------------------------------------*/
1723 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1725 retcode
= SQLFetch(hstmt
);
1726 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1728 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1729 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1731 for (i
=0;i
<noCols
;i
++) // Find the Column name
1732 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1733 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1736 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1738 /*---------------------------------------------------------------------*/
1739 /* Get all the foreign keys that refer to tableName primary key. */
1740 /*---------------------------------------------------------------------*/
1741 retcode
= SQLForeignKeys(hstmt
,
1742 NULL
, 0, /* Primary catalog */
1743 NULL
, 0, /* Primary schema */
1744 (UCHAR
*)tableName
, SQL_NTS
, /* Primary table */
1745 NULL
, 0, /* Foreign catalog */
1746 NULL
, 0, /* Foreign schema */
1747 NULL
, 0); /* Foreign table */
1749 /*---------------------------------------------------------------------*/
1750 /* Fetch and display the result set. This will be all of the foreign */
1751 /* keys in other tables that refer to the tableName primary key. */
1752 /*---------------------------------------------------------------------*/
1755 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1757 retcode
= SQLFetch(hstmt
);
1758 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1760 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1761 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1762 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1763 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1764 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1765 Temp0
.Printf(wxT("%s[%s] "),Temp0
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
1768 Temp0
.Trim(); // Get rid of any unneeded blanks
1769 if (Temp0
!= wxT(""))
1771 for (i
=0;i
<noCols
;i
++)
1772 { // Find the Column name
1773 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column, store the Information
1774 wxStrcpy(colInf
[i
].PkTableName
,Temp0
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
1777 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1779 /*---------------------------------------------------------------------*/
1780 /* Get all the foreign keys in the tablename table. */
1781 /*---------------------------------------------------------------------*/
1782 retcode
= SQLForeignKeys(hstmt
,
1783 NULL
, 0, /* Primary catalog */
1784 NULL
, 0, /* Primary schema */
1785 NULL
, 0, /* Primary table */
1786 NULL
, 0, /* Foreign catalog */
1787 NULL
, 0, /* Foreign schema */
1788 (UCHAR
*)tableName
, SQL_NTS
); /* Foreign table */
1790 /*---------------------------------------------------------------------*/
1791 /* Fetch and display the result set. This will be all of the */
1792 /* primary keys in other tables that are referred to by foreign */
1793 /* keys in the tableName table. */
1794 /*---------------------------------------------------------------------*/
1796 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1798 retcode
= SQLFetch(hstmt
);
1799 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1801 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1802 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1803 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1805 for (i
=0;i
<noCols
;i
++) // Find the Column name
1807 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
1809 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
1810 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
1815 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1819 } // wxDb::GetKeyFields()
1823 /********** wxDb::GetColumns() **********/
1824 wxDbColInf
*wxDb::GetColumns(char *tableName
[], const char *userID
)
1826 * 1) The last array element of the tableName[] argument must be zero (null).
1827 * This is how the end of the array is detected.
1828 * 2) This function returns an array of wxDbColInf structures. If no columns
1829 * were found, or an error occured, this pointer will be zero (null). THE
1830 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
1831 * IS FINISHED WITH IT. i.e.
1833 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
1836 * // Use the column inf
1838 * // Destroy the memory
1842 * userID is evaluated in the following manner:
1843 * userID == NULL ... UserID is ignored
1844 * userID == "" ... UserID set equal to 'this->uid'
1845 * userID != "" ... UserID set equal to 'userID'
1847 * NOTE: ALL column bindings associated with this wxDb instance are unbound
1848 * by this function. This function should use its own wxDb instance
1849 * to avoid undesired unbinding of columns.
1854 wxDbColInf
*colInf
= 0;
1864 if (!wxStrlen(userID
))
1872 // dBase does not use user names, and some drivers fail if you try to pass one
1873 if (Dbms() == dbmsDBASE
)
1876 // Oracle user names may only be in uppercase, so force
1877 // the name to uppercase
1878 if (Dbms() == dbmsORACLE
)
1879 UserID
= UserID
.Upper();
1881 // Pass 1 - Determine how many columns there are.
1882 // Pass 2 - Allocate the wxDbColInf array and fill in
1883 // the array with the column information.
1885 for (pass
= 1; pass
<= 2; pass
++)
1889 if (noCols
== 0) // Probably a bogus table name(s)
1891 // Allocate n wxDbColInf objects to hold the column information
1892 colInf
= new wxDbColInf
[noCols
+1];
1895 // Mark the end of the array
1896 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
1897 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
1898 colInf
[noCols
].sqlDataType
= 0;
1900 // Loop through each table name
1902 for (tbl
= 0; tableName
[tbl
]; tbl
++)
1904 TableName
= tableName
[tbl
];
1905 // Oracle table names are uppercase only, so force
1906 // the name to uppercase just in case programmer forgot to do this
1907 if (Dbms() == dbmsORACLE
)
1908 TableName
= TableName
.Upper();
1910 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1912 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
1913 // use the call below that leaves out the user name
1914 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
1915 Dbms() != dbmsMY_SQL
&&
1916 Dbms() != dbmsACCESS
&&
1917 Dbms() != dbmsMS_SQL_SERVER
)
1919 retcode
= SQLColumns(hstmt
,
1920 NULL
, 0, // All qualifiers
1921 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
1922 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
1923 NULL
, 0); // All columns
1927 retcode
= SQLColumns(hstmt
,
1928 NULL
, 0, // All qualifiers
1930 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
1931 NULL
, 0); // All columns
1933 if (retcode
!= SQL_SUCCESS
)
1934 { // Error occured, abort
1935 DispAllErrors(henv
, hdbc
, hstmt
);
1938 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1942 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
1944 if (pass
== 1) // First pass, just add up the number of columns
1946 else // Pass 2; Fill in the array of structures
1948 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
1950 // NOTE: Only the ODBC 1.x fields are retrieved
1951 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
1952 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
1953 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1954 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1955 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
1956 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
1957 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
1958 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
1959 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
1960 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
1961 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
1962 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
1964 // Determine the wxDb data type that is used to represent the native data type of this data source
1965 colInf
[colNo
].dbDataType
= 0;
1966 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
1969 // IODBC does not return a correct columnSize, so we set
1970 // columnSize = bufferLength if no column size was returned
1971 // IODBC returns the columnSize in bufferLength.. (bug)
1972 if (colInf
[colNo
].columnSize
< 1)
1974 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
1977 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
1979 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
1980 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
1981 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
1982 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
1983 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
1984 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
1990 if (retcode
!= SQL_NO_DATA_FOUND
)
1991 { // Error occured, abort
1992 DispAllErrors(henv
, hdbc
, hstmt
);
1995 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2001 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2004 } // wxDb::GetColumns()
2007 /********** wxDb::GetColumns() **********/
2009 wxDbColInf
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
)
2011 // Same as the above GetColumns() function except this one gets columns
2012 // only for a single table, and if 'numCols' is not NULL, the number of
2013 // columns stored in the returned wxDbColInf is set in '*numCols'
2015 // userID is evaluated in the following manner:
2016 // userID == NULL ... UserID is ignored
2017 // userID == "" ... UserID set equal to 'this->uid'
2018 // userID != "" ... UserID set equal to 'userID'
2020 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2021 // by this function. This function should use its own wxDb instance
2022 // to avoid undesired unbinding of columns.
2027 wxDbColInf
*colInf
= 0;
2037 if (!wxStrlen(userID
))
2045 // dBase does not use user names, and some drivers fail if you try to pass one
2046 if (Dbms() == dbmsDBASE
)
2049 // Oracle user names may only be in uppercase, so force
2050 // the name to uppercase
2051 if (Dbms() == dbmsORACLE
)
2052 UserID
= UserID
.Upper();
2054 // Pass 1 - Determine how many columns there are.
2055 // Pass 2 - Allocate the wxDbColInf array and fill in
2056 // the array with the column information.
2058 for (pass
= 1; pass
<= 2; pass
++)
2062 if (noCols
== 0) // Probably a bogus table name(s)
2064 // Allocate n wxDbColInf objects to hold the column information
2065 colInf
= new wxDbColInf
[noCols
+1];
2068 // Mark the end of the array
2069 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2070 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2071 colInf
[noCols
].sqlDataType
= 0;
2074 TableName
= tableName
;
2075 // Oracle table names are uppercase only, so force
2076 // the name to uppercase just in case programmer forgot to do this
2077 if (Dbms() == dbmsORACLE
)
2078 TableName
= TableName
.Upper();
2080 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2082 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2083 // use the call below that leaves out the user name
2084 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2085 Dbms() != dbmsMY_SQL
&&
2086 Dbms() != dbmsACCESS
&&
2087 Dbms() != dbmsMS_SQL_SERVER
)
2089 retcode
= SQLColumns(hstmt
,
2090 NULL
, 0, // All qualifiers
2091 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2092 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2093 NULL
, 0); // All columns
2097 retcode
= SQLColumns(hstmt
,
2098 NULL
, 0, // All qualifiers
2100 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2101 NULL
, 0); // All columns
2103 if (retcode
!= SQL_SUCCESS
)
2104 { // Error occured, abort
2105 DispAllErrors(henv
, hdbc
, hstmt
);
2108 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2114 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2116 if (pass
== 1) // First pass, just add up the number of columns
2118 else // Pass 2; Fill in the array of structures
2120 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2122 // NOTE: Only the ODBC 1.x fields are retrieved
2123 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2124 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2125 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2126 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2127 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2128 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2129 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2130 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2131 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2132 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2133 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2134 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2135 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2136 // Start Values for Primary/Foriegn Key (=No)
2137 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2138 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2139 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2140 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2142 // BJO 20000428 : Virtuoso returns type names with upper cases!
2143 if (Dbms() == dbmsVIRTUOSO
)
2145 wxString s
= colInf
[colNo
].typeName
;
2147 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2150 // Determine the wxDb data type that is used to represent the native data type of this data source
2151 colInf
[colNo
].dbDataType
= 0;
2152 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2155 // IODBC does not return a correct columnSize, so we set
2156 // columnSize = bufferLength if no column size was returned
2157 // IODBC returns the columnSize in bufferLength.. (bug)
2158 if (colInf
[colNo
].columnSize
< 1)
2160 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2164 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2166 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
2167 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2168 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
2169 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2170 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
2171 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2177 if (retcode
!= SQL_NO_DATA_FOUND
)
2178 { // Error occured, abort
2179 DispAllErrors(henv
, hdbc
, hstmt
);
2182 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2189 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2191 // Store Primary and Foriegn Keys
2192 GetKeyFields(tableName
,colInf
,noCols
);
2198 } // wxDb::GetColumns()
2201 #else // New GetColumns
2206 These are tentative new GetColumns members which should be more database
2207 independant and which always returns the columns in the order they were
2210 - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const
2211 char* userID)) calls the second implementation for each separate table
2212 before merging the results. This makes the code easier to maintain as
2213 only one member (the second) makes the real work
2214 - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const
2215 char *userID) is a little bit improved
2216 - It doesn't anymore rely on the type-name to find out which database-type
2218 - It ends by sorting the columns, so that they are returned in the same
2219 order they were created
2229 wxDbColInf
*wxDb::GetColumns(char *tableName
[], const char* userID
)
2232 // The last array element of the tableName[] argument must be zero (null).
2233 // This is how the end of the array is detected.
2237 // How many tables ?
2239 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2241 // Create a table to maintain the columns for each separate table
2242 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2245 for (i
= 0 ; i
< tbl
; i
++)
2248 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2249 if (TableColumns
[i
].colInf
== NULL
)
2251 noCols
+= TableColumns
[i
].noCols
;
2254 // Now merge all the separate table infos
2255 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2257 // Mark the end of the array
2258 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2259 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2260 colInf
[noCols
].sqlDataType
= 0;
2265 for (i
= 0 ; i
< tbl
; i
++)
2267 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2269 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2273 delete [] TableColumns
;
2276 } // wxDb::GetColumns() -- NEW
2279 wxDbColInf
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
)
2281 // Same as the above GetColumns() function except this one gets columns
2282 // only for a single table, and if 'numCols' is not NULL, the number of
2283 // columns stored in the returned wxDbColInf is set in '*numCols'
2285 // userID is evaluated in the following manner:
2286 // userID == NULL ... UserID is ignored
2287 // userID == "" ... UserID set equal to 'this->uid'
2288 // userID != "" ... UserID set equal to 'userID'
2290 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2291 // by this function. This function should use its own wxDb instance
2292 // to avoid undesired unbinding of columns.
2296 wxDbColInf
*colInf
= 0;
2306 if (!wxStrlen(userID
))
2314 // dBase does not use user names, and some drivers fail if you try to pass one
2315 if (Dbms() == dbmsDBASE
)
2318 // Oracle user names may only be in uppercase, so force
2319 // the name to uppercase
2320 if (Dbms() == dbmsORACLE
)
2321 UserID
= UserID
.Upper();
2323 // Pass 1 - Determine how many columns there are.
2324 // Pass 2 - Allocate the wxDbColInf array and fill in
2325 // the array with the column information.
2327 for (pass
= 1; pass
<= 2; pass
++)
2331 if (noCols
== 0) // Probably a bogus table name(s)
2333 // Allocate n wxDbColInf objects to hold the column information
2334 colInf
= new wxDbColInf
[noCols
+1];
2337 // Mark the end of the array
2338 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2339 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2340 colInf
[noCols
].sqlDataType
= 0;
2343 TableName
= tableName
;
2344 // Oracle table names are uppercase only, so force
2345 // the name to uppercase just in case programmer forgot to do this
2346 if (Dbms() == dbmsORACLE
)
2347 TableName
= TableName
.Upper();
2349 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2351 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2352 // use the call below that leaves out the user name
2353 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2354 Dbms() != dbmsMY_SQL
&&
2355 Dbms() != dbmsACCESS
&&
2356 Dbms() != dbmsMS_SQL_SERVER
)
2358 retcode
= SQLColumns(hstmt
,
2359 NULL
, 0, // All qualifiers
2360 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2361 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2362 NULL
, 0); // All columns
2366 retcode
= SQLColumns(hstmt
,
2367 NULL
, 0, // All qualifiers
2369 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2370 NULL
, 0); // All columns
2372 if (retcode
!= SQL_SUCCESS
)
2373 { // Error occured, abort
2374 DispAllErrors(henv
, hdbc
, hstmt
);
2377 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2383 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2385 if (pass
== 1) // First pass, just add up the number of columns
2387 else // Pass 2; Fill in the array of structures
2389 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2391 // NOTE: Only the ODBC 1.x fields are retrieved
2392 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2393 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2394 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2395 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2396 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2397 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2398 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2399 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2400 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2401 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2402 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2403 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2404 // Start Values for Primary/Foriegn Key (=No)
2405 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2406 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2407 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2408 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2411 // IODBC does not return a correct columnSize, so we set
2412 // columnSize = bufferLength if no column size was returned
2413 // IODBC returns the columnSize in bufferLength.. (bug)
2414 if (colInf
[colNo
].columnSize
< 1)
2416 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2420 // Determine the wxDb data type that is used to represent the native data type of this data source
2421 colInf
[colNo
].dbDataType
= 0;
2422 // Get the intern datatype
2423 switch (colInf
[colNo
].sqlDataType
)
2427 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2433 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2440 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2443 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2448 errMsg
.sprintf("SQL Data type %d currently not supported by wxWindows", colInf
[colNo
].sqlDataType
);
2449 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2456 if (retcode
!= SQL_NO_DATA_FOUND
)
2457 { // Error occured, abort
2458 DispAllErrors(henv
, hdbc
, hstmt
);
2461 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2468 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2470 // Store Primary and Foreign Keys
2471 GetKeyFields(tableName
,colInf
,noCols
);
2473 ///////////////////////////////////////////////////////////////////////////
2474 // Now sort the the columns in order to make them appear in the right order
2475 ///////////////////////////////////////////////////////////////////////////
2477 // Build a generic SELECT statement which returns 0 rows
2480 Stmt
.sprintf("select * from %s where 0=1", tableName
);
2483 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2485 DispAllErrors(henv
, hdbc
, hstmt
);
2489 // Get the number of result columns
2490 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2492 DispAllErrors(henv
, hdbc
, hstmt
);
2496 if (noCols
== 0) // Probably a bogus table name
2505 for (colNum
= 0; colNum
< noCols
; colNum
++)
2507 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2509 &Sword
, &Sdword
) != SQL_SUCCESS
)
2511 DispAllErrors(henv
, hdbc
, hstmt
);
2515 wxString Name1
= name
;
2516 Name1
= Name1
.Upper();
2518 // Where is this name in the array ?
2519 for (i
= colNum
; i
< noCols
; i
++)
2521 wxString Name2
= colInf
[i
].colName
;
2522 Name2
= Name2
.Upper();
2525 if (colNum
!= i
) // swap to sort
2527 wxDbColInf tmpColInf
= colInf
[colNum
];
2528 colInf
[colNum
] = colInf
[i
];
2529 colInf
[i
] = tmpColInf
;
2535 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2537 ///////////////////////////////////////////////////////////////////////////
2539 ///////////////////////////////////////////////////////////////////////////
2545 } // wxDb::GetColumns()
2548 #endif // #else OLD_GETCOLUMNS
2551 /********** wxDb::GetColumnCount() **********/
2552 int wxDb::GetColumnCount(char *tableName
, const char *userID
)
2554 * Returns a count of how many columns are in a table.
2555 * If an error occurs in computing the number of columns
2556 * this function will return a -1 for the count
2558 * userID is evaluated in the following manner:
2559 * userID == NULL ... UserID is ignored
2560 * userID == "" ... UserID set equal to 'this->uid'
2561 * userID != "" ... UserID set equal to 'userID'
2563 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2564 * by this function. This function should use its own wxDb instance
2565 * to avoid undesired unbinding of columns.
2577 if (!wxStrlen(userID
))
2585 // dBase does not use user names, and some drivers fail if you try to pass one
2586 if (Dbms() == dbmsDBASE
)
2589 // Oracle user names may only be in uppercase, so force
2590 // the name to uppercase
2591 if (Dbms() == dbmsORACLE
)
2592 UserID
= UserID
.Upper();
2595 // Loop through each table name
2597 TableName
= tableName
;
2598 // Oracle table names are uppercase only, so force
2599 // the name to uppercase just in case programmer forgot to do this
2600 if (Dbms() == dbmsORACLE
)
2601 TableName
= TableName
.Upper();
2603 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2605 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2606 // use the call below that leaves out the user name
2607 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2608 Dbms() != dbmsMY_SQL
&&
2609 Dbms() != dbmsACCESS
&&
2610 Dbms() != dbmsMS_SQL_SERVER
)
2612 retcode
= SQLColumns(hstmt
,
2613 NULL
, 0, // All qualifiers
2614 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2615 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2616 NULL
, 0); // All columns
2620 retcode
= SQLColumns(hstmt
,
2621 NULL
, 0, // All qualifiers
2623 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2624 NULL
, 0); // All columns
2626 if (retcode
!= SQL_SUCCESS
)
2627 { // Error occured, abort
2628 DispAllErrors(henv
, hdbc
, hstmt
);
2629 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2633 // Count the columns
2634 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2637 if (retcode
!= SQL_NO_DATA_FOUND
)
2638 { // Error occured, abort
2639 DispAllErrors(henv
, hdbc
, hstmt
);
2640 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2646 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2649 } // wxDb::GetColumnCount()
2652 /********** wxDb::GetCatalog() *******/
2653 wxDbInf
*wxDb::GetCatalog(char *userID
)
2655 * ---------------------------------------------------------------------
2656 * -- 19991203 : mj10777 : Create ------
2657 * -- : Creates a wxDbInf with Tables / Cols Array ------
2658 * -- : uses SQLTables and fills pTableInf; ------
2659 * -- : pColInf is set to NULL and numCols to 0; ------
2660 * -- : returns pDbInf (wxDbInf) ------
2661 * -- - if unsuccesfull (pDbInf == NULL) ------
2662 * -- : pColInf can be filled with GetColumns(..); ------
2663 * -- : numCols can be filled with GetColumnCount(..); ------
2664 * ---------------------------------------------------------------------
2666 * userID is evaluated in the following manner:
2667 * userID == NULL ... UserID is ignored
2668 * userID == "" ... UserID set equal to 'this->uid'
2669 * userID != "" ... UserID set equal to 'userID'
2671 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2672 * by this function. This function should use its own wxDb instance
2673 * to avoid undesired unbinding of columns.
2676 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2677 int noTab
= 0; // Counter while filling table entries
2681 wxString tblNameSave
;
2687 if (!wxStrlen(userID
))
2695 // dBase does not use user names, and some drivers fail if you try to pass one
2696 if (Dbms() == dbmsDBASE
)
2699 // Oracle user names may only be in uppercase, so force
2700 // the name to uppercase
2701 if (Dbms() == dbmsORACLE
)
2702 UserID
= UserID
.Upper();
2704 //-------------------------------------------------------------
2705 pDbInf
= new wxDbInf
; // Create the Database Arrray
2706 //-------------------------------------------------------------
2707 // Table Information
2708 // Pass 1 - Determine how many Tables there are.
2709 // Pass 2 - Create the Table array and fill it
2710 // - Create the Cols array = NULL
2711 //-------------------------------------------------------------
2713 for (pass
= 1; pass
<= 2; pass
++)
2715 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2716 tblNameSave
= wxT("");
2718 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2719 Dbms() != dbmsMY_SQL
&&
2720 Dbms() != dbmsACCESS
)
2722 retcode
= SQLTables(hstmt
,
2723 NULL
, 0, // All qualifiers
2724 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2725 NULL
, 0, // All tables
2726 NULL
, 0); // All columns
2730 retcode
= SQLTables(hstmt
,
2731 NULL
, 0, // All qualifiers
2732 NULL
, 0, // User specified
2733 NULL
, 0, // All tables
2734 NULL
, 0); // All columns
2737 if (retcode
!= SQL_SUCCESS
)
2739 DispAllErrors(henv
, hdbc
, hstmt
);
2741 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2745 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2747 if (pass
== 1) // First pass, just count the Tables
2749 if (pDbInf
->numTables
== 0)
2751 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2752 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2754 pDbInf
->numTables
++; // Counter for Tables
2756 if (pass
== 2) // Create and fill the Table entries
2758 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2759 { // no, then create the Array
2760 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2762 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2764 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2765 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2766 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2772 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2774 // Query how many columns are in each table
2775 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2777 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2782 } // wxDb::GetCatalog()
2785 /********** wxDb::Catalog() **********/
2786 bool wxDb::Catalog(const char *userID
, const char *fileName
)
2788 * Creates the text file specified in 'filename' which will contain
2789 * a minimal data dictionary of all tables accessible by the user specified
2792 * userID is evaluated in the following manner:
2793 * userID == NULL ... UserID is ignored
2794 * userID == "" ... UserID set equal to 'this->uid'
2795 * userID != "" ... UserID set equal to 'userID'
2797 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2798 * by this function. This function should use its own wxDb instance
2799 * to avoid undesired unbinding of columns.
2802 assert(fileName
&& wxStrlen(fileName
));
2806 char tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2807 wxString tblNameSave
;
2808 char colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2810 char typeName
[30+1];
2811 SWORD precision
, length
;
2815 FILE *fp
= fopen(fileName
,"wt");
2819 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2823 if (!wxStrlen(userID
))
2831 // dBase does not use user names, and some drivers fail if you try to pass one
2832 if (Dbms() == dbmsDBASE
)
2835 // Oracle user names may only be in uppercase, so force
2836 // the name to uppercase
2837 if (Dbms() == dbmsORACLE
)
2838 UserID
= UserID
.Upper();
2840 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2841 Dbms() != dbmsMY_SQL
&&
2842 Dbms() != dbmsACCESS
)
2844 retcode
= SQLColumns(hstmt
,
2845 NULL
, 0, // All qualifiers
2846 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2847 NULL
, 0, // All tables
2848 NULL
, 0); // All columns
2852 retcode
= SQLColumns(hstmt
,
2853 NULL
, 0, // All qualifiers
2854 NULL
, 0, // User specified
2855 NULL
, 0, // All tables
2856 NULL
, 0); // All columns
2858 if (retcode
!= SQL_SUCCESS
)
2860 DispAllErrors(henv
, hdbc
, hstmt
);
2866 tblNameSave
= wxT("");
2869 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2871 if (wxStrcmp(tblName
,tblNameSave
.c_str()))
2875 fputs("================================ ", fp
);
2876 fputs("================================ ", fp
);
2877 fputs("===================== ", fp
);
2878 fputs("========= ", fp
);
2879 fputs("=========\n", fp
);
2880 outStr
.sprintf(wxT("%-32s %-32s %-21s %9s %9s\n"),
2881 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
2882 fputs(outStr
.c_str(), fp
);
2883 fputs("================================ ", fp
);
2884 fputs("================================ ", fp
);
2885 fputs("===================== ", fp
);
2886 fputs("========= ", fp
);
2887 fputs("=========\n", fp
);
2888 tblNameSave
= tblName
;
2891 GetData(3,SQL_C_CHAR
, (UCHAR
*)tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2892 GetData(4,SQL_C_CHAR
, (UCHAR
*)colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
2893 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
,0, &cb
);
2894 GetData(6,SQL_C_CHAR
, (UCHAR
*)typeName
, sizeof(typeName
), &cb
);
2895 GetData(7,SQL_C_SSHORT
,(UCHAR
*)&precision
, 0, &cb
);
2896 GetData(8,SQL_C_SSHORT
,(UCHAR
*)&length
, 0, &cb
);
2898 outStr
.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2899 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
2900 if (fputs(outStr
.c_str(), fp
) == EOF
)
2902 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2909 if (retcode
!= SQL_NO_DATA_FOUND
)
2910 DispAllErrors(henv
, hdbc
, hstmt
);
2912 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2915 return(retcode
== SQL_NO_DATA_FOUND
);
2917 } // wxDb::Catalog()
2920 bool wxDb::TableExists(const char *tableName
, const char *userID
, const char *tablePath
)
2922 * Table name can refer to a table, view, alias or synonym. Returns true
2923 * if the object exists in the database. This function does not indicate
2924 * whether or not the user has privleges to query or perform other functions
2927 * userID is evaluated in the following manner:
2928 * userID == NULL ... UserID is ignored
2929 * userID == "" ... UserID set equal to 'this->uid'
2930 * userID != "" ... UserID set equal to 'userID'
2936 assert(tableName
&& wxStrlen(tableName
));
2938 if (Dbms() == dbmsDBASE
)
2941 if (tablePath
&& wxStrlen(tablePath
))
2942 dbName
.sprintf("%s\\%s.dbf",tablePath
,tableName
);
2944 dbName
.sprintf("%s.dbf",tableName
);
2947 exists
= wxFileExists(dbName
.c_str());
2953 if (!wxStrlen(userID
))
2961 // Oracle user names may only be in uppercase, so force
2962 // the name to uppercase
2963 if (Dbms() == dbmsORACLE
)
2964 UserID
= UserID
.Upper();
2966 TableName
= tableName
;
2967 // Oracle table names are uppercase only, so force
2968 // the name to uppercase just in case programmer forgot to do this
2969 if (Dbms() == dbmsORACLE
)
2970 TableName
= TableName
.Upper();
2972 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2975 // Some databases cannot accept a user name when looking up table names,
2976 // so we use the call below that leaves out the user name
2977 if (wxStrcmp(UserID
,"") &&
2978 Dbms() != dbmsMY_SQL
&&
2979 Dbms() != dbmsACCESS
&&
2980 Dbms() != dbmsMS_SQL_SERVER
)
2982 retcode
= SQLTables(hstmt
,
2983 NULL
, 0, // All qualifiers
2984 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
2985 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
2986 NULL
, 0); // All table types
2990 retcode
= SQLTables(hstmt
,
2991 NULL
, 0, // All qualifiers
2992 NULL
, 0, // All owners
2993 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
2994 NULL
, 0); // All table types
2996 if (retcode
!= SQL_SUCCESS
)
2997 return(DispAllErrors(henv
, hdbc
, hstmt
));
2999 retcode
= SQLFetch(hstmt
);
3000 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3002 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3003 return(DispAllErrors(henv
, hdbc
, hstmt
));
3006 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3010 } // wxDb::TableExists()
3013 /********** wxDb::TablePrivileges() **********/
3014 bool wxDb::TablePrivileges(const char *tableName
, const char* priv
,
3015 const char *userID
, const char *tablePath
)
3017 wxDbTablePrivilegeInfo result
;
3021 //We probably need to be able to dynamically set this based on
3022 //the driver type, and state.
3023 char curRole
[]="public";
3025 //Prologue here similar to db::TableExists()
3029 assert(tableName
&& wxStrlen(tableName
));
3033 if (!wxStrlen(userID
))
3041 // Oracle user names may only be in uppercase, so force
3042 // the name to uppercase
3043 if (Dbms() == dbmsORACLE
)
3044 UserID
= UserID
.Upper();
3046 TableName
= tableName
;
3047 // Oracle table names are uppercase only, so force
3048 // the name to uppercase just in case programmer forgot to do this
3049 if (Dbms() == dbmsORACLE
)
3050 TableName
= TableName
.Upper();
3052 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3054 retcode
= SQLTablePrivileges(hstmt
,
3057 (UCHAR FAR
*)TableName
.GetData(), SQL_NTS
);
3059 #ifdef DBDEBUG_CONSOLE
3060 fprintf(stderr
,"SQLTablePrivileges() returned %i \n",retcode
);
3063 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3064 return(DispAllErrors(henv
, hdbc
, hstmt
));
3066 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, 128, &cbRetVal
) != SQL_SUCCESS
)
3067 return(DispAllErrors(henv
, hdbc
, hstmt
));
3069 if (SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, 128, &cbRetVal
) != SQL_SUCCESS
)
3070 return(DispAllErrors(henv
, hdbc
, hstmt
));
3072 if (SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, 128, &cbRetVal
) != SQL_SUCCESS
)
3073 return(DispAllErrors(henv
, hdbc
, hstmt
));
3075 if (SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, 128, &cbRetVal
) != SQL_SUCCESS
)
3076 return(DispAllErrors(henv
, hdbc
, hstmt
));
3078 if (SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, 128, &cbRetVal
) != SQL_SUCCESS
)
3079 return(DispAllErrors(henv
, hdbc
, hstmt
));
3081 if (SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, 128, &cbRetVal
) != SQL_SUCCESS
)
3082 return(DispAllErrors(henv
, hdbc
, hstmt
));
3084 if (SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, 3, &cbRetVal
) != SQL_SUCCESS
)
3085 return(DispAllErrors(henv
, hdbc
, hstmt
));
3087 retcode
= SQLFetch(hstmt
);
3088 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3090 #ifdef DBDEBUG_CONSOLE
3091 fprintf(stderr
,"Scanning %s privilege on table %s.%s granted by %s to %s\n",
3092 result
.privilege
,result
.tableOwner
,result
.tableName
,
3093 result
.grantor
, result
.grantee
);
3095 if (UserID
.IsSameAs(result
.tableOwner
,false))
3097 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3101 if (UserID
.IsSameAs(result
.grantee
,false) &&
3102 !wxStrcmp(result
.privilege
,priv
))
3104 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3108 if (!wxStrcmp(result
.grantee
,curRole
) &&
3109 !wxStrcmp(result
.privilege
,priv
))
3111 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3115 retcode
= SQLFetch(hstmt
);
3118 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3121 } // wxDb::TablePrivileges
3124 /********** wxDb::SetSqlLogging() **********/
3125 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const char *filename
, bool append
)
3127 assert(state
== sqlLogON
|| state
== sqlLogOFF
);
3128 assert(state
== sqlLogOFF
|| filename
);
3130 if (state
== sqlLogON
)
3134 fpSqlLog
= fopen(filename
, (append
? "at" : "wt"));
3135 if (fpSqlLog
== NULL
)
3143 if (fclose(fpSqlLog
))
3149 sqlLogState
= state
;
3152 } // wxDb::SetSqlLogging()
3155 /********** wxDb::WriteSqlLog() **********/
3156 bool wxDb::WriteSqlLog(const wxChar
*logMsg
)
3160 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3163 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
3164 if (fputs(logMsg
, fpSqlLog
) == EOF
) return(FALSE
);
3165 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
3169 } // wxDb::WriteSqlLog()
3172 /********** wxDb::Dbms() **********/
3173 wxDBMS
wxDb::Dbms(void)
3175 * Be aware that not all database engines use the exact same syntax, and not
3176 * every ODBC compliant database is compliant to the same level of compliancy.
3177 * Some manufacturers support the minimum Level 1 compliancy, and others up
3178 * through Level 3. Others support subsets of features for levels above 1.
3180 * If you find an inconsistency between the wxDb class and a specific database
3181 * engine, and an identifier to this section, and special handle the database in
3182 * the area where behavior is non-conforming with the other databases.
3185 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3186 * ---------------------------------------------------
3189 * - Currently the only database supported by the class to support VIEWS
3192 * - Does not support the SQL_TIMESTAMP structure
3193 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3194 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3195 * is TRUE. The user must create ALL indexes from their program.
3196 * - Table names can only be 8 characters long
3197 * - Column names can only be 10 characters long
3200 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3201 * after every table name involved in the query/join if that tables matching record(s)
3203 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3205 * SYBASE (Enterprise)
3206 * - If a column is part of the Primary Key, the column cannot be NULL
3207 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3210 * - If a column is part of the Primary Key, the column cannot be NULL
3211 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3212 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3213 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3214 * column definition if it is not defined correctly, but it is experimental
3215 * - Does not support sub-queries in SQL statements
3218 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3219 * - Does not support sub-queries in SQL statements
3222 * - Primary keys must be declared as NOT NULL
3226 // Should only need to do this once for each new database connection
3227 // so return the value we already determined it to be to save time
3228 // and lots of string comparisons
3229 if (dbmsType
!= dbmsUNIDENTIFIED
)
3232 wxChar baseName
[25+1];
3233 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3236 // BJO 20000428 : add support for Virtuoso
3237 if (!wxStricmp(dbInf
.dbmsName
,"OpenLink Virtuoso VDBMS"))
3238 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3240 if (!wxStricmp(dbInf
.dbmsName
,"Adaptive Server Anywhere"))
3241 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3243 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3244 // connected through an OpenLink driver.
3245 // Is it also returned by Sybase Adapatitve server?
3246 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3247 if (!wxStricmp(dbInf
.dbmsName
,"SQL Server"))
3249 if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) ||
3250 !wxStrncmp(dbInf
.driverName
, "OLOD", 4))
3251 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3253 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3256 if (!wxStricmp(dbInf
.dbmsName
,"Microsoft SQL Server"))
3257 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3258 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
3259 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3260 if (!wxStricmp(dbInf
.dbmsName
,"PostgreSQL")) // v6.5.0
3261 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3264 if (!wxStricmp(baseName
,"Informix"))
3265 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3268 if (!wxStricmp(baseName
,"Oracle"))
3269 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3270 if (!wxStricmp(dbInf
.dbmsName
,"ACCESS"))
3271 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3272 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
3273 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3274 if (!wxStricmp(baseName
,"Sybase"))
3275 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3278 if (!wxStricmp(baseName
,"DBASE"))
3279 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3282 if (!wxStricmp(baseName
,"DB2"))
3283 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3285 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3290 /********** wxDbGetConnection() **********/
3291 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3295 // Used to keep a pointer to a DB connection that matches the requested
3296 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3297 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3298 // rather than having to re-query the datasource to get all the values
3299 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3300 wxDb
*matchingDbConnection
= NULL
;
3302 // Scan the linked list searching for an available database connection
3303 // that's already been opened but is currently not in use.
3304 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3306 // The database connection must be for the same datasource
3307 // name and must currently not be in use.
3309 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3310 (!wxStrcmp(pDbConfig
->Dsn
, pList
->Dsn
))) // Found a free connection
3312 pList
->Free
= FALSE
;
3313 return(pList
->PtrDb
);
3316 if (!wxStrcmp(pDbConfig
->Dsn
, pList
->Dsn
) &&
3317 !wxStrcmp(pDbConfig
->Uid
, pList
->Uid
) &&
3318 !wxStrcmp(pDbConfig
->AuthStr
, pList
->AuthStr
))
3319 matchingDbConnection
= pList
->PtrDb
;
3322 // No available connections. A new connection must be made and
3323 // appended to the end of the linked list.
3326 // Find the end of the list
3327 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3328 // Append a new list item
3329 pList
->PtrNext
= new wxDbList
;
3330 pList
->PtrNext
->PtrPrev
= pList
;
3331 pList
= pList
->PtrNext
;
3335 // Create the first node on the list
3336 pList
= PtrBegDbList
= new wxDbList
;
3340 // Initialize new node in the linked list
3342 pList
->Free
= FALSE
;
3343 wxStrcpy(pList
->Dsn
, pDbConfig
->Dsn
);
3344 wxStrcpy(pList
->Uid
, pDbConfig
->Uid
);
3345 wxStrcpy(pList
->AuthStr
, pDbConfig
->AuthStr
);
3347 pList
->PtrDb
= new wxDb(pDbConfig
->Henv
,FwdOnlyCursors
);
3349 bool opened
= FALSE
;
3351 if (!matchingDbConnection
)
3352 opened
= pList
->PtrDb
->Open(pDbConfig
->Dsn
, pDbConfig
->Uid
, pDbConfig
->AuthStr
);
3354 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3356 // Connect to the datasource
3359 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3360 return(pList
->PtrDb
);
3362 else // Unable to connect, destroy list item
3365 pList
->PtrPrev
->PtrNext
= 0;
3367 PtrBegDbList
= 0; // Empty list again
3368 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3369 pList
->PtrDb
->Close(); // Close the wxDb object
3370 delete pList
->PtrDb
; // Deletes the wxDb object
3371 delete pList
; // Deletes the linked list object
3375 } // wxDbGetConnection()
3378 /********** wxDbFreeConnection() **********/
3379 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3383 // Scan the linked list searching for the database connection
3384 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3386 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3387 return (pList
->Free
= TRUE
);
3390 // Never found the database object, return failure
3393 } // wxDbFreeConnection()
3396 /********** wxDbCloseConnections() **********/
3397 void WXDLLEXPORT
wxDbCloseConnections(void)
3399 wxDbList
*pList
, *pNext
;
3401 // Traverse the linked list closing database connections and freeing memory as I go.
3402 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3404 pNext
= pList
->PtrNext
; // Save the pointer to next
3405 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3406 pList
->PtrDb
->Close(); // Close the wxDb object
3407 delete pList
->PtrDb
; // Deletes the wxDb object
3408 delete pList
; // Deletes the linked list object
3411 // Mark the list as empty
3414 } // wxDbCloseConnections()
3417 /********** wxDbNumberConnectionsInUse() **********/
3418 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3423 // Scan the linked list counting db connections that are currently in use
3424 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3426 if (pList
->Free
== FALSE
)
3432 } // wxDbConnectionsInUse()
3435 /********** wxDbSqlLog() **********/
3436 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3438 bool append
= FALSE
;
3441 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3443 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3448 SQLLOGstate
= state
;
3449 SQLLOGfn
= filename
;
3457 /********** wxDbCreateDataSource() **********/
3458 int wxDbCreateDataSource(const char *driverName
, const char *dsn
, const char *description
,
3459 bool sysDSN
, const char *defDir
, wxWindow
*parent
)
3461 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3462 * Very rudimentary creation of an ODBC data source.
3464 * ODBC driver must be ODBC 3.0 compliant to use this function
3469 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3475 dsnLocation
= ODBC_ADD_SYS_DSN
;
3477 dsnLocation
= ODBC_ADD_DSN
;
3479 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3480 // so that is why I used it, as wxString does not deal well with
3481 // embedded nulls in strings
3482 setupStr
.sprintf("DSN=%s%cDescription=%s%cDefaultDir=%s%c",dsn
,2,description
,2,defDir
,2);
3484 // Replace the separator from above with the '\0' seperator needed
3485 // by the SQLConfigDataSource() function
3489 k
= setupStr
.Find((wxChar
)2,TRUE
);
3490 if (k
!= wxNOT_FOUND
)
3491 setupStr
[(UINT
)k
] = '\0';
3493 while (k
!= wxNOT_FOUND
);
3495 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3496 driverName
, setupStr
.c_str());
3498 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3500 // check for errors caused by ConfigDSN based functions
3503 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3506 // This function is only supported in ODBC drivers v3.0 compliant and above
3507 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3510 #ifdef DBDEBUG_CONSOLE
3511 // When run in console mode, use standard out to display errors.
3512 cout
<< errMsg
<< endl
;
3513 cout
<< wxT("Press any key to continue...") << endl
;
3515 #endif // DBDEBUG_CONSOLE
3518 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3519 #endif // __WXDEBUG__
3525 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3526 // necessary to use this function, so this function is not supported
3528 wxLogDebug("wxDbCreateDataSource() not available except under VC++/MSW",wxT("ODBC DEBUG MESSAGE"));
3531 #endif // __VISUALC__
3535 } // wxDbCreateDataSource()
3539 /********** wxDbGetDataSource() **********/
3540 bool wxDbGetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3543 * Dsn and DsDesc will contain the data source name and data source
3544 * description upon return
3549 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3550 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3555 } // wxDbGetDataSource()
3558 // Change this to 0 to remove use of all deprecated functions
3559 #if wxODBC_BACKWARD_COMPATABILITY
3560 /********************************************************************
3561 ********************************************************************
3563 * The following functions are all DEPRECATED and are included for
3564 * backward compatability reasons only
3566 ********************************************************************
3567 ********************************************************************/
3568 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3570 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3572 /***** DEPRECATED: use wxGetDataSource() *****/
3573 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3576 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3578 /***** DEPRECATED: use wxDbGetConnection() *****/
3579 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3581 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3583 /***** DEPRECATED: use wxDbFreeConnection() *****/
3584 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3586 return wxDbFreeConnection(pDb
);
3588 /***** DEPRECATED: use wxDbCloseConnections() *****/
3589 void WXDLLEXPORT
CloseDbConnections(void)
3591 wxDbCloseConnections();
3593 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3594 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3596 return wxDbConnectionsInUse();