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
;
649 wxStrcpy(typeInfVarchar
.TypeName
, copyDb
->typeInfVarchar
.TypeName
);
650 typeInfVarchar
.Precision
= copyDb
->typeInfVarchar
.Precision
;
651 typeInfVarchar
.CaseSensitive
= copyDb
->typeInfVarchar
.CaseSensitive
;
652 typeInfVarchar
.MaximumScale
= copyDb
->typeInfVarchar
.MaximumScale
;
655 typeInfFloat
.FsqlType
= copyDb
->typeInfFloat
.FsqlType
;
656 wxStrcpy(typeInfFloat
.TypeName
, copyDb
->typeInfFloat
.TypeName
);
657 typeInfFloat
.Precision
= copyDb
->typeInfFloat
.Precision
;
658 typeInfFloat
.CaseSensitive
= copyDb
->typeInfFloat
.CaseSensitive
;
659 typeInfFloat
.MaximumScale
= copyDb
->typeInfFloat
.MaximumScale
;
662 typeInfInteger
.FsqlType
= copyDb
->typeInfInteger
.FsqlType
;
663 wxStrcpy(typeInfInteger
.TypeName
, copyDb
->typeInfInteger
.TypeName
);
664 typeInfInteger
.Precision
= copyDb
->typeInfInteger
.Precision
;
665 typeInfInteger
.CaseSensitive
= copyDb
->typeInfInteger
.CaseSensitive
;
666 typeInfInteger
.MaximumScale
= copyDb
->typeInfInteger
.MaximumScale
;
669 typeInfDate
.FsqlType
= copyDb
->typeInfDate
.FsqlType
;
670 wxStrcpy(typeInfDate
.TypeName
, copyDb
->typeInfDate
.TypeName
);
671 typeInfDate
.Precision
= copyDb
->typeInfDate
.Precision
;
672 typeInfDate
.CaseSensitive
= copyDb
->typeInfDate
.CaseSensitive
;
673 typeInfDate
.MaximumScale
= copyDb
->typeInfDate
.MaximumScale
;
675 #ifdef DBDEBUG_CONSOLE
676 cout
<< "VARCHAR DATA TYPE: " << typeInfVarchar
.TypeName
<< endl
;
677 cout
<< "INTEGER DATA TYPE: " << typeInfInteger
.TypeName
<< endl
;
678 cout
<< "FLOAT DATA TYPE: " << typeInfFloat
.TypeName
<< endl
;
679 cout
<< "DATE DATA TYPE: " << typeInfDate
.TypeName
<< endl
;
683 // Completed Successfully
688 /********** wxDb::setConnectionOptions() **********/
689 bool wxDb::setConnectionOptions(void)
691 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
696 // I need to get the DBMS name here, because some of the connection options
697 // are database specific and need to call the Dbms() function.
698 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
699 return(DispAllErrors(henv
, hdbc
));
701 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
702 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
703 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
705 // By default, MS Sql Server closes cursors on commit and rollback. The following
706 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
707 // after a transaction. This is a driver specific option and is not part of the
708 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
709 // The database settings don't have any effect one way or the other.
710 if (Dbms() == dbmsMS_SQL_SERVER
)
712 const long SQL_PRESERVE_CURSORS
= 1204L;
713 const long SQL_PC_ON
= 1L;
714 SQLSetConnectOption(hdbc
, SQL_PRESERVE_CURSORS
, SQL_PC_ON
);
717 // Display the connection options to verify them
718 #ifdef DBDEBUG_CONSOLE
720 cout
<< "****** CONNECTION OPTIONS ******" << endl
;
722 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
723 return(DispAllErrors(henv
, hdbc
));
724 cout
<< "AUTOCOMMIT: " << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
726 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
727 return(DispAllErrors(henv
, hdbc
));
728 cout
<< "ODBC CURSORS: ";
731 case(SQL_CUR_USE_IF_NEEDED
):
732 cout
<< "SQL_CUR_USE_IF_NEEDED";
734 case(SQL_CUR_USE_ODBC
):
735 cout
<< "SQL_CUR_USE_ODBC";
737 case(SQL_CUR_USE_DRIVER
):
738 cout
<< "SQL_CUR_USE_DRIVER";
743 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
744 return(DispAllErrors(henv
, hdbc
));
745 cout
<< "TRACING: " << (l
== SQL_OPT_TRACE_OFF
? "OFF" : "ON") << endl
;
750 // Completed Successfully
753 } // wxDb::setConnectionOptions()
756 /********** wxDb::getDbInfo() **********/
757 bool wxDb::getDbInfo(void)
762 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
763 return(DispAllErrors(henv
, hdbc
));
765 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
766 return(DispAllErrors(henv
, hdbc
));
768 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
769 return(DispAllErrors(henv
, hdbc
));
772 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
773 // causing database connectivity to fail in some cases.
774 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
776 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
777 return(DispAllErrors(henv
, hdbc
));
779 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
780 return(DispAllErrors(henv
, hdbc
));
782 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
783 return(DispAllErrors(henv
, hdbc
));
785 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
786 return(DispAllErrors(henv
, hdbc
));
788 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
789 return(DispAllErrors(henv
, hdbc
));
791 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
792 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
793 return(DispAllErrors(henv
, hdbc
));
795 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
796 return(DispAllErrors(henv
, hdbc
));
798 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
799 return(DispAllErrors(henv
, hdbc
));
801 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
802 // return(DispAllErrors(henv, hdbc));
804 // Not all drivers support this call - Nick Gorham(unixODBC)
805 dbInf
.cliConfLvl
= 0;
808 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
809 return(DispAllErrors(henv
, hdbc
));
811 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
812 return(DispAllErrors(henv
, hdbc
));
814 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
815 return(DispAllErrors(henv
, hdbc
));
817 if (SQLGetInfo(hdbc
, SQL_ACCESSIBLE_TABLES
, (UCHAR
*) dbInf
.accessibleTables
, 2, &cb
) != SQL_SUCCESS
)
818 return(DispAllErrors(henv
, hdbc
));
820 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
821 return(DispAllErrors(henv
, hdbc
));
823 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
824 return(DispAllErrors(henv
, hdbc
));
826 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
827 return(DispAllErrors(henv
, hdbc
));
829 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
830 return(DispAllErrors(henv
, hdbc
));
832 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
833 return(DispAllErrors(henv
, hdbc
));
835 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
836 return(DispAllErrors(henv
, hdbc
));
838 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
839 return(DispAllErrors(henv
, hdbc
));
841 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
842 return(DispAllErrors(henv
, hdbc
));
844 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
845 return(DispAllErrors(henv
, hdbc
));
847 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
848 return(DispAllErrors(henv
, hdbc
));
850 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
851 return(DispAllErrors(henv
, hdbc
));
853 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
854 return(DispAllErrors(henv
, hdbc
));
856 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
857 return(DispAllErrors(henv
, hdbc
));
859 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
860 return(DispAllErrors(henv
, hdbc
));
862 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
863 return(DispAllErrors(henv
, hdbc
));
865 #ifdef DBDEBUG_CONSOLE
866 cout
<< "***** DATA SOURCE INFORMATION *****" << endl
;
867 cout
<< "SERVER Name: " << dbInf
.serverName
<< endl
;
868 cout
<< "DBMS Name: " << dbInf
.dbmsName
<< "; DBMS Version: " << dbInf
.dbmsVer
<< endl
;
869 cout
<< "ODBC Version: " << dbInf
.odbcVer
<< "; Driver Version: " << dbInf
.driverVer
<< endl
;
871 cout
<< "API Conf. Level: ";
872 switch(dbInf
.apiConfLvl
)
874 case SQL_OAC_NONE
: cout
<< "None"; break;
875 case SQL_OAC_LEVEL1
: cout
<< "Level 1"; break;
876 case SQL_OAC_LEVEL2
: cout
<< "Level 2"; break;
880 cout
<< "SAG CLI Conf. Level: ";
881 switch(dbInf
.cliConfLvl
)
883 case SQL_OSCC_NOT_COMPLIANT
: cout
<< "Not Compliant"; break;
884 case SQL_OSCC_COMPLIANT
: cout
<< "Compliant"; break;
888 cout
<< "SQL Conf. Level: ";
889 switch(dbInf
.sqlConfLvl
)
891 case SQL_OSC_MINIMUM
: cout
<< "Minimum Grammar"; break;
892 case SQL_OSC_CORE
: cout
<< "Core Grammar"; break;
893 case SQL_OSC_EXTENDED
: cout
<< "Extended Grammar"; break;
897 cout
<< "Max. Connections: " << dbInf
.maxConnections
<< endl
;
898 cout
<< "Outer Joins: " << dbInf
.outerJoins
<< endl
;
899 cout
<< "Support for Procedures: " << dbInf
.procedureSupport
<< endl
;
900 cout
<< "All tables accessible : " << dbInf
.accessibleTables
<< endl
;
901 cout
<< "Cursor COMMIT Behavior: ";
902 switch(dbInf
.cursorCommitBehavior
)
904 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
905 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
906 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
910 cout
<< "Cursor ROLLBACK Behavior: ";
911 switch(dbInf
.cursorRollbackBehavior
)
913 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
914 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
915 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
919 cout
<< "Support NOT NULL clause: ";
920 switch(dbInf
.supportNotNullClause
)
922 case SQL_NNC_NULL
: cout
<< "No"; break;
923 case SQL_NNC_NON_NULL
: cout
<< "Yes"; break;
927 cout
<< "Support IEF (Ref. Integrity): " << dbInf
.supportIEF
<< endl
;
928 cout
<< "Login Timeout: " << dbInf
.loginTimeout
<< endl
;
930 cout
<< endl
<< endl
<< "more ..." << endl
;
933 cout
<< "Default Transaction Isolation: ";
934 switch(dbInf
.txnIsolation
)
936 case SQL_TXN_READ_UNCOMMITTED
: cout
<< "Read Uncommitted"; break;
937 case SQL_TXN_READ_COMMITTED
: cout
<< "Read Committed"; break;
938 case SQL_TXN_REPEATABLE_READ
: cout
<< "Repeatable Read"; break;
939 case SQL_TXN_SERIALIZABLE
: cout
<< "Serializable"; break;
941 case SQL_TXN_VERSIONING
: cout
<< "Versioning"; break;
946 cout
<< "Transaction Isolation Options: ";
947 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
948 cout
<< "Read Uncommitted, ";
949 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
950 cout
<< "Read Committed, ";
951 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
952 cout
<< "Repeatable Read, ";
953 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
954 cout
<< "Serializable, ";
956 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
957 cout
<< "Versioning";
961 cout
<< "Fetch Directions Supported:" << endl
<< " ";
962 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
964 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
966 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
968 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
970 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
971 cout
<< "Absolute, ";
972 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
973 cout
<< "Relative, ";
975 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
978 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
982 cout
<< "Lock Types Supported (SQLSetPos): ";
983 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
984 cout
<< "No Change, ";
985 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
986 cout
<< "Exclusive, ";
987 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
991 cout
<< "Position Operations Supported (SQLSetPos): ";
992 if (dbInf
.posOperations
& SQL_POS_POSITION
)
993 cout
<< "Position, ";
994 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
996 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
998 if (dbInf
.posOperations
& SQL_POS_DELETE
)
1000 if (dbInf
.posOperations
& SQL_POS_ADD
)
1004 cout
<< "Positioned Statements Supported: ";
1005 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
1006 cout
<< "Pos delete, ";
1007 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
1008 cout
<< "Pos update, ";
1009 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1010 cout
<< "Select for update";
1013 cout
<< "Scroll Concurrency: ";
1014 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
1015 cout
<< "Read Only, ";
1016 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
1018 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
1019 cout
<< "Opt. Rowver, ";
1020 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
1021 cout
<< "Opt. Values";
1024 cout
<< "Scroll Options: ";
1025 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
1026 cout
<< "Fwd Only, ";
1027 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
1029 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
1030 cout
<< "Keyset Driven, ";
1031 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
1032 cout
<< "Dynamic, ";
1033 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
1037 cout
<< "Static Sensitivity: ";
1038 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
1039 cout
<< "Additions, ";
1040 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
1041 cout
<< "Deletions, ";
1042 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
1046 cout
<< "Transaction Capable?: ";
1047 switch(dbInf
.txnCapable
)
1049 case SQL_TC_NONE
: cout
<< "No"; break;
1050 case SQL_TC_DML
: cout
<< "DML Only"; break;
1051 case SQL_TC_DDL_COMMIT
: cout
<< "DDL Commit"; break;
1052 case SQL_TC_DDL_IGNORE
: cout
<< "DDL Ignore"; break;
1053 case SQL_TC_ALL
: cout
<< "DDL & DML"; break;
1060 // Completed Successfully
1063 } // wxDb::getDbInfo()
1066 /********** wxDb::getDataTypeInfo() **********/
1067 bool wxDb::getDataTypeInfo(SWORD fSqlType
, wxDbSqlTypeInfo
&structSQLTypeInfo
)
1070 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1071 * the data type inf. is gathered for.
1073 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1078 // Get information about the data type specified
1079 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
1080 return(DispAllErrors(henv
, hdbc
, hstmt
));
1082 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
1084 #ifdef DBDEBUG_CONSOLE
1085 if (retcode
== SQL_NO_DATA_FOUND
)
1086 cout
<< "SQL_NO_DATA_FOUND fetching inf. about data type." << endl
;
1088 DispAllErrors(henv
, hdbc
, hstmt
);
1089 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1092 // Obtain columns from the record
1093 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) structSQLTypeInfo
.TypeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
1094 return(DispAllErrors(henv
, hdbc
, hstmt
));
1096 // BJO 20000503: no more needed with new GetColumns...
1099 if (Dbms() == dbmsMY_SQL
)
1101 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint");
1102 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint unsigned");
1103 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int");
1104 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "integer unsigned")) wxStrcpy(structSQLTypeInfo
.TypeName
, "int unsigned");
1105 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "middleint")) wxStrcpy(structSQLTypeInfo
.TypeName
, "mediumint");
1106 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "varchar")) wxStrcpy(structSQLTypeInfo
.TypeName
, "char");
1109 // BJO 20000427 : OpenLink driver
1110 if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) ||
1111 !wxStrncmp(dbInf
.driverName
, "OLOD", 4))
1113 if (!wxStrcmp(structSQLTypeInfo
.TypeName
, "double precision"))
1114 wxStrcpy(structSQLTypeInfo
.TypeName
, "real");
1118 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
1119 return(DispAllErrors(henv
, hdbc
, hstmt
));
1120 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
1121 return(DispAllErrors(henv
, hdbc
, hstmt
));
1122 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1123 // return(DispAllErrors(henv, hdbc, hstmt));
1125 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
1126 return(DispAllErrors(henv
, hdbc
, hstmt
));
1128 if (structSQLTypeInfo
.MaximumScale
< 0)
1129 structSQLTypeInfo
.MaximumScale
= 0;
1131 // Close the statement handle which closes open cursors
1132 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
1133 return(DispAllErrors(henv
, hdbc
, hstmt
));
1135 // Completed Successfully
1138 } // wxDb::getDataTypeInfo()
1141 /********** wxDb::Close() **********/
1142 void wxDb::Close(void)
1144 // Close the Sql Log file
1151 // Free statement handle
1154 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
1155 DispAllErrors(henv
, hdbc
);
1158 // Disconnect from the datasource
1159 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
1160 DispAllErrors(henv
, hdbc
);
1162 // Free the connection to the datasource
1163 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
1164 DispAllErrors(henv
, hdbc
);
1166 // There should be zero Ctable objects still connected to this db object
1167 assert(nTables
== 0);
1172 pNode
= TablesInUse
.First();
1176 tiu
= (wxTablesInUse
*)pNode
->Data();
1177 if (tiu
->pDb
== this)
1179 s
.sprintf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
1180 s2
.sprintf(wxT("Orphaned found using pDb:[%p]"),this);
1181 wxLogDebug (s
.c_str(),s2
.c_str());
1183 pNode
= pNode
->Next();
1187 // Copy the error messages to a global variable
1189 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1190 wxStrcpy(DBerrorList
[i
],errorList
[i
]);
1192 dbmsType
= dbmsUNIDENTIFIED
;
1198 /********** wxDb::CommitTrans() **********/
1199 bool wxDb::CommitTrans(void)
1203 // Commit the transaction
1204 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
1205 return(DispAllErrors(henv
, hdbc
));
1208 // Completed successfully
1211 } // wxDb::CommitTrans()
1214 /********** wxDb::RollbackTrans() **********/
1215 bool wxDb::RollbackTrans(void)
1217 // Rollback the transaction
1218 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
1219 return(DispAllErrors(henv
, hdbc
));
1221 // Completed successfully
1224 } // wxDb::RollbackTrans()
1227 /********** wxDb::DispAllErrors() **********/
1228 bool wxDb::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1230 * This function is called internally whenever an error condition prevents the user's
1231 * request from being executed. This function will query the datasource as to the
1232 * actual error(s) that just occured on the previous request of the datasource.
1234 * The function will retrieve each error condition from the datasource and
1235 * sprintf the codes/text values into a string which it then logs via logError().
1236 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1237 * window and program execution will be paused until the user presses a key.
1239 * This function always returns a FALSE, so that functions which call this function
1240 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1241 * of the users request, so that the calling code can then process the error msg log
1244 wxString odbcErrMsg
;
1246 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1248 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
1249 logError(odbcErrMsg
.c_str(), sqlState
);
1252 #ifdef DBDEBUG_CONSOLE
1253 // When run in console mode, use standard out to display errors.
1254 cout
<< odbcErrMsg
.c_str() << endl
;
1255 cout
<< "Press any key to continue..." << endl
;
1260 wxLogDebug(odbcErrMsg
.c_str(),wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1265 return(FALSE
); // This function always returns false.
1267 } // wxDb::DispAllErrors()
1270 /********** wxDb::GetNextError() **********/
1271 bool wxDb::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
1273 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
1278 } // wxDb::GetNextError()
1281 /********** wxDb::DispNextError() **********/
1282 void wxDb::DispNextError(void)
1284 wxString odbcErrMsg
;
1286 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
1287 logError(odbcErrMsg
.c_str(), sqlState
);
1292 #ifdef DBDEBUG_CONSOLE
1293 // When run in console mode, use standard out to display errors.
1294 cout
<< odbcErrMsg
.c_str() << endl
;
1295 cout
<< "Press any key to continue..." << endl
;
1300 wxLogDebug(odbcErrMsg
,wxT("ODBC DEBUG MESSAGE"));
1301 #endif // __WXDEBUG__
1303 } // wxDb::DispNextError()
1306 /********** wxDb::logError() **********/
1307 void wxDb::logError(const char *errMsg
, const char *SQLState
)
1309 assert(errMsg
&& wxStrlen(errMsg
));
1311 static int pLast
= -1;
1314 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1317 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1318 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1322 wxStrcpy(errorList
[pLast
], errMsg
);
1324 if (SQLState
&& wxStrlen(SQLState
))
1325 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1326 DB_STATUS
= dbStatus
;
1328 // Add the errmsg to the sql log
1329 WriteSqlLog(errMsg
);
1331 } // wxDb::logError()
1334 /**********wxDb::TranslateSqlState() **********/
1335 int wxDb::TranslateSqlState(const wxChar
*SQLState
)
1337 if (!wxStrcmp(SQLState
, wxT("01000")))
1338 return(DB_ERR_GENERAL_WARNING
);
1339 if (!wxStrcmp(SQLState
, wxT("01002")))
1340 return(DB_ERR_DISCONNECT_ERROR
);
1341 if (!wxStrcmp(SQLState
, wxT("01004")))
1342 return(DB_ERR_DATA_TRUNCATED
);
1343 if (!wxStrcmp(SQLState
, wxT("01006")))
1344 return(DB_ERR_PRIV_NOT_REVOKED
);
1345 if (!wxStrcmp(SQLState
, wxT("01S00")))
1346 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1347 if (!wxStrcmp(SQLState
, wxT("01S01")))
1348 return(DB_ERR_ERROR_IN_ROW
);
1349 if (!wxStrcmp(SQLState
, wxT("01S02")))
1350 return(DB_ERR_OPTION_VALUE_CHANGED
);
1351 if (!wxStrcmp(SQLState
, wxT("01S03")))
1352 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1353 if (!wxStrcmp(SQLState
, wxT("01S04")))
1354 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1355 if (!wxStrcmp(SQLState
, wxT("07001")))
1356 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1357 if (!wxStrcmp(SQLState
, wxT("07006")))
1358 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1359 if (!wxStrcmp(SQLState
, wxT("08001")))
1360 return(DB_ERR_UNABLE_TO_CONNECT
);
1361 if (!wxStrcmp(SQLState
, wxT("08002")))
1362 return(DB_ERR_CONNECTION_IN_USE
);
1363 if (!wxStrcmp(SQLState
, wxT("08003")))
1364 return(DB_ERR_CONNECTION_NOT_OPEN
);
1365 if (!wxStrcmp(SQLState
, wxT("08004")))
1366 return(DB_ERR_REJECTED_CONNECTION
);
1367 if (!wxStrcmp(SQLState
, wxT("08007")))
1368 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1369 if (!wxStrcmp(SQLState
, wxT("08S01")))
1370 return(DB_ERR_COMM_LINK_FAILURE
);
1371 if (!wxStrcmp(SQLState
, wxT("21S01")))
1372 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1373 if (!wxStrcmp(SQLState
, wxT("21S02")))
1374 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1375 if (!wxStrcmp(SQLState
, wxT("22001")))
1376 return(DB_ERR_STRING_RIGHT_TRUNC
);
1377 if (!wxStrcmp(SQLState
, wxT("22003")))
1378 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1379 if (!wxStrcmp(SQLState
, wxT("22005")))
1380 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1381 if (!wxStrcmp(SQLState
, wxT("22008")))
1382 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1383 if (!wxStrcmp(SQLState
, wxT("22012")))
1384 return(DB_ERR_DIVIDE_BY_ZERO
);
1385 if (!wxStrcmp(SQLState
, wxT("22026")))
1386 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1387 if (!wxStrcmp(SQLState
, wxT("23000")))
1388 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1389 if (!wxStrcmp(SQLState
, wxT("24000")))
1390 return(DB_ERR_INVALID_CURSOR_STATE
);
1391 if (!wxStrcmp(SQLState
, wxT("25000")))
1392 return(DB_ERR_INVALID_TRANS_STATE
);
1393 if (!wxStrcmp(SQLState
, wxT("28000")))
1394 return(DB_ERR_INVALID_AUTH_SPEC
);
1395 if (!wxStrcmp(SQLState
, wxT("34000")))
1396 return(DB_ERR_INVALID_CURSOR_NAME
);
1397 if (!wxStrcmp(SQLState
, wxT("37000")))
1398 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1399 if (!wxStrcmp(SQLState
, wxT("3C000")))
1400 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1401 if (!wxStrcmp(SQLState
, wxT("40001")))
1402 return(DB_ERR_SERIALIZATION_FAILURE
);
1403 if (!wxStrcmp(SQLState
, wxT("42000")))
1404 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1405 if (!wxStrcmp(SQLState
, wxT("70100")))
1406 return(DB_ERR_OPERATION_ABORTED
);
1407 if (!wxStrcmp(SQLState
, wxT("IM001")))
1408 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1409 if (!wxStrcmp(SQLState
, wxT("IM002")))
1410 return(DB_ERR_NO_DATA_SOURCE
);
1411 if (!wxStrcmp(SQLState
, wxT("IM003")))
1412 return(DB_ERR_DRIVER_LOAD_ERROR
);
1413 if (!wxStrcmp(SQLState
, wxT("IM004")))
1414 return(DB_ERR_SQLALLOCENV_FAILED
);
1415 if (!wxStrcmp(SQLState
, wxT("IM005")))
1416 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1417 if (!wxStrcmp(SQLState
, wxT("IM006")))
1418 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1419 if (!wxStrcmp(SQLState
, wxT("IM007")))
1420 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1421 if (!wxStrcmp(SQLState
, wxT("IM008")))
1422 return(DB_ERR_DIALOG_FAILED
);
1423 if (!wxStrcmp(SQLState
, wxT("IM009")))
1424 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1425 if (!wxStrcmp(SQLState
, wxT("IM010")))
1426 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1427 if (!wxStrcmp(SQLState
, wxT("IM011")))
1428 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1429 if (!wxStrcmp(SQLState
, wxT("IM012")))
1430 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1431 if (!wxStrcmp(SQLState
, wxT("IM013")))
1432 return(DB_ERR_TRACE_FILE_ERROR
);
1433 if (!wxStrcmp(SQLState
, wxT("S0001")))
1434 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1435 if (!wxStrcmp(SQLState
, wxT("S0002")))
1436 return(DB_ERR_TABLE_NOT_FOUND
);
1437 if (!wxStrcmp(SQLState
, wxT("S0011")))
1438 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1439 if (!wxStrcmp(SQLState
, wxT("S0012")))
1440 return(DB_ERR_INDEX_NOT_FOUND
);
1441 if (!wxStrcmp(SQLState
, wxT("S0021")))
1442 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1443 if (!wxStrcmp(SQLState
, wxT("S0022")))
1444 return(DB_ERR_COLUMN_NOT_FOUND
);
1445 if (!wxStrcmp(SQLState
, wxT("S0023")))
1446 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1447 if (!wxStrcmp(SQLState
, wxT("S1000")))
1448 return(DB_ERR_GENERAL_ERROR
);
1449 if (!wxStrcmp(SQLState
, wxT("S1001")))
1450 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1451 if (!wxStrcmp(SQLState
, wxT("S1002")))
1452 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1453 if (!wxStrcmp(SQLState
, wxT("S1003")))
1454 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1455 if (!wxStrcmp(SQLState
, wxT("S1004")))
1456 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1457 if (!wxStrcmp(SQLState
, wxT("S1008")))
1458 return(DB_ERR_OPERATION_CANCELLED
);
1459 if (!wxStrcmp(SQLState
, wxT("S1009")))
1460 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1461 if (!wxStrcmp(SQLState
, wxT("S1010")))
1462 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1463 if (!wxStrcmp(SQLState
, wxT("S1011")))
1464 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1465 if (!wxStrcmp(SQLState
, wxT("S1012")))
1466 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1467 if (!wxStrcmp(SQLState
, wxT("S1015")))
1468 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1469 if (!wxStrcmp(SQLState
, wxT("S1090")))
1470 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1471 if (!wxStrcmp(SQLState
, wxT("S1091")))
1472 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1473 if (!wxStrcmp(SQLState
, wxT("S1092")))
1474 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1475 if (!wxStrcmp(SQLState
, wxT("S1093")))
1476 return(DB_ERR_INVALID_PARAM_NO
);
1477 if (!wxStrcmp(SQLState
, wxT("S1094")))
1478 return(DB_ERR_INVALID_SCALE_VALUE
);
1479 if (!wxStrcmp(SQLState
, wxT("S1095")))
1480 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1481 if (!wxStrcmp(SQLState
, wxT("S1096")))
1482 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1483 if (!wxStrcmp(SQLState
, wxT("S1097")))
1484 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1485 if (!wxStrcmp(SQLState
, wxT("S1098")))
1486 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1487 if (!wxStrcmp(SQLState
, wxT("S1099")))
1488 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1489 if (!wxStrcmp(SQLState
, wxT("S1100")))
1490 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1491 if (!wxStrcmp(SQLState
, wxT("S1101")))
1492 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1493 if (!wxStrcmp(SQLState
, wxT("S1103")))
1494 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1495 if (!wxStrcmp(SQLState
, wxT("S1104")))
1496 return(DB_ERR_INVALID_PRECISION_VALUE
);
1497 if (!wxStrcmp(SQLState
, wxT("S1105")))
1498 return(DB_ERR_INVALID_PARAM_TYPE
);
1499 if (!wxStrcmp(SQLState
, wxT("S1106")))
1500 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1501 if (!wxStrcmp(SQLState
, wxT("S1107")))
1502 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1503 if (!wxStrcmp(SQLState
, wxT("S1108")))
1504 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1505 if (!wxStrcmp(SQLState
, wxT("S1109")))
1506 return(DB_ERR_INVALID_CURSOR_POSITION
);
1507 if (!wxStrcmp(SQLState
, wxT("S1110")))
1508 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1509 if (!wxStrcmp(SQLState
, wxT("S1111")))
1510 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1511 if (!wxStrcmp(SQLState
, wxT("S1C00")))
1512 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1513 if (!wxStrcmp(SQLState
, wxT("S1T00")))
1514 return(DB_ERR_TIMEOUT_EXPIRED
);
1519 } // wxDb::TranslateSqlState()
1522 /********** wxDb::Grant() **********/
1523 bool wxDb::Grant(int privileges
, const char *tableName
, const char *userList
)
1527 // Build the grant statement
1529 if (privileges
== DB_GRANT_ALL
)
1534 if (privileges
& DB_GRANT_SELECT
)
1536 sqlStmt
+= "SELECT";
1539 if (privileges
& DB_GRANT_INSERT
)
1543 sqlStmt
+= "INSERT";
1545 if (privileges
& DB_GRANT_UPDATE
)
1549 sqlStmt
+= "UPDATE";
1551 if (privileges
& DB_GRANT_DELETE
)
1555 sqlStmt
+= "DELETE";
1560 sqlStmt
+= tableName
;
1562 sqlStmt
+= userList
;
1564 #ifdef DBDEBUG_CONSOLE
1565 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1568 WriteSqlLog(sqlStmt
.c_str());
1570 return(ExecSql(sqlStmt
.c_str()));
1575 /********** wxDb::CreateView() **********/
1576 bool wxDb::CreateView(const char *viewName
, const char *colList
, const char *pSqlStmt
, bool attemptDrop
)
1580 // Drop the view first
1581 if (attemptDrop
&& !DropView(viewName
))
1584 // Build the create view statement
1585 sqlStmt
= "CREATE VIEW ";
1586 sqlStmt
+= viewName
;
1588 if (wxStrlen(colList
))
1596 sqlStmt
+= pSqlStmt
;
1598 WriteSqlLog(sqlStmt
.c_str());
1600 #ifdef DBDEBUG_CONSOLE
1601 cout
<< sqlStmt
.c_str() << endl
;
1604 return(ExecSql(sqlStmt
.c_str()));
1606 } // wxDb::CreateView()
1609 /********** wxDb::DropView() **********/
1610 bool wxDb::DropView(const char *viewName
)
1613 * NOTE: This function returns TRUE if the View does not exist, but
1614 * only for identified databases. Code will need to be added
1615 * below for any other databases when those databases are defined
1616 * to handle this situation consistently
1618 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1621 sqlStmt
.sprintf("DROP VIEW %s", viewName
);
1623 WriteSqlLog(sqlStmt
.c_str());
1625 #ifdef DBDEBUG_CONSOLE
1626 cout
<< endl
<< sqlStmt
.c_str() << endl
;
1629 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
1631 // Check for "Base table not found" error and ignore
1632 GetNextError(henv
, hdbc
, hstmt
);
1633 if (wxStrcmp(sqlState
,wxT("S0002"))) // "Base table not found"
1635 // Check for product specific error codes
1636 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,wxT("42000"))))) // 5.x (and lower?)
1639 DispAllErrors(henv
, hdbc
, hstmt
);
1646 // Commit the transaction
1647 if (! CommitTrans())
1652 } // wxDb::DropView()
1655 /********** wxDb::ExecSql() **********/
1656 bool wxDb::ExecSql(const char *pSqlStmt
)
1658 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1659 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) == SQL_SUCCESS
)
1663 DispAllErrors(henv
, hdbc
, hstmt
);
1667 } // wxDb::ExecSql()
1670 /********** wxDb::GetNext() **********/
1671 bool wxDb::GetNext(void)
1673 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1677 DispAllErrors(henv
, hdbc
, hstmt
);
1681 } // wxDb::GetNext()
1684 /********** wxDb::GetData() **********/
1685 bool wxDb::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1690 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1694 DispAllErrors(henv
, hdbc
, hstmt
);
1698 } // wxDb::GetData()
1701 /********** wxDb::GetKeyFields() **********/
1702 int wxDb::GetKeyFields(char *tableName
, wxDbColInf
* colInf
, int noCols
)
1704 char szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1705 char szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1707 // SQLSMALLINT iKeySeq;
1708 char szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1709 char szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1715 * ---------------------------------------------------------------------
1716 * -- 19991224 : mj10777 : Create ------
1717 * -- : Three things are done and stored here : ------
1718 * -- : 1) which Column(s) is/are Primary Key(s) ------
1719 * -- : 2) which tables use this Key as a Foreign Key ------
1720 * -- : 3) which columns are Foreign Key and the name ------
1721 * -- : of the Table where the Key is the Primary Key -----
1722 * -- : Called from GetColumns(char *tableName, ------
1723 * -- int *numCols,const char *userID ) ------
1724 * ---------------------------------------------------------------------
1727 /*---------------------------------------------------------------------*/
1728 /* Get the names of the columns in the primary key. */
1729 /*---------------------------------------------------------------------*/
1730 retcode
= SQLPrimaryKeys(hstmt
,
1731 NULL
, 0, /* Catalog name */
1732 NULL
, 0, /* Schema name */
1733 (UCHAR
*) tableName
, SQL_NTS
); /* Table name */
1735 /*---------------------------------------------------------------------*/
1736 /* Fetch and display the result set. This will be a list of the */
1737 /* columns in the primary key of the tableName table. */
1738 /*---------------------------------------------------------------------*/
1739 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1741 retcode
= SQLFetch(hstmt
);
1742 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1744 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1745 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1747 for (i
=0;i
<noCols
;i
++) // Find the Column name
1748 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1749 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1752 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1754 /*---------------------------------------------------------------------*/
1755 /* Get all the foreign keys that refer to tableName primary key. */
1756 /*---------------------------------------------------------------------*/
1757 retcode
= SQLForeignKeys(hstmt
,
1758 NULL
, 0, /* Primary catalog */
1759 NULL
, 0, /* Primary schema */
1760 (UCHAR
*)tableName
, SQL_NTS
, /* Primary table */
1761 NULL
, 0, /* Foreign catalog */
1762 NULL
, 0, /* Foreign schema */
1763 NULL
, 0); /* Foreign table */
1765 /*---------------------------------------------------------------------*/
1766 /* Fetch and display the result set. This will be all of the foreign */
1767 /* keys in other tables that refer to the tableName primary key. */
1768 /*---------------------------------------------------------------------*/
1771 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1773 retcode
= SQLFetch(hstmt
);
1774 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1776 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1777 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1778 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1779 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1780 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1781 Temp0
.Printf(wxT("%s[%s] "),Temp0
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
1784 Temp0
.Trim(); // Get rid of any unneeded blanks
1785 if (Temp0
!= wxT(""))
1787 for (i
=0;i
<noCols
;i
++)
1788 { // Find the Column name
1789 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column, store the Information
1790 wxStrcpy(colInf
[i
].PkTableName
,Temp0
.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
1793 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1795 /*---------------------------------------------------------------------*/
1796 /* Get all the foreign keys in the tablename table. */
1797 /*---------------------------------------------------------------------*/
1798 retcode
= SQLForeignKeys(hstmt
,
1799 NULL
, 0, /* Primary catalog */
1800 NULL
, 0, /* Primary schema */
1801 NULL
, 0, /* Primary table */
1802 NULL
, 0, /* Foreign catalog */
1803 NULL
, 0, /* Foreign schema */
1804 (UCHAR
*)tableName
, SQL_NTS
); /* Foreign table */
1806 /*---------------------------------------------------------------------*/
1807 /* Fetch and display the result set. This will be all of the */
1808 /* primary keys in other tables that are referred to by foreign */
1809 /* keys in the tableName table. */
1810 /*---------------------------------------------------------------------*/
1812 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1814 retcode
= SQLFetch(hstmt
);
1815 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1817 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1818 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1819 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1821 for (i
=0;i
<noCols
;i
++) // Find the Column name
1823 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
1825 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
1826 wxStrcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
1831 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1835 } // wxDb::GetKeyFields()
1839 /********** wxDb::GetColumns() **********/
1840 wxDbColInf
*wxDb::GetColumns(char *tableName
[], const char *userID
)
1842 * 1) The last array element of the tableName[] argument must be zero (null).
1843 * This is how the end of the array is detected.
1844 * 2) This function returns an array of wxDbColInf structures. If no columns
1845 * were found, or an error occured, this pointer will be zero (null). THE
1846 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
1847 * IS FINISHED WITH IT. i.e.
1849 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
1852 * // Use the column inf
1854 * // Destroy the memory
1858 * userID is evaluated in the following manner:
1859 * userID == NULL ... UserID is ignored
1860 * userID == "" ... UserID set equal to 'this->uid'
1861 * userID != "" ... UserID set equal to 'userID'
1863 * NOTE: ALL column bindings associated with this wxDb instance are unbound
1864 * by this function. This function should use its own wxDb instance
1865 * to avoid undesired unbinding of columns.
1870 wxDbColInf
*colInf
= 0;
1880 if (!wxStrlen(userID
))
1888 // dBase does not use user names, and some drivers fail if you try to pass one
1889 if (Dbms() == dbmsDBASE
)
1892 // Oracle and Interbase user names may only be in uppercase, so force
1893 // the name to uppercase
1894 if (Dbms() == dbmsORACLE
)
1895 UserID
= UserID
.Upper();
1897 // Pass 1 - Determine how many columns there are.
1898 // Pass 2 - Allocate the wxDbColInf array and fill in
1899 // the array with the column information.
1901 for (pass
= 1; pass
<= 2; pass
++)
1905 if (noCols
== 0) // Probably a bogus table name(s)
1907 // Allocate n wxDbColInf objects to hold the column information
1908 colInf
= new wxDbColInf
[noCols
+1];
1911 // Mark the end of the array
1912 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
1913 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
1914 colInf
[noCols
].sqlDataType
= 0;
1916 // Loop through each table name
1918 for (tbl
= 0; tableName
[tbl
]; tbl
++)
1920 TableName
= tableName
[tbl
];
1921 // Oracle and Interbase table names are uppercase only, so force
1922 // the name to uppercase just in case programmer forgot to do this
1923 if ((Dbms() == dbmsORACLE
) ||
1924 (Dbms() == dbmsINTERBASE
))
1925 TableName
= TableName
.Upper();
1927 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1929 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
1930 // use the call below that leaves out the user name
1931 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
1932 Dbms() != dbmsMY_SQL
&&
1933 Dbms() != dbmsACCESS
&&
1934 Dbms() != dbmsMS_SQL_SERVER
)
1936 retcode
= SQLColumns(hstmt
,
1937 NULL
, 0, // All qualifiers
1938 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
1939 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
1940 NULL
, 0); // All columns
1944 retcode
= SQLColumns(hstmt
,
1945 NULL
, 0, // All qualifiers
1947 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
1948 NULL
, 0); // All columns
1950 if (retcode
!= SQL_SUCCESS
)
1951 { // Error occured, abort
1952 DispAllErrors(henv
, hdbc
, hstmt
);
1955 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1959 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
1961 if (pass
== 1) // First pass, just add up the number of columns
1963 else // Pass 2; Fill in the array of structures
1965 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
1967 // NOTE: Only the ODBC 1.x fields are retrieved
1968 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
1969 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
1970 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1971 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1972 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
1973 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
1974 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
1975 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
1976 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
1977 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
1978 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
1979 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
1981 // Determine the wxDb data type that is used to represent the native data type of this data source
1982 colInf
[colNo
].dbDataType
= 0;
1983 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
1986 // IODBC does not return a correct columnSize, so we set
1987 // columnSize = bufferLength if no column size was returned
1988 // IODBC returns the columnSize in bufferLength.. (bug)
1989 if (colInf
[colNo
].columnSize
< 1)
1991 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
1994 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
1996 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
1997 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
1998 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
1999 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2000 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
2001 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2007 if (retcode
!= SQL_NO_DATA_FOUND
)
2008 { // Error occured, abort
2009 DispAllErrors(henv
, hdbc
, hstmt
);
2012 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2018 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2021 } // wxDb::GetColumns()
2024 /********** wxDb::GetColumns() **********/
2026 wxDbColInf
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
)
2028 // Same as the above GetColumns() function except this one gets columns
2029 // only for a single table, and if 'numCols' is not NULL, the number of
2030 // columns stored in the returned wxDbColInf is set in '*numCols'
2032 // userID is evaluated in the following manner:
2033 // userID == NULL ... UserID is ignored
2034 // userID == "" ... UserID set equal to 'this->uid'
2035 // userID != "" ... UserID set equal to 'userID'
2037 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2038 // by this function. This function should use its own wxDb instance
2039 // to avoid undesired unbinding of columns.
2044 wxDbColInf
*colInf
= 0;
2054 if (!wxStrlen(userID
))
2062 // dBase does not use user names, and some drivers fail if you try to pass one
2063 if (Dbms() == dbmsDBASE
)
2066 // Oracle user names may only be in uppercase, so force
2067 // the name to uppercase
2068 if (Dbms() == dbmsORACLE
)
2069 UserID
= UserID
.Upper();
2071 // Pass 1 - Determine how many columns there are.
2072 // Pass 2 - Allocate the wxDbColInf array and fill in
2073 // the array with the column information.
2075 for (pass
= 1; pass
<= 2; pass
++)
2079 if (noCols
== 0) // Probably a bogus table name(s)
2081 // Allocate n wxDbColInf objects to hold the column information
2082 colInf
= new wxDbColInf
[noCols
+1];
2085 // Mark the end of the array
2086 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2087 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2088 colInf
[noCols
].sqlDataType
= 0;
2091 TableName
= tableName
;
2092 // Oracle and Interbase table names are uppercase only, so force
2093 // the name to uppercase just in case programmer forgot to do this
2094 if ((Dbms() == dbmsORACLE
) ||
2095 (Dbms() == dbmsINTERBASE
))
2096 TableName
= TableName
.Upper();
2098 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2100 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2101 // use the call below that leaves out the user name
2102 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2103 Dbms() != dbmsMY_SQL
&&
2104 Dbms() != dbmsACCESS
&&
2105 Dbms() != dbmsMS_SQL_SERVER
)
2107 retcode
= SQLColumns(hstmt
,
2108 NULL
, 0, // All qualifiers
2109 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2110 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2111 NULL
, 0); // All columns
2115 retcode
= SQLColumns(hstmt
,
2116 NULL
, 0, // All qualifiers
2118 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2119 NULL
, 0); // All columns
2121 if (retcode
!= SQL_SUCCESS
)
2122 { // Error occured, abort
2123 DispAllErrors(henv
, hdbc
, hstmt
);
2126 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2132 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2134 if (pass
== 1) // First pass, just add up the number of columns
2136 else // Pass 2; Fill in the array of structures
2138 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2140 // NOTE: Only the ODBC 1.x fields are retrieved
2141 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2142 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2143 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2144 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2145 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2146 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2147 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2148 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2149 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2150 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2151 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2152 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2153 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2154 // Start Values for Primary/Foriegn Key (=No)
2155 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2156 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2157 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2158 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2160 // BJO 20000428 : Virtuoso returns type names with upper cases!
2161 if (Dbms() == dbmsVIRTUOSO
)
2163 wxString s
= colInf
[colNo
].typeName
;
2165 wxStrcmp(colInf
[colNo
].typeName
, s
.c_str());
2168 // Determine the wxDb data type that is used to represent the native data type of this data source
2169 colInf
[colNo
].dbDataType
= 0;
2170 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
2173 // IODBC does not return a correct columnSize, so we set
2174 // columnSize = bufferLength if no column size was returned
2175 // IODBC returns the columnSize in bufferLength.. (bug)
2176 if (colInf
[colNo
].columnSize
< 1)
2178 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2182 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2184 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
2185 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2186 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
2187 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2188 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
2189 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2195 if (retcode
!= SQL_NO_DATA_FOUND
)
2196 { // Error occured, abort
2197 DispAllErrors(henv
, hdbc
, hstmt
);
2200 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2207 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2209 // Store Primary and Foriegn Keys
2210 GetKeyFields(tableName
,colInf
,noCols
);
2216 } // wxDb::GetColumns()
2219 #else // New GetColumns
2224 These are tentative new GetColumns members which should be more database
2225 independant and which always returns the columns in the order they were
2228 - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const
2229 char* userID)) calls the second implementation for each separate table
2230 before merging the results. This makes the code easier to maintain as
2231 only one member (the second) makes the real work
2232 - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const
2233 char *userID) is a little bit improved
2234 - It doesn't anymore rely on the type-name to find out which database-type
2236 - It ends by sorting the columns, so that they are returned in the same
2237 order they were created
2247 wxDbColInf
*wxDb::GetColumns(char *tableName
[], const char* userID
)
2250 // The last array element of the tableName[] argument must be zero (null).
2251 // This is how the end of the array is detected.
2255 // How many tables ?
2257 for (tbl
= 0 ; tableName
[tbl
]; tbl
++);
2259 // Create a table to maintain the columns for each separate table
2260 _TableColumns
*TableColumns
= new _TableColumns
[tbl
];
2263 for (i
= 0 ; i
< tbl
; i
++)
2266 TableColumns
[i
].colInf
= GetColumns(tableName
[i
], &TableColumns
[i
].noCols
, userID
);
2267 if (TableColumns
[i
].colInf
== NULL
)
2269 noCols
+= TableColumns
[i
].noCols
;
2272 // Now merge all the separate table infos
2273 wxDbColInf
*colInf
= new wxDbColInf
[noCols
+1];
2275 // Mark the end of the array
2276 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2277 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2278 colInf
[noCols
].sqlDataType
= 0;
2283 for (i
= 0 ; i
< tbl
; i
++)
2285 for (j
= 0 ; j
< TableColumns
[i
].noCols
; j
++)
2287 colInf
[offset
++] = TableColumns
[i
].colInf
[j
];
2291 delete [] TableColumns
;
2294 } // wxDb::GetColumns() -- NEW
2297 wxDbColInf
*wxDb::GetColumns(char *tableName
, int *numCols
, const char *userID
)
2299 // Same as the above GetColumns() function except this one gets columns
2300 // only for a single table, and if 'numCols' is not NULL, the number of
2301 // columns stored in the returned wxDbColInf is set in '*numCols'
2303 // userID is evaluated in the following manner:
2304 // userID == NULL ... UserID is ignored
2305 // userID == "" ... UserID set equal to 'this->uid'
2306 // userID != "" ... UserID set equal to 'userID'
2308 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2309 // by this function. This function should use its own wxDb instance
2310 // to avoid undesired unbinding of columns.
2314 wxDbColInf
*colInf
= 0;
2324 if (!wxStrlen(userID
))
2332 // dBase does not use user names, and some drivers fail if you try to pass one
2333 if (Dbms() == dbmsDBASE
)
2336 // Oracle user names may only be in uppercase, so force
2337 // the name to uppercase
2338 if (Dbms() == dbmsORACLE
)
2339 UserID
= UserID
.Upper();
2341 // Pass 1 - Determine how many columns there are.
2342 // Pass 2 - Allocate the wxDbColInf array and fill in
2343 // the array with the column information.
2345 for (pass
= 1; pass
<= 2; pass
++)
2349 if (noCols
== 0) // Probably a bogus table name(s)
2351 // Allocate n wxDbColInf objects to hold the column information
2352 colInf
= new wxDbColInf
[noCols
+1];
2355 // Mark the end of the array
2356 wxStrcpy(colInf
[noCols
].tableName
, wxT(""));
2357 wxStrcpy(colInf
[noCols
].colName
, wxT(""));
2358 colInf
[noCols
].sqlDataType
= 0;
2361 TableName
= tableName
;
2362 // Oracle and Interbase table names are uppercase only, so force
2363 // the name to uppercase just in case programmer forgot to do this
2364 if ((Dbms() == dbmsORACLE
) ||
2365 (Dbms() == dbmsINTERBASE
))
2366 TableName
= TableName
.Upper();
2368 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2370 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2371 // use the call below that leaves out the user name
2372 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2373 Dbms() != dbmsMY_SQL
&&
2374 Dbms() != dbmsACCESS
&&
2375 Dbms() != dbmsMS_SQL_SERVER
)
2377 retcode
= SQLColumns(hstmt
,
2378 NULL
, 0, // All qualifiers
2379 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2380 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2381 NULL
, 0); // All columns
2385 retcode
= SQLColumns(hstmt
,
2386 NULL
, 0, // All qualifiers
2388 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2389 NULL
, 0); // All columns
2391 if (retcode
!= SQL_SUCCESS
)
2392 { // Error occured, abort
2393 DispAllErrors(henv
, hdbc
, hstmt
);
2396 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2402 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2404 if (pass
== 1) // First pass, just add up the number of columns
2406 else // Pass 2; Fill in the array of structures
2408 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
2410 // NOTE: Only the ODBC 1.x fields are retrieved
2411 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
2412 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
2413 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2414 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
2415 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
2416 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
2417 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
2418 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
2419 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
2420 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
2421 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
2422 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
2423 // Start Values for Primary/Foriegn Key (=No)
2424 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2425 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2426 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2427 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
2430 // IODBC does not return a correct columnSize, so we set
2431 // columnSize = bufferLength if no column size was returned
2432 // IODBC returns the columnSize in bufferLength.. (bug)
2433 if (colInf
[colNo
].columnSize
< 1)
2435 colInf
[colNo
].columnSize
= colInf
[colNo
].bufferLength
;
2439 // Determine the wxDb data type that is used to represent the native data type of this data source
2440 colInf
[colNo
].dbDataType
= 0;
2441 // Get the intern datatype
2442 switch (colInf
[colNo
].sqlDataType
)
2446 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
2452 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
2459 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
2462 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
2467 errMsg
.sprintf("SQL Data type %d currently not supported by wxWindows", colInf
[colNo
].sqlDataType
);
2468 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
2475 if (retcode
!= SQL_NO_DATA_FOUND
)
2476 { // Error occured, abort
2477 DispAllErrors(henv
, hdbc
, hstmt
);
2480 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2487 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2489 // Store Primary and Foreign Keys
2490 GetKeyFields(tableName
,colInf
,noCols
);
2492 ///////////////////////////////////////////////////////////////////////////
2493 // Now sort the the columns in order to make them appear in the right order
2494 ///////////////////////////////////////////////////////////////////////////
2496 // Build a generic SELECT statement which returns 0 rows
2499 Stmt
.sprintf("select * from %s where 0=1", tableName
);
2502 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) Stmt
.c_str(), SQL_NTS
) != SQL_SUCCESS
)
2504 DispAllErrors(henv
, hdbc
, hstmt
);
2508 // Get the number of result columns
2509 if (SQLNumResultCols (hstmt
, &noCols
) != SQL_SUCCESS
)
2511 DispAllErrors(henv
, hdbc
, hstmt
);
2515 if (noCols
== 0) // Probably a bogus table name
2524 for (colNum
= 0; colNum
< noCols
; colNum
++)
2526 if (SQLColAttributes(hstmt
,colNum
+1, SQL_COLUMN_NAME
,
2528 &Sword
, &Sdword
) != SQL_SUCCESS
)
2530 DispAllErrors(henv
, hdbc
, hstmt
);
2534 wxString Name1
= name
;
2535 Name1
= Name1
.Upper();
2537 // Where is this name in the array ?
2538 for (i
= colNum
; i
< noCols
; i
++)
2540 wxString Name2
= colInf
[i
].colName
;
2541 Name2
= Name2
.Upper();
2544 if (colNum
!= i
) // swap to sort
2546 wxDbColInf tmpColInf
= colInf
[colNum
];
2547 colInf
[colNum
] = colInf
[i
];
2548 colInf
[i
] = tmpColInf
;
2554 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2556 ///////////////////////////////////////////////////////////////////////////
2558 ///////////////////////////////////////////////////////////////////////////
2564 } // wxDb::GetColumns()
2567 #endif // #else OLD_GETCOLUMNS
2570 /********** wxDb::GetColumnCount() **********/
2571 int wxDb::GetColumnCount(char *tableName
, const char *userID
)
2573 * Returns a count of how many columns are in a table.
2574 * If an error occurs in computing the number of columns
2575 * this function will return a -1 for the count
2577 * userID is evaluated in the following manner:
2578 * userID == NULL ... UserID is ignored
2579 * userID == "" ... UserID set equal to 'this->uid'
2580 * userID != "" ... UserID set equal to 'userID'
2582 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2583 * by this function. This function should use its own wxDb instance
2584 * to avoid undesired unbinding of columns.
2596 if (!wxStrlen(userID
))
2604 // dBase does not use user names, and some drivers fail if you try to pass one
2605 if (Dbms() == dbmsDBASE
)
2608 // Oracle user names may only be in uppercase, so force
2609 // the name to uppercase
2610 if (Dbms() == dbmsORACLE
)
2611 UserID
= UserID
.Upper();
2614 // Loop through each table name
2616 TableName
= tableName
;
2617 // Oracle and Interbase table names are uppercase only, so force
2618 // the name to uppercase just in case programmer forgot to do this
2619 if ((Dbms() == dbmsORACLE
) ||
2620 (Dbms() == dbmsINTERBASE
))
2621 TableName
= TableName
.Upper();
2623 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2625 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2626 // use the call below that leaves out the user name
2627 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2628 Dbms() != dbmsMY_SQL
&&
2629 Dbms() != dbmsACCESS
&&
2630 Dbms() != dbmsMS_SQL_SERVER
)
2632 retcode
= SQLColumns(hstmt
,
2633 NULL
, 0, // All qualifiers
2634 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Owner
2635 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2636 NULL
, 0); // All columns
2640 retcode
= SQLColumns(hstmt
,
2641 NULL
, 0, // All qualifiers
2643 (UCHAR
*) TableName
.c_str(), SQL_NTS
,
2644 NULL
, 0); // All columns
2646 if (retcode
!= SQL_SUCCESS
)
2647 { // Error occured, abort
2648 DispAllErrors(henv
, hdbc
, hstmt
);
2649 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2653 // Count the columns
2654 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2657 if (retcode
!= SQL_NO_DATA_FOUND
)
2658 { // Error occured, abort
2659 DispAllErrors(henv
, hdbc
, hstmt
);
2660 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2666 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2669 } // wxDb::GetColumnCount()
2672 /********** wxDb::GetCatalog() *******/
2673 wxDbInf
*wxDb::GetCatalog(char *userID
)
2675 * ---------------------------------------------------------------------
2676 * -- 19991203 : mj10777 : Create ------
2677 * -- : Creates a wxDbInf with Tables / Cols Array ------
2678 * -- : uses SQLTables and fills pTableInf; ------
2679 * -- : pColInf is set to NULL and numCols to 0; ------
2680 * -- : returns pDbInf (wxDbInf) ------
2681 * -- - if unsuccesfull (pDbInf == NULL) ------
2682 * -- : pColInf can be filled with GetColumns(..); ------
2683 * -- : numCols can be filled with GetColumnCount(..); ------
2684 * ---------------------------------------------------------------------
2686 * userID is evaluated in the following manner:
2687 * userID == NULL ... UserID is ignored
2688 * userID == "" ... UserID set equal to 'this->uid'
2689 * userID != "" ... UserID set equal to 'userID'
2691 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2692 * by this function. This function should use its own wxDb instance
2693 * to avoid undesired unbinding of columns.
2696 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2697 int noTab
= 0; // Counter while filling table entries
2701 wxString tblNameSave
;
2707 if (!wxStrlen(userID
))
2715 // dBase does not use user names, and some drivers fail if you try to pass one
2716 if (Dbms() == dbmsDBASE
)
2719 // Oracle user names may only be in uppercase, so force
2720 // the name to uppercase
2721 if (Dbms() == dbmsORACLE
)
2722 UserID
= UserID
.Upper();
2724 //-------------------------------------------------------------
2725 pDbInf
= new wxDbInf
; // Create the Database Arrray
2726 //-------------------------------------------------------------
2727 // Table Information
2728 // Pass 1 - Determine how many Tables there are.
2729 // Pass 2 - Create the Table array and fill it
2730 // - Create the Cols array = NULL
2731 //-------------------------------------------------------------
2733 for (pass
= 1; pass
<= 2; pass
++)
2735 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2736 tblNameSave
= wxT("");
2738 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2739 Dbms() != dbmsMY_SQL
&&
2740 Dbms() != dbmsACCESS
)
2742 retcode
= SQLTables(hstmt
,
2743 NULL
, 0, // All qualifiers
2744 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2745 NULL
, 0, // All tables
2746 NULL
, 0); // All columns
2750 retcode
= SQLTables(hstmt
,
2751 NULL
, 0, // All qualifiers
2752 NULL
, 0, // User specified
2753 NULL
, 0, // All tables
2754 NULL
, 0); // All columns
2757 if (retcode
!= SQL_SUCCESS
)
2759 DispAllErrors(henv
, hdbc
, hstmt
);
2761 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2765 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2767 if (pass
== 1) // First pass, just count the Tables
2769 if (pDbInf
->numTables
== 0)
2771 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2772 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2774 pDbInf
->numTables
++; // Counter for Tables
2776 if (pass
== 2) // Create and fill the Table entries
2778 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2779 { // no, then create the Array
2780 pDbInf
->pTableInf
= new wxDbTableInf
[pDbInf
->numTables
];
2782 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2784 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2785 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2786 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2792 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2794 // Query how many columns are in each table
2795 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2797 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2802 } // wxDb::GetCatalog()
2805 /********** wxDb::Catalog() **********/
2806 bool wxDb::Catalog(const char *userID
, const char *fileName
)
2808 * Creates the text file specified in 'filename' which will contain
2809 * a minimal data dictionary of all tables accessible by the user specified
2812 * userID is evaluated in the following manner:
2813 * userID == NULL ... UserID is ignored
2814 * userID == "" ... UserID set equal to 'this->uid'
2815 * userID != "" ... UserID set equal to 'userID'
2817 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2818 * by this function. This function should use its own wxDb instance
2819 * to avoid undesired unbinding of columns.
2822 assert(fileName
&& wxStrlen(fileName
));
2826 char tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2827 wxString tblNameSave
;
2828 char colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2830 char typeName
[30+1];
2831 SWORD precision
, length
;
2835 FILE *fp
= fopen(fileName
,"wt");
2839 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2843 if (!wxStrlen(userID
))
2851 // dBase does not use user names, and some drivers fail if you try to pass one
2852 if (Dbms() == dbmsDBASE
)
2855 // Oracle user names may only be in uppercase, so force
2856 // the name to uppercase
2857 if (Dbms() == dbmsORACLE
)
2858 UserID
= UserID
.Upper();
2860 if (wxStrcmp(UserID
.c_str(),wxT("")) &&
2861 Dbms() != dbmsMY_SQL
&&
2862 Dbms() != dbmsACCESS
)
2864 retcode
= SQLColumns(hstmt
,
2865 NULL
, 0, // All qualifiers
2866 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // User specified
2867 NULL
, 0, // All tables
2868 NULL
, 0); // All columns
2872 retcode
= SQLColumns(hstmt
,
2873 NULL
, 0, // All qualifiers
2874 NULL
, 0, // User specified
2875 NULL
, 0, // All tables
2876 NULL
, 0); // All columns
2878 if (retcode
!= SQL_SUCCESS
)
2880 DispAllErrors(henv
, hdbc
, hstmt
);
2886 tblNameSave
= wxT("");
2889 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2891 if (wxStrcmp(tblName
,tblNameSave
.c_str()))
2895 fputs("================================ ", fp
);
2896 fputs("================================ ", fp
);
2897 fputs("===================== ", fp
);
2898 fputs("========= ", fp
);
2899 fputs("=========\n", fp
);
2900 outStr
.sprintf(wxT("%-32s %-32s %-21s %9s %9s\n"),
2901 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
2902 fputs(outStr
.c_str(), fp
);
2903 fputs("================================ ", fp
);
2904 fputs("================================ ", fp
);
2905 fputs("===================== ", fp
);
2906 fputs("========= ", fp
);
2907 fputs("=========\n", fp
);
2908 tblNameSave
= tblName
;
2911 GetData(3,SQL_C_CHAR
, (UCHAR
*)tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2912 GetData(4,SQL_C_CHAR
, (UCHAR
*)colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
2913 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
,0, &cb
);
2914 GetData(6,SQL_C_CHAR
, (UCHAR
*)typeName
, sizeof(typeName
), &cb
);
2915 GetData(7,SQL_C_SSHORT
,(UCHAR
*)&precision
, 0, &cb
);
2916 GetData(8,SQL_C_SSHORT
,(UCHAR
*)&length
, 0, &cb
);
2918 outStr
.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2919 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
2920 if (fputs(outStr
.c_str(), fp
) == EOF
)
2922 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2929 if (retcode
!= SQL_NO_DATA_FOUND
)
2930 DispAllErrors(henv
, hdbc
, hstmt
);
2932 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2935 return(retcode
== SQL_NO_DATA_FOUND
);
2937 } // wxDb::Catalog()
2940 bool wxDb::TableExists(const char *tableName
, const char *userID
, const char *tablePath
)
2942 * Table name can refer to a table, view, alias or synonym. Returns true
2943 * if the object exists in the database. This function does not indicate
2944 * whether or not the user has privleges to query or perform other functions
2947 * userID is evaluated in the following manner:
2948 * userID == NULL ... UserID is ignored
2949 * userID == "" ... UserID set equal to 'this->uid'
2950 * userID != "" ... UserID set equal to 'userID'
2956 assert(tableName
&& wxStrlen(tableName
));
2958 if (Dbms() == dbmsDBASE
)
2961 if (tablePath
&& wxStrlen(tablePath
))
2962 dbName
.sprintf("%s\\%s.dbf",tablePath
,tableName
);
2964 dbName
.sprintf("%s.dbf",tableName
);
2967 exists
= wxFileExists(dbName
.c_str());
2973 if (!wxStrlen(userID
))
2981 // Oracle user names may only be in uppercase, so force
2982 // the name to uppercase
2983 if (Dbms() == dbmsORACLE
)
2984 UserID
= UserID
.Upper();
2986 TableName
= tableName
;
2987 // Oracle and Interbase table names are uppercase only, so force
2988 // the name to uppercase just in case programmer forgot to do this
2989 if ((Dbms() == dbmsORACLE
) ||
2990 (Dbms() == dbmsINTERBASE
))
2991 TableName
= TableName
.Upper();
2993 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2996 // Some databases cannot accept a user name when looking up table names,
2997 // so we use the call below that leaves out the user name
2998 if (wxStrcmp(UserID
,"") &&
2999 Dbms() != dbmsMY_SQL
&&
3000 Dbms() != dbmsACCESS
&&
3001 Dbms() != dbmsMS_SQL_SERVER
)
3003 retcode
= SQLTables(hstmt
,
3004 NULL
, 0, // All qualifiers
3005 (UCHAR
*) UserID
.c_str(), SQL_NTS
, // Only tables owned by this user
3006 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3007 NULL
, 0); // All table types
3011 retcode
= SQLTables(hstmt
,
3012 NULL
, 0, // All qualifiers
3013 NULL
, 0, // All owners
3014 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
,
3015 NULL
, 0); // All table types
3017 if (retcode
!= SQL_SUCCESS
)
3018 return(DispAllErrors(henv
, hdbc
, hstmt
));
3020 retcode
= SQLFetch(hstmt
);
3021 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
3023 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3024 return(DispAllErrors(henv
, hdbc
, hstmt
));
3027 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3031 } // wxDb::TableExists()
3034 /********** wxDb::TablePrivileges() **********/
3035 bool wxDb::TablePrivileges(const char *tableName
, const char* priv
, const char *userID
,
3036 const char *schema
, const char *tablePath
)
3038 wxDbTablePrivilegeInfo result
;
3042 //We probably need to be able to dynamically set this based on
3043 //the driver type, and state.
3044 char curRole
[]="public";
3046 //Prologue here similar to db::TableExists()
3051 assert(tableName
&& wxStrlen(tableName
));
3055 if (!wxStrlen(userID
))
3063 // Oracle user names may only be in uppercase, so force
3064 // the name to uppercase
3065 if (Dbms() == dbmsORACLE
)
3066 UserID
= UserID
.Upper();
3068 TableName
= tableName
;
3069 // Oracle and Interbase table names are uppercase only, so force
3070 // the name to uppercase just in case programmer forgot to do this
3071 if ((Dbms() == dbmsORACLE
) ||
3072 (Dbms() == dbmsINTERBASE
))
3073 TableName
= TableName
.Upper();
3075 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3079 retcode
= SQLTablePrivileges(hstmt
,
3082 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3086 retcode
= SQLTablePrivileges(hstmt
,
3088 (UCHAR FAR
*)schema
, SQL_NTS
, // Schema
3089 (UCHAR FAR
*)TableName
.c_str(), SQL_NTS
);
3092 #ifdef DBDEBUG_CONSOLE
3093 fprintf(stderr
,"SQLTablePrivileges() returned %i \n",retcode
);
3096 if ((retcode
!= SQL_SUCCESS
) && (retcode
!= SQL_SUCCESS_WITH_INFO
))
3097 return(DispAllErrors(henv
, hdbc
, hstmt
));
3099 retcode
= SQLFetch(hstmt
);
3100 while (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
3102 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) result
.tableQual
, sizeof(result
.tableQual
), &cbRetVal
) != SQL_SUCCESS
)
3103 return(DispAllErrors(henv
, hdbc
, hstmt
));
3105 if (SQLGetData(hstmt
, 2, SQL_C_CHAR
, (UCHAR
*) result
.tableOwner
, sizeof(result
.tableOwner
), &cbRetVal
) != SQL_SUCCESS
)
3106 return(DispAllErrors(henv
, hdbc
, hstmt
));
3108 if (SQLGetData(hstmt
, 3, SQL_C_CHAR
, (UCHAR
*) result
.tableName
, sizeof(result
.tableName
), &cbRetVal
) != SQL_SUCCESS
)
3109 return(DispAllErrors(henv
, hdbc
, hstmt
));
3111 if (SQLGetData(hstmt
, 4, SQL_C_CHAR
, (UCHAR
*) result
.grantor
, sizeof(result
.grantor
), &cbRetVal
) != SQL_SUCCESS
)
3112 return(DispAllErrors(henv
, hdbc
, hstmt
));
3114 if (SQLGetData(hstmt
, 5, SQL_C_CHAR
, (UCHAR
*) result
.grantee
, sizeof(result
.grantee
), &cbRetVal
) != SQL_SUCCESS
)
3115 return(DispAllErrors(henv
, hdbc
, hstmt
));
3117 if (SQLGetData(hstmt
, 6, SQL_C_CHAR
, (UCHAR
*) result
.privilege
, sizeof(result
.privilege
), &cbRetVal
) != SQL_SUCCESS
)
3118 return(DispAllErrors(henv
, hdbc
, hstmt
));
3120 if (SQLGetData(hstmt
, 7, SQL_C_CHAR
, (UCHAR
*) result
.grantable
, sizeof(result
.grantable
), &cbRetVal
) != SQL_SUCCESS
)
3121 return(DispAllErrors(henv
, hdbc
, hstmt
));
3123 #ifdef DBDEBUG_CONSOLE
3124 fprintf(stderr
,"Scanning %s privilege on table %s.%s granted by %s to %s\n",
3125 result
.privilege
,result
.tableOwner
,result
.tableName
,
3126 result
.grantor
, result
.grantee
);
3129 if (UserID
.IsSameAs(result
.tableOwner
,false))
3131 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3135 if (UserID
.IsSameAs(result
.grantee
,false) &&
3136 !wxStrcmp(result
.privilege
,priv
))
3138 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3142 if (!wxStrcmp(result
.grantee
,curRole
) &&
3143 !wxStrcmp(result
.privilege
,priv
))
3145 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3149 retcode
= SQLFetch(hstmt
);
3152 SQLFreeStmt(hstmt
, SQL_CLOSE
);
3155 } // wxDb::TablePrivileges
3158 /********** wxDb::SetSqlLogging() **********/
3159 bool wxDb::SetSqlLogging(wxDbSqlLogState state
, const char *filename
, bool append
)
3161 assert(state
== sqlLogON
|| state
== sqlLogOFF
);
3162 assert(state
== sqlLogOFF
|| filename
);
3164 if (state
== sqlLogON
)
3168 fpSqlLog
= fopen(filename
, (append
? "at" : "wt"));
3169 if (fpSqlLog
== NULL
)
3177 if (fclose(fpSqlLog
))
3183 sqlLogState
= state
;
3186 } // wxDb::SetSqlLogging()
3189 /********** wxDb::WriteSqlLog() **********/
3190 bool wxDb::WriteSqlLog(const wxChar
*logMsg
)
3194 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
3197 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
3198 if (fputs(logMsg
, fpSqlLog
) == EOF
) return(FALSE
);
3199 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
3203 } // wxDb::WriteSqlLog()
3206 /********** wxDb::Dbms() **********/
3207 wxDBMS
wxDb::Dbms(void)
3209 * Be aware that not all database engines use the exact same syntax, and not
3210 * every ODBC compliant database is compliant to the same level of compliancy.
3211 * Some manufacturers support the minimum Level 1 compliancy, and others up
3212 * through Level 3. Others support subsets of features for levels above 1.
3214 * If you find an inconsistency between the wxDb class and a specific database
3215 * engine, and an identifier to this section, and special handle the database in
3216 * the area where behavior is non-conforming with the other databases.
3219 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3220 * ---------------------------------------------------
3223 * - Currently the only database supported by the class to support VIEWS
3226 * - Does not support the SQL_TIMESTAMP structure
3227 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3228 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3229 * is TRUE. The user must create ALL indexes from their program.
3230 * - Table names can only be 8 characters long
3231 * - Column names can only be 10 characters long
3234 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3235 * after every table name involved in the query/join if that tables matching record(s)
3237 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3239 * SYBASE (Enterprise)
3240 * - If a column is part of the Primary Key, the column cannot be NULL
3241 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3244 * - If a column is part of the Primary Key, the column cannot be NULL
3245 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3246 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3247 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3248 * column definition if it is not defined correctly, but it is experimental
3249 * - Does not support sub-queries in SQL statements
3252 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3253 * - Does not support sub-queries in SQL statements
3256 * - Primary keys must be declared as NOT NULL
3260 // Should only need to do this once for each new database connection
3261 // so return the value we already determined it to be to save time
3262 // and lots of string comparisons
3263 if (dbmsType
!= dbmsUNIDENTIFIED
)
3266 wxChar baseName
[25+1];
3267 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
3270 // RGG 20001025 : add support for Interbase
3271 // GT : Integrated to base classes on 20001121
3272 if (!wxStricmp(dbInf
.dbmsName
,"Interbase"))
3273 return((wxDBMS
)(dbmsType
= dbmsINTERBASE
));
3275 // BJO 20000428 : add support for Virtuoso
3276 if (!wxStricmp(dbInf
.dbmsName
,"OpenLink Virtuoso VDBMS"))
3277 return((wxDBMS
)(dbmsType
= dbmsVIRTUOSO
));
3279 if (!wxStricmp(dbInf
.dbmsName
,"Adaptive Server Anywhere"))
3280 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASA
));
3282 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3283 // connected through an OpenLink driver.
3284 // Is it also returned by Sybase Adapatitve server?
3285 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3286 if (!wxStricmp(dbInf
.dbmsName
,"SQL Server"))
3288 if (!wxStrncmp(dbInf
.driverName
, "oplodbc", 7) ||
3289 !wxStrncmp(dbInf
.driverName
, "OLOD", 4))
3290 return ((wxDBMS
)(dbmsMS_SQL_SERVER
));
3292 return ((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3295 if (!wxStricmp(dbInf
.dbmsName
,"Microsoft SQL Server"))
3296 return((wxDBMS
)(dbmsType
= dbmsMS_SQL_SERVER
));
3297 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
3298 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3299 if (!wxStricmp(dbInf
.dbmsName
,"PostgreSQL")) // v6.5.0
3300 return((wxDBMS
)(dbmsType
= dbmsPOSTGRES
));
3303 if (!wxStricmp(baseName
,"Informix"))
3304 return((wxDBMS
)(dbmsType
= dbmsINFORMIX
));
3307 if (!wxStricmp(baseName
,"Oracle"))
3308 return((wxDBMS
)(dbmsType
= dbmsORACLE
));
3309 if (!wxStricmp(dbInf
.dbmsName
,"ACCESS"))
3310 return((wxDBMS
)(dbmsType
= dbmsACCESS
));
3311 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
3312 return((wxDBMS
)(dbmsType
= dbmsMY_SQL
));
3313 if (!wxStricmp(baseName
,"Sybase"))
3314 return((wxDBMS
)(dbmsType
= dbmsSYBASE_ASE
));
3317 if (!wxStricmp(baseName
,"DBASE"))
3318 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3321 if (!wxStricmp(baseName
,"DB2"))
3322 return((wxDBMS
)(dbmsType
= dbmsDBASE
));
3324 return((wxDBMS
)(dbmsType
= dbmsUNIDENTIFIED
));
3329 /********** wxDbGetConnection() **********/
3330 wxDb WXDLLEXPORT
*wxDbGetConnection(wxDbConnectInf
*pDbConfig
, bool FwdOnlyCursors
)
3334 // Used to keep a pointer to a DB connection that matches the requested
3335 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
3336 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
3337 // rather than having to re-query the datasource to get all the values
3338 // using the wxDb::Open(Dsn,Uid,AuthStr) function
3339 wxDb
*matchingDbConnection
= NULL
;
3341 // Scan the linked list searching for an available database connection
3342 // that's already been opened but is currently not in use.
3343 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3345 // The database connection must be for the same datasource
3346 // name and must currently not be in use.
3348 (pList
->PtrDb
->FwdOnlyCursors() == FwdOnlyCursors
) &&
3349 (!wxStrcmp(pDbConfig
->Dsn
, pList
->Dsn
))) // Found a free connection
3351 pList
->Free
= FALSE
;
3352 return(pList
->PtrDb
);
3355 if (!wxStrcmp(pDbConfig
->Dsn
, pList
->Dsn
) &&
3356 !wxStrcmp(pDbConfig
->Uid
, pList
->Uid
) &&
3357 !wxStrcmp(pDbConfig
->AuthStr
, pList
->AuthStr
))
3358 matchingDbConnection
= pList
->PtrDb
;
3361 // No available connections. A new connection must be made and
3362 // appended to the end of the linked list.
3365 // Find the end of the list
3366 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
3367 // Append a new list item
3368 pList
->PtrNext
= new wxDbList
;
3369 pList
->PtrNext
->PtrPrev
= pList
;
3370 pList
= pList
->PtrNext
;
3374 // Create the first node on the list
3375 pList
= PtrBegDbList
= new wxDbList
;
3379 // Initialize new node in the linked list
3381 pList
->Free
= FALSE
;
3382 wxStrcpy(pList
->Dsn
, pDbConfig
->Dsn
);
3383 wxStrcpy(pList
->Uid
, pDbConfig
->Uid
);
3384 wxStrcpy(pList
->AuthStr
, pDbConfig
->AuthStr
);
3386 pList
->PtrDb
= new wxDb(pDbConfig
->Henv
,FwdOnlyCursors
);
3388 bool opened
= FALSE
;
3390 if (!matchingDbConnection
)
3391 opened
= pList
->PtrDb
->Open(pDbConfig
->Dsn
, pDbConfig
->Uid
, pDbConfig
->AuthStr
);
3393 opened
= pList
->PtrDb
->Open(matchingDbConnection
);
3395 // Connect to the datasource
3398 pList
->PtrDb
->SetSqlLogging(SQLLOGstate
,SQLLOGfn
,TRUE
);
3399 return(pList
->PtrDb
);
3401 else // Unable to connect, destroy list item
3404 pList
->PtrPrev
->PtrNext
= 0;
3406 PtrBegDbList
= 0; // Empty list again
3407 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3408 pList
->PtrDb
->Close(); // Close the wxDb object
3409 delete pList
->PtrDb
; // Deletes the wxDb object
3410 delete pList
; // Deletes the linked list object
3414 } // wxDbGetConnection()
3417 /********** wxDbFreeConnection() **********/
3418 bool WXDLLEXPORT
wxDbFreeConnection(wxDb
*pDb
)
3422 // Scan the linked list searching for the database connection
3423 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3425 if (pList
->PtrDb
== pDb
) // Found it, now free it!!!
3426 return (pList
->Free
= TRUE
);
3429 // Never found the database object, return failure
3432 } // wxDbFreeConnection()
3435 /********** wxDbCloseConnections() **********/
3436 void WXDLLEXPORT
wxDbCloseConnections(void)
3438 wxDbList
*pList
, *pNext
;
3440 // Traverse the linked list closing database connections and freeing memory as I go.
3441 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
3443 pNext
= pList
->PtrNext
; // Save the pointer to next
3444 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDb object
3445 pList
->PtrDb
->Close(); // Close the wxDb object
3446 delete pList
->PtrDb
; // Deletes the wxDb object
3447 delete pList
; // Deletes the linked list object
3450 // Mark the list as empty
3453 } // wxDbCloseConnections()
3456 /********** wxDbConnectionsInUse() **********/
3457 int WXDLLEXPORT
wxDbConnectionsInUse(void)
3462 // Scan the linked list counting db connections that are currently in use
3463 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3465 if (pList
->Free
== FALSE
)
3471 } // wxDbConnectionsInUse()
3474 /********** wxDbSqlLog() **********/
3475 bool wxDbSqlLog(wxDbSqlLogState state
, const wxChar
*filename
)
3477 bool append
= FALSE
;
3480 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
3482 if (!pList
->PtrDb
->SetSqlLogging(state
,filename
,append
))
3487 SQLLOGstate
= state
;
3488 SQLLOGfn
= filename
;
3496 /********** wxDbCreateDataSource() **********/
3497 int wxDbCreateDataSource(const char *driverName
, const char *dsn
, const char *description
,
3498 bool sysDSN
, const char *defDir
, wxWindow
*parent
)
3500 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3501 * Very rudimentary creation of an ODBC data source.
3503 * ODBC driver must be ODBC 3.0 compliant to use this function
3508 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3514 dsnLocation
= ODBC_ADD_SYS_DSN
;
3516 dsnLocation
= ODBC_ADD_DSN
;
3518 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3519 // so that is why I used it, as wxString does not deal well with
3520 // embedded nulls in strings
3521 setupStr
.sprintf("DSN=%s%cDescription=%s%cDefaultDir=%s%c",dsn
,2,description
,2,defDir
,2);
3523 // Replace the separator from above with the '\0' seperator needed
3524 // by the SQLConfigDataSource() function
3528 k
= setupStr
.Find((wxChar
)2,TRUE
);
3529 if (k
!= wxNOT_FOUND
)
3530 setupStr
[(UINT
)k
] = '\0';
3532 while (k
!= wxNOT_FOUND
);
3534 result
= SQLConfigDataSource((HWND
)parent
->GetHWND(), dsnLocation
,
3535 driverName
, setupStr
.c_str());
3537 if ((result
!= SQL_SUCCESS
) && (result
!= SQL_SUCCESS_WITH_INFO
))
3539 // check for errors caused by ConfigDSN based functions
3542 wxChar errMsg
[SQL_MAX_MESSAGE_LENGTH
];
3545 // This function is only supported in ODBC drivers v3.0 compliant and above
3546 SQLInstallerError(1,&retcode
,errMsg
,SQL_MAX_MESSAGE_LENGTH
-1,&cb
);
3549 #ifdef DBDEBUG_CONSOLE
3550 // When run in console mode, use standard out to display errors.
3551 cout
<< errMsg
<< endl
;
3552 cout
<< wxT("Press any key to continue...") << endl
;
3554 #endif // DBDEBUG_CONSOLE
3557 wxLogDebug(errMsg
,wxT("ODBC DEBUG MESSAGE"));
3558 #endif // __WXDEBUG__
3564 // Using iODBC/unixODBC or some other compiler which does not support the APIs
3565 // necessary to use this function, so this function is not supported
3567 wxLogDebug("wxDbCreateDataSource() not available except under VC++/MSW",wxT("ODBC DEBUG MESSAGE"));
3570 #endif // __VISUALC__
3574 } // wxDbCreateDataSource()
3578 /********** wxDbGetDataSource() **********/
3579 bool wxDbGetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3582 * Dsn and DsDesc will contain the data source name and data source
3583 * description upon return
3588 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
3589 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
3594 } // wxDbGetDataSource()
3597 // Change this to 0 to remove use of all deprecated functions
3598 #if wxODBC_BACKWARD_COMPATABILITY
3599 /********************************************************************
3600 ********************************************************************
3602 * The following functions are all DEPRECATED and are included for
3603 * backward compatability reasons only
3605 ********************************************************************
3606 ********************************************************************/
3607 bool SqlLog(sqlLog state
, const wxChar
*filename
)
3609 return wxDbSqlLog((enum wxDbSqlLogState
)state
, filename
);
3611 /***** DEPRECATED: use wxGetDataSource() *****/
3612 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
3615 return wxDbGetDataSource(henv
, Dsn
, DsnMax
, DsDesc
, DsDescMax
, direction
);
3617 /***** DEPRECATED: use wxDbGetConnection() *****/
3618 wxDb WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
3620 return wxDbGetConnection((wxDbConnectInf
*)pDbStuff
, FwdOnlyCursors
);
3622 /***** DEPRECATED: use wxDbFreeConnection() *****/
3623 bool WXDLLEXPORT
FreeDbConnection(wxDb
*pDb
)
3625 return wxDbFreeConnection(pDb
);
3627 /***** DEPRECATED: use wxDbCloseConnections() *****/
3628 void WXDLLEXPORT
CloseDbConnections(void)
3630 wxDbCloseConnections();
3632 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3633 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
3635 return wxDbConnectionsInUse();